refactor(ContactsColumn): replace `ChannelList` with `StatusChatList`

Closes #2745
This commit is contained in:
Pascal Precht 2021-06-30 16:17:05 +02:00 committed by Iuri Matias
parent eb7809ecdf
commit 23b103795a
6 changed files with 317 additions and 87 deletions

View File

@ -19,6 +19,7 @@ QtObject:
activeChannel*: ChatItemView
previousActiveChannelIndex*: int
contextChannel*: ChatItemView
chatItemViews: Table[string, ChatItemView]
proc setup(self: ChannelView) = self.QObject.setup
proc delete*(self: ChannelView) =
@ -84,12 +85,21 @@ QtObject:
proc contextChannelChanged*(self: ChannelView) {.signal.}
# TODO(pascal): replace with `markChatItemAsRead`, which is id based
# instead of index based, when refactoring/removing `CommunityColumn` and `ChannelContextMenu`
# (they still make use of this)
proc markAllChannelMessagesReadByIndex*(self: ChannelView, channelIndex: int) {.slot.} =
if (self.chats.chats.len == 0): return
let selectedChannel = self.getChannel(channelIndex)
if (selectedChannel == nil): return
discard self.status.chat.markAllChannelMessagesRead(selectedChannel.id)
proc markChatItemAsRead*(self: ChannelView, id: string) {.slot.} =
if (self.chats.chats.len == 0): return
let selectedChannel = self.getChannelById(id)
if (selectedChannel == nil): return
discard self.status.chat.markAllChannelMessagesRead(selectedChannel.id)
proc clearUnreadIfNeeded*(self: ChannelView, channel: var Chat) =
if (not channel.isNil and (channel.unviewedMessagesCount > 0 or channel.mentionsCount > 0)):
var response = self.status.chat.markAllChannelMessagesRead(channel.id)
@ -188,6 +198,9 @@ QtObject:
self.setActiveChannel(pubKey)
ChatType.OneToOne.int
# TODO(pascal): replace with `leaveChat`, which is id based
# instead of index based, when refactoring/removing `CommunityColumn` and `ChannelContextMenu`
# (they still make use of this)
proc leaveChatByIndex*(self: ChannelView, channelIndex: int) {.slot.} =
if (self.chats.chats.len == 0): return
let selectedChannel = self.getChannel(channelIndex)
@ -196,6 +209,14 @@ QtObject:
self.activeChannel.chatItem = nil
self.status.chat.leave(selectedChannel.id)
proc leaveChat*(self: ChannelView, id: string) {.slot.} =
if (self.chats.chats.len == 0): return
let selectedChannel = self.getChannelById(id)
if (selectedChannel == nil): return
if (self.activeChannel.id == selectedChannel.id):
self.activeChannel.chatItem = nil
self.status.chat.leave(selectedChannel.id)
proc leaveActiveChat*(self: ChannelView) {.slot.} =
self.status.chat.leave(self.activeChannel.id)
@ -220,6 +241,9 @@ QtObject:
channel.muted = false
self.updateChannelInRightList(channel)
# TODO(pascal): replace with `muteChatItem`, which is id based
# instead of index based, when refactoring/removing `CommunityColumn` and `ChannelContextMenu`
# (they still make use of this)
proc muteChannel*(self: ChannelView, channelIndex: int) {.slot.} =
if (self.chats.chats.len == 0): return
let selectedChannel = self.getChannel(channelIndex)
@ -231,6 +255,20 @@ QtObject:
self.status.chat.muteChat(selectedChannel)
self.updateChannelInRightList(selectedChannel)
proc muteChatItem*(self: ChannelView, id: string) {.slot.} =
if (self.chats.chats.len == 0): return
let selectedChannel = self.getChannelById(id)
if (selectedChannel == nil): return
if (selectedChannel.id == self.activeChannel.id):
self.muteCurrentChannel()
return
selectedChannel.muted = true
self.status.chat.muteChat(selectedChannel)
self.updateChannelInRightList(selectedChannel)
# TODO(pascal): replace with `unmuteChatItem`, which is id based
# instead of index based, when refactoring/removing `CommunityColumn` and `ChannelContextMenu`
# (they still make use of this)
proc unmuteChannel*(self: ChannelView, channelIndex: int) {.slot.} =
if (self.chats.chats.len == 0): return
let selectedChannel = self.getChannel(channelIndex)
@ -242,8 +280,27 @@ QtObject:
self.status.chat.unmuteChat(selectedChannel)
self.updateChannelInRightList(selectedChannel)
proc unmuteChatItem*(self: ChannelView, id: string) {.slot.} =
if (self.chats.chats.len == 0): return
let selectedChannel = self.getChannelById(id)
if (selectedChannel == nil): return
if (selectedChannel.id == self.activeChannel.id):
self.unmuteCurrentChannel()
return
selectedChannel.muted = false
self.status.chat.unmuteChat(selectedChannel)
self.updateChannelInRightList(selectedChannel)
proc channelIsMuted*(self: ChannelView, channelIndex: int): bool {.slot.} =
if (self.chats.chats.len == 0): return false
let selectedChannel = self.getChannel(channelIndex)
if (selectedChannel == nil): return false
result = selectedChannel.muted
proc getChatItemById*(self: ChannelView, id: string): QObject {.slot.} =
if self.chatItemViews.hasKey(id): return self.chatItemViews[id]
let chat = self.getChannelById(id)
let chatItemView = newChatItemView(self.status)
chatItemView.setChatItem(chat)
self.chatItemViews[id] = chatItemView
return chatItemView

@ -1 +1 @@
Subproject commit fd7a5530cf6c4395eec616dbf4622266793cd2b3
Subproject commit 7136968b3ad50d6d222849d53c793ecf82566786

View File

@ -183,6 +183,15 @@ StackLayout {
}
}
Connections {
target: chatsModel.channelView
onActiveChannelChanged: {
chatsModel.messageView.hideLoadingIndicator()
SelectedMessage.reset();
chatColumn.isReply = false;
}
}
function clickOnNotification() {
applicationWindow.show()
applicationWindow.raise()

View File

@ -10,23 +10,233 @@ import "./components"
import "./ContactsColumn"
import "./CommunityComponents"
Rectangle {
property alias chatGroupsListViewCount: channelList.channelListCount
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Popups 0.1
Item {
property int chatGroupsListViewCount: channelList.chatListItems.count
property alias searchStr: searchBox.text
id: contactsColumn
Layout.fillHeight: true
color: Style.current.secondaryMenuBackground
StyledText {
id: title
//% "Chat"
text: qsTrId("chat")
Layout.fillHeight: true
width: 304
StatusNavigationPanelHeadline {
id: headline
anchors.top: parent.top
anchors.topMargin: Style.current.padding
anchors.topMargin: 16
anchors.horizontalCenter: parent.horizontalCenter
font.weight: Font.Bold
font.pixelSize: 17
text: qsTr("Chat")
}
SearchBox {
id: searchBox
anchors.top: headline.bottom
anchors.topMargin: Style.current.padding
anchors.right: addChat.left
anchors.rightMargin: Style.current.padding
anchors.left: parent.left
anchors.leftMargin: Style.current.padding
}
AddChat {
id: addChat
anchors.right: parent.right
anchors.rightMargin: Style.current.padding
anchors.top: headline.bottom
anchors.topMargin: Style.current.padding
}
StatusContactRequestsIndicatorListItem {
id: contactRequests
property int nbRequests: profileModel.contacts.contactRequests.count
anchors.top: searchBox.bottom
anchors.topMargin: visible ? Style.current.padding : 0
anchors.horizontalCenter: parent.horizontalCenter
visible: nbRequests > 0
height: visible ? implicitHeight : 0
title: qsTr("Contact requests")
requestsCount: nbRequests
sensor.onClicked: openPopup(contactRequestsPopup)
}
ScrollView {
id: chatGroupsContainer
anchors.top: contactRequests.bottom
anchors.topMargin: Style.current.padding
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width
leftPadding: Style.current.halfPadding
rightPadding: Style.current.halfPadding
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
contentHeight: channelList.height + 2 * Style.current.padding + emptyViewAndSuggestions.height + emptyViewAndSuggestions.anchors.topMargin
clip: true
Item {
id: noSearchResults
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width
visible: !!!channelList.height && contactsColumn.searchStr !== ""
height: visible ? 300 : 0
StatusBaseText {
font.pixelSize: 15
color: Theme.palette.directColor5
anchors.centerIn: parent
text: qsTr("No search results")
}
}
StatusChatList {
id: channelList
chatNameFn: function (chatItem) {
return chatItem.chatType !== Constants.chatTypePublic ?
Emoji.parse(Utils.removeStatusEns(Utils.filterXSS(chatItem.name))) :
Utils.filterXSS(chatItem.name)
}
profileImageFn: function (id) {
return appMain.getProfileImage(id)
}
filterFn: function (chatListItem) {
return !!!contactsColumn.searchStr || chatListItem.name.toLowerCase().includes(contactsColumn.searchStr.toLowerCase())
}
Connections {
target: profileModel.contacts.list
onContactChanged: {
for (var i = 0; i < channelList.chatListItems.count; i++) {
let chatItem = channelList.chatListItems.itemAt(i);
if (chatItem.chatId === pubkey) {
let profileImage = appMain.getProfileImage(pubkey)
if (!!profileImage) {
chatItem.image.isIdenticon = false
chatItem.image.source = profileImage
}
break;
}
}
}
}
chatListItems.model: chatsModel.channelView.chats
selectedChatId: chatsModel.channelView.activeChannel.id
onChatItemSelected: chatsModel.channelView.setActiveChannel(id)
onChatItemUnmuted: chatsModel.channelView.unmuteChatItem(id)
popupMenu: StatusPopupMenu {
id: chatListContextMenu
property var chatItem
openHandler: function (id) {
chatItem = chatsModel.channelView.getChatItemById(id)
}
StatusMenuItem {
id: viewProfileMenuItem
text: {
if (chatItem) {
switch (chatItem.chatType) {
case Constants.chatTypeOneToOne:
return qsTr("View Profile")
break;
case Constants.chatTypePrivateGroupChat:
return qsTr("View Group")
break;
default:
return qsTr("Share Chat")
break;
}
}
return ""
}
icon.name: "group-chat"
enabled: chatItem && chatItem.chatType !== Constants.chatTypePublic
onTriggered: {
if (chatItem.chatType === Constants.chatTypeOneToOne) {
const userProfileImage = appMain.getProfileImage(chatItem.id)
return openProfilePopup(
chatItem.name,
chatItem.id,
userProfileImage || chatItem.identicon
)
}
if (chatItem.chatType === Constants.chatTypePrivateGroupChat) {
return openPopup(groupInfoPopupComponent, {channelType: GroupInfoPopup.ChannelType.ContextChannel})
}
}
}
StatusMenuSeparator {
visible: viewProfileMenuItem.enabled
}
StatusMenuItem {
text: chatItem && chatItem.muted ?
qsTr("Unmute chat") :
qsTr("Mute chat")
icon.name: "notification"
onTriggered: {
if (chatItem && chatItem.muted) {
return chatsModel.channelView.unmuteChatItem(chatItem.id)
}
chatsModel.channelView.muteChatItem(chatItem.id)
}
}
StatusMenuItem {
text: "Mark as Read"
icon.name: "checkmark-circle"
onTriggered: chatsModel.channelView.markChatItemAsRead(chatItem.id)
}
StatusMenuItem {
text: "Clear history"
icon.name: "close-circle"
onTriggered: chatsModel.channelView.clearChatHistory(chatItem.id)
}
StatusMenuSeparator {}
StatusMenuItem {
text: chatItem && chatItem.chatType === Constants.chatTypeOneToOne ? "Delete chat" : "Leave chat"
icon.name: chatItem && chatItem.chatType === Constants.chatTypeOneToOne ? "delete" : "arrow-right"
icon.width: chatItem && chatItem.chatType === Constants.chatTypeOneToOne ? 18 : 14
iconRotation: chatItem && chatItem.chatType === Constants.chatTypeOneToOne ? 0 : 180
type: StatusMenuItem.Type.Danger
onTriggered: openPopup(deleteChatConfirmationDialogComponent, { chatId: chatItem.id })
}
}
}
EmptyView {
id: emptyViewAndSuggestions
width: parent.width
visible: !appSettings.hideChannelSuggestions && !noSearchResults.visible
anchors.top: noSearchResults.visible ? noSearchResults.bottom : channelList.bottom
anchors.topMargin: 32
}
}
Component {
@ -101,84 +311,22 @@ Rectangle {
}
}
SearchBox {
id: searchBox
anchors.top: title.bottom
anchors.topMargin: Style.current.padding
anchors.right: addChat.left
anchors.rightMargin: Style.current.padding
anchors.left: parent.left
anchors.leftMargin: Style.current.padding
}
AddChat {
id: addChat
anchors.right: parent.right
anchors.rightMargin: Style.current.padding
anchors.top: title.bottom
anchors.topMargin: Style.current.padding
}
Connections {
target: profileModel.contacts
onContactRequestAdded: {
if (!appSettings.notifyOnNewRequests) {
return
Component {
id: deleteChatConfirmationDialogComponent
ConfirmationDialog {
property string chatId
btnType: "warn"
confirmationText: qsTr("Are you sure you want to leave this chat?")
onClosed: {
destroy()
}
onConfirmButtonClicked: {
chatsModel.channelView.leaveChat(chatId)
close();
}
const isContact = profileModel.contacts.isAdded(address)
systemTray.showMessage(isContact ? qsTr("Contact request accepted") :
qsTr("New contact request"),
isContact ? qsTr("You can now chat with %1").arg(Utils.removeStatusEns(name)) :
qsTr("%1 requests to become contacts").arg(Utils.removeStatusEns(name)),
SystemTrayIcon.NoIcon,
Constants.notificationPopupTTL)
}
}
StatusSettingsLineButton {
property int nbRequests: profileModel.contacts.contactRequests.count
id: contactRequest
anchors.top: searchBox.bottom
anchors.topMargin: visible ? Style.current.padding : 0
anchors.left: parent.left
anchors.leftMargin: Style.current.halfPadding
anchors.right: parent.right
anchors.rightMargin: Style.current.halfPadding
visible: nbRequests > 0
height: visible ? implicitHeight : 0
text: qsTr("Contact requests")
isBadge: true
badgeText: nbRequests.toString()
onClicked: openPopup(contactRequestsPopup)
}
ScrollView {
id: chatGroupsContainer
anchors.top: contactRequest.bottom
anchors.topMargin: Style.current.padding
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
leftPadding: Style.current.halfPadding
rightPadding: Style.current.halfPadding
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
contentHeight: channelList.height + 2 * Style.current.padding + emptyViewAndSuggestions.height
clip: true
ChannelList {
id: channelList
searchStr: contactsColumn.searchStr.toLowerCase()
channelModel: chatsModel.channelView.chats
}
EmptyView {
id: emptyViewAndSuggestions
width: parent.width
anchors.top: channelList.bottom
anchors.topMargin: Style.current.smallPadding
}
}
}
/*##^##

View File

@ -12,7 +12,6 @@ Rectangle {
id: emptyView
Layout.fillHeight: true
Layout.fillWidth: true
visible: !appSettings.hideChannelSuggestions
height: suggestionContainer.height + inviteFriendsContainer.height + Style.current.padding * 2
border.color: Style.current.secondaryMenuBorder

View File

@ -11,6 +11,7 @@ import "./AppLayouts/Timeline"
import "./AppLayouts/Wallet"
import "./AppLayouts/Chat/components"
import "./AppLayouts/Chat/CommunityComponents"
import Qt.labs.platform 1.1
import Qt.labs.settings 1.0
import StatusQ.Core.Theme 0.1
@ -458,6 +459,22 @@ StatusAppLayout {
}
}
Connections {
target: profileModel.contacts
onContactRequestAdded: {
if (!appSettings.notifyOnNewRequests) {
return
}
const isContact = profileModel.contacts.isAdded(address)
systemTray.showMessage(isContact ? qsTr("Contact request accepted") :
qsTr("New contact request"),
isContact ? qsTr("You can now chat with %1").arg(Utils.removeStatusEns(name)) :
qsTr("%1 requests to become contacts").arg(Utils.removeStatusEns(name)),
SystemTrayIcon.NoIcon,
Constants.notificationPopupTTL)
}
}
Component {
id: chooseBrowserPopupComponent
ChooseBrowserPopup {