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.
This commit is contained in:
Pascal Precht 2022-04-08 10:11:43 +02:00 committed by r4bbit.eth
parent 85ba478c49
commit 10f6d9e89b
6 changed files with 131 additions and 8 deletions

View File

@ -8,7 +8,50 @@ import signal_type
type CommunitySignal* = ref object of Signal type CommunitySignal* = ref object of Signal
community*: CommunityDto community*: CommunityDto
type HistoryArchivesSignal* = ref object of Signal
communityId*: string
begin*: int
to*: int
proc fromEvent*(T: type CommunitySignal, event: JsonNode): CommunitySignal = proc fromEvent*(T: type CommunitySignal, event: JsonNode): CommunitySignal =
result = CommunitySignal() result = CommunitySignal()
result.signalType = SignalType.CommunityFound result.signalType = SignalType.CommunityFound
result.community = event["event"].toCommunityDto() 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

View File

@ -29,6 +29,14 @@ type SignalType* {.pure.} = enum
KeycardConnected = "keycard.connected" KeycardConnected = "keycard.connected"
MailserverAvailable = "mailserver.available" MailserverAvailable = "mailserver.available"
MailserverChanged = "mailserver.changed" 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 Unknown
proc event*(self:SignalType):string = proc event*(self:SignalType):string =

View File

@ -84,6 +84,14 @@ QtObject:
of SignalType.KeycardConnected: KeycardConnectedSignal.fromEvent(jsonSignal) of SignalType.KeycardConnected: KeycardConnectedSignal.fromEvent(jsonSignal)
of SignalType.MailserverAvailable: MailserverAvailableSignal.fromEvent(jsonSignal) of SignalType.MailserverAvailable: MailserverAvailableSignal.fromEvent(jsonSignal)
of SignalType.MailserverChanged: MailserverChangedSignal.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() else: Signal()
result.signalType = signalType result.signalType = signalType

View File

@ -53,6 +53,13 @@ proc newController*(delegate: io_interface.AccessInterface, sectionId: string, i
proc delete*(self: Controller) = proc delete*(self: Controller) =
discard discard
proc getActiveChatId*(self: Controller): string =
if(self.activeSubItemId.len > 0):
return self.activeSubItemId
else:
return self.activeItemId
proc init*(self: Controller) = proc init*(self: Controller) =
self.events.on(SIGNAL_NEW_MESSAGE_RECEIVED) do(e: Args): self.events.on(SIGNAL_NEW_MESSAGE_RECEIVED) do(e: Args):
let args = MessagesArgs(e) let args = MessagesArgs(e)
@ -154,6 +161,11 @@ proc init*(self: Controller) =
if (args.communityId == self.sectionId): if (args.communityId == self.sectionId):
self.delegate.onReorderChatOrCategory(args.chatId, args.position) 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): self.events.on(SIGNAL_CONTACT_NICKNAME_CHANGED) do(e: Args):
var args = ContactArgs(e) var args = ContactArgs(e)
self.delegate.onContactDetailsUpdated(args.contactId) self.delegate.onContactDetailsUpdated(args.contactId)
@ -190,12 +202,6 @@ proc init*(self: Controller) =
proc getMySectionId*(self: Controller): string = proc getMySectionId*(self: Controller): string =
return self.sectionId 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 = proc isCommunity*(self: Controller): bool =
return self.isCommunitySection return self.isCommunitySection

View File

@ -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/tasks/[qt, threadpool]
import ../../../app/core/signals/types 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* = 20
const MESSAGES_PER_PAGE_MAX* = 300 const MESSAGES_PER_PAGE_MAX* = 300
const CURSOR_VALUE_IGNORE = "ignore" const CURSOR_VALUE_IGNORE = "ignore"
const WEEK_AS_MILLISECONDS = initDuration(seconds = 60*60*24*7).inMilliSeconds
# Signals which may be emitted by this service: # Signals which may be emitted by this service:
const SIGNAL_MESSAGES_LOADED* = "messagesLoaded" const SIGNAL_MESSAGES_LOADED* = "messagesLoaded"
@ -49,6 +50,7 @@ const SIGNAL_MESSAGE_DELETION* = "messageDeleted"
const SIGNAL_MESSAGE_EDITED* = "messageEdited" const SIGNAL_MESSAGE_EDITED* = "messageEdited"
const SIGNAL_MESSAGE_LINK_PREVIEW_DATA_LOADED* = "messageLinkPreviewDataLoaded" const SIGNAL_MESSAGE_LINK_PREVIEW_DATA_LOADED* = "messageLinkPreviewDataLoaded"
const SIGNAL_MENTIONED_IN_EDITED_MESSAGE* = "mentionedInEditedMessage" const SIGNAL_MENTIONED_IN_EDITED_MESSAGE* = "mentionedInEditedMessage"
const SIGNAL_RELOAD_MESSAGES* = "reloadMessages"
include async_tasks include async_tasks
@ -94,6 +96,9 @@ type
LinkPreviewDataArgs* = ref object of Args LinkPreviewDataArgs* = ref object of Args
response*: string response*: string
ReloadMessagesArgs* = ref object of Args
communityId*: string
QtObject: QtObject:
type Service* = ref object of QObject type Service* = ref object of QObject
events: EventEmitter events: EventEmitter
@ -222,6 +227,37 @@ QtObject:
reactionId: r.id, reactionFrom: r.`from`) reactionId: r.id, reactionFrom: r.`from`)
self.events.emit(SIGNAL_MESSAGE_REACTION_FROM_OTHERS, data) 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) = proc init*(self: Service) =
self.events.on(SignalType.Message.event) do(e: Args): self.events.on(SignalType.Message.event) do(e: Args):
var receivedData = MessageSignal(e) var receivedData = MessageSignal(e)
@ -239,13 +275,18 @@ QtObject:
if (receivedData.emojiReactions.len > 0): if (receivedData.emojiReactions.len > 0):
self.handleEmojiReactionsUpdate(receivedData.emojiReactions) 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 = proc initialMessagesFetched(self: Service, chatId: string): bool =
return self.msgCursor.hasKey(chatId) return self.msgCursor.hasKey(chatId)
proc getCurrentMessageCursor(self: Service, chatId: string): string = proc getCurrentMessageCursor(self: Service, chatId: string): string =
if(not self.msgCursor.hasKey(chatId)): if(not self.msgCursor.hasKey(chatId)):
self.msgCursor[chatId] = "" self.msgCursor[chatId] = ""
return self.msgCursor[chatId] return self.msgCursor[chatId]
proc getCurrentPinnedMessageCursor(self: Service, chatId: string): string = proc getCurrentPinnedMessageCursor(self: Service, chatId: string): string =
@ -276,6 +317,15 @@ QtObject:
var chatId: string var chatId: string
discard responseObj.getProp("chatId", chatId) 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. # this is important case we don't want to fetch the same messages multiple times.
self.lastUsedMsgCursor[chatId] = self.msgCursor[chatId] self.lastUsedMsgCursor[chatId] = self.msgCursor[chatId]
self.lastUsedPinnedMsgCursor[chatId] = self.pinnedMsgCursor[chatId] self.lastUsedPinnedMsgCursor[chatId] = self.pinnedMsgCursor[chatId]

View File

@ -32,6 +32,14 @@ proc decode*(jsonSignal: JsonNode): Signal =
of SignalType.KeycardConnected: KeycardConnectedSignal.fromEvent(jsonSignal) of SignalType.KeycardConnected: KeycardConnectedSignal.fromEvent(jsonSignal)
of SignalType.MailserverAvailable: MailserverAvailableSignal.fromEvent(jsonSignal) of SignalType.MailserverAvailable: MailserverAvailableSignal.fromEvent(jsonSignal)
of SignalType.MailserverChanged: MailserverChangedSignal.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() else: Signal()
result.signalType = signalType result.signalType = signalType