From 10f6d9e89b175182c899a195d3e3645986914922 Mon Sep 17 00:00:00 2001 From: Pascal Precht <445106+PascalPrecht@users.noreply.github.com> Date: Fri, 8 Apr 2022 10:11:43 +0200 Subject: [PATCH] feat: reload community chats when history messages have been downloaded This introduces the new signal types related to the community archive protocol and makes Status Desktop listen to the download event which is emitted by status-go every time history archives were downloaded. If the downloaded archive covers data within the recent 7 days, it causes Status Desktop to reload the corresponding chats. --- .../core/signals/remote_signals/community.nim | 43 +++++++++++++++ .../signals/remote_signals/signal_type.nim | 8 +++ src/app/core/signals/signals_manager.nim | 8 +++ .../modules/main/chat_section/controller.nim | 18 ++++--- src/app_service/service/message/service.nim | 54 ++++++++++++++++++- src/backend/signals.nim | 8 +++ 6 files changed, 131 insertions(+), 8 deletions(-) diff --git a/src/app/core/signals/remote_signals/community.nim b/src/app/core/signals/remote_signals/community.nim index 84afb0e990..9e9ac93cf8 100644 --- a/src/app/core/signals/remote_signals/community.nim +++ b/src/app/core/signals/remote_signals/community.nim @@ -8,7 +8,50 @@ import signal_type type CommunitySignal* = ref object of Signal community*: CommunityDto +type HistoryArchivesSignal* = ref object of Signal + communityId*: string + begin*: int + to*: int + proc fromEvent*(T: type CommunitySignal, event: JsonNode): CommunitySignal = result = CommunitySignal() result.signalType = SignalType.CommunityFound result.community = event["event"].toCommunityDto() + +proc createFromEvent*(T: type HistoryArchivesSignal, event: JsonNode): HistoryArchivesSignal = + result = HistoryArchivesSignal() + result.communityId = event["event"]{"communityId"}.getStr() + result.begin = event["event"]{"from"}.getInt() + result.to = event["event"]{"to"}.getInt() + +proc historyArchivesProtocolEnabledFromEvent*(T: type HistoryArchivesSignal, event: JsonNode): HistoryArchivesSignal = + result = HistoryArchivesSignal.createFromEvent(event) + result.signalType = SignalType.HistoryArchivesProtocolEnabled + +proc historyArchivesProtocolDisabledFromEvent*(T: type HistoryArchivesSignal, event: JsonNode): HistoryArchivesSignal = + result = HistoryArchivesSignal.createFromEvent(event) + result.signalType = SignalType.HistoryArchivesProtocolDisabled + +proc creatingHistoryArchivesFromEvent*(T: type HistoryArchivesSignal, event: JsonNode): HistoryArchivesSignal = + result = HistoryArchivesSignal.createFromEvent(event) + result.signalType = SignalType.CreatingHistoryArchives + +proc historyArchivesCreatedFromEvent*(T: type HistoryArchivesSignal, event: JsonNode): HistoryArchivesSignal = + result = HistoryArchivesSignal.createFromEvent(event) + result.signalType = SignalType.HistoryArchivesCreated + +proc noHistoryArchivesCreatedFromEvent*(T: type HistoryArchivesSignal, event: JsonNode): HistoryArchivesSignal = + result = HistoryArchivesSignal.createFromEvent(event) + result.signalType = SignalType.NoHistoryArchivesCreated + +proc historyArchivesSeedingFromEvent*(T: type HistoryArchivesSignal, event: JsonNode): HistoryArchivesSignal = + result = HistoryArchivesSignal.createFromEvent(event) + result.signalType = SignalType.HistoryArchivesSeeding + +proc historyArchivesUnseededFromEvent*(T: type HistoryArchivesSignal, event: JsonNode): HistoryArchivesSignal = + result = HistoryArchivesSignal.createFromEvent(event) + result.signalType = SignalType.HistoryArchivesUnseeded + +proc historyArchiveDownloadedFromEvent*(T: type HistoryArchivesSignal, event: JsonNode): HistoryArchivesSignal = + result = HistoryArchivesSignal.createFromEvent(event) + result.signalType = SignalType.HistoryArchiveDownloaded diff --git a/src/app/core/signals/remote_signals/signal_type.nim b/src/app/core/signals/remote_signals/signal_type.nim index 0684f6c3e3..5da4d0828c 100644 --- a/src/app/core/signals/remote_signals/signal_type.nim +++ b/src/app/core/signals/remote_signals/signal_type.nim @@ -29,6 +29,14 @@ type SignalType* {.pure.} = enum KeycardConnected = "keycard.connected" MailserverAvailable = "mailserver.available" MailserverChanged = "mailserver.changed" + HistoryArchivesProtocolEnabled = "community.historyArchivesProtocolEnabled" + HistoryArchivesProtocolDisabled = "community.historyArchivesProtocolDisabled" + CreatingHistoryArchives = "community.creatingHistoryArchives" + HistoryArchivesCreated = "community.historyArchivesCreated" + NoHistoryArchivesCreated = "community.noHistoryArchivesCreated" + HistoryArchivesSeeding = "community.historyArchivesSeeding" + HistoryArchivesUnseeded = "community.historyArchivesUnseeded" + HistoryArchiveDownloaded = "community.historyArchiveDownloaded" Unknown proc event*(self:SignalType):string = diff --git a/src/app/core/signals/signals_manager.nim b/src/app/core/signals/signals_manager.nim index 64f974d533..99c8090540 100644 --- a/src/app/core/signals/signals_manager.nim +++ b/src/app/core/signals/signals_manager.nim @@ -84,6 +84,14 @@ QtObject: of SignalType.KeycardConnected: KeycardConnectedSignal.fromEvent(jsonSignal) of SignalType.MailserverAvailable: MailserverAvailableSignal.fromEvent(jsonSignal) of SignalType.MailserverChanged: MailserverChangedSignal.fromEvent(jsonSignal) + of SignalType.HistoryArchivesProtocolEnabled: HistoryArchivesSignal.historyArchivesProtocolEnabledFromEvent(jsonSignal) + of SignalType.HistoryArchivesProtocolDisabled: HistoryArchivesSignal.historyArchivesProtocolDisabledFromEvent(jsonSignal) + of SignalType.CreatingHistoryArchives: HistoryArchivesSignal.creatingHistoryArchivesFromEvent(jsonSignal) + of SignalType.NoHistoryArchivesCreated: HistoryArchivesSignal.noHistoryArchivesCreatedFromEvent(jsonSignal) + of SignalType.HistoryArchivesCreated: HistoryArchivesSignal.historyArchivesCreatedFromEvent(jsonSignal) + of SignalType.HistoryArchivesSeeding: HistoryArchivesSignal.historyArchivesSeedingFromEvent(jsonSignal) + of SignalType.HistoryArchivesUnseeded: HistoryArchivesSignal.historyArchivesUnseededFromEvent(jsonSignal) + of SignalType.HistoryArchiveDownloaded: HistoryArchivesSignal.historyArchiveDownloadedFromEvent(jsonSignal) else: Signal() result.signalType = signalType diff --git a/src/app/modules/main/chat_section/controller.nim b/src/app/modules/main/chat_section/controller.nim index 4dbbf7e675..f1d573ca81 100644 --- a/src/app/modules/main/chat_section/controller.nim +++ b/src/app/modules/main/chat_section/controller.nim @@ -53,6 +53,13 @@ proc newController*(delegate: io_interface.AccessInterface, sectionId: string, i proc delete*(self: Controller) = discard +proc getActiveChatId*(self: Controller): string = + if(self.activeSubItemId.len > 0): + return self.activeSubItemId + else: + return self.activeItemId + + proc init*(self: Controller) = self.events.on(SIGNAL_NEW_MESSAGE_RECEIVED) do(e: Args): let args = MessagesArgs(e) @@ -154,6 +161,11 @@ proc init*(self: Controller) = if (args.communityId == self.sectionId): self.delegate.onReorderChatOrCategory(args.chatId, args.position) + self.events.on(SIGNAL_RELOAD_MESSAGES) do(e: Args): + let args = ReloadMessagesArgs(e) + if (args.communityId == self.sectionId): + self.messageService.asyncLoadInitialMessagesForChat(self.getActiveChatId()) + self.events.on(SIGNAL_CONTACT_NICKNAME_CHANGED) do(e: Args): var args = ContactArgs(e) self.delegate.onContactDetailsUpdated(args.contactId) @@ -190,12 +202,6 @@ proc init*(self: Controller) = proc getMySectionId*(self: Controller): string = return self.sectionId -proc getActiveChatId*(self: Controller): string = - if(self.activeSubItemId.len > 0): - return self.activeSubItemId - else: - return self.activeItemId - proc isCommunity*(self: Controller): bool = return self.isCommunitySection diff --git a/src/app_service/service/message/service.nim b/src/app_service/service/message/service.nim index d0ef4ed723..7af3e72737 100644 --- a/src/app_service/service/message/service.nim +++ b/src/app_service/service/message/service.nim @@ -1,4 +1,4 @@ -import NimQml, tables, json, re, sequtils, strformat, strutils, chronicles +import NimQml, tables, json, re, sequtils, strformat, strutils, chronicles, times import ../../../app/core/tasks/[qt, threadpool] import ../../../app/core/signals/types @@ -34,6 +34,7 @@ let NEW_LINE = re"\n|\r" #must be defined as let, not const const MESSAGES_PER_PAGE* = 20 const MESSAGES_PER_PAGE_MAX* = 300 const CURSOR_VALUE_IGNORE = "ignore" +const WEEK_AS_MILLISECONDS = initDuration(seconds = 60*60*24*7).inMilliSeconds # Signals which may be emitted by this service: const SIGNAL_MESSAGES_LOADED* = "messagesLoaded" @@ -49,6 +50,7 @@ const SIGNAL_MESSAGE_DELETION* = "messageDeleted" const SIGNAL_MESSAGE_EDITED* = "messageEdited" const SIGNAL_MESSAGE_LINK_PREVIEW_DATA_LOADED* = "messageLinkPreviewDataLoaded" const SIGNAL_MENTIONED_IN_EDITED_MESSAGE* = "mentionedInEditedMessage" +const SIGNAL_RELOAD_MESSAGES* = "reloadMessages" include async_tasks @@ -94,6 +96,9 @@ type LinkPreviewDataArgs* = ref object of Args response*: string + ReloadMessagesArgs* = ref object of Args + communityId*: string + QtObject: type Service* = ref object of QObject events: EventEmitter @@ -222,6 +227,37 @@ QtObject: reactionId: r.id, reactionFrom: r.`from`) self.events.emit(SIGNAL_MESSAGE_REACTION_FROM_OTHERS, data) + proc handleMessagesReload(self: Service, communityId: string) = + var keys = newSeq[string]() + for k in self.msgCursor.keys: + if k.startsWith(communityId): + keys.add(k) + for k in keys: + self.msgCursor.del(k) + + keys = @[] + for k in self.lastUsedMsgCursor.keys: + if k.startsWith(communityId): + keys.add(k) + for k in keys: + self.lastUsedMsgCursor.del(k) + + keys = @[] + for k in self.pinnedMsgCursor.keys: + if k.startsWith(communityId): + keys.add(k) + for k in keys: + self.pinnedMsgCursor.del(k) + + keys = @[] + for k in self.lastUsedPinnedMsgCursor.keys: + if k.startsWith(communityId): + keys.add(k) + for k in keys: + self.lastUsedPinnedMsgCursor.del(k) + + self.events.emit(SIGNAL_RELOAD_MESSAGES, ReloadMessagesArgs(communityId: communityId)) + proc init*(self: Service) = self.events.on(SignalType.Message.event) do(e: Args): var receivedData = MessageSignal(e) @@ -239,13 +275,18 @@ QtObject: if (receivedData.emojiReactions.len > 0): self.handleEmojiReactionsUpdate(receivedData.emojiReactions) + self.events.on(SignalType.HistoryArchiveDownloaded.event) do(e: Args): + var receivedData = HistoryArchivesSignal(e) + if now().toTime().toUnix()-receivedData.begin <= WEEK_AS_MILLISECONDS: + # we don't need to reload the messages for archives older than 7 days + self.handleMessagesReload(receivedData.communityId) + proc initialMessagesFetched(self: Service, chatId: string): bool = return self.msgCursor.hasKey(chatId) proc getCurrentMessageCursor(self: Service, chatId: string): string = if(not self.msgCursor.hasKey(chatId)): self.msgCursor[chatId] = "" - return self.msgCursor[chatId] proc getCurrentPinnedMessageCursor(self: Service, chatId: string): string = @@ -276,6 +317,15 @@ QtObject: var chatId: string discard responseObj.getProp("chatId", chatId) + if not self.msgCursor.hasKey(chatId): + self.msgCursor[chatId] = "" + if not self.lastUsedMsgCursor.hasKey(chatId): + self.lastUsedMsgCursor[chatId] = "" + if not self.pinnedMsgCursor.hasKey(chatId): + self.pinnedMsgCursor[chatId] = "" + if not self.lastUsedPinnedMsgCursor.hasKey(chatId): + self.lastUsedPinnedMsgCursor[chatId] = "" + # this is important case we don't want to fetch the same messages multiple times. self.lastUsedMsgCursor[chatId] = self.msgCursor[chatId] self.lastUsedPinnedMsgCursor[chatId] = self.pinnedMsgCursor[chatId] diff --git a/src/backend/signals.nim b/src/backend/signals.nim index 80476339ed..3b02d8018c 100644 --- a/src/backend/signals.nim +++ b/src/backend/signals.nim @@ -32,6 +32,14 @@ proc decode*(jsonSignal: JsonNode): Signal = of SignalType.KeycardConnected: KeycardConnectedSignal.fromEvent(jsonSignal) of SignalType.MailserverAvailable: MailserverAvailableSignal.fromEvent(jsonSignal) of SignalType.MailserverChanged: MailserverChangedSignal.fromEvent(jsonSignal) + of SignalType.HistoryArchivesProtocolEnabled: historyArchivesProtocolEnabledFromEvent(jsonSignal) + of SignalType.HistoryArchivesProtocolDisabled: historyArchivesProtocolDisabledFromEvent(jsonSignal) + of SignalType.CreatingHistoryArchives: creatingHistoryArchivesFromEvent(jsonSignal) + of SignalType.NoHistoryArchivesCreated: noHistoryArchivesCreatedFromEvent(jsonSignal) + of SignalType.HistoryArchivesCreated: historyArchivesCreatedFromEvent(jsonSignal) + of SignalType.HistoryArchivesSeeding: historyArchivesSeedingFromEvent(jsonSignal) + of SignalType.HistoryArchivesUnseeded: historyArchivesUnseededFromEvent(jsonSignal) + of SignalType.HistoryArchiveDownloaded: historyArchiveDownloadedFromEvent(jsonSignal) else: Signal() result.signalType = signalType