From 8ca51c34cc64b242a7f5fac4400efa7e2542c960 Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Thu, 20 Jun 2024 18:46:12 -0400 Subject: [PATCH] refactor(messages): make message sending async (#15237) Fixes #12411 Makes message sending async and adds a placeholder "Sending.." message --- .../chat_content/input_area/controller.nim | 18 ++- .../chat_content/input_area/io_interface.nim | 8 +- .../chat_content/input_area/module.nim | 8 +- .../chat_content/input_area/view.nim | 25 +++- .../chat_content/messages/controller.nim | 4 +- .../chat_content/messages/io_interface.nim | 2 +- .../chat_content/messages/module.nim | 4 +- .../chat_content/messages/view.nim | 6 +- src/app/modules/main/stickers/controller.nim | 2 +- src/app_service/service/chat/async_tasks.nim | 74 ++++++++++++ src/app_service/service/chat/service.nim | 113 ++++++++++++------ .../service/stickers/async_tasks.nim | 35 ++++++ src/app_service/service/stickers/service.nim | 38 ++++-- src/backend/chat.nim | 9 +- .../AppLayouts/Chat/views/ChatColumnView.qml | 30 +++-- .../Chat/views/ChatMessagesView.qml | 7 +- 16 files changed, 298 insertions(+), 85 deletions(-) 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 99c72b3807..d0843a34aa 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 @@ -95,6 +95,18 @@ proc init*(self: Controller) = self.unfurlingPlanActiveRequest = "" self.handleUnfurlingPlan(self.unfurlingPlanActiveRequestUnfurlAfter) + self.events.on(SIGNAL_SENDING_SUCCESS) do(e:Args): + let args = MessageSendingSuccess(e) + if self.chatId != args.chat.id: + return + self.delegate.onSendingMessageSuccess() + + self.events.on(SIGNAL_SENDING_FAILED) do(e:Args): + let args = MessageSendingFailure(e) + if self.chatId != args.chatId: + return + self.delegate.onSendingMessageFailure() + proc getChatId*(self: Controller): string = return self.chatId @@ -116,9 +128,9 @@ proc sendImages*(self: Controller, msg: string, replyTo: string, preferredUsername: string = "", - linkPreviews: seq[LinkPreview]): string = + linkPreviews: seq[LinkPreview]) = self.resetLinkPreviews() - self.chatService.sendImages( + self.chatService.asyncSendImages( self.chatId, imagePathsAndDataJson, msg, @@ -134,7 +146,7 @@ proc sendChatMessage*(self: Controller, preferredUsername: string = "", linkPreviews: seq[LinkPreview]) = self.resetLinkPreviews() - self.chatService.sendChatMessage(self.chatId, + self.chatService.asyncSendChatMessage(self.chatId, msg, replyTo, contentType, diff --git a/src/app/modules/main/chat_section/chat_content/input_area/io_interface.nim b/src/app/modules/main/chat_section/chat_content/input_area/io_interface.nim index c4ca48d9bd..0f614d7392 100644 --- a/src/app/modules/main/chat_section/chat_content/input_area/io_interface.nim +++ b/src/app/modules/main/chat_section/chat_content/input_area/io_interface.nim @@ -22,7 +22,7 @@ method getModuleAsVariant*(self: AccessInterface): QVariant {.base.} = method sendChatMessage*(self: AccessInterface, msg: string, replyTo: string, contentType: int, linkPreviews: seq[LinkPreview]) {.base.} = raise newException(ValueError, "No implementation available") -method sendImages*(self: AccessInterface, imagePathsJson: string, msg: string, replyTo: string, linkPreviews: seq[LinkPreview]): string {.base.} = +method sendImages*(self: AccessInterface, imagePathsJson: string, msg: string, replyTo: string, linkPreviews: seq[LinkPreview]) {.base.} = raise newException(ValueError, "No implementation available") method requestAddressForTransaction*(self: AccessInterface, fromAddress: string, amount: string, tokenAddress: string) {.base.} = @@ -138,3 +138,9 @@ method setUrls*(self: AccessInterface, urls: seq[string]) {.base.} = method getContactDetails*(self: AccessInterface, contactId: string): ContactDetails {.base.} = raise newException(ValueError, "No implementation available") + +method onSendingMessageSuccess*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method onSendingMessageFailure*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") 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 0db83695a9..51a4a2001d 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 @@ -65,7 +65,7 @@ method getModuleAsVariant*(self: Module): QVariant = proc getChatId*(self: Module): string = return self.controller.getChatId() -method sendImages*(self: Module, imagePathsAndDataJson: string, msg: string, replyTo: string, linkPreviews: seq[LinkPreview]): string = +method sendImages*(self: Module, imagePathsAndDataJson: string, msg: string, replyTo: string, linkPreviews: seq[LinkPreview]) = self.controller.sendImages(imagePathsAndDataJson, msg, replyTo, singletonInstance.userProfile.getPreferredName(), linkPreviews) method sendChatMessage*( @@ -136,3 +136,9 @@ method setUrls*(self: Module, urls: seq[string]) = method getContactDetails*(self: Module, contactId: string): ContactDetails = return self.controller.getContactDetails(contactId) + +method onSendingMessageSuccess*(self: Module) = + self.view.emitSendingMessageSuccess() + +method onSendingMessageFailure*(self: Module) = + self.view.emitSendingMessageFailure() 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 049732456f..004698836f 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 @@ -15,10 +15,13 @@ QtObject: linkPreviewModelVariant: QVariant urlsModel: urls_model.Model urlsModelVariant: QVariant + sendingInProgress: bool askToEnableLinkPreview: bool emojiReactionsModel: emoji_reactions_model.Model emojiReactionsModelVariant: QVariant + proc setSendingInProgress*(self: View, value: bool) + proc delete*(self: View) = self.QObject.delete self.preservedPropertiesVariant.delete @@ -53,11 +56,13 @@ QtObject: replyTo: string, contentType: int) {.slot.} = # FIXME: Update this when `setText` is async. + self.setSendingInProgress(true) self.delegate.setText(msg, false) self.delegate.sendChatMessage(msg, replyTo, contentType, self.linkPreviewModel.getUnfuledLinkPreviews()) - proc sendImages*(self: View, imagePathsAndDataJson: string, msg: string, replyTo: string): string {.slot.} = + proc sendImages*(self: View, imagePathsAndDataJson: string, msg: string, replyTo: string) {.slot.} = # FIXME: Update this when `setText` is async. + self.setSendingInProgress(true) self.delegate.setText(msg, false) self.delegate.sendImages(imagePathsAndDataJson, msg, replyTo, self.linkPreviewModel.getUnfuledLinkPreviews()) @@ -154,3 +159,21 @@ QtObject: QtProperty[QVariant] urlsModel: read = getUrlsModel notify = urlsModelChanged + + proc sendingInProgressChanged(self: View) {.signal.} + proc getSendingInProgress*(self: View): bool {.slot.} = + return self.sendingInProgress + + QtProperty[bool] sendingInProgress: + read = getSendingInProgress + notify = sendingInProgressChanged + + proc setSendingInProgress*(self: View, value: bool) = + self.sendingInProgress = value + self.sendingInProgressChanged() + + proc emitSendingMessageSuccess*(self: View) = + self.setSendingInProgress(false) + + proc emitSendingMessageFailure*(self: View) = + self.setSendingInProgress(false) diff --git a/src/app/modules/main/chat_section/chat_content/messages/controller.nim b/src/app/modules/main/chat_section/chat_content/messages/controller.nim index 41b01310c4..27b493ac5b 100644 --- a/src/app/modules/main/chat_section/chat_content/messages/controller.nim +++ b/src/app/modules/main/chat_section/chat_content/messages/controller.nim @@ -79,10 +79,10 @@ proc init*(self: Controller) = self.delegate.onSendingMessageSuccess(args.message) self.events.on(SIGNAL_SENDING_FAILED) do(e:Args): - let args = ChatArgs(e) + let args = MessageSendingFailure(e) if(self.chatId != args.chatId): return - self.delegate.onSendingMessageError() + self.delegate.onSendingMessageError(args.error) self.events.on(SIGNAL_ENVELOPE_SENT) do(e:Args): let args = EnvelopeSentArgs(e) diff --git a/src/app/modules/main/chat_section/chat_content/messages/io_interface.nim b/src/app/modules/main/chat_section/chat_content/messages/io_interface.nim index d536206458..1448c2bc4b 100644 --- a/src/app/modules/main/chat_section/chat_content/messages/io_interface.nim +++ b/src/app/modules/main/chat_section/chat_content/messages/io_interface.nim @@ -60,7 +60,7 @@ method messagesAdded*(self: AccessInterface, messages: seq[MessageDto]) {.base.} method onSendingMessageSuccess*(self: AccessInterface, message: MessageDto) {.base.} = raise newException(ValueError, "No implementation available") -method onSendingMessageError*(self: AccessInterface) {.base.} = +method onSendingMessageError*(self: AccessInterface, error: string) {.base.} = raise newException(ValueError, "No implementation available") method onEnvelopeSent*(self: AccessInterface, messagesIds: seq[string]) {.base.} = 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 632bd67375..eb500cac34 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 @@ -433,8 +433,8 @@ method onSendingMessageSuccess*(self: Module, message: MessageDto) = self.view.emitSendingMessageSuccessSignal() self.removeNewMessagesMarker() -method onSendingMessageError*(self: Module) = - self.view.emitSendingMessageErrorSignal() +method onSendingMessageError*(self: Module, error: string) = + self.view.emitSendingMessageErrorSignal(error) method onEnvelopeSent*(self: Module, messagesIds: seq[string]) = for messageId in messagesIds: diff --git a/src/app/modules/main/chat_section/chat_content/messages/view.nim b/src/app/modules/main/chat_section/chat_content/messages/view.nim index a7825dce8d..8d4c1eab3e 100644 --- a/src/app/modules/main/chat_section/chat_content/messages/view.nim +++ b/src/app/modules/main/chat_section/chat_content/messages/view.nim @@ -106,10 +106,10 @@ QtObject: proc emitSendingMessageSuccessSignal*(self: View) = self.messageSuccessfullySent() - proc sendingMessageFailed*(self: View) {.signal.} + proc sendingMessageFailed*(self: View, error: string) {.signal.} - proc emitSendingMessageErrorSignal*(self: View) = - self.sendingMessageFailed() + proc emitSendingMessageErrorSignal*(self: View, error: string) = + self.sendingMessageFailed(error) proc deleteMessage*(self: View, messageId: string) {.slot.} = self.delegate.deleteMessage(messageId) diff --git a/src/app/modules/main/stickers/controller.nim b/src/app/modules/main/stickers/controller.nim index 637f82614b..3f065fef94 100644 --- a/src/app/modules/main/stickers/controller.nim +++ b/src/app/modules/main/stickers/controller.nim @@ -151,7 +151,7 @@ proc sendSticker*( replyTo: string, sticker: StickerDto, preferredUsername: string) = - self.stickerService.sendSticker(channelId, replyTo, sticker, preferredUsername) + self.stickerService.asyncSendSticker(channelId, replyTo, sticker, preferredUsername) proc wei2Eth*(self: Controller, price: Stuint[256]): string = eth_utils.wei2Eth(price) diff --git a/src/app_service/service/chat/async_tasks.nim b/src/app_service/service/chat/async_tasks.nim index f0f74da5b7..d6dfc5fb35 100644 --- a/src/app_service/service/chat/async_tasks.nim +++ b/src/app_service/service/chat/async_tasks.nim @@ -62,3 +62,77 @@ proc asyncCheckAllChannelsPermissionsTask(argEncoded: string) {.gcsafe, nimcall. "communityId": arg.communityId, "error": e.msg, }) + +type + AsyncSendMessageTaskArg = ref object of QObjectTaskArg + chatId: string + processedMsg: string + replyTo: string + contentType: int + preferredUsername: string + communityId: string + standardLinkPreviews: JsonNode + statusLinkPreviews: JsonNode + +const asyncSendMessageTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let arg = decode[AsyncSendMessageTaskArg](argEncoded) + try: + + let response = status_chat.sendChatMessage( + arg.chatId, + arg.processedMsg, + arg.replyTo, + arg.contentType, + arg.preferredUsername, + arg.standardLinkPreviews, + arg.statusLinkPreviews, + arg.communityId) + + arg.finish(%* { + "response": response, + "chatId": arg.chatId, + "error": "", + }) + except Exception as e: + arg.finish(%* { + "error": e.msg, + "chatId": arg.chatId, + }) + +type + AsyncSendImagesTaskArg = ref object of QObjectTaskArg + chatId: string + imagePathsAndDataJson: string + tempDir: string + msg: string + replyTo: string + preferredUsername: string + linkPreviews: JsonNode + +const asyncSendImagesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let arg = decode[AsyncSendImagesTaskArg](argEncoded) + try: + var images = Json.decode(arg.imagePathsAndDataJson, seq[string]) + var imagePaths: seq[string] = @[] + + for imagePathOrSource in images.mitems: + let imagePath = image_resizer(imagePathOrSource, 2000, arg.tempDir) + if imagePath != "": + imagePaths.add(imagePath) + + let response = status_chat.sendImages(arg.chatId, imagePaths, arg.msg, arg.replyTo, arg.preferredUsername, + arg.linkPreviews) + + for imagePath in imagePaths: + removeFile(imagePath) + + arg.finish(%* { + "response": response, + "chatId": arg.chatId, + "error": "", + }) + except Exception as e: + arg.finish(%* { + "error": e.msg, + "chatId": arg.chatId, + }) diff --git a/src/app_service/service/chat/service.nim b/src/app_service/service/chat/service.nim index 50f1c9ef5f..561f1e583f 100644 --- a/src/app_service/service/chat/service.nim +++ b/src/app_service/service/chat/service.nim @@ -1,10 +1,10 @@ -import NimQml, Tables, json, sequtils, stew/shims/strformat, chronicles, os, strutils, uuids, base64 +import NimQml, Tables, json, sequtils, chronicles, os, strutils, uuids, base64 import std/[times, os] import ../../../app/core/tasks/[qt, threadpool] import ./dto/chat as chat_dto import ../message/dto/message as message_dto -import ../message/dto/link_preview +import ../message/dto/[link_preview, standard_link_preview, status_link_preview] import ../activity_center/dto/notification as notification_dto import ../community/dto/community as community_dto import ../contacts/service as contact_service @@ -47,6 +47,10 @@ type chat*: ChatDto message*: MessageDto + MessageSendingFailure* = ref object of Args + chatId*: string + error*: string + MessageArgs* = ref object of Args id*: string channel*: string @@ -420,61 +424,94 @@ QtObject: error "Error deleting channel", chatId, msg = e.msg return - proc sendImages*(self: Service, + proc asyncSendImages*(self: Service, chatId: string, imagePathsAndDataJson: string, msg: string, replyTo: string, preferredUsername: string = "", - linkPreviews: seq[LinkPreview] = @[]): string = - result = "" + linkPreviews: seq[LinkPreview] = @[]) = + + + let arg = AsyncSendImagesTaskArg( + tptr: asyncSendImagesTask, + vptr: cast[ByteAddress](self.vptr), + slot: "onAsyncSendImagesDone", + chatId: chatId, + imagePathsAndDataJson: imagePathsAndDataJson, + tempDir: TMPDIR, + msg: msg, + replyTo: replyTo, + preferredUsername: preferredUsername, + linkPreviews: %linkPreviews, + ) + + self.threadpool.start(arg) + + proc onAsyncSendImagesDone*(self: Service, rpcResponseJson: string) {.slot.} = + let rpcResponseObj = rpcResponseJson.parseJson try: - var images = Json.decode(imagePathsAndDataJson, seq[string]) - let base64JPGPrefix = "data:image/jpeg;base64," - var imagePaths: seq[string] = @[] - for imagePathOrSource in images.mitems: - let imagePath = image_resizer(imagePathOrSource, 2000, TMPDIR) - if imagePath != "": - imagePaths.add(imagePath) + let errorString = rpcResponseObj{"error"}.getStr() + if errorString != "": + raise newException(CatchableError, errorString) - let response = status_chat.sendImages(chatId, imagePaths, msg, replyTo, preferredUsername, linkPreviews) + let rpcResponse = Json.decode($rpcResponseObj["response"], RpcResponse[JsonNode]) - for imagePath in imagePaths: - removeFile(imagePath) - - discard self.processMessengerResponse(response) + discard self.processMessengerResponse(rpcResponse) except Exception as e: error "Error sending images", msg = e.msg - result = fmt"Error sending images: {e.msg}" + self.events.emit(SIGNAL_SENDING_FAILED, MessageSendingFailure(chatId: rpcResponseObj["chatId"].getStr, error: e.msg)) - proc sendChatMessage*( - self: Service, - chatId: string, - msg: string, - replyTo: string, - contentType: int, - preferredUsername: string = "", - linkPreviews: seq[LinkPreview] = @[], - communityId: string = "") = + proc asyncSendChatMessage*(self: Service, + chatId: string, + msg: string, + replyTo: string, + contentType: int, + preferredUsername: string = "", + linkPreviews: seq[LinkPreview] = @[], + communityId: string = "") = try: let allKnownContacts = self.contactService.getContactsByGroup(ContactsGroup.AllKnownContacts) let processedMsg = message_common.replaceMentionsWithPubKeys(allKnownContacts, msg) - let response = status_chat.sendChatMessage( - chatId, - processedMsg, - replyTo, - contentType, - preferredUsername, - linkPreviews, - communityId) # Only send a community ID for the community invites + let (standardLinkPreviews, statusLinkPreviews) = extractLinkPreviewsLists(linkPreviews) - let (chats, messages) = self.processMessengerResponse(response) - if chats.len == 0 or messages.len == 0: - self.events.emit(SIGNAL_SENDING_FAILED, ChatArgs(chatId: chatId)) + let arg = AsyncSendMessageTaskArg( + tptr: asyncSendMessageTask, + vptr: cast[ByteAddress](self.vptr), + slot: "onAsyncSendMessageDone", + chatId: chatId, + processedMsg: processedMsg, + replyTo: replyTo, + contentType: contentType, + preferredUsername: preferredUsername, + communityId: communityId, # Only send a community ID for the community invites + standardLinkPreviews: %standardLinkPreviews, + statusLinkPreviews: %statusLinkPreviews, + ) + + self.threadpool.start(arg) except Exception as e: error "Error sending message", msg = e.msg + self.events.emit(SIGNAL_SENDING_FAILED, MessageSendingFailure(chatId: chatId, error: e.msg)) + + proc onAsyncSendMessageDone*(self: Service, rpcResponseJson: string) {.slot.} = + let rpcResponseObj = rpcResponseJson.parseJson + try: + + let errorString = rpcResponseObj{"error"}.getStr() + if errorString != "": + raise newException(CatchableError, errorString) + + let rpcResponse = Json.decode($rpcResponseObj["response"], RpcResponse[JsonNode]) + + let (chats, messages) = self.processMessengerResponse(rpcResponse) + if chats.len == 0 or messages.len == 0: + raise newException(CatchableError, "no chat or message returned") + except Exception as e: + error "Error sending message", msg = e.msg + self.events.emit(SIGNAL_SENDING_FAILED, MessageSendingFailure(chatId: rpcResponseObj["chatId"].getStr, error: e.msg)) proc requestAddressForTransaction*(self: Service, chatId: string, fromAddress: string, amount: string, tokenAddress: string) = try: diff --git a/src/app_service/service/stickers/async_tasks.nim b/src/app_service/service/stickers/async_tasks.nim index e209d505e4..efb4fc5a6d 100644 --- a/src/app_service/service/stickers/async_tasks.nim +++ b/src/app_service/service/stickers/async_tasks.nim @@ -93,3 +93,38 @@ proc installStickerPackTask(argEncoded: string) {.gcsafe, nimcall.} = error "Error installing stickers", message = getCurrentExceptionMsg() let tpl: tuple[packId: string, installed: bool] = (arg.packId, installed) arg.finish(tpl) + +type + AsyncSendStickerTaskArg = ref object of QObjectTaskArg + chatId: string + replyTo: string + stickerHash: string + stickerPackId: string + preferredUsername: string + +const asyncSendStickerTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let arg = decode[AsyncSendStickerTaskArg](argEncoded) + try: + let response = status_chat.sendChatMessage( + arg.chatId, + "You can see a nice sticker here!", + arg.replyTo, + ContentType.Sticker.int, + arg.preferredUsername, + standardLinkPreviews = JsonNode(), + statusLinkPreviews = JsonNode(), + communityId = "", # communityId is not necessary when sending a sticker + arg.stickerHash, + arg.stickerPackId, + ) + + arg.finish(%* { + "response": response, + "chatId": arg.chatId, + "error": "", + }) + except Exception as e: + arg.finish(%* { + "error": e.msg, + "chatId": arg.chatId, + }) diff --git a/src/app_service/service/stickers/service.nim b/src/app_service/service/stickers/service.nim index 4480d4157c..c539df1665 100644 --- a/src/app_service/service/stickers/service.nim +++ b/src/app_service/service/stickers/service.nim @@ -345,25 +345,39 @@ QtObject: except RpcException: error "Error removing installed sticker", message = getCurrentExceptionMsg() - proc sendSticker*( + proc asyncSendSticker*( self: Service, chatId: string, replyTo: string, sticker: StickerDto, preferredUsername: string) = - let response = status_chat.sendChatMessage( - chatId, - "Update to latest version to see a nice sticker here!", - replyTo, - ContentType.Sticker.int, - preferredUsername, - linkPreviews = @[], - communityId = "", # communityId is not necessary when sending a sticker - sticker.hash, - sticker.packId) - discard self.chatService.processMessengerResponse(response) + let arg = AsyncSendStickerTaskArg( + tptr: asyncSendStickerTask, + vptr: cast[ByteAddress](self.vptr), + slot: "onAsyncSendStickerDone", + chatId: chatId, + replyTo: replyTo, + stickerHash: sticker.hash, + stickerPackId: sticker.packId, + preferredUsername: preferredUsername, + ) + + self.threadpool.start(arg) self.addStickerToRecent(sticker) + proc onAsyncSendStickerDone*(self: Service, rpcResponseJson: string) {.slot.} = + let rpcResponseObj = rpcResponseJson.parseJson + try: + let errorString = rpcResponseObj{"error"}.getStr() + if errorString != "": + raise newException(CatchableError, errorString) + + let rpcResponse = Json.decode($rpcResponseObj["response"], RpcResponse[JsonNode]) + discard self.chatService.processMessengerResponse(rpcResponse) + except Exception as e: + error "Error sending sticker", msg = e.msg + self.events.emit(SIGNAL_SENDING_FAILED, ChatArgs(chatId: rpcResponseObj["chatId"].getStr)) + proc removeRecentStickers*(self: Service, packId: string) = self.recentStickers.keepItIf(it.packId != packId) diff --git a/src/backend/chat.nim b/src/backend/chat.nim index 9231d0de40..752253e6f1 100644 --- a/src/backend/chat.nim +++ b/src/backend/chat.nim @@ -2,9 +2,6 @@ import json, sequtils, sugar, strutils import core, ../app_service/common/utils import response_type import interpret/cropped_image -import ../app_service/service/message/dto/link_preview -import ../app_service/service/message/dto/standard_link_preview -import ../app_service/service/message/dto/status_link_preview export response_type @@ -59,12 +56,12 @@ proc sendChatMessage*( replyTo: string, contentType: int, preferredUsername: string = "", - linkPreviews: seq[LinkPreview], + standardLinkPreviews: JsonNode, + statusLinkPreviews: JsonNode, communityId: string = "", stickerHash: string = "", stickerPack: string = "0", ): RpcResponse[JsonNode] = - let (standardLinkPreviews, statusLinkPreviews) = extractLinkPreviewsLists(linkPreviews) result = callPrivateRPC("sendChatMessage".prefix, %* [ { "chatId": chatId, @@ -87,7 +84,7 @@ proc sendImages*(chatId: string, msg: string, replyTo: string, preferredUsername: string, - linkPreviews: seq[LinkPreview], + linkPreviews: JsonNode, ): RpcResponse[JsonNode] = let imagesJson = %* images.map(image => %* { diff --git a/ui/app/AppLayouts/Chat/views/ChatColumnView.qml b/ui/app/AppLayouts/Chat/views/ChatColumnView.qml index ccc44c454b..358fb35fde 100644 --- a/ui/app/AppLayouts/Chat/views/ChatColumnView.qml +++ b/ui/app/AppLayouts/Chat/views/ChatColumnView.qml @@ -80,6 +80,8 @@ Item { id: d readonly property var activeChatContentModule: d.getChatContentModule(root.activeChatId) + property bool sendingInProgress: !!d.activeChatContentModule? d.activeChatContentModule.inputAreaModule.sendingInProgress : false + readonly property var urlsList: { if (!d.activeChatContentModule) { return @@ -276,6 +278,7 @@ Item { && root.rootStore.sectionDetails.joined && !root.rootStore.sectionDetails.amIBanned && root.rootStore.isUserAllowedToSendMessage + && !d.sendingInProgress } store: root.rootStore @@ -298,6 +301,9 @@ Item { if (!root.canPost) { return qsTr("Sorry, you don't have permissions to post in this channel.") } + if (d.sendingInProgress) { + return qsTr("Sending...") + } return root.rootStore.chatInputPlaceHolderText } else { return ""; @@ -339,19 +345,19 @@ Item { return } - if (root.rootStore.sendMessage(d.activeChatContentModule.getMyChatId(), - event, - chatInput.getTextWithPublicKeys(), - chatInput.isReply? chatInput.replyMessageId : "", - chatInput.fileUrlsAndSources - )) - { - Global.playSendMessageSound() + if (root.rootStore.sendMessage(d.activeChatContentModule.getMyChatId(), + event, + chatInput.getTextWithPublicKeys(), + chatInput.isReply? chatInput.replyMessageId : "", + chatInput.fileUrlsAndSources + )) + { + Global.playSendMessageSound() - chatInput.setText("") - chatInput.textInput.textFormat = TextEdit.PlainText; - chatInput.textInput.textFormat = TextEdit.RichText; - } + chatInput.setText("") + chatInput.textInput.textFormat = TextEdit.PlainText; + chatInput.textInput.textFormat = TextEdit.RichText; + } } onKeyUpPress: { diff --git a/ui/app/AppLayouts/Chat/views/ChatMessagesView.qml b/ui/app/AppLayouts/Chat/views/ChatMessagesView.qml index 72968f4d52..b158747736 100644 --- a/ui/app/AppLayouts/Chat/views/ChatMessagesView.qml +++ b/ui/app/AppLayouts/Chat/views/ChatMessagesView.qml @@ -86,7 +86,8 @@ Item { chatLogView.positionViewAtBeginning() } - function onSendingMessageFailed() { + function onSendingMessageFailed(error) { + sendingMsgFailedPopup.error = error sendingMsgFailedPopup.open() } @@ -377,9 +378,11 @@ Item { } MessageDialog { + property string error + id: sendingMsgFailedPopup standardButtons: StandardButton.Ok - text: qsTr("Failed to send message.") + text: qsTr("Failed to send message.\n" + error) icon: StandardIcon.Critical }