diff --git a/src/app/modules/main/chat_section/base_item.nim b/src/app/modules/main/chat_section/base_item.nim index 5db743f1dd..e9a76c401c 100644 --- a/src/app/modules/main/chat_section/base_item.nim +++ b/src/app/modules/main/chat_section/base_item.nim @@ -69,6 +69,9 @@ method color*(self: BaseItem): string {.inline base.} = method description*(self: BaseItem): string {.inline base.} = self.description +method `description=`*(self: var BaseItem, value: string) {.inline base.} = + self.description = value + method type*(self: BaseItem): int {.inline base.} = self.`type` @@ -100,4 +103,4 @@ method position*(self: BaseItem): int {.inline base.} = self.position method `position=`*(self: var BaseItem, value: int) {.inline base.} = - self.position = value \ No newline at end of file + self.position = value diff --git a/src/app/modules/main/chat_section/chat_content/controller.nim b/src/app/modules/main/chat_section/chat_content/controller.nim index 187e1f6310..2bfdf0a395 100644 --- a/src/app/modules/main/chat_section/chat_content/controller.nim +++ b/src/app/modules/main/chat_section/chat_content/controller.nim @@ -107,6 +107,12 @@ method init*(self: Controller) = # remove from pinned messages model self.delegate.onUnpinMessage(args.messageId) + self.events.on(SIGNAL_COMMUNITY_CHANNEL_EDITED) do(e:Args): + let args = CommunityChatArgs(e) + if(args.communityId != self.sectionId or self.chatId != args.chat.id or not self.belongsToCommunity): + return + self.delegate.onChatEdited(args.chat) + method getMyChatId*(self: Controller): string = return self.chatId @@ -164,4 +170,4 @@ method getRenderedText*(self: Controller, parsedTextArray: seq[ParsedText]): str method decodeContentHash*(self: Controller, hash: string): string = - return eth_utils.decodeContentHash(hash) \ No newline at end of file + return eth_utils.decodeContentHash(hash) diff --git a/src/app/modules/main/chat_section/chat_content/messages/module.nim b/src/app/modules/main/chat_section/chat_content/messages/module.nim index b9d14b0b85..8a975334d7 100644 --- a/src/app/modules/main/chat_section/chat_content/messages/module.nim +++ b/src/app/modules/main/chat_section/chat_content/messages/module.nim @@ -281,3 +281,8 @@ method onMessageEdited*(self: Module, message: MessageDto) = let renderedMessageText = self.controller.getRenderedText(message.parsedText) self.view.model().updateEditedMsg(message.id, renderedMessageText, message.containsContactMentions()) +method updateChatIdentifier*(self: Module) = + # Delete the old ChatIdentifier message first + self.view.model().removeItem(CHAT_IDENTIFIER_MESSAGE_ID) + # Add new loaded messages + self.view.model().appendItem(self.createChatIdentifierItem()) diff --git a/src/app/modules/main/chat_section/chat_content/messages/private_interfaces/module_controller_delegate_interface.nim b/src/app/modules/main/chat_section/chat_content/messages/private_interfaces/module_controller_delegate_interface.nim index 7ba134f119..bc49c96fb3 100644 --- a/src/app/modules/main/chat_section/chat_content/messages/private_interfaces/module_controller_delegate_interface.nim +++ b/src/app/modules/main/chat_section/chat_content/messages/private_interfaces/module_controller_delegate_interface.nim @@ -30,3 +30,6 @@ method updateContactDetails*(self: AccessInterface, contactId: string) {.base.} method onMessageEdited*(self: AccessInterface, message: MessageDto) {.base.} = raise newException(ValueError, "No implementation available") + +method updateChatIdentifier*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") diff --git a/src/app/modules/main/chat_section/chat_content/module.nim b/src/app/modules/main/chat_section/chat_content/module.nim index 70fbc4435c..8f237c1a78 100644 --- a/src/app/modules/main/chat_section/chat_content/module.nim +++ b/src/app/modules/main/chat_section/chat_content/module.nim @@ -271,3 +271,7 @@ method onContactDetailsUpdated*(self: Module, contactId: string) = method onNotificationsUpdated*(self: Module, hasUnreadMessages: bool, notificationCount: int) = self.view.updateChatDetailsNotifications(hasUnreadMessages, notificationCount) + +method onChatEdited*(self: Module, chatDto: ChatDto) = + self.view.updateChatDetails(chatDto.name, chatDto.description) + self.messagesModule.updateChatIdentifier() diff --git a/src/app/modules/main/chat_section/chat_content/private_interfaces/module_controller_delegate_interface.nim b/src/app/modules/main/chat_section/chat_content/private_interfaces/module_controller_delegate_interface.nim index 56cb4300fe..7457152363 100644 --- a/src/app/modules/main/chat_section/chat_content/private_interfaces/module_controller_delegate_interface.nim +++ b/src/app/modules/main/chat_section/chat_content/private_interfaces/module_controller_delegate_interface.nim @@ -1,4 +1,5 @@ import ../../../../../../app_service/service/message/dto/pinned_message +import ../../../../../../app_service/service/chat/dto/chat method newPinnedMessagesLoaded*(self: AccessInterface, pinnedMessages: seq[PinnedMessageDto]) {.base.} = raise newException(ValueError, "No implementation available") @@ -22,4 +23,7 @@ method onReactionRemoved*(self: AccessInterface, messageId: string, emojiId: int raise newException(ValueError, "No implementation available") method onContactDetailsUpdated*(self: AccessInterface, contactId: string) {.base.} = - raise newException(ValueError, "No implementation available") \ No newline at end of file + raise newException(ValueError, "No implementation available") + +method onChatEdited*(self: AccessInterface, chatDto: ChatDto) {.base.} = + raise newException(ValueError, "No implementation available") diff --git a/src/app/modules/main/chat_section/chat_content/view.nim b/src/app/modules/main/chat_section/chat_content/view.nim index 06d753853e..5a239477be 100644 --- a/src/app/modules/main/chat_section/chat_content/view.nim +++ b/src/app/modules/main/chat_section/chat_content/view.nim @@ -112,4 +112,9 @@ QtObject: self.delegate.getCurrentFleet() proc amIChatAdmin*(self: View): bool {.slot.} = - return self.delegate.amIChatAdmin() \ No newline at end of file + return self.delegate.amIChatAdmin() + + proc updateChatDetails*(self: View, name, description: string) = + self.chatDetails.setName(name) + self.chatDetails.setDescription(description) + self.chatDetailsChanged() diff --git a/src/app/modules/main/chat_section/controller.nim b/src/app/modules/main/chat_section/controller.nim index 527c4716b6..a30d69f195 100644 --- a/src/app/modules/main/chat_section/controller.nim +++ b/src/app/modules/main/chat_section/controller.nim @@ -89,24 +89,9 @@ method init*(self: Controller) = self.events.on(SIGNAL_COMMUNITY_CHANNEL_CREATED) do(e:Args): let args = CommunityChatArgs(e) if (args.communityId == self.sectionId): - let chatDto = ChatDto( - id: args.chat.id, - name: args.chat.name, - chatType: ChatType.CommunityChat, - color: args.chat.color, - emoji: args.chat.emoji, - description: args.chat.description, - # permissions: args.chat.permissions, # TODO implement chat permissions - canPost: args.chat.canPost, - position: args.chat.position, - categoryId: args.chat.categoryId, - communityId: args.communityId - ) - - self.chatService.updateOrAddChat(chatDto) - + self.chatService.updateOrAddChat(args.chat) self.delegate.addNewChat( - chatDto, + args.chat, true, self.events, self.settingsService, @@ -122,6 +107,17 @@ method init*(self: Controller) = if (args.communityId == self.sectionId): self.delegate.onCommunityChannelDeleted(args.chatId) + self.events.on(SIGNAL_COMMUNITY_CHANNEL_EDITED) do(e:Args): + let args = CommunityChatArgs(e) + if (args.communityId == self.sectionId): + self.chatService.updateOrAddChat(args.chat) + self.delegate.onCommunityChannelEdited(args.chat) + + self.events.on(SIGNAL_COMMUNITY_CHANNEL_REORDERED) do(e:Args): + let args = CommunityChatOrderArgs(e) + if (args.communityId == self.sectionId): + self.delegate.reorderChannels(args.chatId, args.categoryId, args.position) + self.events.on(SIGNAL_CONTACT_NICKNAME_CHANGED) do(e: Args): var args = ContactArgs(e) self.delegate.onContactDetailsUpdated(args.contactId) diff --git a/src/app/modules/main/chat_section/model.nim b/src/app/modules/main/chat_section/model.nim index 3b32736823..5ababab410 100644 --- a/src/app/modules/main/chat_section/model.nim +++ b/src/app/modules/main/chat_section/model.nim @@ -1,4 +1,4 @@ -import NimQml, Tables, strutils, strformat, json +import NimQml, Tables, strutils, strformat, json, algorithm from ../../../../app_service/service/chat/dto/chat import ChatType import item, sub_item, base_item, sub_model @@ -44,6 +44,11 @@ QtObject: [{i}]:({$self.items[i]}) """ + proc sortChats(x, y: Item): int = + if x.position < y.position: -1 + elif x.position == y.position: 0 + else: 1 + proc countChanged(self: Model) {.signal.} proc getCount*(self: Model): int {.slot.} = @@ -227,6 +232,16 @@ QtObject: self.dataChanged(index, index, @[ModelRole.Name.int]) return + proc updateItemDetails*(self: Model, id, name, description: 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.description = description + let index = self.createIndex(i, 0, nil) + self.dataChanged(index, index, @[ModelRole.Name.int, ModelRole.Description.int]) + return + proc updateNotificationsForItemOrSubItemById*(self: Model, id: string, hasUnreadMessages: bool, notificationsCount: int) = for i in 0 ..< self.items.len: @@ -251,3 +266,20 @@ QtObject: result.hasNotifications = result.hasNotifications or self.items[i].BaseItem.hasUnreadMessages result.notificationsCount = result.notificationsCount + self.items[i].BaseItem.notificationsCount + + + proc reorder*(self: Model, chatId, categoryId: string, position: int) = + let index = self.getItemIdxById(chatId) + if(index == -1 or position == index): + return + + let idx = self.createIndex(index, 0, nil) + self.items[index].BaseItem.position = position + self.dataChanged(idx, idx, @[ModelRole.Position.int]) + + let tempItem = self.items[position] + self.beginResetModel() + self.items[position] = self.items[index] + self.items[index] = tempItem + self.endResetModel() + diff --git a/src/app/modules/main/chat_section/module.nim b/src/app/modules/main/chat_section/module.nim index 34fcc2796e..48f0354735 100644 --- a/src/app/modules/main/chat_section/module.nim +++ b/src/app/modules/main/chat_section/module.nim @@ -379,6 +379,11 @@ method onCommunityChannelDeleted*(self: Module, chatId: string) = let subItem = item.subItems.getItemAtIndex(0) self.setActiveItemSubItem(item.id, subItem.id) +method onCommunityChannelEdited*(self: Module, chat: ChatDto) = + if(not self.chatContentModules.contains(chat.id)): + return + self.view.chatsModel().updateItemDetails(chat.id, chat.name, chat.description) + method createOneToOneChat*(self: Module, chatId: string, ensName: string) = if(self.controller.isCommunity()): debug "creating an one to one chat is not allowed for community, most likely it's an error in qml" @@ -503,3 +508,6 @@ method joinGroupChatFromInvitation*(self: Module, groupName: string, chatId: str method onChatRenamed*(self: Module, chatId: string, newName: string) = self.view.chatsModel().renameItem(chatId, newName) + +method reorderChannels*(self: Module, chatId, categoryId: string, position: int) = + self.view.chatsModel().reorder(chatId, categoryId, position) diff --git a/src/app/modules/main/chat_section/private_interfaces/module_controller_delegate_interface.nim b/src/app/modules/main/chat_section/private_interfaces/module_controller_delegate_interface.nim index 6181972378..2d5ad887d7 100644 --- a/src/app/modules/main/chat_section/private_interfaces/module_controller_delegate_interface.nim +++ b/src/app/modules/main/chat_section/private_interfaces/module_controller_delegate_interface.nim @@ -37,3 +37,9 @@ method onCommunityChannelDeleted*(self: AccessInterface, chatId: string) {.base. method onChatRenamed*(self: AccessInterface, chatId: string, newName: string) {.base.} = raise newException(ValueError, "No implementation available") + +method onCommunityChannelEdited*(self: AccessInterface, chat: ChatDto) {.base.} = + raise newException(ValueError, "No implementation available") + +method reorderChannels*(self: AccessInterface, chatId, categoryId: string, position: int) {.base.} = + raise newException(ValueError, "No implementation available") diff --git a/src/app_service/service/community/dto/community.nim b/src/app_service/service/community/dto/community.nim index ae709dd43a..91ce4206f0 100644 --- a/src/app_service/service/community/dto/community.nim +++ b/src/app_service/service/community/dto/community.nim @@ -164,4 +164,4 @@ proc toCommunityMembershipRequestDto*(jsonObj: JsonNode): CommunityMembershipReq proc parseCommunities*(response: RpcResponse[JsonNode]): seq[CommunityDto] = result = map(response.result.getElems(), - proc(x: JsonNode): CommunityDto = x.toCommunityDto()) \ No newline at end of file + proc(x: JsonNode): CommunityDto = x.toCommunityDto()) diff --git a/src/app_service/service/community/service.nim b/src/app_service/service/community/service.nim index a73ffdd083..574d8969ff 100644 --- a/src/app_service/service/community/service.nim +++ b/src/app_service/service/community/service.nim @@ -23,7 +23,7 @@ type CommunityChatArgs* = ref object of Args communityId*: string - chat*: Chat + chat*: ChatDto CommunityIdArgs* = ref object of Args communityId*: string @@ -86,6 +86,84 @@ QtObject: result.allCommunities = initTable[string, CommunityDto]() result.myCommunityRequests = @[] + proc mapChatToChatDto(chat: Chat): ChatDto = + result = ChatDto() + result.id = chat.id + result.name = chat.name + result.color = chat.color + result.emoji = chat.emoji + result.description = chat.description + result.canPost = chat.canPost + result.position = chat.position + result.categoryId = chat.categoryId + + proc findChatById(id: string, chats: seq[ChatDto]): ChatDto = + for chat in chats: + if(chat.id == id): + return chat + + proc findIndexById(id: string, chats: seq[Chat]): int = + for chat in chats: + if(chat.id == id): + return 0 + return -1 + + proc handleCommunityUpdates(self: Service, communities: seq[CommunityDto], updatedChats: seq[ChatDto]) = + let community = communities[0] + var sortingOrderChanged = false + if(not self.joinedCommunities.hasKey(community.id)): + error "error: community doesn't exist" + return + + let prev_community = self.joinedCommunities[community.id] + + # channel was added + if(community.chats.len > prev_community.chats.len): + for chat in community.chats: + if findIndexById(chat.id, prev_community.chats) == -1: + # update missing params + let updated_chat = findChatById(community.id&chat.id, updatedChats) + var chat_to_be_added = chat + chat_to_be_added.id = community.id&chat.id + chat_to_be_added.color = updated_chat.color + + self.events.emit(SIGNAL_COMMUNITY_CHANNEL_CREATED, CommunityChatArgs(communityId: community.id, chat: mapChatToChatDto(chat_to_be_added))) + + # channel was removed + elif(community.chats.len < prev_community.chats.len): + for prv_chat in prev_community.chats: + if findIndexById(prv_chat.id, community.chats) == -1: + self.events.emit(SIGNAL_COMMUNITY_CHANNEL_DELETED, CommunityChatIdArgs(communityId: community.id, chatId: community.id&prv_chat.id)) + # some property has changed + else: + for chat in community.chats: + # id is present + if findIndexById(chat.id, prev_community.chats) == -1: + error "error: chat not present" + return + # but something is different + for prev_chat in prev_community.chats: + + # Category changes not handled yet + #if(chat.id == prev_chat.id and chat.categoryId != prev_chat.categoryId): + + # Handle position changes + if(chat.id == prev_chat.id and chat.position != prev_chat.position): + self.events.emit(SIGNAL_COMMUNITY_CHANNEL_REORDERED, CommunityChatOrderArgs(communityId: community.id, chatId: community.id&chat.id, categoryId: chat.categoryId, position: chat.position)) + + # Handle name/description changes + if(chat.id == prev_chat.id and (chat.name != prev_chat.name or chat.description != prev_chat.description)): + # update missing params + let updated_chat = findChatById(community.id&chat.id, updatedChats) + var chat_to_be_edited = chat + chat_to_be_edited.id = community.id&chat.id + if(updated_chat.color != ""): + chat_to_be_edited.color = updated_chat.color + + self.events.emit(SIGNAL_COMMUNITY_CHANNEL_EDITED, CommunityChatArgs(communityId: community.id, chat: mapChatToChatDto(chat_to_be_edited))) + + self.joinedCommunities[community.id].chats = community.chats + proc init*(self: Service) = try: let joinedCommunities = self.loadJoinedComunities() @@ -108,6 +186,11 @@ QtObject: # Handling community updates if (receivedData.communities.len > 0): + + # Channel added removed is notified in the chats param + if (receivedData.chats.len > 0): + self.handleCommunityUpdates(receivedData.communities, receivedData.chats) + self.events.emit(SIGNAL_COMMUNITIES_UPDATE, CommunitiesArgs(communities: receivedData.communities)) proc loadAllCommunities(self: Service): seq[CommunityDto] = @@ -341,9 +424,13 @@ QtObject: if response.result != nil and response.result.kind != JNull: let chat = response.result["chats"][0].toChat() + + # update the joined communities + self.joinedCommunities[communityId].chats.add(chat) + self.events.emit(SIGNAL_COMMUNITY_CHANNEL_CREATED, CommunityChatArgs( communityId: communityId, - chat: chat) + chat: mapChatToChatDto(chat)) ) except Exception as e: error "Error creating community channel", msg = e.msg, communityId, name, description @@ -371,7 +458,13 @@ QtObject: if response.result != nil and response.result.kind != JNull: let chat = response.result["chats"][0].toChat() - self.events.emit(SIGNAL_COMMUNITY_CHANNEL_EDITED, CommunityChatArgs(chat: chat)) + + # update the joined communities + let idx = self.joinedCommunities[communityId].chats.find(chat) + if(idx != -1): + self.joinedCommunities[communityId].chats[idx] = chat + + self.events.emit(SIGNAL_COMMUNITY_CHANNEL_EDITED, CommunityChatArgs(chat: mapChatToChatDto(chat))) except Exception as e: error "Error editing community channel", msg = e.msg, communityId, channelId @@ -411,6 +504,15 @@ QtObject: let error = Json.decode($response.error, RpcError) raise newException(RpcException, "Error deleting community chat: " & error.message) + if response.result == nil or response.result.kind == JNull: + error "response is invalid" + return + + let community = response.result["communities"][0].toCommunityDto() + + # update the joined communities + self.joinedCommunities[community.id].chats = community.chats + self.events.emit(SIGNAL_COMMUNITY_CHANNEL_DELETED, CommunityChatIdArgs( communityId: communityId,