From a9c968b984d15f2c74e42689007d0fc88fb1086a Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Thu, 9 Dec 2021 11:25:38 -0500 Subject: [PATCH] refactor(chat): refactor 1-1 chat to new architecture Fixes #4225 --- src/app/boot/app_controller.nim | 2 +- .../chat_content/input_area/controller.nim | 35 ++- .../input_area/controller_interface.nim | 21 +- .../chat_content/input_area/module.nim | 37 ++- .../module_access_interface.nim | 21 ++ .../chat_content/input_area/view.nim | 24 +- .../modules/main/chat_section/controller.nim | 29 +- .../chat_section/controller_interface.nim | 9 + src/app/modules/main/chat_section/model.nim | 22 ++ src/app/modules/main/chat_section/module.nim | 35 ++- .../module_controller_delegate_interface.nim | 2 +- .../module_view_delegate_interface.nim | 12 + src/app/modules/main/chat_section/view.nim | 11 +- src/app_service/service/chat/service.nim | 259 +++++++++++++++--- .../service/chat/service_interface.nim | 39 +++ ui/app/AppLayouts/Chat/ChatLayout.qml | 1 + .../Chat/popups/ContactRequestsPopup.qml | 6 +- .../Chat/popups/PinnedMessagesPopup.qml | 2 + .../Chat/popups/PrivateChatPopup.qml | 6 +- .../AppLayouts/Chat/views/ChatColumnView.qml | 9 +- .../Chat/views/ChatContextMenuView.qml | 3 +- .../Chat/views/CommunityColumnView.qml | 1 + .../Chat/views/ContactsColumnView.qml | 6 +- .../AppLayouts/Profile/stores/RootStore.qml | 3 +- ui/app/AppLayouts/Timeline/TimelineLayout.qml | 2 + ui/app/AppMain.qml | 3 + .../views/chat/MessageContextMenuView.qml | 6 +- ui/imports/utils/Utils.qml | 4 +- 28 files changed, 549 insertions(+), 61 deletions(-) diff --git a/src/app/boot/app_controller.nim b/src/app/boot/app_controller.nim index 02f4d1afc7..6e4fec85cd 100644 --- a/src/app/boot/app_controller.nim +++ b/src/app/boot/app_controller.nim @@ -181,7 +181,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController = result.accountsService = accounts_service.newService(statusFoundation.fleetConfiguration) result.networkService = network_service.newService() result.contactsService = contacts_service.newService(statusFoundation.status.events, statusFoundation.threadpool) - result.chatService = chat_service.newService(result.contactsService) + result.chatService = chat_service.newService(statusFoundation.status.events, result.contactsService) result.communityService = community_service.newService(result.chatService) result.messageService = message_service.newService(statusFoundation.status.events, statusFoundation.threadpool) result.tokenService = token_service.newService(statusFoundation.status.events, statusFoundation.threadpool, diff --git a/src/app/modules/main/chat_section/chat_content/input_area/controller.nim b/src/app/modules/main/chat_section/chat_content/input_area/controller.nim index e1a1988de0..6c91fa331c 100644 --- a/src/app/modules/main/chat_section/chat_content/input_area/controller.nim +++ b/src/app/modules/main/chat_section/chat_content/input_area/controller.nim @@ -2,6 +2,7 @@ import controller_interface import io_interface import ../../../../../../app_service/service/community/service as community_service +import ../../../../../../app_service/service/chat/service as chat_service export controller_interface @@ -11,13 +12,20 @@ type chatId: string belongsToCommunity: bool communityService: community_service.ServiceInterface + chatService: chat_service.ServiceInterface -proc newController*(delegate: io_interface.AccessInterface, chatId: string, belongsToCommunity: bool, - communityService: community_service.ServiceInterface): Controller = +proc newController*( + delegate: io_interface.AccessInterface, + chatId: string, + belongsToCommunity: bool, + chatService: chat_service.ServiceInterface, + communityService: community_service.ServiceInterface + ): Controller = result = Controller() result.delegate = delegate result.chatId = chatId result.belongsToCommunity = belongsToCommunity + result.chatService = chatService result.communityService = communityService method delete*(self: Controller) = @@ -30,4 +38,25 @@ method getChatId*(self: Controller): string = return self.chatId method belongsToCommunity*(self: Controller): bool = - return self.belongsToCommunity \ No newline at end of file + return self.belongsToCommunity + +method sendImages*(self: Controller, imagePathsJson: string): string = + self.chatService.sendImages(self.chatId, imagePathsJson) + +method requestAddressForTransaction*(self: Controller, chatId: string, fromAddress: string, amount: string, tokenAddress: string) = + self.chatService.requestAddressForTransaction(chatId, fromAddress, amount, tokenAddress) + +method requestTransaction*(self: Controller, chatId: string, fromAddress: string, amount: string, tokenAddress: string) = + self.chatService.requestAddressForTransaction(chatId, fromAddress, amount, tokenAddress) + +method declineRequestTransaction*(self: Controller, messageId: string) = + self.chatService.declineRequestTransaction(messageId) + +method declineRequestAddressForTransaction*(self: Controller, messageId: string) = + self.chatService.declineRequestAddressForTransaction(messageId) + +method acceptRequestAddressForTransaction*(self: Controller, messageId: string, address: string) = + self.chatService.acceptRequestAddressForTransaction(messageId, address) + +method acceptRequestTransaction*(self: Controller, transactionHash: string, messageId: string, signature: string) = + self.chatService.acceptRequestTransaction(transactionHash, messageId, signature) \ No newline at end of file diff --git a/src/app/modules/main/chat_section/chat_content/input_area/controller_interface.nim b/src/app/modules/main/chat_section/chat_content/input_area/controller_interface.nim index 615d65e361..ad38959157 100644 --- a/src/app/modules/main/chat_section/chat_content/input_area/controller_interface.nim +++ b/src/app/modules/main/chat_section/chat_content/input_area/controller_interface.nim @@ -1,4 +1,3 @@ -import ../../../../../../app_service/service/community/service_interface as community_service type AccessInterface* {.pure inheritable.} = ref object of RootObj @@ -16,4 +15,24 @@ method getChatId*(self: AccessInterface): string {.base.} = method belongsToCommunity*(self: AccessInterface): bool {.base.} = raise newException(ValueError, "No implementation available") +method sendImages*(self: AccessInterface, imagePathsJson: string): string {.base.} = + raise newException(ValueError, "No implementation available") + +method requestAddressForTransaction*(self: AccessInterface, chatId: string, fromAddress: string, amount: string, tokenAddress: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method requestTransaction*(self: AccessInterface, chatId: string, fromAddress: string, amount: string, tokenAddress: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method declineRequestTransaction*(self: AccessInterface, messageId: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method declineRequestAddressForTransaction*(self: AccessInterface, messageId: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method acceptRequestAddressForTransaction*(self: AccessInterface, messageId: string, address: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method acceptRequestTransaction*(self: AccessInterface, transactionHash: string, messageId: string, signature: string) {.base.} = + raise newException(ValueError, "No implementation available") \ No newline at end of file diff --git a/src/app/modules/main/chat_section/chat_content/input_area/module.nim b/src/app/modules/main/chat_section/chat_content/input_area/module.nim index a00e7c53f2..4385f0beab 100644 --- a/src/app/modules/main/chat_section/chat_content/input_area/module.nim +++ b/src/app/modules/main/chat_section/chat_content/input_area/module.nim @@ -17,14 +17,19 @@ type controller: controller.AccessInterface moduleLoaded: bool -proc newModule*(delegate: delegate_interface.AccessInterface, chatId: string, belongsToCommunity: bool, - chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface): +proc newModule*( + delegate: delegate_interface.AccessInterface, + chatId: string, + belongsToCommunity: bool, + chatService: chat_service.ServiceInterface, + communityService: community_service.ServiceInterface + ): Module = result = Module() result.delegate = delegate result.view = view.newView(result) result.viewVariant = newQVariant(result.view) - result.controller = controller.newController(result, chatId, belongsToCommunity, communityService) + result.controller = controller.newController(result, chatId, belongsToCommunity, chatService, communityService) result.moduleLoaded = false method delete*(self: Module) = @@ -44,4 +49,28 @@ method viewDidLoad*(self: Module) = self.delegate.inputAreaDidLoad() method getModuleAsVariant*(self: Module): QVariant = - return self.viewVariant \ No newline at end of file + return self.viewVariant + +method getChatId*(self: Module): string = + return self.controller.getChatId() + +method sendImages*(self: Module, imagePathsJson: string): string = + self.controller.sendImages(imagePathsJson) + +method requestAddressForTransaction*(self: Module, chatId: string, fromAddress: string, amount: string, tokenAddress: string) = + self.controller.requestAddressForTransaction(chatId, fromAddress, amount, tokenAddress) + +method requestTransaction*(self: Module, chatId: string, fromAddress: string, amount: string, tokenAddress: string) = + self.controller.requestTransaction(chatId, fromAddress, amount, tokenAddress) + +method declineRequestTransaction*(self: Module, messageId: string) = + self.controller.declineRequestTransaction(messageId) + +method declineRequestAddressForTransaction*(self: Module, messageId: string) = + self.controller.declineRequestAddressForTransaction(messageId) + +method acceptRequestAddressForTransaction*(self: Module, messageId: string, address: string) = + self.controller.acceptRequestAddressForTransaction(messageId, address) + +method acceptRequestTransaction*(self: Module, transactionHash: string, messageId: string, signature: string) = + self.controller.acceptRequestTransaction(transactionHash, messageId, signature) \ No newline at end of file diff --git a/src/app/modules/main/chat_section/chat_content/input_area/private_interfaces/module_access_interface.nim b/src/app/modules/main/chat_section/chat_content/input_area/private_interfaces/module_access_interface.nim index ef768276d9..aa57ee5d5e 100644 --- a/src/app/modules/main/chat_section/chat_content/input_area/private_interfaces/module_access_interface.nim +++ b/src/app/modules/main/chat_section/chat_content/input_area/private_interfaces/module_access_interface.nim @@ -10,4 +10,25 @@ method isLoaded*(self: AccessInterface): bool {.base.} = raise newException(ValueError, "No implementation available") method getModuleAsVariant*(self: AccessInterface): QVariant {.base.} = + raise newException(ValueError, "No implementation available") + +method sendImages*(self: AccessInterface, imagePathsJson: string): string {.base.} = + raise newException(ValueError, "No implementation available") + +method requestAddressForTransaction*(self: AccessInterface, chatId: string, fromAddress: string, amount: string, tokenAddress: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method requestTransaction*(self: AccessInterface, chatId: string, fromAddress: string, amount: string, tokenAddress: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method declineRequestTransaction*(self: AccessInterface, messageId: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method declineRequestAddressForTransaction*(self: AccessInterface, messageId: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method acceptRequestAddressForTransaction*(self: AccessInterface, messageId: string, address: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method acceptRequestTransaction*(self: AccessInterface, transactionHash: string, messageId: string, signature: string) {.base.} = raise newException(ValueError, "No implementation available") \ No newline at end of file diff --git a/src/app/modules/main/chat_section/chat_content/input_area/view.nim b/src/app/modules/main/chat_section/chat_content/input_area/view.nim index 481558237c..d98187c571 100644 --- a/src/app/modules/main/chat_section/chat_content/input_area/view.nim +++ b/src/app/modules/main/chat_section/chat_content/input_area/view.nim @@ -19,4 +19,26 @@ QtObject: result.model = newModel() proc load*(self: View) = - self.delegate.viewDidLoad() \ No newline at end of file + self.delegate.viewDidLoad() + + proc sendImages*(self: View, sendImages: string): string {.slot.} = + self.delegate.sendImages(sendImages) + + proc acceptAddressRequest*(self: View, messageId: string , address: string) {.slot.} = + self.delegate.acceptRequestAddressForTransaction(messageId, address) + + proc declineAddressRequest*(self: View, messageId: string) {.slot.} = + self.delegate.declineRequestAddressForTransaction(messageId) + + proc requestAddress*(self: View, chatId: string, fromAddress: string, amount: string, tokenAddress: string) {.slot.} = + self.delegate.requestAddressForTransaction(chatId, fromAddress, amount, tokenAddress) + + proc request*(self: View, chatId: string, fromAddress: string, amount: string, tokenAddress: string) {.slot.} = + self.delegate.requestTransaction(chatId, fromAddress, amount, tokenAddress) + + proc declineRequest*(self: View, messageId: string) {.slot.} = + self.delegate.declineRequestTransaction(messageId) + + proc acceptRequestTransaction*(self: View, transactionHash: string, messageId: string, signature: string) {.slot.} = + self.delegate.acceptRequestTransaction(transactionHash, messageId, signature) + \ No newline at end of file diff --git a/src/app/modules/main/chat_section/controller.nim b/src/app/modules/main/chat_section/controller.nim index f41d28268d..bda0a8ef75 100644 --- a/src/app/modules/main/chat_section/controller.nim +++ b/src/app/modules/main/chat_section/controller.nim @@ -82,6 +82,20 @@ method setActiveItemSubItem*(self: Controller, itemId: string, subItemId: string self.delegate.activeItemSubItemSet(self.activeItemId, self.activeSubItemId) +method removeActiveFromThisChat*(self: Controller, itemId: string) = + if self.activeItemId != itemId: + return + + let allChats = self.chatService.getAllChats() + + self.activeSubItemId = "" + if allChats.len == 0: + self.activeItemId = "" + else: + self.activeItemId = allChats[0].id + + self.delegate.activeItemSubItemSet(self.activeItemId, self.activeSubItemId) + method getOneToOneChatNameAndImage*(self: Controller, chatId: string): tuple[name: string, image: string, isIdenticon: bool] = return self.chatService.getOneToOneChatNameAndImage(chatId) @@ -89,5 +103,16 @@ method getOneToOneChatNameAndImage*(self: Controller, chatId: string): method createPublicChat*(self: Controller, chatId: string) = let response = self.chatService.createPublicChat(chatId) if(response.success): - self.delegate.addNewPublicChat(response.chatDto, self.events, self.contactService, self.chatService, - self.communityService, self.messageService) \ No newline at end of file + self.delegate.addNewChat(response.chatDto, self.events, self.contactService, self.chatService, + self.communityService, self.messageService) + +method createOneToOneChat*(self: Controller, chatId: string, ensName: string) = + let response = self.chatService.createOneToOneChat(chatId, ensName) + if(response.success): + self.delegate.addNewChat(response.chatDto, self.events, self.contactService, self.chatService, + self.communityService, self.messageService) + +method leaveChat*(self: Controller, chatId: string) = + let success = self.chatService.leaveChat(chatId) + if success: + self.delegate.removeChat(chatId) \ No newline at end of file diff --git a/src/app/modules/main/chat_section/controller_interface.nim b/src/app/modules/main/chat_section/controller_interface.nim index ee659d0294..51cfafe293 100644 --- a/src/app/modules/main/chat_section/controller_interface.nim +++ b/src/app/modules/main/chat_section/controller_interface.nim @@ -37,10 +37,19 @@ method getChatDetailsForChatTypes*(self: AccessInterface, types: seq[ChatType]): method setActiveItemSubItem*(self: AccessInterface, itemId: string, subItemId: string) {.base.} = raise newException(ValueError, "No implementation available") + +method removeActiveFromThisChat*(self: AccessInterface, itemId: string) {.base.} = + raise newException(ValueError, "No implementation available") method getOneToOneChatNameAndImage*(self: AccessInterface, chatId: string): tuple[name: string, image: string, isIdenticon: bool] {.base.} = raise newException(ValueError, "No implementation available") method createPublicChat*(self: AccessInterface, chatId: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method createOneToOneChat*(self: AccessInterface, chatId: string, ensName: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method leaveChat*(self: AccessInterface, chatId: string) {.base.} = raise newException(ValueError, "No implementation available") \ No newline at end of file diff --git a/src/app/modules/main/chat_section/model.nim b/src/app/modules/main/chat_section/model.nim index b8bb932222..ad323bbd38 100644 --- a/src/app/modules/main/chat_section/model.nim +++ b/src/app/modules/main/chat_section/model.nim @@ -120,6 +120,28 @@ QtObject: self.countChanged() + proc getItemIdxById*(self: Model, id: string): int = + var idx = 0 + for it in self.items: + if(it.id == id): + return idx + idx.inc + return -1 + + proc removeItemById*(self: Model, id: string) = + let idx = self.getItemIdxById(id) + if idx == -1: + return + + let parentModelIndex = newQModelIndex() + defer: parentModelIndex.delete + + self.beginRemoveRows(parentModelIndex, idx, idx) + self.items.delete(idx) + self.endRemoveRows() + + self.countChanged() + proc prependItem*(self: Model, item: Item) = let parentModelIndex = newQModelIndex() defer: parentModelIndex.delete diff --git a/src/app/modules/main/chat_section/module.nim b/src/app/modules/main/chat_section/module.nim index 1aae7ad9fc..070975bb9c 100644 --- a/src/app/modules/main/chat_section/module.nim +++ b/src/app/modules/main/chat_section/module.nim @@ -64,6 +64,11 @@ proc addSubmodule(self: Module, chatId: string, belongToCommunity: bool, isUsers self.chatContentModule[chatId] = chat_content_module.newModule(self, events, self.controller.getMySectionId(), chatId, belongToCommunity, isUsersListAvailable, contactService, chatService, communityService, messageService) +proc removeSubmodule(self: Module, chatId: string) = + if(not self.chatContentModule.contains(chatId)): + return + self.chatContentModule.del(chatId) + proc buildChatUI(self: Module, events: EventEmitter, contactService: contact_service.Service, chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface, messageService: message_service.Service) = @@ -232,12 +237,12 @@ method createPublicChat*(self: Module, chatId: string) = return if(self.chatContentModule.hasKey(chatId)): - error "error: public chat is already added, ", chatId + error "error: public chat is already added", chatId return self.controller.createPublicChat(chatId) -method addNewPublicChat*(self: Module, chatDto: ChatDto, events: EventEmitter, contactService: contact_service.Service, +method addNewChat*(self: Module, chatDto: ChatDto, events: EventEmitter, contactService: contact_service.Service, chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface, messageService: message_service.Service) = let hasNotification = chatDto.unviewedMessagesCount > 0 or chatDto.unviewedMentionsCount > 0 @@ -248,4 +253,28 @@ method addNewPublicChat*(self: Module, chatDto: ChatDto, events: EventEmitter, c self.addSubmodule(chatDto.id, false, true, events, contactService, chatService, communityService, messageService) # make new added chat active one - self.setActiveItemSubItem(item.id, "") \ No newline at end of file + self.setActiveItemSubItem(item.id, "") + +method removeChat*(self: Module, chatId: string) = + if(not self.chatContentModule.contains(chatId)): + return + + self.view.removeItem(chatId) + self.removeSubmodule(chatId) + + # remove active state form the removed chat (if applicable) + self.controller.removeActiveFromThisChat(chatId) + +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" + return + + if(self.chatContentModule.hasKey(chatId)): + error "error: one to one chat is already added", chatId + return + + self.controller.createOneToOneChat(chatId, ensName) + +method leaveChat*(self: Module, chatId: string) = + self.controller.leaveChat(chatId) \ No newline at end of file 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 d63bcbddb5..741c0f512f 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 @@ -1,7 +1,7 @@ method activeItemSubItemSet*(self: AccessInterface, itemId: string, subItemId: string) {.base.} = raise newException(ValueError, "No implementation available") -method addNewPublicChat*(self: AccessInterface, chatDto: ChatDto, events: EventEmitter, +method addNewChat*(self: AccessInterface, chatDto: ChatDto, events: EventEmitter, contactService: contact_service.Service, chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface, messageService: message_service.Service) {.base.} = raise newException(ValueError, "No implementation available") \ No newline at end of file diff --git a/src/app/modules/main/chat_section/private_interfaces/module_view_delegate_interface.nim b/src/app/modules/main/chat_section/private_interfaces/module_view_delegate_interface.nim index 3a3667b3fc..196d71c715 100644 --- a/src/app/modules/main/chat_section/private_interfaces/module_view_delegate_interface.nim +++ b/src/app/modules/main/chat_section/private_interfaces/module_view_delegate_interface.nim @@ -13,4 +13,16 @@ method isCommunity*(self: AccessInterface): bool {.base.} = raise newException(ValueError, "No implementation available") method createPublicChat*(self: AccessInterface, chatId: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method createOneToOneChat*(self: AccessInterface, chatId: string, ensName: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method leaveChat*(self: AccessInterface, chatId: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method removeChat*(self: AccessInterface, chatId: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method getActiveChatId*(self: AccessInterface): string {.base.} = raise newException(ValueError, "No implementation available") \ No newline at end of file diff --git a/src/app/modules/main/chat_section/view.nim b/src/app/modules/main/chat_section/view.nim index 15ebc41975..fe0afb2a3f 100644 --- a/src/app/modules/main/chat_section/view.nim +++ b/src/app/modules/main/chat_section/view.nim @@ -49,6 +49,9 @@ QtObject: proc appendItem*(self: View, item: Item) = self.model.appendItem(item) + proc removeItem*(self: View, id: string) = + self.model.removeItemById(id) + proc prependItem*(self: View, item: Item) = self.model.prependItem(item) @@ -84,4 +87,10 @@ QtObject: return chatContentVariant proc createPublicChat*(self: View, chatId: string) {.slot.} = - self.delegate.createPublicChat(chatId) \ No newline at end of file + self.delegate.createPublicChat(chatId) + + proc createOneToOneChat*(self: View, chatId: string, ensName: string) {.slot.} = + self.delegate.createOneToOneChat(chatId, ensName) + + proc leaveChat*(self: View, id: string) {.slot.} = + self.delegate.leaveChat(id) diff --git a/src/app_service/service/chat/service.nim b/src/app_service/service/chat/service.nim index e53de346a2..b940d42c61 100644 --- a/src/app_service/service/chat/service.nim +++ b/src/app_service/service/chat/service.nim @@ -1,9 +1,16 @@ -import Tables, json, sequtils, strformat, chronicles +import NimQml, Tables, json, sequtils, strformat, chronicles, os import service_interface +import eventemitter import ./dto/chat as chat_dto +import ../message/dto/message as message_dto import ../contacts/service as contact_service import status/statusgo_backend_new/chat as status_chat +import status/statusgo_backend_new/chatCommands as status_chat_commands +import ../../../app/utils/image_utils +import ../../../constants + +from ../../common/account_constants import ZERO_ADDRESS # TODO: We need to remove these `status-lib` types from here import status/types/[message] @@ -16,16 +23,54 @@ logScope: include ../../common/json_utils +type + # TODO remove New when refactored + ChatUpdateArgsNew* = ref object of Args + chats*: seq[ChatDto] + messages*: seq[MessageDto] + # TODO refactor that part + # pinnedMessages*: seq[MessageDto] + # emojiReactions*: seq[Reaction] + # communities*: seq[Community] + # communityMembershipRequests*: seq[CommunityMembershipRequest] + # activityCenterNotifications*: seq[ActivityCenterNotification] + # statusUpdates*: seq[StatusUpdate] + # deletedMessages*: seq[RemovedMessage] + + ChatIdArg* = ref object of Args + chatId*: string + + MessageSendingSuccess* = ref object of Args + chat*: ChatDto + message*: MessageDto + + MessageArgs* = ref object of Args + id*: string + channel*: string + +# Events this service emits +# TODO remove new when refactor is done +const SIGNAL_CHAT_UPDATE* = "chatUpdate_new" +const SIGNAL_CHAT_LEFT* = "channelLeft_new" +const SIGNAL_SENDING_FAILED* = "messageSendingFailed_new" +const SIGNAL_SENDING_SUCCESS* = "messageSendingSuccess_new" +const SIGNAL_MESSAGE_DELETED* = "messageDeleted_new" + type Service* = ref object of service_interface.ServiceInterface chats: Table[string, ChatDto] # [chat_id, ChatDto] contactService: contact_service.Service + events: EventEmitter method delete*(self: Service) = discard -proc newService*(contactService: contact_service.Service): Service = +proc newService*( + events: EventEmitter, + contactService: contact_service.Service + ): Service = result = Service() + result.events = events result.contactService = contactService result.chats = initTable[string, ChatDto]() @@ -47,6 +92,64 @@ method init*(self: Service) = method getAllChats*(self: Service): seq[ChatDto] = return toSeq(self.chats.values) +method hasChannel*(self: Service, chatId: string): bool = + self.chats.hasKey(chatId) + +# TODO refactor this to new object types +method parseChatResponse*(self: Service, response: string): (seq[chat_type.Chat], seq[Message]) = + var parsedResponse = parseJson(response) + var chats: seq[Chat] = @[] + var messages: seq[Message] = @[] + if parsedResponse{"result"}{"messages"} != nil: + for jsonMsg in parsedResponse["result"]["messages"]: + messages.add(jsonMsg.toMessage()) + if parsedResponse{"result"}{"chats"} != nil: + for jsonChat in parsedResponse["result"]["chats"]: + let chat = chat_type.toChat(jsonChat) + # TODO add the channel back to `chat` when it is refactored + # self.channels[chat.id] = chat + chats.add(chat) + result = (chats, messages) + +# TODO refactor this to new object types +method parseChatResponse2*(self: Service, response: RpcResponse[JsonNode]): (seq[ChatDto], seq[MessageDto]) = + var chats: seq[ChatDto] = @[] + var messages: seq[MessageDto] = @[] + if response.result{"messages"} != nil: + for jsonMsg in response.result["messages"]: + messages.add(jsonMsg.toMessageDto) + if response.result{"chats"} != nil: + for jsonChat in response.result["chats"]: + let chat = chat_dto.toChatDto(jsonChat) + # TODO add the channel back to `chat` when it is refactored + self.chats[chat.id] = chat + chats.add(chat) + result = (chats, messages) + +method processMessageUpdateAfterSend(self: Service, response: RpcResponse[JsonNode]): (seq[ChatDto], seq[MessageDto]) = + result = self.parseChatResponse2(response) + var (chats, messages) = result + if chats.len == 0 and messages.len == 0: + self.events.emit(SIGNAL_SENDING_FAILED, Args()) + return + + # This fixes issue#3490 + var msg = messages[0] + for m in messages: + if(m.responseTo.len > 0): + msg = m + break + + self.events.emit(SIGNAL_SENDING_SUCCESS, MessageSendingSuccess(message: msg, chat: chats[0])) + +method processUpdateForTransaction*(self: Service, messageId: string, response: RpcResponse[JsonNode]) = + var (chats, messages) = self.processMessageUpdateAfterSend(response) + self.events.emit(SIGNAL_MESSAGE_DELETED, MessageArgs(id: messageId, channel: chats[0].id)) + +method emitUpdate(self: Service, response: RpcResponse[JsonNode]) = + var (chats, messages) = self.parseChatResponse2(response) + self.events.emit(SIGNAL_CHAT_UPDATE, ChatUpdateArgsNew(messages: messages, chats: chats)) + method getChatsOfChatTypes*(self: Service, types: seq[chat_dto.ChatType]): seq[ChatDto] = return self.getAllChats().filterIt(it.chatType in types) @@ -61,42 +164,132 @@ method getOneToOneChatNameAndImage*(self: Service, chatId: string): tuple[name: string, image: string, isIdenticon: bool] = return self.contactService.getContactNameAndImage(chatId) +method createChatFromResponse*(self: Service, response: RpcResponse[JsonNode]): tuple[chatDto: ChatDto, success: bool] = + var jsonArr: JsonNode + if (not response.result.getProp("chats", jsonArr)): + error "error: response of creating chat doesn't contain created chats" + result.success = false + return + + let chats = map(jsonArr.getElems(), proc(x: JsonNode): ChatDto = x.toChatDto()) + # created chat is returned as the first elemnt of json array (it's up to `status-go`) + if(chats.len == 0): + error "error: unknown error occured creating chat" + result.success = false + return + + result.chatDto = chats[0] + result.success = true + method createPublicChat*(self: Service, chatId: string): tuple[chatDto: ChatDto, success: bool] = try: let response = status_chat.createPublicChat(chatId) - var jsonArr: JsonNode - if (not response.result.getProp("chats", jsonArr)): - error "error: response of creating public chat doesn't contain created chats for chat: ", chatId - result.success = false - return - - let chats = map(jsonArr.getElems(), proc(x: JsonNode): ChatDto = x.toChatDto()) - # created chat is returned as the first elemnt of json array (it's up to `status-go`) - if(chats.len == 0): - error "error: unknown error occured creating public chat ", chatId - result.success = false - return - - result.chatDto = chats[0] - result.success = true - + result = self.createChatFromResponse(response) except Exception as e: let errDesription = e.msg error "error: ", errDesription return -# TODO refactor this to new object types -method parseChatResponse*(self: Service, response: string): (seq[Chat], seq[Message]) = - var parsedResponse = parseJson(response) - var chats: seq[Chat] = @[] - var messages: seq[Message] = @[] - if parsedResponse{"result"}{"messages"} != nil: - for jsonMsg in parsedResponse["result"]["messages"]: - messages.add(jsonMsg.toMessage()) - if parsedResponse{"result"}{"chats"} != nil: - for jsonChat in parsedResponse["result"]["chats"]: - let chat = jsonChat.toChat - # TODO add the channel back to `chat` when it is refactored - # self.channels[chat.id] = chat - chats.add(chat) - result = (chats, messages) \ No newline at end of file +method createOneToOneChat*(self: Service, chatId: string, ensName: string): tuple[chatDto: ChatDto, success: bool] = + try: + if self.hasChannel(chatId): + # We want to show the chat to the user and for that we activate the chat + discard status_chat.saveChat( + chatId, + chat_dto.ChatType.OneToOne.int, + color=self.chats[chatId].color, + ensName=ensName) + result.success = true + result.chatDto = self.chats[chatId] + return + + let response = status_chat.createOneToOneChat(chatId) + result = self.createChatFromResponse(response) + except Exception as e: + let errDesription = e.msg + error "error: ", errDesription + return + +method leaveChat*(self: Service, chatId: string): bool = + try: + if self.chats.len == 0: + return false + if(not self.chats.contains(chatId)): + error "trying to leave chat for an unexisting chat id", chatId + return false + + let chat = self.chats[chatId] + if chat.chatType == chat_dto.ChatType.PrivateGroupChat: + let leaveGroupResponse = status_chat.leaveGroupChat(chatId) + self.emitUpdate(leaveGroupResponse) + + discard status_chat.deactivateChat(chatId) + + self.chats.del(chatId) + discard status_chat.clearChatHistory(chatId) + self.events.emit(SIGNAL_CHAT_LEFT, ChatIdArg(chatId: chatId)) + return true + except Exception as e: + error "Error deleting channel", chatId, msg = e.msg + return false + +method sendImages*(self: Service, chatId: string, imagePathsJson: string): string = + result = "" + try: + var images = Json.decode(imagePathsJson, seq[string]) + + for imagePath in images.mitems: + var image = image_utils.formatImagePath(imagePath) + imagePath = image_resizer(image, 2000, TMPDIR) + + discard status_chat.sendImages(chatId, images) + + for imagePath in images.items: + removeFile(imagePath) + except Exception as e: + error "Error sending images", msg = e.msg + result = fmt"Error sending images: {e.msg}" + +method requestAddressForTransaction*(self: Service, chatId: string, fromAddress: string, amount: string, tokenAddress: string) = + try: + let address = if (tokenAddress == ZERO_ADDRESS): "" else: tokenAddress + let response = status_chat_commands.requestAddressForTransaction(chatId, fromAddress, amount, address) + discard self.processMessageUpdateAfterSend(response) + except Exception as e: + error "Error requesting address for transaction", msg = e.msg + +method requestTransaction*(self: Service, chatId: string, fromAddress: string, amount: string, tokenAddress: string) = + try: + let address = if (tokenAddress == ZERO_ADDRESS): "" else: tokenAddress + let response = status_chat_commands.requestTransaction(chatId, fromAddress, amount, address) + discard self.processMessageUpdateAfterSend(response) + except Exception as e: + error "Error requesting transaction", msg = e.msg + +method declineRequestTransaction*(self: Service, messageId: string) = + try: + let response = status_chat_commands.declineRequestTransaction(messageId) + self.processUpdateForTransaction(messageId, response) + except Exception as e: + error "Error requesting transaction", msg = e.msg + +method declineRequestAddressForTransaction*(self: Service, messageId: string) = + try: + let response = status_chat_commands.declineRequestAddressForTransaction(messageId) + self.processUpdateForTransaction(messageId, response) + except Exception as e: + error "Error requesting transaction", msg = e.msg + +method acceptRequestAddressForTransaction*(self: Service, messageId: string, address: string) = + try: + let response = status_chat_commands.acceptRequestAddressForTransaction(messageId, address) + self.processUpdateForTransaction(messageId, response) + except Exception as e: + error "Error requesting transaction", msg = e.msg + +method acceptRequestTransaction*(self: Service, transactionHash: string, messageId: string, signature: string) = + try: + let response = status_chat_commands.acceptRequestTransaction(transactionHash, messageId, signature) + discard self.processMessageUpdateAfterSend(response) + except Exception as e: + error "Error requesting transaction", msg = e.msg \ No newline at end of file diff --git a/src/app_service/service/chat/service_interface.nim b/src/app_service/service/chat/service_interface.nim index 729a8f7313..1fc9d0445d 100644 --- a/src/app_service/service/chat/service_interface.nim +++ b/src/app_service/service/chat/service_interface.nim @@ -1,4 +1,7 @@ +import json import ./dto/chat as chat_dto +import ../message/dto/message as message_dto +import status/statusgo_backend_new/chat as status_chat # TODO: We need to remove these `status-lib` types from here import status/types/[message] @@ -16,6 +19,9 @@ method delete*(self: ServiceInterface) {.base.} = method init*(self: ServiceInterface) {.base.} = raise newException(ValueError, "No implementation available") +method hasChannel*(self: ServiceInterface, chatId: string): bool {.base.} = + raise newException(ValueError, "No implementation available") + method getAllChats*(self: ServiceInterface): seq[ChatDto] {.base.} = raise newException(ValueError, "No implementation available") @@ -32,5 +38,38 @@ method getOneToOneChatNameAndImage*(self: ServiceInterface, chatId: string): method createPublicChat*(self: ServiceInterface, chatId: string): tuple[chatDto: ChatDto, success: bool] {.base.} = raise newException(ValueError, "No implementation available") +method createOneToOneChat*(self: ServiceInterface, chatId: string, ensName: string): tuple[chatDto: ChatDto, success: bool] {.base.} = + raise newException(ValueError, "No implementation available") + method parseChatResponse*(self: ServiceInterface, response: string): (seq[Chat], seq[Message]) {.base.} = + raise newException(ValueError, "No implementation available") + +method parseChatResponse2*(self: ServiceInterface, response: RpcResponse[JsonNode]): (seq[ChatDto], seq[MessageDto]) {.base.} = + raise newException(ValueError, "No implementation available") + +method processMessageUpdateAfterSend*(self: ServiceInterface, messageId: string, response: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method processUpdateForTransaction*(self: ServiceInterface, response: RpcResponse[JsonNode]): (seq[ChatDto], seq[MessageDto]) {.base.} = + raise newException(ValueError, "No implementation available") + +method leaveChat*(self: ServiceInterface, chatId: string): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method sendImages*(self: ServiceInterface, chatId: string, imagePathsJson: string): string {.base.} = + raise newException(ValueError, "No implementation available") + +method requestAddressForTransaction*(self: ServiceInterface, chatId: string, fromAddress: string, amount: string, tokenAddress: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method declineRequestTransaction*(self: ServiceInterface, messageId: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method declineRequestAddressForTransaction*(self: ServiceInterface, messageId: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method acceptRequestAddressForTransaction*(self: ServiceInterface, messageId: string, address: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method acceptRequestTransaction*(self: ServiceInterface, transactionHash: string, messageId: string, signature: string) {.base.} = raise newException(ValueError, "No implementation available") \ No newline at end of file diff --git a/ui/app/AppLayouts/Chat/ChatLayout.qml b/ui/app/AppLayouts/Chat/ChatLayout.qml index 274cd865e8..f8dfd215d1 100644 --- a/ui/app/AppLayouts/Chat/ChatLayout.qml +++ b/ui/app/AppLayouts/Chat/ChatLayout.qml @@ -178,6 +178,7 @@ StatusAppThreePanelLayout { MessageContextMenuView { id: quickActionMessageOptionsMenu + chatSectionModule: root.chatCommunitySectionModule // Not Refactored store: root.rootStore // reactionModel: root.rootStore.emojiReactionsModel diff --git a/ui/app/AppLayouts/Chat/popups/ContactRequestsPopup.qml b/ui/app/AppLayouts/Chat/popups/ContactRequestsPopup.qml index c2892f78a5..b58750a438 100644 --- a/ui/app/AppLayouts/Chat/popups/ContactRequestsPopup.qml +++ b/ui/app/AppLayouts/Chat/popups/ContactRequestsPopup.qml @@ -17,6 +17,10 @@ ModalPopup { id: popup property var store + // Important: + // We're here in case of ChatSection + // This module is set from `ChatLayout` (each `ChatLayout` has its own chatSectionModule) + property var chatSectionModule //% "Contact requests" title: qsTrId("contact-requests") @@ -51,7 +55,7 @@ ModalPopup { blockContactConfirmationDialog.open() } onAcceptClicked: { - popup.store.chatsModelInst.channelView.joinPrivateChat(model.address, "") + chatSectionModule.createOneToOneChat(model.address, "") popup.store.contactsModuleInst.addContact(model.address) } onDeclineClicked: { diff --git a/ui/app/AppLayouts/Chat/popups/PinnedMessagesPopup.qml b/ui/app/AppLayouts/Chat/popups/PinnedMessagesPopup.qml index 3dd31d85d3..983b444b3a 100644 --- a/ui/app/AppLayouts/Chat/popups/PinnedMessagesPopup.qml +++ b/ui/app/AppLayouts/Chat/popups/PinnedMessagesPopup.qml @@ -19,6 +19,7 @@ import StatusQ.Controls 0.1 as StatusQControls ModalPopup { property var rootStore property var messageStore + property var chatSectionModule property bool userCanPin: { switch (popup.rootStore.chatsModelInst.channelView.activeChannel.chatType) { case Constants.chatTypePublic: return false @@ -210,6 +211,7 @@ ModalPopup { id: msgContextMenu pinnedPopup: true pinnedMessage: true + chatSectionModule: popup.chatSectionModule store: popup.rootStore reactionModel: popup.rootStore.emojiReactionsModel onShouldCloseParentPopup: { diff --git a/ui/app/AppLayouts/Chat/popups/PrivateChatPopup.qml b/ui/app/AppLayouts/Chat/popups/PrivateChatPopup.qml index d6aaefcca9..27cd23d97e 100644 --- a/ui/app/AppLayouts/Chat/popups/PrivateChatPopup.qml +++ b/ui/app/AppLayouts/Chat/popups/PrivateChatPopup.qml @@ -16,10 +16,11 @@ ModalPopup { //% "New chat" title: qsTrId("new-chat") property var store + signal joinPrivateChat(string publicKey, string ensName) signal profileClicked() function doJoin(pk, ensName) { - popup.store.chatsModelInst.channelView.joinPrivateChat(pk, Utils.isChatKey(pk) ? "" : ensName); + popup.joinPrivateChat(pk, Utils.isChatKey(pk) ? "" : ensName); popup.close(); } @@ -39,8 +40,7 @@ ModalPopup { width: parent.width addContactEnabled: false onUserClicked: function (isContact, pubKey, ensName) { - popup.store.chatsModelInst.channelView.joinPrivateChat(pubKey, Utils.isChatKey(pubKey) ? "" : ensName); - popup.close(); + popup.doJoin(pubKey, ensName); } } diff --git a/ui/app/AppLayouts/Chat/views/ChatColumnView.qml b/ui/app/AppLayouts/Chat/views/ChatColumnView.qml index 3477baedc3..3080d62b46 100644 --- a/ui/app/AppLayouts/Chat/views/ChatColumnView.qml +++ b/ui/app/AppLayouts/Chat/views/ChatColumnView.qml @@ -132,6 +132,7 @@ Item { MessageContextMenuView { id: contextmenu + chatSectionModule: root.parentModule store: root.rootStore reactionModel: root.rootStore.emojiReactionsModel } @@ -239,6 +240,7 @@ Item { popupMenu: ChatContextMenuView { store: root.rootStore + chatSectionModule: parentModule onOpened: { chatItem = root.rootStore.chatsModelInst.channelView.activeChannel } @@ -392,8 +394,10 @@ Item { packId) } onSendMessage: { - if (chatInput.fileUrls.length > 0){ - root.rootStore.chatsModelInst.sendImages(JSON.stringify(fileUrls)); + if (chatInput.fileUrls.length > 0) { + let inputAreaModule = parentModule.getChatContentModule.getInputAreaModule() + inputAreaModule.sendImages(JSON.stringify(fileUrls)); + return } let msg = root.rootStore.chatsModelInst.plainText(Emoji.deparse(chatInput.textInput.text)) if (msg.length > 0){ @@ -568,6 +572,7 @@ Item { id: pinnedMessagesPopupComponent PinnedMessagesPopup { id: pinnedMessagesPopup + chatSectionModule: root.parentModule rootStore: root.rootStore messageStore: root.rootStore.messageStore onClosed: destroy() diff --git a/ui/app/AppLayouts/Chat/views/ChatContextMenuView.qml b/ui/app/AppLayouts/Chat/views/ChatContextMenuView.qml index d6298cb1b4..01e7eca3e4 100644 --- a/ui/app/AppLayouts/Chat/views/ChatContextMenuView.qml +++ b/ui/app/AppLayouts/Chat/views/ChatContextMenuView.qml @@ -13,6 +13,7 @@ StatusPopupMenu { id: root property var chatItem property var store + property var chatSectionModule // Not Refactored Yet property bool communityActive: false // root.store.chatsModelInst.communities.activeCommunity.active @@ -186,7 +187,7 @@ StatusPopupMenu { if (communityActive) { root.store.chatsModelInst.communities.deleteCommunityChat(root.store.chatsModelInst.communities.activeCommunity.id, chatId) } else { - root.store.chatsModelInst.channelView.leaveChat(chatId) + chatSectionModule.leaveChat(chatId) } close(); } diff --git a/ui/app/AppLayouts/Chat/views/CommunityColumnView.qml b/ui/app/AppLayouts/Chat/views/CommunityColumnView.qml index f6457d5ce3..178146f986 100644 --- a/ui/app/AppLayouts/Chat/views/CommunityColumnView.qml +++ b/ui/app/AppLayouts/Chat/views/CommunityColumnView.qml @@ -258,6 +258,7 @@ Item { chatListPopupMenu: ChatContextMenuView { id: chatContextMenuView store: root.store + chatSectionModule: communitySectionModule openHandler: function (id) { root.store.chatsModelInst.channelView.setContextChannel(id) chatContextMenuView.chatItem = root.store.chatsModelInst.channelView.contextChannel diff --git a/ui/app/AppLayouts/Chat/views/ContactsColumnView.qml b/ui/app/AppLayouts/Chat/views/ContactsColumnView.qml index 327e0686b4..5a250a2a33 100644 --- a/ui/app/AppLayouts/Chat/views/ContactsColumnView.qml +++ b/ui/app/AppLayouts/Chat/views/ContactsColumnView.qml @@ -34,7 +34,7 @@ Item { Component.onCompleted: { appMain.openContactsPopup.connect(function(){ - Global.openPopup(contactRequestsPopup) + Global.openPopup(contactRequestsPopup, {chatSectionModule}) }) } @@ -262,6 +262,7 @@ Item { popupMenu: ChatContextMenuView { id: chatContextMenuView store: root.store + chatSectionModule: root.chatSectionModule openHandler: function (id) { root.store.chatsModelInst.channelView.setContextChannel(id) chatContextMenuView.chatItem = root.store.chatsModelInst.channelView.contextChannel @@ -309,6 +310,9 @@ Item { id: privateChatPopupComponent PrivateChatPopup { store: root.store + onJoinPrivateChat: { + chatSectionModule.createOneToOneChat(publicKey, ensName) + } onClosed: { destroy() } diff --git a/ui/app/AppLayouts/Profile/stores/RootStore.qml b/ui/app/AppLayouts/Profile/stores/RootStore.qml index 31e7e50295..b439d72920 100644 --- a/ui/app/AppLayouts/Profile/stores/RootStore.qml +++ b/ui/app/AppLayouts/Profile/stores/RootStore.qml @@ -198,7 +198,8 @@ QtObject { } function joinPrivateChat(address) { - chatsModelInst.channelView.joinPrivateChat(address, ""); + let chatCommunitySectionModule = mainModule.getChatSectionModule() + chatCommunitySectionModule.createOneToOneChat(address, "") } function unblockContact(address) { diff --git a/ui/app/AppLayouts/Timeline/TimelineLayout.qml b/ui/app/AppLayouts/Timeline/TimelineLayout.qml index f10d37e644..b9ef7473ba 100644 --- a/ui/app/AppLayouts/Timeline/TimelineLayout.qml +++ b/ui/app/AppLayouts/Timeline/TimelineLayout.qml @@ -22,6 +22,7 @@ ScrollView { id: root property RootStore store: RootStore { } + property var chatSectionModule Layout.fillWidth: true Layout.fillHeight: true @@ -219,6 +220,7 @@ ScrollView { MessageContextMenuView { id: msgCntxtMenu store: root.store + chatSectionModule: root.chatSectionModule reactionModel: EmojiReactions { } } } diff --git a/ui/app/AppMain.qml b/ui/app/AppMain.qml index 6a16af4524..53ba527c15 100644 --- a/ui/app/AppMain.qml +++ b/ui/app/AppMain.qml @@ -463,6 +463,9 @@ Item { TimelineLayout { messageStore: appMain.rootStore.messageStore rootStore: appMain.rootStore + Component.onCompleted: { + chatCommunitySectionModule = mainModule.getChatSectionModule() + } } } onLoaded: timelineLayoutContainer.item.onActivated() diff --git a/ui/imports/shared/views/chat/MessageContextMenuView.qml b/ui/imports/shared/views/chat/MessageContextMenuView.qml index d360045b52..90523f6ddb 100644 --- a/ui/imports/shared/views/chat/MessageContextMenuView.qml +++ b/ui/imports/shared/views/chat/MessageContextMenuView.qml @@ -19,6 +19,10 @@ StatusPopupMenu { width: emojiContainer.visible ? emojiContainer.width : 176 property var store + // Important: + // We're here in case of ChatSection + // This module is set from `ChatLayout` (each `ChatLayout` has its own chatSectionModule) + property var chatSectionModule property string messageId property int contentType property bool isProfile: false @@ -256,7 +260,7 @@ StatusPopupMenu { onTriggered: { if (root.isProfile) { Global.changeAppSectionBySectionType(Constants.appSection.chat) - root.store.chatsModelInst.channelView.joinPrivateChat(fromAuthor, "") + chatSectionModule.createOneToOneChat(fromAuthor, "") } else { showReplyArea() } diff --git a/ui/imports/utils/Utils.qml b/ui/imports/utils/Utils.qml index 1c029c779d..370efd8891 100644 --- a/ui/imports/utils/Utils.qml +++ b/ui/imports/utils/Utils.qml @@ -416,7 +416,9 @@ QtObject { .arg(isChatKey(pk) ? utilsModel.generateAlias(pk) : ("@" + removeStatusEns(pk))) result.callback = function () { if(isChatKey(pk)){ - chatsModel.channelView.joinPrivateChat(pk, ""); + // TODO refector those to call a store (somehow) + let chatCommunitySectionModule = mainModule.getChatSectionModule() + chatCommunitySectionModule.createOneToOneChat(pk, "") } else { chatsModel.channelView.joinWithENS(pk); }