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 da80f571a8..ecf461586f 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 @@ -265,9 +265,9 @@ proc getOneToOneChatNameAndImage*(self: Controller): proc belongsToCommunity*(self: Controller): bool = return self.belongsToCommunity -proc loadMoreMessages*(self: Controller) = +proc loadMoreMessages*(self: Controller): bool = let limit = self.loadingMessagesPerPageFactor * MESSAGES_PER_PAGE - self.messageService.asyncLoadMoreMessagesForChat(self.chatId, limit) + return self.messageService.asyncLoadMoreMessagesForChat(self.chatId, limit) proc addReaction*(self: Controller, messageId: string, emojiId: int) = self.messageService.addReaction(self.chatId, messageId, emojiId) 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 9f9c7c24c2..3e7f0f2b71 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 @@ -1,4 +1,4 @@ -import NimQml, chronicles, sequtils, uuids, sets, times, tables, system +import NimQml, chronicles, sequtils, uuids, sets, times, tables, system, sugar import io_interface import ../io_interface as delegate_interface import view, controller @@ -354,19 +354,27 @@ proc createChatIdentifierItem(self: Module): Item = bridgeMessage = BridgeMessage(), ) -proc checkIfMessageLoadedAndScrollToItIfItIs(self: Module) = +proc checkIfMessageLoadedAndScroll(self: Module) = let searchedMessageId = self.controller.getSearchedMessageId() - if(searchedMessageId.len > 0): - let index = self.view.model().findIndexForMessageId(searchedMessageId) - if(index != -1): - self.controller.clearSearchedMessageId() - self.controller.resetLoadingMessagesPerPageFactor() - self.view.emitScrollToMessageSignal(index) - self.view.setMessageSearchOngoing(false) - self.reevaluateViewLoadingState() - else: - self.controller.increaseLoadingMessagesPerPageFactor() - self.loadMoreMessages() + + if searchedMessageId.len == 0: + return + + let index = self.view.model().findIndexForMessageId(searchedMessageId) + if index == -1: + self.controller.increaseLoadingMessagesPerPageFactor() + if self.controller.loadMoreMessages(): + warn "failed to start loading more messages" + return + # If failed to `loadMoreMessages`, then the most recent message is already loaded. + # Then message is not found. + + self.controller.clearSearchedMessageId() + self.controller.resetLoadingMessagesPerPageFactor() + if index != -1: + self.view.emitScrollToMessageSignal(index) + self.view.setMessageSearchOngoing(false) + self.reevaluateViewLoadingState() proc currentUserWalletContainsAddress(self: Module, address: string): bool = if (address.len == 0): @@ -378,13 +386,14 @@ proc currentUserWalletContainsAddress(self: Module, address: string): bool = return false method reevaluateViewLoadingState*(self: Module) = - self.view.setLoading(not self.initialMessagesLoaded or - not self.firstUnseenMessageState.initialized or - self.firstUnseenMessageState.fetching or - self.view.getMessageSearchOngoing()) + let loading = not self.initialMessagesLoaded or + not self.firstUnseenMessageState.initialized or + self.firstUnseenMessageState.fetching or + self.view.getMessageSearchOngoing() + self.view.setLoading(loading) method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: seq[ReactionDto]) = - if(messages.len > 0): + if messages.len > 0: var viewItems = self.createMessageItemsFromMessageDtos(messages, reactions) if self.controller.getChatDetails().hasMoreMessagesToRequest(): @@ -397,7 +406,7 @@ method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: se self.view.model().resetNewMessagesMarker() # check if this loading was caused by the click on a messages from the app search result - self.checkIfMessageLoadedAndScrollToItIfItIs() + self.checkIfMessageLoadedAndScroll() self.initialMessagesLoaded = true self.reevaluateViewLoadingState() @@ -433,7 +442,7 @@ method onMessageDelivered*(self: Module, messageId: string) = self.view.model().itemDelivered(messageId) method loadMoreMessages*(self: Module) = - self.controller.loadMoreMessages() + discard self.controller.loadMoreMessages() method toggleReaction*(self: Module, messageId: string, emojiId: int) = var emojiIdAsEnum: EmojiId @@ -627,7 +636,7 @@ method updateChatFetchMoreMessages*(self: Module) = proc switchToMessage*(self: Module, messageId: string) = let index = self.view.model().findIndexForMessageId(messageId) - if(index != -1): + if index != -1: self.controller.clearSearchedMessageId() self.view.emitSwitchToMessageSignal(index) else: @@ -641,10 +650,10 @@ method scrollToMessage*(self: Module, messageId: string) = return self.getMessageRequestId = self.controller.asyncGetMessageById(messageId) + self.controller.setSearchedMessageId(messageId) self.view.setMessageSearchOngoing(true) method onGetMessageById*(self: Module, requestId: UUID, messageId: string, message: MessageDto, errorMessage: string) = - if self.getMessageRequestId != requestId: return @@ -653,8 +662,13 @@ method onGetMessageById*(self: Module, requestId: UUID, messageId: string, messa self.view.setMessageSearchOngoing(false) return - self.controller.setSearchedMessageId(messageId) - self.checkIfMessageLoadedAndScrollToItIfItIs() + if message.contentType == ContentType.ContactIdentityVerification or + message.contentType == ContentType.ContactRequest: + warn "attempted to scroll to a non-displayed message", messageId, contentType = $message.contentType + self.view.setMessageSearchOngoing(false) + return + + self.checkIfMessageLoadedAndScroll() self.reevaluateViewLoadingState() method requestMoreMessages*(self: Module) = diff --git a/src/app_service/service/message/async_tasks.nim b/src/app_service/service/message/async_tasks.nim index d19a72c318..2e39212fd7 100644 --- a/src/app_service/service/message/async_tasks.nim +++ b/src/app_service/service/message/async_tasks.nim @@ -33,22 +33,19 @@ const asyncFetchChatMessagesTask: Task = proc(argEncoded: string) {.gcsafe, nimc } # handle messages - if(arg.msgCursor != CURSOR_VALUE_IGNORE): - var messagesArr: JsonNode - var messagesCursor: JsonNode - let msgsResponse = status_go.fetchMessages(arg.chatId, arg.msgCursor, arg.limit) - discard msgsResponse.result.getProp("cursor", messagesCursor) - discard msgsResponse.result.getProp("messages", messagesArr) - responseJson["messages"] = messagesArr - responseJson["messagesCursor"] = messagesCursor + var messagesArr: JsonNode + var messagesCursor: JsonNode + let msgsResponse = status_go.fetchMessages(arg.chatId, arg.msgCursor, arg.limit) + discard msgsResponse.result.getProp("cursor", messagesCursor) + discard msgsResponse.result.getProp("messages", messagesArr) + responseJson["messages"] = messagesArr + responseJson["messagesCursor"] = messagesCursor # handle reactions - if(arg.msgCursor != CURSOR_VALUE_IGNORE): - # messages and reactions are using the same cursor - var reactionsArr: JsonNode - let rResponse = status_go.fetchReactions(arg.chatId, arg.msgCursor, arg.limit) - reactionsArr = rResponse.result - responseJson["reactions"] = reactionsArr + var reactionsArr: JsonNode + let rResponse = status_go.fetchReactions(arg.chatId, arg.msgCursor, arg.limit) + reactionsArr = rResponse.result + responseJson["reactions"] = reactionsArr arg.finish(responseJson) @@ -61,15 +58,14 @@ const asyncFetchPinnedChatMessagesTask: Task = proc(argEncoded: string) {.gcsafe var responseJson = %*{ "chatId": arg.chatId } - #handle pinned messages - if(arg.msgCursor != CURSOR_VALUE_IGNORE): - var pinnedMsgArr: JsonNode - var msgCursor: JsonNode - let pinnedMsgsResponse = status_go.fetchPinnedMessages(arg.chatId, arg.msgCursor, arg.limit) - discard pinnedMsgsResponse.result.getProp("cursor", msgCursor) - discard pinnedMsgsResponse.result.getProp("pinnedMessages", pinnedMsgArr) - responseJson["pinnedMessages"] = pinnedMsgArr - responseJson["pinnedMessagesCursor"] = msgCursor + # handle pinned messages + var pinnedMsgArr: JsonNode + var msgCursor: JsonNode + let pinnedMsgsResponse = status_go.fetchPinnedMessages(arg.chatId, arg.msgCursor, arg.limit) + discard pinnedMsgsResponse.result.getProp("cursor", msgCursor) + discard pinnedMsgsResponse.result.getProp("pinnedMessages", pinnedMsgArr) + responseJson["pinnedMessages"] = pinnedMsgArr + responseJson["pinnedMessagesCursor"] = msgCursor arg.finish(responseJson) @@ -341,4 +337,4 @@ const asyncMarkMessageAsUnreadTask: Task = proc(argEncoded: string) {.gcsafe, ni error "asyncMarkMessageAsUnreadTask failed", message = e.msg responseJson["error"] = %e.msg - arg.finish(responseJson) \ No newline at end of file + arg.finish(responseJson) diff --git a/src/app_service/service/message/message_cursor.nim b/src/app_service/service/message/message_cursor.nim index 4c75fa1662..67ef21d604 100644 --- a/src/app_service/service/message/message_cursor.nim +++ b/src/app_service/service/message/message_cursor.nim @@ -28,6 +28,12 @@ proc setPending*(self: MessageCursor) = proc isFetchable*(self: MessageCursor): bool = return not (self.pending or self.mostRecent) +proc isPending*(self: MessageCursor): bool = + return self.pending + +proc isMostRecent*(self: MessageCursor): bool = + return self.mostRecent + proc isEmpty*(self: MessageCursor): bool = return self.value == "" diff --git a/src/app_service/service/message/service.nim b/src/app_service/service/message/service.nim index 32ae9a89ef..12ce831826 100644 --- a/src/app_service/service/message/service.nim +++ b/src/app_service/service/message/service.nim @@ -39,7 +39,6 @@ logScope: let NEW_LINE = re"\n|\r" #must be defined as let, not const const MESSAGES_PER_PAGE* = 20 const MESSAGES_PER_PAGE_MAX* = 40 -const CURSOR_VALUE_IGNORE = "ignore" const WEEK_AS_MILLISECONDS = initDuration(seconds = 60*60*24*7).inMilliSeconds # Signals which may be emitted by this service: @@ -213,19 +212,21 @@ QtObject: return self.pinnedMsgCursor[chatId] - proc asyncLoadMoreMessagesForChat*(self: Service, chatId: string, limit = MESSAGES_PER_PAGE) = + proc asyncLoadMoreMessagesForChat*(self: Service, chatId: string, limit = MESSAGES_PER_PAGE): bool = if (chatId.len == 0): error "empty chat id", procName="asyncLoadMoreMessagesForChat" - return + return false let msgCursor = self.initOrGetMessageCursor(chatId) - let msgCursorValue = if (msgCursor.isFetchable()): msgCursor.getValue() else: CURSOR_VALUE_IGNORE - if(msgCursorValue == CURSOR_VALUE_IGNORE): - return + if msgCursor.isPending(): + return true - if(msgCursorValue != CURSOR_VALUE_IGNORE): - msgCursor.setPending() + if msgCursor.isMostRecent(): + return false + + let msgCursorValue = msgCursor.getValue() + msgCursor.setPending() let arg = AsyncFetchChatMessagesTaskArg( tptr: cast[ByteAddress](asyncFetchChatMessagesTask), @@ -237,6 +238,7 @@ QtObject: ) self.threadpool.start(arg) + return true proc asyncLoadPinnedMessagesForChat*(self: Service, chatId: string) = if (chatId.len == 0): @@ -244,11 +246,10 @@ QtObject: return let pinnedMsgCursor = self.initOrGetPinnedMessageCursor(chatId) - let pinnedMsgCursorValue = if (pinnedMsgCursor.isFetchable()): pinnedMsgCursor.getValue() else: CURSOR_VALUE_IGNORE - - if(pinnedMsgCursorValue == CURSOR_VALUE_IGNORE): + if not pinnedMsgCursor.isFetchable(): return + let pinnedMsgCursorValue = pinnedMsgCursor.getValue() pinnedMsgCursor.setPending() let arg = AsyncFetchChatMessagesTaskArg( @@ -263,7 +264,7 @@ QtObject: self.threadpool.start(arg) proc asyncLoadInitialMessagesForChat*(self: Service, chatId: string) = - if(self.isChatCursorInitialized(chatId)): + if self.isChatCursorInitialized(chatId): let data = MessagesLoadedArgs(chatId: chatId, messages: @[], reactions: @[]) @@ -271,7 +272,7 @@ QtObject: self.events.emit(SIGNAL_MESSAGES_LOADED, data) return - self.asyncLoadMoreMessagesForChat(chatId) + discard self.asyncLoadMoreMessagesForChat(chatId) proc handleMessagesUpdate(self: Service, chats: var seq[ChatDto], messages: var seq[MessageDto]) = # We included `chats` in this condition cause that's the form how `status-go` sends updates. @@ -445,7 +446,7 @@ QtObject: self.events.on(SignalType.DiscordChannelImportFinished.event) do(e: Args): var receivedData = DiscordChannelImportFinishedSignal(e) self.resetMessageCursor(receivedData.channelId) - self.asyncLoadMoreMessagesForChat(receivedData.channelId) + discard self.asyncLoadMoreMessagesForChat(receivedData.channelId) self.events.on(SIGNAL_CHAT_LEFT) do(e: Args): var chatArg = ChatArgs(e) diff --git a/ui/app/mainui/activitycenter/views/ActivityNotificationContactRequest.qml b/ui/app/mainui/activitycenter/views/ActivityNotificationContactRequest.qml index 2bec3c84f2..8ce8e3ae46 100644 --- a/ui/app/mainui/activitycenter/views/ActivityNotificationContactRequest.qml +++ b/ui/app/mainui/activitycenter/views/ActivityNotificationContactRequest.qml @@ -66,6 +66,10 @@ ActivityNotificationMessage { } } + onMessageClicked: { + root.openProfilePopup() + } + Component { id: reviewContactRequestPopupComponent diff --git a/ui/app/mainui/activitycenter/views/ActivityNotificationContactVerification.qml b/ui/app/mainui/activitycenter/views/ActivityNotificationContactVerification.qml index 54029f5507..a88ee4d166 100644 --- a/ui/app/mainui/activitycenter/views/ActivityNotificationContactVerification.qml +++ b/ui/app/mainui/activitycenter/views/ActivityNotificationContactVerification.qml @@ -52,6 +52,10 @@ ActivityNotificationMessage { ctaComponent: isOutgoingMessage ? outgoingContactVerificationCta : incomingContactVerificationCta + onMessageClicked: { + root.openProfilePopup() + } + Component { id: outgoingContactVerificationCta diff --git a/ui/app/mainui/activitycenter/views/ActivityNotificationMessage.qml b/ui/app/mainui/activitycenter/views/ActivityNotificationMessage.qml index 559b3b2850..275141eb07 100644 --- a/ui/app/mainui/activitycenter/views/ActivityNotificationMessage.qml +++ b/ui/app/mainui/activitycenter/views/ActivityNotificationMessage.qml @@ -84,8 +84,7 @@ ActivityNotificationBase { hoverEnabled: root.messageBadgeComponent cursorShape: Qt.PointingHandCursor onClicked: { - root.activityCenterStore.switchTo(notification) - root.closeActivityCenter() + root.messageClicked() } SimplifiedMessageView { diff --git a/ui/imports/shared/views/chat/ChatContextMenuView.qml b/ui/imports/shared/views/chat/ChatContextMenuView.qml index e7378e051c..3a6c086b01 100644 --- a/ui/imports/shared/views/chat/ChatContextMenuView.qml +++ b/ui/imports/shared/views/chat/ChatContextMenuView.qml @@ -125,13 +125,26 @@ StatusMenu { } } - StatusAction { - objectName: "chatFetchMessagesMenuItem" - text: qsTr("Fetch messages") - icon.name: "download" + + StatusMenu { + title: qsTr("Debug actions") enabled: root.showDebugOptions - onTriggered: { - root.requestMoreMessages(root.chatId) + + StatusAction { + text: root.isCommunityChat ? qsTr("Copy channel ID") : qsTr("Copy chat ID") + icon.name: "copy" + onTriggered: { + Utils.copyToClipboard(root.chatId) + } + } + + StatusAction { + objectName: "chatFetchMessagesMenuItem" + text: qsTr("Fetch messages") + icon.name: "download" + onTriggered: { + root.requestMoreMessages(root.chatId) + } } }