feat(@desktop/chat): Handle group chat colors changes

Use new status-go function to update group chat details.
Changes in RenameGroupPopup to handle choosing colors and images.
Changes in EditCroppedImagePanel to handle background component.

Issue #5982
This commit is contained in:
Michal Iskierko 2022-07-12 10:27:35 +02:00 committed by Michał Iskierko
parent 36acf3bc30
commit 8b09eec506
18 changed files with 268 additions and 37 deletions

View File

@ -165,6 +165,12 @@ proc init*(self: Controller) =
return
self.delegate.onChatRenamed(args.newName)
self.events.on(SIGNAL_GROUP_CHAT_DETAILS_UPDATED) do(e: Args):
var args = ChatUpdateDetailsArgs(e)
if(self.chatId != args.id):
return
self.delegate.onGroupChatDetailsUpdated(args.newName, args.newColor, args.newImage)
self.events.on(SIGNAL_CHAT_UPDATE) do(e: Args):
var args = ChatUpdateArgs(e)
for chat in args.chats:

View File

@ -55,6 +55,9 @@ method onChatEdited*(self: AccessInterface, chatDto: ChatDto) {.base.} =
method onChatRenamed*(self: AccessInterface, newName: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onGroupChatDetailsUpdated*(self: AccessInterface, newName: string, newColor: string, newImage: string) {.base.} =
raise newException(ValueError, "No implementation available")
method inputAreaDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -343,6 +343,10 @@ method onChatRenamed*(self: Module, newName: string) =
self.view.updateChatDetailsName(newName)
self.messagesModule.updateChatIdentifier()
method onGroupChatDetailsUpdated*(self: Module, newName: string, newColor: string, newImage: string) =
self.view.updateChatDetailsNameColorIcon(newName, newColor, newImage)
self.messagesModule.updateChatIdentifier()
method downloadMessages*(self: Module, filePath: string) =
let messages = self.messagesModule.getMessages()
self.controller.downloadMessages(messages, filePath)

View File

@ -99,6 +99,10 @@ QtObject:
self.chatDetails.setName(name)
self.chatDetails.setIcon(icon)
proc updateChatDetailsNameColorIcon*(self: View, name, color, icon: string) =
self.updateChatDetailsNameAndIcon(name, icon)
self.chatDetails.setColor(color)
proc updateTrustStatus*(self: View, isUntrustworthy: bool) =
self.chatDetails.setIsUntrustworthy(isUntrustworthy)

View File

@ -206,6 +206,10 @@ proc init*(self: Controller) =
var args = ChatRenameArgs(e)
self.delegate.onChatRenamed(args.id, args.newName)
self.events.on(SIGNAL_GROUP_CHAT_DETAILS_UPDATED) do(e: Args):
var args = ChatUpdateDetailsArgs(e)
self.delegate.onGroupChatDetailsUpdated(args.id, args.newName, args.newColor, args.newImage)
self.events.on(SIGNAL_MAKE_SECTION_CHAT_ACTIVE) do(e: Args):
var args = ActiveSectionChatArgs(e)
if (self.sectionId != args.sectionId):
@ -334,6 +338,10 @@ proc renameGroupChat*(self: Controller, chatId: string, newName: string) =
let communityId = if self.isCommunitySection: self.sectionId else: ""
self.chatService.renameGroupChat(communityId, chatId, newName)
proc updateGroupChatDetails*(self: Controller, chatId: string, newGroupName: string, newGroupColor: string, newGroupImage: string) =
let communityId = if self.isCommunitySection: self.sectionId else: ""
self.chatService.updateGroupChatDetails(communityId, chatId, newGroupName, newGroupColor, newGroupImage)
proc makeAdmin*(self: Controller, communityID: string, chatId: string, pubKey: string) =
self.chatService.makeAdmin(communityID, chatId, pubKey)

View File

@ -115,6 +115,9 @@ method onCommunityChannelDeletedOrChatLeft*(self: AccessInterface, chatId: strin
method onChatRenamed*(self: AccessInterface, chatId: string, newName: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onGroupChatDetailsUpdated*(self: AccessInterface, chatId: string, newName: string, newColor: string, newImage: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onCommunityChannelEdited*(self: AccessInterface, chat: ChatDto) {.base.} =
raise newException(ValueError, "No implementation available")
@ -223,6 +226,9 @@ method removeMembersFromGroupChat*(self: AccessInterface, communityID: string, c
method renameGroupChat*(self: AccessInterface, chatId: string, newName: string) {.base.} =
raise newException(ValueError, "No implementation available")
method updateGroupChatDetails*(self: AccessInterface, chatId: string, newGroupName: string, newGroupColor: string, newGroupImage: string) {.base.} =
raise newException(ValueError, "No implementation available")
method makeAdmin*(self: AccessInterface, communityID: string, chatId: string, pubKey: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -276,6 +276,18 @@ QtObject:
self.dataChanged(index, index, @[ModelRole.Name.int])
return
proc updateNameColorIcon*(self: Model, id, name, color, icon: string) =
## This updates only first level items, it doesn't update subitems, since subitems cannot have custom icon.
for i in 0 ..< self.items.len:
if(self.items[i].id == id):
self.items[i].BaseItem.name = name
self.items[i].BaseItem.color = color
self.items[i].BaseItem.icon = icon
let index = self.createIndex(i, 0, nil)
self.dataChanged(index, index,
@[ModelRole.Name.int, ModelRole.Color.int, ModelRole.Icon.int])
return
proc updateItemDetails*(self: Model, id, name, description, emoji, color: string) =
## This updates only first level items, it doesn't update subitems, since subitems cannot have custom icon.
for i in 0 ..< self.items.len:

View File

@ -147,7 +147,12 @@ proc buildChatSectionUI(
colorHash = self.controller.getColorHash(chatDto.id)
colorId = self.controller.getColorId(chatDto.id)
let amIChatAdmin = (self.amIMarkedAsAdminUser(chatDto.members) or channelGroup.admin)
# for group chats only member.admin should be checked,
# because channelGroup.admin is alway true
var amIChatAdmin = self.amIMarkedAsAdminUser(chatDto.members)
if(chatDto.chatType != ChatType.PrivateGroupChat):
amIChatAdmin = amIChatAdmin or channelGroup.admin
let channelItem = initItem(chatDto.id, chatName, chatImage, chatDto.color,
chatDto.emoji, chatDto.description, chatDto.chatType.int, amIChatAdmin, hasNotification,
notificationsCount, chatDto.muted, blocked, chatDto.active, chatDto.position,
@ -715,6 +720,9 @@ method removeMembersFromGroupChat*(self: Module, communityID: string, chatId: st
method renameGroupChat*(self: Module, chatId: string, newName: string) =
self.controller.renameGroupChat(chatId, newName)
method updateGroupChatDetails*(self: Module, chatId: string, newGroupName: string, newGroupColor: string, newGroupImage: string) =
self.controller.updateGroupChatDetails(chatId, newGroupName, newGroupColor, newGroupImage)
method makeAdmin*(self: Module, communityID: string, chatId: string, pubKey: string) =
self.controller.makeAdmin(communityID, chatId, pubKey)
@ -730,6 +738,9 @@ method joinGroupChatFromInvitation*(self: Module, groupName: string, chatId: str
method onChatRenamed*(self: Module, chatId: string, newName: string) =
self.view.chatsModel().renameItem(chatId, newName)
method onGroupChatDetailsUpdated*(self: Module, chatId, newName, newColor, newImage: string) =
self.view.chatsModel().updateNameColorIcon(chatId, newName, newColor, newImage)
method acceptRequestToJoinCommunity*(self: Module, requestId: string) =
self.controller.acceptRequestToJoinCommunity(requestId)
@ -829,7 +840,9 @@ method addChatIfDontExist*(self: Module,
return
if self.doesCatOrChatExist(chat.id):
if(chat.chatType != ChatType.OneToOne):
if (chat.chatType == ChatType.PrivateGroupChat):
self.onGroupChatDetailsUpdated(chat.id, chat.name, chat.color, chat.icon)
elif (chat.chatType != ChatType.OneToOne):
self.onChatRenamed(chat.id, chat.name)
return
self.addNewChat(chat, belongsToCommunity, events, settingsService, contactService, chatService,

View File

@ -202,6 +202,9 @@ QtObject:
proc renameGroupChat*(self: View, chatId: string, newName: string) {.slot.} =
self.delegate.renameGroupChat(chatId, newName)
proc updateGroupChatDetails(self: View, chatId: string, newGroupName: string, newGroupColor: string, newGroupImage: string) {.slot.} =
self.delegate.updateGroupChatDetails(chatId, newGroupName, newGroupColor, newGroupImage)
proc makeAdmin*(self: View, communityID: string, chatId: string, pubKey: string) {.slot.} =
self.delegate.makeAdmin(communityID, chatId, pubKey)

View File

@ -58,6 +58,12 @@ type
id*: string
newName*: string
ChatUpdateDetailsArgs* = ref object of Args
id*: string
newName*: string
newColor*: string
newImage*: string
ChatMembersAddedArgs* = ref object of Args
chatId*: string
ids*: seq[string]
@ -83,6 +89,7 @@ const SIGNAL_CHAT_MUTED* = "chatMuted"
const SIGNAL_CHAT_UNMUTED* = "chatUnmuted"
const SIGNAL_CHAT_HISTORY_CLEARED* = "chatHistoryCleared"
const SIGNAL_CHAT_RENAMED* = "chatRenamed"
const SIGNAL_GROUP_CHAT_DETAILS_UPDATED* = "groupChatDetailsUpdated"
const SIGNAL_CHAT_MEMBERS_ADDED* = "chatMemberAdded"
const SIGNAL_CHAT_MEMBER_REMOVED* = "chatMemberRemoved"
const SIGNAL_CHAT_MEMBER_UPDATED* = "chatMemberUpdated"
@ -490,6 +497,23 @@ QtObject:
except Exception as e:
error "error while renaming group chat: ", msg = e.msg
proc updateGroupChatDetails*(self: Service, communityID: string, chatID: string, name: string, color: string, image: string) =
try:
let response = status_chat.editChat(communityID, chatID, name, color, image)
if (not response.error.isNil):
let msg = response.error.message & " chatId=" & chatId
error "error while editing group chat details", msg
return
var chat = self.chats[chatID]
chat.name = name
chat.color = color
# TODO set image
self.updateOrAddChat(chat)
self.events.emit(SIGNAL_GROUP_CHAT_DETAILS_UPDATED, ChatUpdateDetailsArgs(id: chatID, newName: name, newColor: color, newImage: image))
except Exception as e:
error "error while updating group chat: ", msg = e.msg
proc makeAdmin*(self: Service, communityID: string, chatID: string, memberId: string) =
try:

View File

@ -133,3 +133,7 @@ proc getLinkPreviewData*(link: string): RpcResponse[JsonNode] {.raises: [Excepti
proc getMembers*(communityId, chatId: string): RpcResponse[JsonNode] {.raises: [Exception].} =
result = callPrivateRPC("chat_getMembers", %* [communityId, chatId])
proc editChat*(communityID: string, chatID: string, name: string, color: string, image: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [communityID, chatID, name, color, image]
return core.callPrivateRPC("chat_editChat", payload)

View File

@ -280,9 +280,10 @@ StatusModal {
RenameGroupPopup {
id: renameGroupPopup
activeChannelName: popup.chatDetails ? popup.chatDetails.name : ""
onDoRename: {
popup.chatSectionModule.renameGroupChat(popup.chatSectionModule.activeItem.id, groupName)
activeGroupName: popup.chatDetails ? popup.chatDetails.name : ""
activeGroupColor: popup.chatDetails ? popup.chatDetails.color: ""
onUpdateGroupChatDetails: {
popup.chatSectionModule.updateGroupChatDetails(popup.chatSectionModule.activeItem.id, groupName, groupColor, groupImage)
popup.header.title = groupName
close()
}

View File

@ -1,37 +1,152 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13
import QtQml.Models 2.14
import utils 1.0
import shared.controls 1.0
import shared.panels 1.0
import utils 1.0
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Core 0.1
import StatusQ.Popups 0.1
import StatusQ.Popups.Dialog 0.1
StatusDialog {
id: root
property string activeChannelName
signal doRename(string groupName)
property string activeGroupImageData
property string activeGroupColor
property string activeGroupName
width: 400
title: qsTr("Group name")
standardButtons: Dialog.Save
signal updateGroupChatDetails(string groupName, string groupColor, string groupImage)
anchors.centerIn: parent
title: qsTr("Edit group name and image")
width: 480
height: 610
QtObject {
id: d
readonly property int nameCharLimit: 24
}
onOpened: {
groupName.forceActiveFocus(Qt.MouseFocusReason)
groupName.text = root.activeChannelName
groupName.text = root.activeGroupName.substring(0, d.nameCharLimit)
colorSelectionGrid.selectedColor = activeGroupColor
for (let i = 0; i < colorSelectionGrid.model.length; i++) {
if(colorSelectionGrid.model[i] === root.activeGroupColor.toUpperCase())
colorSelectionGrid.selectedColorIndex = i
}
onAccepted: root.doRename(groupName.text)
// TODO next phase
// imageEditor.dataImage: root.activeGroupImageData
Input {
id: groupName
}
ColumnLayout {
anchors.fill: parent
spacing: 20
placeholderText: qsTr("Group name")
Keys.onEnterPressed: doRename(groupName.text)
Keys.onReturnPressed: doRename(groupName.text)
StatusInput {
id: groupName
Layout.alignment: Qt.AlignHCenter
label: qsTr("Name the group")
charLimit: d.nameCharLimit
}
StatusBaseText {
id: imgText
text: qsTr("Group image")
leftPadding: groupName.leftPadding - root.padding
font.pixelSize: 15
}
EditCroppedImagePanel {
id: imageEditor
Layout.preferredWidth: 128
Layout.preferredHeight: Layout.preferredWidth / aspectRatio
Layout.alignment: Qt.AlignHCenter
imageFileDialogTitle: qsTr("Choose an image as logo")
title: qsTr("Edit group name and image")
acceptButtonText: qsTr("Use as an icon for this group chat")
backgroundComponent:
StatusLetterIdenticon {
id: letter
color: colorSelectionGrid.selectedColor
name: root.activeGroupName
height: 100
width: 100
letterSize: 64
StatusRoundButton {
id: addButton
icon.name: "add"
type: StatusRoundButton.Type.Secondary
transform: [
Translate {
x: -addButton.width/2 - 5
y: -addButton.height/2 + 5
},
Rotation { angle: -addRotationTransform.angle },
Rotation {
id: addRotationTransform
angle: 135
origin.x: letter.radius
},
Translate {
x: letter.width - 2 * letter.radius
y: letter.radius
}
]
onClicked: imageEditor.chooseImageToCrop()
}
}
}
StatusBaseText {
id: colorText
text: qsTr("Standard colours")
leftPadding: groupName.leftPadding - root.padding
font.pixelSize: 15
}
StatusColorSelectorGrid {
id: colorSelectionGrid
Layout.alignment: Qt.AlignHCenter
diameter: 40
selectorDiameter: 16
columns: 6
selectedColorIndex: -1
}
Item {
id: spacerItem
height: 10
}
}
footer: StatusDialogFooter {
rightButtons: ObjectModel {
StatusButton {
id: saveBtn
text: qsTr("Save changes")
enabled: groupName.text.trim().length > 0 &&
((groupName.text != root.activeGroupName) || (root.activeGroupColor != colorSelectionGrid.selectedColor))
onClicked : {
updateGroupChatDetails(groupName.text, colorSelectionGrid.selectedColor, ""/*imageEditor.dataImage*/)
}
}
}
}
}

View File

@ -298,10 +298,12 @@ ColumnLayout {
onLeaveGroup: {
chatContentModule.leaveChat();
}
onRenameGroupChat: {
root.rootStore.chatCommunitySectionModule.renameGroupChat(
onUpdateGroupChatDetails: {
root.rootStore.chatCommunitySectionModule.updateGroupChatDetails(
chatId,
groupName
groupName,
groupColor,
groupImage
)
}
}

View File

@ -40,7 +40,7 @@ StatusPopupMenu {
signal deleteCommunityChat(string chatId)
signal leaveChat(string chatId)
signal leaveGroup(string chatId)
signal renameGroupChat(string chatId, string groupName)
signal updateGroupChatDetails(string chatId, string groupName, string groupColor, string groupImage)
signal createCommunityChannel(string chatId, string newName, string newDescription, string newEmoji, string newColor)
signal editCommunityChannel(string chatId, string newName, string newDescription, string newEmoji, string newColor, string newCategory)
@ -85,13 +85,14 @@ StatusPopupMenu {
}
StatusMenuItem {
text: qsTr("Edit name")
icon.name: "edit"
text: qsTr("Edit name and image")
icon.name: "edit_pencil"
enabled: root.chatType === Constants.chatType.privateGroupChat
&& root.amIChatAdmin
onTriggered: {
Global.openPopup(renameGroupPopupComponent, {
activeChannelName: root.chatName,
activeGroupName: root.chatName,
activeGroupColor: root.chatColor
});
}
}
@ -99,8 +100,8 @@ StatusPopupMenu {
Component {
id: renameGroupPopupComponent
RenameGroupPopup {
onDoRename: {
root.renameGroupChat(root.chatId, groupName)
onUpdateGroupChatDetails: {
root.updateGroupChatDetails(root.chatId, groupName, groupColor, groupImage)
close()
}
}

View File

@ -224,10 +224,12 @@ Item {
chatDetails: chatContentModule.chatDetails
})
}
onRenameGroupChat: {
chatSectionModule.renameGroupChat(
onUpdateGroupChatDetails: {
chatSectionModule.updateGroupChatDetails(
chatId,
groupName
groupName,
groupColor,
groupImage
)
}
}

View File

@ -30,9 +30,15 @@ Item {
property string dataImage: ""
property Component backgroundComponent
property bool userSelectedImage: false
readonly property bool nothingToShow: state === d.noImageState
function chooseImageToCrop() {
imageCropWorkflow.chooseImageToCrop()
}
implicitWidth: mainLayout.implicitWidth
implicitHeight: mainLayout.implicitHeight
@ -44,11 +50,15 @@ Item {
},
State {
name: d.noImageState
when: root.dataImage.length === 0 && !userSelectedImage
when: root.dataImage.length === 0 && !userSelectedImage && !backgroundComponent
},
State {
name: d.imageSelectedState
when: userSelectedImage
},
State {
name: d.backgroundComponentState
when: root.dataImage.length === 0 && !userSelectedImage && backgroundComponent
}
]
@ -58,6 +68,7 @@ Item {
readonly property string dataImageState: "dataImage"
readonly property string noImageState: "noImage"
readonly property string imageSelectedState: "imageSelected"
readonly property string backgroundComponentState: "backgroundComponent"
readonly property int buttonsInsideOffset: 5
}
@ -70,7 +81,7 @@ Item {
Layout.fillWidth: true
Layout.fillHeight: true
visible: !imageCropEditor.visible
visible: !imageCropEditor.visible && (root.state !== d.backgroundComponentState)
StatusRoundedImage {
anchors.fill: parent
@ -140,6 +151,7 @@ Item {
icon.name: "add"
readonly property real rotationRadius: root.roundedImage ? parent.width/2 : imageCropEditor.radius
transform: [
Translate {
x: -addButton.width/2 - d.buttonsInsideOffset
@ -173,5 +185,16 @@ Item {
}
}
}
Loader {
id: backgroundLoader
Layout.fillWidth: true
Layout.fillHeight: true
visible: root.state == d.backgroundComponentState
sourceComponent: root.backgroundComponent
}
}
}

View File

@ -1,6 +1,6 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Dialogs 1.3
import Qt.labs.platform 1.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1