refactor(@desktop/chat-communities): asynchronous fetching messages

This commit is contained in:
Sale Djenic 2021-10-29 13:10:17 +02:00
parent d8dea2dc58
commit eca74532ac
15 changed files with 562 additions and 48 deletions

View File

@ -6,6 +6,7 @@ import ../../app_service/service/contacts/service as contacts_service
import ../../app_service/service/language/service as language_service
import ../../app_service/service/chat/service as chat_service
import ../../app_service/service/community/service as community_service
import ../../app_service/service/message/service as message_service
import ../../app_service/service/token/service as token_service
import ../../app_service/service/transaction/service as transaction_service
import ../../app_service/service/collectible/service as collectible_service
@ -22,9 +23,11 @@ import ../core/local_account_settings
import ../../app_service/service/profile/service as profile_service
import ../../app_service/service/settings/service as settings_service
import ../../app_service/service/about/service as about_service
import ../modules/startup/module as startup_module
import ../modules/main/module as main_module
import ../core/local_account_settings
import ../core/global_singleton
#################################################
@ -70,6 +73,7 @@ type
contactsService: contacts_service.Service
chatService: chat_service.Service
communityService: community_service.Service
messageService: message_service.Service
tokenService: token_service.Service
transactionService: transaction_service.Service
collectibleService: collectible_service.Service
@ -136,12 +140,14 @@ proc newAppController*(appService: AppService): AppController =
result.contactsService = contacts_service.newService(appService.status.events, appService.threadpool)
result.chatService = chat_service.newService()
result.communityService = community_service.newService(result.chatService)
result.tokenService = token_service.newService(appService.status.events, appService.threadpool, result.settingService, result.settingsService)
result.messageService = message_service.newService(appService.status.events, appService.threadpool)
result.tokenService = token_service.newService(appService.status.events, appService.threadpool, result.settingService,
result.settingsService)
result.collectibleService = collectible_service.newService(result.settingService)
result.walletAccountService = wallet_account_service.newService(
appService.status.events, result.settingService, result.tokenService
)
result.transactionService = transaction_service.newService(appService.status.events, appService.threadpool, result.walletAccountService)
result.walletAccountService = wallet_account_service.newService(appService.status.events, result.settingService,
result.tokenService)
result.transactionService = transaction_service.newService(appService.status.events, appService.threadpool,
result.walletAccountService)
result.bookmarkService = bookmark_service.newService()
result.profileService = profile_service.newService()
result.aboutService = about_service.newService()
@ -173,6 +179,7 @@ proc newAppController*(appService: AppService): AppController =
result.accountsService,
result.chatService,
result.communityService,
result.messageService,
result.tokenService,
result.transactionService,
result.collectibleService,
@ -288,7 +295,7 @@ proc load(self: AppController) =
self.buildAndRegisterUserProfile()
# load main module
self.mainModule.load(self.chatService, self.communityService)
self.mainModule.load(self.appService.status.events, self.chatService, self.communityService, self.messageService)
proc userLoggedIn*(self: AppController) =
#################################################

View File

@ -5,6 +5,7 @@ import io_interface
import ../../../../app_service/service/chat/service as chat_service
import ../../../../app_service/service/community/service as community_service
import ../../../../app_service/service/message/service as message_service
export controller_interface
@ -17,16 +18,19 @@ type
activeSubItemId: string
chatService: chat_service.ServiceInterface
communityService: community_service.ServiceInterface
messageService: message_service.Service
proc newController*(delegate: io_interface.AccessInterface, id: string, isCommunity: bool,
chatService: chat_service.ServiceInterface,
communityService: community_service.ServiceInterface): Controller =
communityService: community_service.ServiceInterface,
messageService: message_service.Service): Controller =
result = Controller()
result.delegate = delegate
result.id = id
result.isCommunityModule = isCommunity
result.chatService = chatService
result.communityService = communityService
result.messageService = messageService
method delete*(self: Controller) =
discard
@ -60,6 +64,11 @@ method setActiveItemSubItem*(self: Controller, itemId: string, subItemId: string
self.activeItemId = itemId
self.activeSubItemId = subItemId
if(self.activeSubItemId.len > 0):
self.messageService.loadInitialMessagesForChat(self.activeSubItemId)
else:
self.messageService.loadInitialMessagesForChat(self.activeItemId)
# We need to take other actions here like notify status go that unviewed mentions count is updated and so...
self.delegate.activeItemSubItemSet(self.activeItemId, self.activeSubItemId)

View File

@ -2,29 +2,39 @@ import controller_interface
import io_interface
import ../../../../../app_service/service/community/service as community_service
import ../../../../../app_service/service/message/service as message_service
import eventemitter
import status/[signals]
export controller_interface
type
Controller* = ref object of controller_interface.AccessInterface
delegate: io_interface.AccessInterface
events: EventEmitter
id: string
isCommunityModule: bool
communityService: community_service.ServiceInterface
messageService: message_service.Service
proc newController*(delegate: io_interface.AccessInterface, id: string, isCommunity: bool,
communityService: community_service.ServiceInterface): Controller =
proc newController*(delegate: io_interface.AccessInterface, events: EventEmitter, id: string, isCommunity: bool,
communityService: community_service.ServiceInterface, messageService: message_service.Service): Controller =
result = Controller()
result.delegate = delegate
result.events = events
result.id = id
result.isCommunityModule = isCommunity
result.communityService = communityService
result.messageService = messageService
method delete*(self: Controller) =
discard
method init*(self: Controller) =
discard
self.events.on(SIGNAL_MESSAGES_LOADED) do(e:Args):
let args = MessagesLoadedArgs(e)
echo "RECEIVED MESSAGES ASYNC: ", repr(args)
method getId*(self: Controller): string =
return self.id

View File

@ -1,21 +1,77 @@
import NimQml
type
ContentType* {.pure.} = enum
FetchMoreMessagesButton = -2
ChatIdentifier = -1
Unknown = 0
Message = 1
Sticker = 2
Status = 3
Emoji = 4
Transaction = 5
Group = 6
Image = 7
Audio = 8
Community = 9
Gap = 10
Edit = 11
QtObject:
type
Item* = ref object of QObject
proc setup(self: Item) =
self.QObject.setup
type
Item* = object
id: string
`from`: string
alias: string
identicon: string
seen: bool
outgoingStatus: string
text: string
stickerHash: string
stickerPack: int
image: string
gapFrom: int64
gapTo: int64
timestamp: int64
contentType: ContentType
messageType: int
proc delete*(self: Item) =
self.QObject.delete
proc initItem*(id, `from`, alias, identicon, outgoingStatus, text: string, seen: bool, timestamp: int64,
contentType: ContentType, messageType: int): Item =
result.id = id
result.`from` = `from`
result.alias = alias
result.identicon = identicon
result.seen = seen
result.outgoingStatus = outgoingStatus
result.text = text
result.timestamp = timestamp
result.contentType = contentType
result.messageType = messageType
proc newItem*(): Item =
new(result, delete)
result.setup()
proc id*(self: Item): string {.inline.} =
self.id
proc id*(self: Item): string {.slot.} =
self.id
proc `from`*(self: Item): string {.inline.} =
self.`from`
QtProperty[string] id:
read = id
proc alias*(self: Item): string {.inline.} =
self.alias
proc identicon*(self: Item): string {.inline.} =
self.identicon
proc outgoingStatus*(self: Item): string {.inline.} =
self.outgoingStatus
proc text*(self: Item): string {.inline.} =
self.text
proc seen*(self: Item): bool {.inline.} =
self.seen
proc timestamp*(self: Item): int64 {.inline.} =
self.timestamp
proc contentType*(self: Item): ContentType {.inline.} =
self.contentType
proc messageType*(self: Item): int {.inline.} =
self.messageType

View File

@ -1,17 +1,101 @@
import NimQml
import NimQml, Tables, strutils, strformat
import item
type
ModelRole {.pure.} = enum
Id = UserRole + 1
From
Alias
Identicon
Seen
OutgoingStatus
Text
Timestamp
ContentType
MessageType
# StickerHash
# StickerPack
# Image
# GapFrom
# GapTo
QtObject:
type
type
Model* = ref object of QAbstractListModel
sections: seq[Item]
items: seq[Item]
proc setup(self: Model) =
self.QAbstractListModel.setup
proc delete*(self: Model) =
proc delete(self: Model) =
self.items = @[]
self.QAbstractListModel.delete
proc setup(self: Model) =
self.QAbstractListModel.setup
proc newModel*(): Model =
new(result, delete)
result.setup
result.setup
method rowCount(self: Model, index: QModelIndex = nil): int =
return self.items.len
method roleNames(self: Model): Table[int, string] =
{
ModelRole.Id.int:"id",
ModelRole.From.int:"from",
ModelRole.Alias.int:"alias",
ModelRole.Identicon.int:"identicon",
ModelRole.Seen.int:"seen",
ModelRole.OutgoingStatus.int:"outgoingStatus",
ModelRole.Text.int:"text",
ModelRole.Timestamp.int:"timestamp",
ModelRole.ContentType.int:"contentType",
ModelRole.MessageType.int:"messageType",
# ModelRole.StickerHash.int:"stickerHash",
# ModelRole.StickerPack.int:"stickerPack",
# ModelRole.Image.int:"image",
# ModelRole.GapFrom.int:"gapFrom",
# ModelRole.GapTo.int:"gapTo"
}.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant =
if (not index.isValid):
return
if (index.row < 0 or index.row >= self.items.len):
return
let item = self.items[index.row]
let enumRole = role.ModelRole
case enumRole:
of ModelRole.Id:
result = newQVariant(item.id)
of ModelRole.From:
result = newQVariant(item.`from`)
of ModelRole.Alias:
result = newQVariant(item.alias)
of ModelRole.Identicon:
result = newQVariant(item.identicon)
of ModelRole.Seen:
result = newQVariant(item.seen)
of ModelRole.OutgoingStatus:
result = newQVariant(item.outgoingStatus)
of ModelRole.Text:
result = newQVariant(item.text)
of ModelRole.Timestamp:
result = newQVariant(item.timestamp)
of ModelRole.ContentType:
result = newQVariant(item.contentType.int)
of ModelRole.MessageType:
result = newQVariant(item.messageType)
# of ModelRole.StickerHash:
# result = newQVariant(item.stickerHash)
# of ModelRole.StickerPack:
# result = newQVariant(item.stickerPack)
# of ModelRole.Image:
# result = newQVariant(item.image)
# of ModelRole.GapFrom:
# result = newQVariant(item.gapFrom)
# of ModelRole.GapTo:
# result = newQVariant(item.gapTo)

View File

@ -6,6 +6,9 @@ import ../../../../core/global_singleton
import ../../../../../app_service/service/chat/service as chat_service
import ../../../../../app_service/service/community/service as community_service
import ../../../../../app_service/service/message/service as message_service
import eventemitter
export io_interface
@ -17,14 +20,14 @@ type
controller: controller.AccessInterface
moduleLoaded: bool
proc newModule*(delegate: delegate_interface.AccessInterface, id: string, isCommunity: bool,
chatService: chat_service.Service, communityService: community_service.Service):
proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitter, id: string, isCommunity: bool,
chatService: chat_service.Service, communityService: community_service.Service, messageService: message_service.Service):
Module =
result = Module()
result.delegate = delegate
result.view = view.newView(result)
result.viewVariant = newQVariant(result.view)
result.controller = controller.newController(result, id, isCommunity, communityService)
result.controller = controller.newController(result, events, id, isCommunity, communityService, messageService)
result.moduleLoaded = false
method delete*(self: Module) =

View File

@ -10,6 +10,9 @@ import users/module as users_module
import ../../../../app_service/service/chat/service as chat_service
import ../../../../app_service/service/community/service as community_service
import ../../../../app_service/service/message/service as message_service
import eventemitter
export io_interface
@ -27,18 +30,20 @@ type
usersModule: users_module.AccessInterface
moduleLoaded: bool
proc newModule*(delegate: delegate_interface.AccessInterface, id: string, isCommunity: bool,
chatService: chat_service.Service, communityService: community_service.Service):
proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitter, id: string, isCommunity: bool,
chatService: chat_service.Service, communityService: community_service.Service,
messageService: message_service.Service):
Module =
result = Module()
result.delegate = delegate
result.view = view.newView(result)
result.viewVariant = newQVariant(result.view)
result.controller = controller.newController(result, id, isCommunity, chatService, communityService)
result.controller = controller.newController(result, id, isCommunity, chatService, communityService, messageService)
result.moduleLoaded = false
result.inputAreaModule = input_area_module.newModule(result, id, isCommunity, chatService, communityService)
result.messagesModule = messages_module.newModule(result, id, isCommunity, chatService, communityService)
result.messagesModule = messages_module.newModule(result, events, id, isCommunity, chatService, communityService,
messageService)
result.usersModule = users_module.newModule(result, id, isCommunity, chatService, communityService)
method delete*(self: Module) =
@ -62,7 +67,7 @@ proc buildChatUI(self: Module) =
if(selectedItemId.len == 0):
selectedItemId = item.id
self.controller.setActiveItemSubItem(selectedItemId, "")
self.setActiveItemSubItem(selectedItemId, "")
proc buildCommunityUI(self: Module) =
var selectedItemId = ""

View File

@ -44,6 +44,13 @@ method delete*(self: Controller) =
discard
method init*(self: Controller) =
self.events.on("mailserverAvailable") do(e:Args):
echo "MAILSERVER AVAILABLE: ", repr(e)
# We need to take some actions here. This is the only pace where "mailserverAvailable" signal should be handled.
# Do the following, if we really need that.
# requestAllHistoricMessagesResult
# requestMissingCommunityInfos
if(defined(macosx)):
let account = self.accountsService.getLoggedInAccount()
singletonInstance.localAccountSettings.setFileName(account.name)

View File

@ -11,6 +11,7 @@ import profile_section/module as profile_section_module
import ../../../app_service/service/keychain/service as keychain_service
import ../../../app_service/service/chat/service as chat_service
import ../../../app_service/service/community/service as community_service
import ../../../app_service/service/message/service as message_service
import ../../../app_service/service/token/service as token_service
import ../../../app_service/service/transaction/service as transaction_service
import ../../../app_service/service/collectible/service as collectible_service
@ -20,7 +21,6 @@ import ../../../app_service/service/bookmarks/service as bookmark_service
import ../../../app_service/service/dapp_permissions/service as dapp_permissions_service
import ../../../app_service/service/provider/service as provider_service
import eventemitter
import ../../../app_service/service/profile/service as profile_service
import ../../../app_service/service/accounts/service as accounts_service
import ../../../app_service/service/settings/service as settings_service
@ -30,6 +30,8 @@ import ../../../app_service/service/language/service as language_service
import ../../../app_service/service/mnemonic/service as mnemonic_service
import ../../../app_service/service/privacy/service as privacy_service
import eventemitter
export io_interface
type
@ -52,6 +54,7 @@ proc newModule*[T](
accountsService: accounts_service.ServiceInterface,
chatService: chat_service.Service,
communityService: community_service.Service,
messageService: message_service.Service,
tokenService: token_service.Service,
transactionService: transaction_service.Service,
collectibleService: collectible_service.Service,
@ -77,7 +80,8 @@ proc newModule*[T](
result.moduleLoaded = false
# Submodules
result.chatSectionModule = chat_section_module.newModule(result, "chat", false, chatService, communityService)
result.chatSectionModule = chat_section_module.newModule(result, events, "chat", false, chatService, communityService,
messageService)
result.communitySectionsModule = initOrderedTable[string, chat_section_module.AccessInterface]()
result.walletSectionModule = wallet_section_module.newModule[Module[T]](result, events, tokenService,
transactionService, collectible_service, walletAccountService, settingService)
@ -98,8 +102,8 @@ method delete*[T](self: Module[T]) =
self.viewVariant.delete
self.controller.delete
method load*[T](self: Module[T], chatService: chat_service.Service,
communityService: community_service.Service) =
method load*[T](self: Module[T], events: EventEmitter, chatService: chat_service.Service,
communityService: community_service.Service, messageService: message_service.Service) =
singletonInstance.engine.setRootContextProperty("mainModule", self.viewVariant)
self.controller.init()
self.view.load()
@ -107,7 +111,8 @@ method load*[T](self: Module[T], chatService: chat_service.Service,
# Create community modules here, since we don't know earlier how many communities we have.
let communities = self.controller.getCommunities()
for c in communities:
self.communitySectionsModule[c.id] = chat_section_module.newModule(self, c.id, true, chatService, communityService)
self.communitySectionsModule[c.id] = chat_section_module.newModule(self, events, c.id, true, chatService,
communityService, messageService)
var activeSection: Item
var activeSectionId = singletonInstance.localAccountSensitiveSettings.getActiveSection()

View File

@ -1,10 +1,14 @@
import ../../../../app_service/service/chat/service as chat_service
import ../../../../app_service/service/community/service as community_service
import ../../../../app_service/service/message/service as message_service
import eventemitter
method delete*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method load*(self: AccessInterface, chatService: chat_service.Service, communityService: community_service.Service,)
method load*(self: AccessInterface, events: EventEmitter, chatService: chat_service.Service, communityService: community_service.Service,
messageService: message_service.Service)
{.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -0,0 +1,46 @@
include ../../common/json_utils
include ../../tasks/common
#################################################
# Async load messages
#################################################
type
AsyncFetchChatMessagesTaskArg = ref object of QObjectTaskArg
chatId: string
msgCursor: string
pinnedMsgCursor: string
limit: int
const asyncFetchChatMessagesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[AsyncFetchChatMessagesTaskArg](argEncoded)
# handle messages
var messagesArr: JsonNode
var messagesCursor: string
let msgsResponse = status_go.fetchMessages(arg.chatId, arg.msgCursor, arg.limit)
discard msgsResponse.result.getProp("cursor", messagesCursor)
discard msgsResponse.result.getProp("messages", messagesArr)
# handle pinned messages
var pinnedMsgArr: JsonNode
var pinnedMsgCursor: string
let pinnedMsgsResponse = status_go.fetchPinnedMessages(arg.chatId, arg.pinnedMsgCursor, arg.limit)
discard pinnedMsgsResponse.result.getProp("cursor", pinnedMsgCursor)
discard pinnedMsgsResponse.result.getProp("pinnedMessages", pinnedMsgArr)
# handle reactions
var reactionsArr: JsonNode
# messages and reactions are using the same cursor
let rResponse = status_go.fetchReactions(arg.chatId, arg.msgCursor, arg.limit)
reactionsArr = rResponse.result
let responseJson = %*{
"chatId": arg.chatId,
"messages": messagesArr,
"messagesCursor": messagesCursor,
"pinnedMessages": pinnedMsgArr,
"pinnedMessagesCursor": pinnedMsgCursor,
"reactions": reactionsArr
}
arg.finish(responseJson)

View File

@ -0,0 +1,100 @@
{.used.}
import json
include ../../../common/json_utils
type QuotedMessage* = object
`from`*: string
text*: string
#parsedText*: Not sure if we use it
type Sticker* = object
hash*: string
pack*: int
type GapParameters* = object
`from`*: int64
to*: int64
type MessageDto* = object
id*: string
whisperTimestamp*: int64
`from`*: string
alias*: string
identicon*: string
seen*: bool
outgoingStatus*: string
quotedMessage*: QuotedMessage
rtl*: bool
#parsedText*: Not sure if we use it
lineCount*: int
text*: string
chatId*: string
localChatId*: string
clock*: int64
replace*: string
responseTo*: string
ensName*: string
sticker*: Sticker
image*: string
gapParameters*: GapParameters
timestamp*: int64
contentType*: int
messageType*: int
links*: seq[string]
proc toQuotedMessage*(jsonObj: JsonNode): QuotedMessage =
result = QuotedMessage()
discard jsonObj.getProp("from", result.from)
discard jsonObj.getProp("text", result.text)
proc toSticker*(jsonObj: JsonNode): Sticker =
result = Sticker()
discard jsonObj.getProp("hash", result.hash)
discard jsonObj.getProp("pack", result.pack)
proc toGapParameters*(jsonObj: JsonNode): GapParameters =
result = GapParameters()
discard jsonObj.getProp("from", result.from)
discard jsonObj.getProp("to", result.to)
proc toMessageDto*(jsonObj: JsonNode): MessageDto =
result = MessageDto()
discard jsonObj.getProp("id", result.id)
discard jsonObj.getProp("whisperTimestamp", result.whisperTimestamp)
discard jsonObj.getProp("from", result.from)
discard jsonObj.getProp("alias", result.alias)
discard jsonObj.getProp("identicon", result.identicon)
discard jsonObj.getProp("seen", result.seen)
discard jsonObj.getProp("outgoingStatus", result.outgoingStatus)
discard jsonObj.getProp("rtl", result.rtl)
discard jsonObj.getProp("lineCount", result.lineCount)
discard jsonObj.getProp("text", result.text)
discard jsonObj.getProp("chatId", result.chatId)
discard jsonObj.getProp("localChatId", result.localChatId)
discard jsonObj.getProp("clock", result.clock)
discard jsonObj.getProp("replace", result.replace)
discard jsonObj.getProp("responseTo", result.responseTo)
discard jsonObj.getProp("ensName", result.ensName)
discard jsonObj.getProp("timestamp", result.timestamp)
discard jsonObj.getProp("contentType", result.contentType)
discard jsonObj.getProp("messageType", result.messageType)
discard jsonObj.getProp("image", result.image)
var quotedMessageObj: JsonNode
if(jsonObj.getProp("quotedMessage", quotedMessageObj)):
result.quotedMessage = toQuotedMessage(quotedMessageObj)
var stickerObj: JsonNode
if(jsonObj.getProp("sticker", stickerObj)):
result.sticker = toSticker(stickerObj)
var gapParametersObj: JsonNode
if(jsonObj.getProp("gapParameters", gapParametersObj)):
result.gapParameters = toGapParameters(gapParametersObj)
var linksArr: JsonNode
if(jsonObj.getProp("links", linksArr)):
for link in linksArr:
result.links.add(link.getStr)

View File

@ -0,0 +1,21 @@
{.used.}
import json
import message
include ../../../common/json_utils
type PinnedMessageDto* = object
pinnedAt*: int64
pinnedBy*: string
message*: MessageDto
proc toPinnedMessageDto*(jsonObj: JsonNode): PinnedMessageDto =
result = PinnedMessageDto()
discard jsonObj.getProp("pinnedAt", result.pinnedAt)
discard jsonObj.getProp("pinnedBy", result.pinnedBy)
var messageObj: JsonNode
if(jsonObj.getProp("message", messageObj)):
result.message = toMessageDto(messageObj)

View File

@ -0,0 +1,24 @@
{.used.}
import json
include ../../../common/json_utils
type ReactionDto* = object
id*: string
clock*: int64
chatId*: string
localChatId*: string
`from`*: string
messageId*: string
emojiId*: int
proc toReactionDto*(jsonObj: JsonNode): ReactionDto =
result = ReactionDto()
discard jsonObj.getProp("id", result.id)
discard jsonObj.getProp("clock", result.clock)
discard jsonObj.getProp("chatId", result.chatId)
discard jsonObj.getProp("localChatId", result.localChatId)
discard jsonObj.getProp("from", result.from)
discard jsonObj.getProp("messageId", result.messageId)
discard jsonObj.getProp("emojiId", result.emojiId)

View File

@ -0,0 +1,133 @@
import NimQml, tables, json, sequtils, chronicles
import eventemitter
import ../../tasks/[qt, threadpool]
import status/statusgo_backend_new/messages as status_go
import ./dto/message as message_dto
import ./dto/pinnedMessage as pinned_msg_dto
import ./dto/reaction as reaction_dto
export message_dto
export pinned_msg_dto
export reaction_dto
include async_tasks
logScope:
topics = "messages-service"
const MESSAGES_PER_PAGE = 20
# Signals which may be emitted by this service:
const SIGNAL_MESSAGES_LOADED* = "new-messagesLoaded" #Once we are done with refactoring we should remove "new-" from this signal name
type
MessagesLoadedArgs* = ref object of Args
chatId*: string
messages*: seq[MessageDto]
pinnedMessages*: seq[PinnedMessageDto]
reactions*: seq[ReactionDto]
QtObject:
type Service* = ref object of QObject
events: EventEmitter
threadpool: ThreadPool
msgCursor: Table[string, string]
pinnedMsgCursor: Table[string, string]
proc delete*(self: Service) =
self.QObject.delete
proc newService*(events: EventEmitter, threadpool: ThreadPool): Service =
new(result, delete)
result.QObject.setup
result.events = events
result.threadpool = threadpool
result.msgCursor = initTable[string, string]()
result.pinnedMsgCursor = initTable[string, string]()
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 =
if(not self.pinnedMsgCursor.hasKey(chatId)):
self.pinnedMsgCursor[chatId] = ""
return self.pinnedMsgCursor[chatId]
proc onLoadMoreMessagesForChat*(self: Service, response: string) {.slot.} =
let responseObj = response.parseJson
if (responseObj.kind != JObject):
info "load more messages response is not a json object"
# notify view, this is important
self.events.emit(SIGNAL_MESSAGES_LOADED, MessagesLoadedArgs())
return
var chatId: string
discard responseObj.getProp("chatId", chatId)
# handling messages
var msgCursor: string
if(responseObj.getProp("messagesCursor", msgCursor)):
self.msgCursor[chatId] = msgCursor
var messagesArr: JsonNode
var messages: seq[MessageDto]
if(responseObj.getProp("messages", messagesArr)):
messages = map(messagesArr.getElems(), proc(x: JsonNode): MessageDto = x.toMessageDto())
# handling pinned messages
var pinnedMsgCursor: string
if(responseObj.getProp("pinnedMessagesCursor", pinnedMsgCursor)):
self.pinnedMsgCursor[chatId] = pinnedMsgCursor
var pinnedMsgArr: JsonNode
var pinnedMessages: seq[PinnedMessageDto]
if(responseObj.getProp("pinnedMessages", pinnedMsgArr)):
pinnedMessages = map(pinnedMsgArr.getElems(), proc(x: JsonNode): PinnedMessageDto = x.toPinnedMessageDto())
# handling reactions
var reactionsArr: JsonNode
var reactions: seq[ReactionDto]
if(responseObj.getProp("reactions", reactionsArr)):
reactions = map(reactionsArr.getElems(), proc(x: JsonNode): ReactionDto = x.toReactionDto())
let data = MessagesLoadedArgs(chatId: chatId,
messages: messages,
pinnedMessages: pinnedMessages,
reactions: reactions)
self.events.emit(SIGNAL_MESSAGES_LOADED, data)
proc loadMoreMessagesForChat*(self: Service, chatId: string) =
if (chatId.len == 0):
error "empty chat id", methodName="loadMoreMessagesForChat"
return
let arg = AsyncFetchChatMessagesTaskArg(
tptr: cast[ByteAddress](asyncFetchChatMessagesTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onLoadMoreMessagesForChat",
chatId: chatId,
msgCursor: self.getCurrentMessageCursor(chatId),
pinnedMsgCursor: self.getCurrentPinnedMessageCursor(chatId),
limit: MESSAGES_PER_PAGE
)
self.threadpool.start(arg)
proc loadInitialMessagesForChat*(self: Service, chatId: string) =
if(self.getCurrentMessageCursor(chatId).len > 0):
return
# we're here if initial messages are not loaded yet
self.loadMoreMessagesForChat(chatId)