From f9335c09698c899213b5df591777a7d3924dd4c2 Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Tue, 14 Jul 2020 11:35:21 -0400 Subject: [PATCH] feat: retry sending messages --- src/app/chat/core.nim | 3 +- src/app/chat/signal_handling.nim | 6 ++++ src/app/chat/view.nim | 10 ++++++- src/app/chat/views/message_list.nim | 30 +++++++++++++++++-- src/nim_status_client.nim | 1 + src/signals/core.nim | 4 ++- src/signals/expired.nim | 10 +++++++ src/signals/types.nim | 3 ++ src/status/chat.nim | 3 ++ src/status/libstatus/chat.nim | 3 ++ src/status/messages.nim | 6 ++-- .../Chat/ChatColumn/ChatMessages.qml | 1 + ui/app/AppLayouts/Chat/ChatColumn/Message.qml | 1 + .../MessageComponents/CompactMessage.qml | 7 ++++- .../MessageComponents/NormalMessage.qml | 9 ++++++ .../ChatColumn/MessageComponents/Retry.qml | 19 ++++++++++++ .../MessageComponents/SentMessage.qml | 2 +- 17 files changed, 108 insertions(+), 10 deletions(-) create mode 100644 src/signals/expired.nim create mode 100644 ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/Retry.qml diff --git a/src/app/chat/core.nim b/src/app/chat/core.nim index 6d7246f8e3..4d3a9468d3 100644 --- a/src/app/chat/core.nim +++ b/src/app/chat/core.nim @@ -6,7 +6,7 @@ import ../../signals/types import ../../status/libstatus/types as status_types import ../../status/libstatus/wallet as status_wallet import ../../status/[chat, contacts, status] -import view, views/channels_list +import view, views/channels_list, views/message_list from eth/common/utils import parseAddress @@ -63,5 +63,6 @@ method onSignal(self: ChatController, data: Signal) = of SignalType.Message: handleMessage(self, MessageSignal(data)) of SignalType.DiscoverySummary: handleDiscoverySummary(self, DiscoverySummarySignal(data)) of SignalType.EnvelopeSent: handleEnvelopeSent(self, EnvelopeSentSignal(data)) + of SignalType.EnvelopeExpired: handleEnvelopeExpired(self, EnvelopeExpiredSignal(data)) else: warn "Unhandled signal received", signalType = data.signalType diff --git a/src/app/chat/signal_handling.nim b/src/app/chat/signal_handling.nim index ddcc42436e..a28bbbad17 100644 --- a/src/app/chat/signal_handling.nim +++ b/src/app/chat/signal_handling.nim @@ -8,3 +8,9 @@ proc handleDiscoverySummary(self: ChatController, data: DiscoverySummarySignal) proc handleEnvelopeSent(self: ChatController, data: EnvelopeSentSignal) = self.status.messages.updateStatus(data.messageIds) + +proc handleEnvelopeExpired(self: ChatController, data: EnvelopeExpiredSignal) = + for messageId in data.messageIds: + if self.status.messages.messages.hasKey(messageId): + let chatId = self.status.messages.messages[messageId].chatId + self.view.messageList[chatId].checkTimeout(messageId) diff --git a/src/app/chat/view.nim b/src/app/chat/view.nim index db534edf97..5d971c4cc7 100644 --- a/src/app/chat/view.nim +++ b/src/app/chat/view.nim @@ -23,7 +23,7 @@ QtObject: chats*: ChannelsList currentSuggestions*: SuggestionsList callResult: string - messageList: Table[string, ChatMessageList] + messageList*: Table[string, ChatMessageList] activeChannel*: ChatItemView stickerPacks*: StickerPackList recentStickers*: StickerList @@ -98,6 +98,14 @@ QtObject: self.status.chat.sendMessage(self.activeChannel.id, m, replyTo) + proc verifyMessageSent*(self: ChatsView, data: string) {.slot.} = + let messageData = data.parseJson + self.messageList[messageData["chatId"].getStr].checkTimeout(messageData["id"].getStr) + + proc resendMessage*(self: ChatsView, chatId: string, messageId: string) {.slot.} = + self.status.chat.resendMessage(messageId) + self.messageList[chatId].resetTimeOut(messageId) + proc activeChannelChanged*(self: ChatsView) {.signal.} proc userNameOrAlias*(self: ChatsView, pubKey: string): string {.slot.} = diff --git a/src/app/chat/views/message_list.nim b/src/app/chat/views/message_list.nim index 37f6521d02..4ea8e39379 100644 --- a/src/app/chat/views/message_list.nim +++ b/src/app/chat/views/message_list.nim @@ -1,4 +1,4 @@ -import NimQml, Tables +import NimQml, Tables, sets import ../../../status/status import ../../../status/accounts import ../../../status/chat @@ -26,6 +26,7 @@ type PlainText = UserRole + 15 Index = UserRole + 16 ImageUrls = UserRole + 17 + Timeout = UserRole + 18 QtObject: type @@ -33,10 +34,12 @@ QtObject: messages*: seq[Message] status: Status messageIndex: Table[string, int] + timedoutMessages: HashSet[string] proc delete(self: ChatMessageList) = self.messages = @[] self.messageIndex = initTable[string, int]() + self.timedoutMessages = initHashSet[string]() self.QAbstractListModel.delete proc setup(self: ChatMessageList) = @@ -53,9 +56,30 @@ QtObject: new(result, delete) result.messages = @[result.chatIdentifier(chatId)] result.messageIndex = initTable[string, int]() + result.timedoutMessages = initHashSet[string]() result.status = status result.setup + proc resetTimeOut*(self: ChatMessageList, messageId: string) = + if not self.messageIndex.hasKey(messageId): return + let msgIdx = self.messageIndex[messageId] + self.timedoutMessages.excl(messageId) + let topLeft = self.createIndex(msgIdx, 0, nil) + let bottomRight = self.createIndex(msgIdx, 0, nil) + self.dataChanged(topLeft, bottomRight, @[ChatMessageRoles.Timeout.int]) + + proc checkTimeout*(self: ChatMessageList, messageId: string) = + if not self.messageIndex.hasKey(messageId): return + + let msgIdx = self.messageIndex[messageId] + if self.messages[msgIdx].outgoingStatus != "sending": return + + self.timedoutMessages.incl(messageId) + + let topLeft = self.createIndex(msgIdx, 0, nil) + let bottomRight = self.createIndex(msgIdx, 0, nil) + self.dataChanged(topLeft, bottomRight, @[ChatMessageRoles.Timeout.int]) + method rowCount(self: ChatMessageList, index: QModelIndex = nil): int = return self.messages.len @@ -84,6 +108,7 @@ QtObject: of ChatMessageRoles.ResponseTo: result = newQVariant(message.responseTo) of ChatMessageRoles.Index: result = newQVariant(index.row) of ChatMessageRoles.ImageUrls: result = newQVariant(message.imageUrls) + of ChatMessageRoles.Timeout: result = newQVariant(self.timedoutMessages.contains(message.id)) method roleNames(self: ChatMessageList): Table[int, string] = { @@ -103,7 +128,8 @@ QtObject: ChatMessageRoles.OutgoingStatus.int: "outgoingStatus", ChatMessageRoles.ResponseTo.int: "responseTo", ChatMessageRoles.Index.int: "index", - ChatMessageRoles.ImageUrls.int: "imageUrls" + ChatMessageRoles.ImageUrls.int: "imageUrls", + ChatMessageRoles.Timeout.int: "timeout" }.toTable proc getMessageIndex(self: ChatMessageList, messageId: string): int {.slot.} = diff --git a/src/nim_status_client.nim b/src/nim_status_client.nim index 4b50954023..7b28f7d58c 100644 --- a/src/nim_status_client.nim +++ b/src/nim_status_client.nim @@ -123,6 +123,7 @@ proc mainProc() = signalController.addSubscriber(SignalType.Message, profile) signalController.addSubscriber(SignalType.DiscoverySummary, chat) signalController.addSubscriber(SignalType.EnvelopeSent, chat) + signalController.addSubscriber(SignalType.EnvelopeExpired, chat) signalController.addSubscriber(SignalType.NodeLogin, login) signalController.addSubscriber(SignalType.NodeLogin, onboarding) signalController.addSubscriber(SignalType.NodeStopped, login) diff --git a/src/signals/core.nim b/src/signals/core.nim index a0a12706da..5a13e0096f 100644 --- a/src/signals/core.nim +++ b/src/signals/core.nim @@ -1,6 +1,6 @@ import NimQml, tables, json, chronicles, strutils, json_serialization import ../status/libstatus/types as status_types -import types, messages, discovery, whisperFilter, envelopes +import types, messages, discovery, whisperFilter, envelopes, expired logScope: topics = "signals" @@ -59,6 +59,8 @@ QtObject: signal = messages.fromEvent(jsonSignal) of SignalType.EnvelopeSent: signal = envelopes.fromEvent(jsonSignal) + of SignalType.EnvelopeExpired: + signal = expired.fromEvent(jsonSignal) of SignalType.WhisperFilterAdded: signal = whisperFilter.fromEvent(jsonSignal) of SignalType.Wallet: diff --git a/src/signals/expired.nim b/src/signals/expired.nim new file mode 100644 index 0000000000..7438445fe6 --- /dev/null +++ b/src/signals/expired.nim @@ -0,0 +1,10 @@ +import json +import types + +proc fromEvent*(jsonSignal: JsonNode): Signal = + var signal:EnvelopeExpiredSignal = EnvelopeExpiredSignal() + if jsonSignal["event"].kind != JNull and jsonSignal["event"].hasKey("ids") and jsonSignal["event"]["ids"].kind != JNull: + for messageId in jsonSignal["event"]["ids"]: + signal.messageIds.add(messageId.getStr) + result = signal + \ No newline at end of file diff --git a/src/signals/types.nim b/src/signals/types.nim index 680835ff03..301574475b 100644 --- a/src/signals/types.nim +++ b/src/signals/types.nim @@ -20,6 +20,9 @@ type WalletSignal* = ref object of Signal type EnvelopeSentSignal* = ref object of Signal messageIds*: seq[string] +type EnvelopeExpiredSignal* = ref object of Signal + messageIds*: seq[string] + # Override this method method onSignal*(self: SignalSubscriber, data: Signal) {.base.} = error "onSignal must be overriden in controller. Signal is unhandled" diff --git a/src/status/chat.nim b/src/status/chat.nim index 67a1027155..58f62749c8 100644 --- a/src/status/chat.nim +++ b/src/status/chat.nim @@ -287,3 +287,6 @@ proc kickGroupMember*(self: ChatModel, chatId: string, pubKey: string) = proc makeAdmin*(self: ChatModel, chatId: string, pubKey: string) = var response = status_chat.makeAdmin(chatId, pubKey) self.emitUpdate(response) + +proc resendMessage*(self: ChatModel, messageId: string) = + discard status_chat.reSendChatMessage(messageId) \ No newline at end of file diff --git a/src/status/libstatus/chat.nim b/src/status/libstatus/chat.nim index 66f597ab00..5147ff41f9 100644 --- a/src/status/libstatus/chat.nim +++ b/src/status/libstatus/chat.nim @@ -128,3 +128,6 @@ proc makeAdmin*(chatId: string, pubKey: string): string = proc updateOutgoingMessageStatus*(messageId: string, status: string): string = result = callPrivateRPC("updateMessageOutgoingStatus".prefix, %* [messageId, status]) # TODO: handle errors + +proc reSendChatMessage*(messageId: string): string = + result = callPrivateRPC("reSendChatMessage".prefix, %*[messageId]) \ No newline at end of file diff --git a/src/status/messages.nim b/src/status/messages.nim index 3482f494e2..16341ee347 100644 --- a/src/status/messages.nim +++ b/src/status/messages.nim @@ -2,9 +2,9 @@ import tables, sets, eventemitter import libstatus/chat type - MessageDetails = object - status: string - chatId: string + MessageDetails* = object + status*: string + chatId*: string MessagesModel* = ref object events*: EventEmitter diff --git a/ui/app/AppLayouts/Chat/ChatColumn/ChatMessages.qml b/ui/app/AppLayouts/Chat/ChatColumn/ChatMessages.qml index a6ff4d2b0a..91885c716d 100644 --- a/ui/app/AppLayouts/Chat/ChatColumn/ChatMessages.qml +++ b/ui/app/AppLayouts/Chat/ChatColumn/ChatMessages.qml @@ -155,6 +155,7 @@ ScrollView { } appSettings: scrollView.appSettings scrollToBottom: scrollView.scrollToBottom + timeout: model.timeout } } } diff --git a/ui/app/AppLayouts/Chat/ChatColumn/Message.qml b/ui/app/AppLayouts/Chat/ChatColumn/Message.qml index b6e1704a85..f646bcf9ed 100644 --- a/ui/app/AppLayouts/Chat/ChatColumn/Message.qml +++ b/ui/app/AppLayouts/Chat/ChatColumn/Message.qml @@ -19,6 +19,7 @@ Item { property string responseTo: "" property string messageId: "" property int prevMessageIndex: -1 + property bool timeout: false property string authorCurrentMsg: "authorCurrentMsg" property string authorPrevMsg: "authorPrevMsg" diff --git a/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/CompactMessage.qml b/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/CompactMessage.qml index df3a2d2db6..98177ffe73 100644 --- a/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/CompactMessage.qml +++ b/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/CompactMessage.qml @@ -91,12 +91,17 @@ Item { SentMessage { id: sentMessage - visible: isCurrentUser && outgoingStatus != "sent" anchors.verticalCenter: chatTime.verticalCenter anchors.left: chatTime.right anchors.rightMargin: 5 } + Retry { + id: retry + anchors.right: chatTime.right + anchors.rightMargin: 5 + } + Loader { id: imageLoader active: showImages diff --git a/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/NormalMessage.qml b/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/NormalMessage.qml index f91eea1c08..be1c6a141b 100644 --- a/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/NormalMessage.qml +++ b/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/NormalMessage.qml @@ -120,6 +120,15 @@ Item { anchors.right: chatTime.left anchors.rightMargin: 5 } + + Retry { + id: retry + anchors.top: chatTime.top + anchors.right: chatTime.left + anchors.rightMargin: 5 + anchors.bottomMargin: Style.current.padding + } + Loader { id: imageLoader active: showImages diff --git a/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/Retry.qml b/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/Retry.qml new file mode 100644 index 0000000000..2ba6912936 --- /dev/null +++ b/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/Retry.qml @@ -0,0 +1,19 @@ + +import QtQuick 2.3 +import "../../../../../shared" +import "../../../../../imports" + +StyledText { + id: retryLbl + color: Style.current.red + text: qsTr("Resend") + font.pixelSize: 12 + visible: isCurrentUser && timeout + MouseArea { + cursorShape: Qt.PointingHandCursor + anchors.fill: parent + onClicked: { + chatsModel.resendMessage(chatId, messageId) + } + } +} \ No newline at end of file diff --git a/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/SentMessage.qml b/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/SentMessage.qml index 13977a4e43..fb40f50d8c 100644 --- a/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/SentMessage.qml +++ b/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/SentMessage.qml @@ -4,7 +4,7 @@ import "../../../../../imports" StyledText { id: sentMessage - visible: isCurrentUser && (isEmoji || isMessage || isSticker) + visible: isCurrentUser && !timeout && (isEmoji || isMessage || isSticker) color: Style.current.darkGrey text: outgoingStatus == "sent" ? //% "Sent"