refactor(@desktop/chat-messages): load more messages
- load more messages on scroll up for chat/channel added - sending messages improved in terms of adding new messages to appropriate position - scroll to message added on the qml side - qml connected to the sending message success/failed signals
This commit is contained in:
parent
0eb40287fa
commit
16a33f8fa7
|
@ -56,7 +56,13 @@ method init*(self: Controller) =
|
||||||
let args = MessageSendingSuccess(e)
|
let args = MessageSendingSuccess(e)
|
||||||
if(self.chatId != args.chat.id):
|
if(self.chatId != args.chat.id):
|
||||||
return
|
return
|
||||||
self.delegate.newMessagesLoaded(@[args.message], @[], @[])
|
self.delegate.onSendingMessageSuccess(args.message)
|
||||||
|
|
||||||
|
self.events.on(SIGNAL_SENDING_FAILED) do(e:Args):
|
||||||
|
let args = ChatArgs(e)
|
||||||
|
if(self.chatId != args.chatId):
|
||||||
|
return
|
||||||
|
self.delegate.onSendingMessageError()
|
||||||
|
|
||||||
self.events.on(SIGNAL_MESSAGE_PINNED) do(e:Args):
|
self.events.on(SIGNAL_MESSAGE_PINNED) do(e:Args):
|
||||||
let args = MessagePinUnpinArgs(e)
|
let args = MessagePinUnpinArgs(e)
|
||||||
|
@ -100,6 +106,9 @@ method getOneToOneChatNameAndImage*(self: Controller): tuple[name: string, image
|
||||||
method belongsToCommunity*(self: Controller): bool =
|
method belongsToCommunity*(self: Controller): bool =
|
||||||
return self.belongsToCommunity
|
return self.belongsToCommunity
|
||||||
|
|
||||||
|
method loadMoreMessages*(self: Controller) =
|
||||||
|
self.messageService.asyncLoadMoreMessagesForChat(self.chatId)
|
||||||
|
|
||||||
method addReaction*(self: Controller, messageId: string, emojiId: int) =
|
method addReaction*(self: Controller, messageId: string, emojiId: int) =
|
||||||
self.messageService.addReaction(self.chatId, messageId, emojiId)
|
self.messageService.addReaction(self.chatId, messageId, emojiId)
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,9 @@ method getOneToOneChatNameAndImage*(self: AccessInterface): tuple[name: string,
|
||||||
method belongsToCommunity*(self: AccessInterface): bool {.base.} =
|
method belongsToCommunity*(self: AccessInterface): bool {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method loadMoreMessages*(self: AccessInterface) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
method addReaction*(self: AccessInterface, messageId: string, emojiId: int) {.base.} =
|
method addReaction*(self: AccessInterface, messageId: string, emojiId: int) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,7 @@ method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: se
|
||||||
pinnedMessages: seq[PinnedMessageDto]) =
|
pinnedMessages: seq[PinnedMessageDto]) =
|
||||||
var viewItems: seq[Item]
|
var viewItems: seq[Item]
|
||||||
|
|
||||||
|
if(messages.len > 0):
|
||||||
for m in messages:
|
for m in messages:
|
||||||
let sender = self.controller.getContactById(m.`from`)
|
let sender = self.controller.getContactById(m.`from`)
|
||||||
let senderDisplayName = sender.userNameOrAlias()
|
let senderDisplayName = sender.userNameOrAlias()
|
||||||
|
@ -118,7 +119,35 @@ method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: se
|
||||||
# Delete the old ChatIdentifier message first
|
# Delete the old ChatIdentifier message first
|
||||||
self.view.model().removeItem(CHAT_IDENTIFIER_MESSAGE_ID)
|
self.view.model().removeItem(CHAT_IDENTIFIER_MESSAGE_ID)
|
||||||
# Add new loaded messages
|
# Add new loaded messages
|
||||||
self.view.model().prependItems(viewItems)
|
self.view.model().appendItems(viewItems)
|
||||||
|
|
||||||
|
if(not self.view.getInitialMessagesLoaded()):
|
||||||
|
self.view.initialMessagesAreLoaded()
|
||||||
|
|
||||||
|
self.view.setLoadingHistoryMessagesInProgress(false)
|
||||||
|
|
||||||
|
method onSendingMessageSuccess*(self: Module, message: MessageDto) =
|
||||||
|
let sender = self.controller.getContactById(message.`from`)
|
||||||
|
let senderDisplayName = sender.userNameOrAlias()
|
||||||
|
let amISender = message.`from` == singletonInstance.userProfile.getPubKey()
|
||||||
|
var senderIcon = sender.identicon
|
||||||
|
var isSenderIconIdenticon = sender.identicon.len > 0
|
||||||
|
if(sender.image.thumbnail.len > 0):
|
||||||
|
senderIcon = sender.image.thumbnail
|
||||||
|
isSenderIconIdenticon = false
|
||||||
|
|
||||||
|
var item = initItem(message.id, message.responseTo, message.`from`, senderDisplayName, sender.localNickname,
|
||||||
|
senderIcon, isSenderIconIdenticon, amISender, message.outgoingStatus, message.text, message.image, message.seen,
|
||||||
|
message.timestamp, message.contentType.ContentType, message.messageType)
|
||||||
|
|
||||||
|
self.view.model().prependItem(item)
|
||||||
|
self.view.emitSendingMessageSuccessSignal()
|
||||||
|
|
||||||
|
method onSendingMessageError*(self: Module) =
|
||||||
|
self.view.emitSendingMessageErrorSignal()
|
||||||
|
|
||||||
|
method loadMoreMessages*(self: Module) =
|
||||||
|
self.controller.loadMoreMessages()
|
||||||
|
|
||||||
method toggleReaction*(self: Module, messageId: string, emojiId: int) =
|
method toggleReaction*(self: Module, messageId: string, emojiId: int) =
|
||||||
var emojiIdAsEnum: EmojiId
|
var emojiIdAsEnum: EmojiId
|
||||||
|
|
|
@ -12,3 +12,9 @@ method onReactionRemoved*(self: AccessInterface, messageId: string, emojiId: int
|
||||||
|
|
||||||
method onPinUnpinMessage*(self: AccessInterface, messageId: string, pin: bool) {.base.} =
|
method onPinUnpinMessage*(self: AccessInterface, messageId: string, pin: bool) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method onSendingMessageSuccess*(self: AccessInterface, message: MessageDto) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method onSendingMessageError*(self: AccessInterface) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
|
@ -1,6 +1,9 @@
|
||||||
method viewDidLoad*(self: AccessInterface) {.base.} =
|
method viewDidLoad*(self: AccessInterface) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method loadMoreMessages*(self: AccessInterface) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
method toggleReaction*(self: AccessInterface, messageId: string, emojiId: int) {.base.} =
|
method toggleReaction*(self: AccessInterface, messageId: string, emojiId: int) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@ QtObject:
|
||||||
delegate: io_interface.AccessInterface
|
delegate: io_interface.AccessInterface
|
||||||
model: Model
|
model: Model
|
||||||
modelVariant: QVariant
|
modelVariant: QVariant
|
||||||
|
initialMessagesLoaded: bool
|
||||||
|
loadingHistoryMessagesInProgress: bool
|
||||||
|
|
||||||
proc delete*(self: View) =
|
proc delete*(self: View) =
|
||||||
self.model.delete
|
self.model.delete
|
||||||
|
@ -20,6 +22,8 @@ QtObject:
|
||||||
result.delegate = delegate
|
result.delegate = delegate
|
||||||
result.model = newModel()
|
result.model = newModel()
|
||||||
result.modelVariant = newQVariant(result.model)
|
result.modelVariant = newQVariant(result.model)
|
||||||
|
result.initialMessagesLoaded = false
|
||||||
|
result.loadingHistoryMessagesInProgress = false
|
||||||
|
|
||||||
proc load*(self: View) =
|
proc load*(self: View) =
|
||||||
self.delegate.viewDidLoad()
|
self.delegate.viewDidLoad()
|
||||||
|
@ -29,7 +33,6 @@ QtObject:
|
||||||
|
|
||||||
proc getModel(self: View): QVariant {.slot.} =
|
proc getModel(self: View): QVariant {.slot.} =
|
||||||
return self.modelVariant
|
return self.modelVariant
|
||||||
|
|
||||||
QtProperty[QVariant] model:
|
QtProperty[QVariant] model:
|
||||||
read = getModel
|
read = getModel
|
||||||
|
|
||||||
|
@ -65,3 +68,41 @@ QtObject:
|
||||||
|
|
||||||
proc getNumberOfPinnedMessages*(self: View): int {.slot.} =
|
proc getNumberOfPinnedMessages*(self: View): int {.slot.} =
|
||||||
return self.delegate.getNumberOfPinnedMessages()
|
return self.delegate.getNumberOfPinnedMessages()
|
||||||
|
|
||||||
|
proc initialMessagesLoadedChanged*(self: View) {.signal.}
|
||||||
|
proc getInitialMessagesLoaded*(self: View): bool {.slot.} =
|
||||||
|
return self.initialMessagesLoaded
|
||||||
|
QtProperty[bool] initialMessagesLoaded:
|
||||||
|
read = getInitialMessagesLoaded
|
||||||
|
notify = initialMessagesLoadedChanged
|
||||||
|
|
||||||
|
proc initialMessagesAreLoaded*(self: View) = # this is not a slot
|
||||||
|
if (self.initialMessagesLoaded):
|
||||||
|
return
|
||||||
|
self.initialMessagesLoaded = true
|
||||||
|
self.initialMessagesLoadedChanged()
|
||||||
|
|
||||||
|
proc loadingHistoryMessagesInProgressChanged*(self: View) {.signal.}
|
||||||
|
proc getLoadingHistoryMessagesInProgress*(self: View): bool {.slot.} =
|
||||||
|
return self.loadingHistoryMessagesInProgress
|
||||||
|
QtProperty[bool] loadingHistoryMessagesInProgress:
|
||||||
|
read = getLoadingHistoryMessagesInProgress
|
||||||
|
notify = loadingHistoryMessagesInProgressChanged
|
||||||
|
|
||||||
|
proc setLoadingHistoryMessagesInProgress*(self: View, value: bool) = # this is not a slot
|
||||||
|
if (value == self.loadingHistoryMessagesInProgress):
|
||||||
|
return
|
||||||
|
self.loadingHistoryMessagesInProgress = value
|
||||||
|
self.loadingHistoryMessagesInProgressChanged()
|
||||||
|
|
||||||
|
proc loadMoreMessages*(self: View) {.slot.} =
|
||||||
|
self.setLoadingHistoryMessagesInProgress(true)
|
||||||
|
self.delegate.loadMoreMessages()
|
||||||
|
|
||||||
|
proc messageSuccessfullySent*(self: View) {.signal.}
|
||||||
|
proc emitSendingMessageSuccessSignal*(self: View) =
|
||||||
|
self.messageSuccessfullySent()
|
||||||
|
|
||||||
|
proc sendingMessageFailed*(self: View) {.signal.}
|
||||||
|
proc emitSendingMessageErrorSignal*(self: View) =
|
||||||
|
self.sendingMessageFailed()
|
|
@ -173,6 +173,8 @@ method newPinnedMessagesLoaded*(self: Module, pinnedMessages: seq[PinnedMessageD
|
||||||
|
|
||||||
viewItems = item & viewItems # messages are sorted from the most recent to the least recent one
|
viewItems = item & viewItems # messages are sorted from the most recent to the least recent one
|
||||||
|
|
||||||
|
if(viewItems.len == 0):
|
||||||
|
return
|
||||||
self.view.pinnedModel().prependItems(viewItems)
|
self.view.pinnedModel().prependItems(viewItems)
|
||||||
|
|
||||||
method unpinMessage*(self: Module, messageId: string) =
|
method unpinMessage*(self: Module, messageId: string) =
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import Tables, json, strformat
|
import json, strformat
|
||||||
import ../../../app_service/common/types
|
import ../../../app_service/common/types
|
||||||
|
|
||||||
export types.ContentType
|
export types.ContentType
|
||||||
|
|
|
@ -158,6 +158,20 @@ QtObject:
|
||||||
self.endInsertRows()
|
self.endInsertRows()
|
||||||
self.countChanged()
|
self.countChanged()
|
||||||
|
|
||||||
|
proc appendItems*(self: Model, items: seq[Item]) =
|
||||||
|
if(items.len == 0):
|
||||||
|
return
|
||||||
|
|
||||||
|
let parentModelIndex = newQModelIndex()
|
||||||
|
defer: parentModelIndex.delete
|
||||||
|
|
||||||
|
let first = self.items.len
|
||||||
|
let last = first + items.len - 1
|
||||||
|
self.beginInsertRows(parentModelIndex, first, last)
|
||||||
|
self.items.add(items)
|
||||||
|
self.endInsertRows()
|
||||||
|
self.countChanged()
|
||||||
|
|
||||||
proc appendItem*(self: Model, item: Item) =
|
proc appendItem*(self: Model, item: Item) =
|
||||||
let parentModelIndex = newQModelIndex()
|
let parentModelIndex = newQModelIndex()
|
||||||
defer: parentModelIndex.delete
|
defer: parentModelIndex.delete
|
||||||
|
@ -167,6 +181,15 @@ QtObject:
|
||||||
self.endInsertRows()
|
self.endInsertRows()
|
||||||
self.countChanged()
|
self.countChanged()
|
||||||
|
|
||||||
|
proc prependItem*(self: Model, item: Item) =
|
||||||
|
let parentModelIndex = newQModelIndex()
|
||||||
|
defer: parentModelIndex.delete
|
||||||
|
|
||||||
|
self.beginInsertRows(parentModelIndex, 0, 0)
|
||||||
|
self.items.insert(item, 0)
|
||||||
|
self.endInsertRows()
|
||||||
|
self.countChanged()
|
||||||
|
|
||||||
proc removeItem*(self: Model, messageId: string) =
|
proc removeItem*(self: Model, messageId: string) =
|
||||||
let ind = self.findIndexForMessageId(messageId)
|
let ind = self.findIndexForMessageId(messageId)
|
||||||
if(ind == -1):
|
if(ind == -1):
|
||||||
|
|
|
@ -111,8 +111,8 @@ QtObject:
|
||||||
proc processMessageUpdateAfterSend*(self: Service, response: RpcResponse[JsonNode]): (seq[ChatDto], seq[MessageDto]) =
|
proc processMessageUpdateAfterSend*(self: Service, response: RpcResponse[JsonNode]): (seq[ChatDto], seq[MessageDto]) =
|
||||||
result = self.parseChatResponse(response)
|
result = self.parseChatResponse(response)
|
||||||
var (chats, messages) = result
|
var (chats, messages) = result
|
||||||
if chats.len == 0 and messages.len == 0:
|
if chats.len == 0 or messages.len == 0:
|
||||||
self.events.emit(SIGNAL_SENDING_FAILED, Args())
|
error "no chats or messages in the parsed response"
|
||||||
return
|
return
|
||||||
|
|
||||||
# This fixes issue#3490
|
# This fixes issue#3490
|
||||||
|
@ -251,7 +251,9 @@ QtObject:
|
||||||
preferredUsername,
|
preferredUsername,
|
||||||
communityId)
|
communityId)
|
||||||
|
|
||||||
discard self.processMessageUpdateAfterSend(response)
|
let (chats, messages) = self.processMessageUpdateAfterSend(response)
|
||||||
|
if chats.len == 0 or messages.len == 0:
|
||||||
|
self.events.emit(SIGNAL_SENDING_FAILED, ChatArgs(chatId: chatId))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error "Error sending message", msg = e.msg
|
error "Error sending message", msg = e.msg
|
||||||
|
|
||||||
|
|
|
@ -14,34 +14,37 @@ type
|
||||||
const asyncFetchChatMessagesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
const asyncFetchChatMessagesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||||
let arg = decode[AsyncFetchChatMessagesTaskArg](argEncoded)
|
let arg = decode[AsyncFetchChatMessagesTaskArg](argEncoded)
|
||||||
|
|
||||||
|
var responseJson = %*{
|
||||||
|
"chatId": arg.chatId
|
||||||
|
}
|
||||||
|
|
||||||
# handle messages
|
# handle messages
|
||||||
|
if(arg.msgCursor != CURSOR_VALUE_IGNORE):
|
||||||
var messagesArr: JsonNode
|
var messagesArr: JsonNode
|
||||||
var messagesCursor: string
|
var messagesCursor: JsonNode
|
||||||
let msgsResponse = status_go.fetchMessages(arg.chatId, arg.msgCursor, arg.limit)
|
let msgsResponse = status_go.fetchMessages(arg.chatId, arg.msgCursor, arg.limit)
|
||||||
discard msgsResponse.result.getProp("cursor", messagesCursor)
|
discard msgsResponse.result.getProp("cursor", messagesCursor)
|
||||||
discard msgsResponse.result.getProp("messages", messagesArr)
|
discard msgsResponse.result.getProp("messages", messagesArr)
|
||||||
|
responseJson["messages"] = messagesArr
|
||||||
|
responseJson["messagesCursor"] = messagesCursor
|
||||||
|
|
||||||
# handle pinned messages
|
# handle pinned messages
|
||||||
|
if(arg.pinnedMsgCursor != CURSOR_VALUE_IGNORE):
|
||||||
var pinnedMsgArr: JsonNode
|
var pinnedMsgArr: JsonNode
|
||||||
var pinnedMsgCursor: string
|
var pinnedMsgCursor: JsonNode
|
||||||
let pinnedMsgsResponse = status_go.fetchPinnedMessages(arg.chatId, arg.pinnedMsgCursor, arg.limit)
|
let pinnedMsgsResponse = status_go.fetchPinnedMessages(arg.chatId, arg.pinnedMsgCursor, arg.limit)
|
||||||
discard pinnedMsgsResponse.result.getProp("cursor", pinnedMsgCursor)
|
discard pinnedMsgsResponse.result.getProp("cursor", pinnedMsgCursor)
|
||||||
discard pinnedMsgsResponse.result.getProp("pinnedMessages", pinnedMsgArr)
|
discard pinnedMsgsResponse.result.getProp("pinnedMessages", pinnedMsgArr)
|
||||||
|
responseJson["pinnedMessages"] = pinnedMsgArr
|
||||||
|
responseJson["pinnedMessagesCursor"] = pinnedMsgCursor
|
||||||
|
|
||||||
# handle reactions
|
# handle reactions
|
||||||
var reactionsArr: JsonNode
|
if(arg.msgCursor != CURSOR_VALUE_IGNORE):
|
||||||
# messages and reactions are using the same cursor
|
# messages and reactions are using the same cursor
|
||||||
|
var reactionsArr: JsonNode
|
||||||
let rResponse = status_go.fetchReactions(arg.chatId, arg.msgCursor, arg.limit)
|
let rResponse = status_go.fetchReactions(arg.chatId, arg.msgCursor, arg.limit)
|
||||||
reactionsArr = rResponse.result
|
reactionsArr = rResponse.result
|
||||||
|
responseJson["pinnedMessages"] = reactionsArr
|
||||||
let responseJson = %*{
|
|
||||||
"chatId": arg.chatId,
|
|
||||||
"messages": messagesArr,
|
|
||||||
"messagesCursor": messagesCursor,
|
|
||||||
"pinnedMessages": pinnedMsgArr,
|
|
||||||
"pinnedMessagesCursor": pinnedMsgCursor,
|
|
||||||
"reactions": reactionsArr
|
|
||||||
}
|
|
||||||
|
|
||||||
arg.finish(responseJson)
|
arg.finish(responseJson)
|
||||||
|
|
||||||
|
|
|
@ -13,12 +13,11 @@ export message_dto
|
||||||
export pinned_msg_dto
|
export pinned_msg_dto
|
||||||
export reaction_dto
|
export reaction_dto
|
||||||
|
|
||||||
include async_tasks
|
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "messages-service"
|
topics = "messages-service"
|
||||||
|
|
||||||
const MESSAGES_PER_PAGE = 20
|
const MESSAGES_PER_PAGE = 20
|
||||||
|
const CURSOR_VALUE_IGNORE = "ignore"
|
||||||
|
|
||||||
# Signals which may be emitted by this service:
|
# 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 all signals
|
const SIGNAL_MESSAGES_LOADED* = "new-messagesLoaded" #Once we are done with refactoring we should remove "new-" from all signals
|
||||||
|
@ -29,6 +28,8 @@ const SIGNAL_MESSAGES_MARKED_AS_READ* = "new-messagesMarkedAsRead"
|
||||||
const SIGNAL_MESSAGE_REACTION_ADDED* = "new-messageReactionAdded"
|
const SIGNAL_MESSAGE_REACTION_ADDED* = "new-messageReactionAdded"
|
||||||
const SIGNAL_MESSAGE_REACTION_REMOVED* = "new-messageReactionRemoved"
|
const SIGNAL_MESSAGE_REACTION_REMOVED* = "new-messageReactionRemoved"
|
||||||
|
|
||||||
|
include async_tasks
|
||||||
|
|
||||||
type
|
type
|
||||||
SearchMessagesLoadedArgs* = ref object of Args
|
SearchMessagesLoadedArgs* = ref object of Args
|
||||||
messages*: seq[MessageDto]
|
messages*: seq[MessageDto]
|
||||||
|
@ -59,7 +60,9 @@ QtObject:
|
||||||
events: EventEmitter
|
events: EventEmitter
|
||||||
threadpool: ThreadPool
|
threadpool: ThreadPool
|
||||||
msgCursor: Table[string, string]
|
msgCursor: Table[string, string]
|
||||||
|
lastUsedMsgCursor: Table[string, string]
|
||||||
pinnedMsgCursor: Table[string, string]
|
pinnedMsgCursor: Table[string, string]
|
||||||
|
lastUsedPinnedMsgCursor: Table[string, string]
|
||||||
numOfPinnedMessagesPerChat: Table[string, int] # [chat_id, num_of_pinned_messages]
|
numOfPinnedMessagesPerChat: Table[string, int] # [chat_id, num_of_pinned_messages]
|
||||||
|
|
||||||
proc delete*(self: Service) =
|
proc delete*(self: Service) =
|
||||||
|
@ -71,7 +74,9 @@ QtObject:
|
||||||
result.events = events
|
result.events = events
|
||||||
result.threadpool = threadpool
|
result.threadpool = threadpool
|
||||||
result.msgCursor = initTable[string, string]()
|
result.msgCursor = initTable[string, string]()
|
||||||
|
result.lastUsedMsgCursor = initTable[string, string]()
|
||||||
result.pinnedMsgCursor = initTable[string, string]()
|
result.pinnedMsgCursor = initTable[string, string]()
|
||||||
|
result.lastUsedPinnedMsgCursor = initTable[string, string]()
|
||||||
|
|
||||||
proc initialMessagesFetched(self: Service, chatId: string): bool =
|
proc initialMessagesFetched(self: Service, chatId: string): bool =
|
||||||
return self.msgCursor.hasKey(chatId)
|
return self.msgCursor.hasKey(chatId)
|
||||||
|
@ -100,10 +105,17 @@ QtObject:
|
||||||
var chatId: string
|
var chatId: string
|
||||||
discard responseObj.getProp("chatId", chatId)
|
discard responseObj.getProp("chatId", 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]
|
||||||
|
|
||||||
# handling messages
|
# handling messages
|
||||||
var msgCursor: string
|
var msgCursor: string
|
||||||
if(responseObj.getProp("messagesCursor", msgCursor)):
|
if(responseObj.getProp("messagesCursor", msgCursor)):
|
||||||
|
if(msgCursor.len > 0):
|
||||||
self.msgCursor[chatId] = msgCursor
|
self.msgCursor[chatId] = msgCursor
|
||||||
|
else:
|
||||||
|
self.msgCursor[chatId] = self.lastUsedMsgCursor[chatId]
|
||||||
|
|
||||||
var messagesArr: JsonNode
|
var messagesArr: JsonNode
|
||||||
var messages: seq[MessageDto]
|
var messages: seq[MessageDto]
|
||||||
|
@ -113,7 +125,10 @@ QtObject:
|
||||||
# handling pinned messages
|
# handling pinned messages
|
||||||
var pinnedMsgCursor: string
|
var pinnedMsgCursor: string
|
||||||
if(responseObj.getProp("pinnedMessagesCursor", pinnedMsgCursor)):
|
if(responseObj.getProp("pinnedMessagesCursor", pinnedMsgCursor)):
|
||||||
|
if(pinnedMsgCursor.len > 0):
|
||||||
self.pinnedMsgCursor[chatId] = pinnedMsgCursor
|
self.pinnedMsgCursor[chatId] = pinnedMsgCursor
|
||||||
|
else:
|
||||||
|
self.pinnedMsgCursor[chatId] = self.lastUsedPinnedMsgCursor[chatId]
|
||||||
|
|
||||||
var pinnedMsgArr: JsonNode
|
var pinnedMsgArr: JsonNode
|
||||||
var pinnedMessages: seq[PinnedMessageDto]
|
var pinnedMessages: seq[PinnedMessageDto]
|
||||||
|
@ -142,13 +157,27 @@ QtObject:
|
||||||
error "empty chat id", methodName="asyncLoadMoreMessagesForChat"
|
error "empty chat id", methodName="asyncLoadMoreMessagesForChat"
|
||||||
return
|
return
|
||||||
|
|
||||||
|
var msgCursor = self.getCurrentMessageCursor(chatId)
|
||||||
|
if(self.lastUsedMsgCursor.hasKey(chatId) and msgCursor == self.lastUsedMsgCursor[chatId]):
|
||||||
|
msgCursor = CURSOR_VALUE_IGNORE
|
||||||
|
|
||||||
|
var pinnedMsgCursor = self.getCurrentPinnedMessageCursor(chatId)
|
||||||
|
if(self.lastUsedPinnedMsgCursor.hasKey(chatId) and pinnedMsgCursor == self.lastUsedPinnedMsgCursor[chatId]):
|
||||||
|
pinnedMsgCursor = CURSOR_VALUE_IGNORE
|
||||||
|
|
||||||
|
if(msgCursor == CURSOR_VALUE_IGNORE and pinnedMsgCursor == CURSOR_VALUE_IGNORE):
|
||||||
|
# it's important to emit signal in case we are not fetching messages, so we can update the view appropriatelly.
|
||||||
|
let data = MessagesLoadedArgs(chatId: chatId)
|
||||||
|
self.events.emit(SIGNAL_MESSAGES_LOADED, data)
|
||||||
|
return
|
||||||
|
|
||||||
let arg = AsyncFetchChatMessagesTaskArg(
|
let arg = AsyncFetchChatMessagesTaskArg(
|
||||||
tptr: cast[ByteAddress](asyncFetchChatMessagesTask),
|
tptr: cast[ByteAddress](asyncFetchChatMessagesTask),
|
||||||
vptr: cast[ByteAddress](self.vptr),
|
vptr: cast[ByteAddress](self.vptr),
|
||||||
slot: "onAsyncLoadMoreMessagesForChat",
|
slot: "onAsyncLoadMoreMessagesForChat",
|
||||||
chatId: chatId,
|
chatId: chatId,
|
||||||
msgCursor: self.getCurrentMessageCursor(chatId),
|
msgCursor: msgCursor,
|
||||||
pinnedMsgCursor: self.getCurrentPinnedMessageCursor(chatId),
|
pinnedMsgCursor: pinnedMsgCursor,
|
||||||
limit: MESSAGES_PER_PAGE
|
limit: MESSAGES_PER_PAGE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,16 @@ QtObject {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property var messageModule
|
property var messageModule
|
||||||
|
property var messagesModel: messageModule.model
|
||||||
|
|
||||||
|
function loadMoreMessages () {
|
||||||
|
if(!messageModule)
|
||||||
|
return
|
||||||
|
if(!messageModule.initialMessagesLoaded || messageModule.loadingHistoryMessagesInProgress)
|
||||||
|
return
|
||||||
|
|
||||||
|
messageModule.loadMoreMessages()
|
||||||
|
}
|
||||||
|
|
||||||
function getMessageByIdAsJson (id) {
|
function getMessageByIdAsJson (id) {
|
||||||
if(!messageModule)
|
if(!messageModule)
|
||||||
|
|
|
@ -36,9 +36,33 @@ Item {
|
||||||
property int countOnStartUp: 0
|
property int countOnStartUp: 0
|
||||||
signal openStickerPackPopup(string stickerPackId)
|
signal openStickerPackPopup(string stickerPackId)
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: loadingMessagesIndicator
|
||||||
|
visible: messageStore.messageModule.loadingHistoryMessagesInProgress
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
height: visible? 20 : 0
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
active: messageStore.messageModule.loadingHistoryMessagesInProgress
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
sourceComponent: Component {
|
||||||
|
LoadingAnimation {
|
||||||
|
width: 18
|
||||||
|
height: 18
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: chatLogView
|
id: chatLogView
|
||||||
anchors.fill: parent
|
anchors.top: loadingMessagesIndicator.bottom
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
spacing: 0
|
spacing: 0
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
clip: true
|
clip: true
|
||||||
|
@ -92,9 +116,9 @@ Item {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// ScrollBar.vertical: ScrollBar {
|
ScrollBar.vertical: ScrollBar {
|
||||||
// visible: chatLogView.visibleArea.heightRatio < 1
|
visible: chatLogView.visibleArea.heightRatio < 1
|
||||||
// }
|
}
|
||||||
|
|
||||||
// Connections {
|
// Connections {
|
||||||
// id: contentHeightConnection
|
// id: contentHeightConnection
|
||||||
|
@ -112,80 +136,79 @@ Item {
|
||||||
id: timer
|
id: timer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Button {
|
Button {
|
||||||
// readonly property int buttonPadding: 5
|
readonly property int buttonPadding: 5
|
||||||
|
|
||||||
// id: scrollDownButton
|
id: scrollDownButton
|
||||||
// visible: false
|
visible: false
|
||||||
// height: 32
|
height: 32
|
||||||
// width: nbMessages.width + arrowImage.width + 2 * Style.current.halfPadding + (nbMessages.visible ? scrollDownButton.buttonPadding : 0)
|
width: nbMessages.width + arrowImage.width + 2 * Style.current.halfPadding + (nbMessages.visible ? scrollDownButton.buttonPadding : 0)
|
||||||
// anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
// anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
// anchors.rightMargin: Style.current.padding
|
anchors.rightMargin: Style.current.padding
|
||||||
// background: Rectangle {
|
background: Rectangle {
|
||||||
// color: Style.current.buttonSecondaryColor
|
color: Style.current.buttonSecondaryColor
|
||||||
// border.width: 0
|
border.width: 0
|
||||||
// radius: 16
|
radius: 16
|
||||||
// }
|
}
|
||||||
// onClicked: {
|
onClicked: {
|
||||||
// newMessages = 0
|
newMessages = 0
|
||||||
// scrollDownButton.visible = false
|
scrollDownButton.visible = false
|
||||||
// chatLogView.scrollToBottom(true)
|
chatLogView.scrollToBottom(true)
|
||||||
// }
|
}
|
||||||
|
|
||||||
// StyledText {
|
StyledText {
|
||||||
// id: nbMessages
|
id: nbMessages
|
||||||
// visible: newMessages > 0
|
visible: newMessages > 0
|
||||||
// width: visible ? implicitWidth : 0
|
width: visible ? implicitWidth : 0
|
||||||
// text: newMessages
|
text: newMessages
|
||||||
// anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
// anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
// color: Style.current.pillButtonTextColor
|
color: Style.current.pillButtonTextColor
|
||||||
// font.pixelSize: 15
|
font.pixelSize: 15
|
||||||
// anchors.leftMargin: Style.current.halfPadding
|
anchors.leftMargin: Style.current.halfPadding
|
||||||
// }
|
}
|
||||||
|
|
||||||
// SVGImage {
|
SVGImage {
|
||||||
// id: arrowImage
|
id: arrowImage
|
||||||
// width: 24
|
width: 24
|
||||||
// height: 24
|
height: 24
|
||||||
// anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
// anchors.left: nbMessages.right
|
anchors.left: nbMessages.right
|
||||||
// source: Style.svg("leave_chat")
|
source: Style.svg("leave_chat")
|
||||||
// anchors.leftMargin: nbMessages.visible ? scrollDownButton.buttonPadding : 0
|
anchors.leftMargin: nbMessages.visible ? scrollDownButton.buttonPadding : 0
|
||||||
// rotation: -90
|
rotation: -90
|
||||||
|
|
||||||
// ColorOverlay {
|
ColorOverlay {
|
||||||
// anchors.fill: parent
|
anchors.fill: parent
|
||||||
// source: parent
|
source: parent
|
||||||
// color: Style.current.pillButtonTextColor
|
color: Style.current.pillButtonTextColor
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// MouseArea {
|
MouseArea {
|
||||||
// cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
// anchors.fill: parent
|
anchors.fill: parent
|
||||||
// onPressed: mouse.accepted = false
|
onPressed: mouse.accepted = false
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
function scrollToBottom(force, caller) {
|
function scrollToBottom(force, caller) {
|
||||||
// Not Refactored Yet
|
if (!force && !chatLogView.atYEnd) {
|
||||||
// if (!force && !chatLogView.atYEnd) {
|
// User has scrolled up, we don't want to scroll back
|
||||||
// // User has scrolled up, we don't want to scroll back
|
return false
|
||||||
// return false
|
}
|
||||||
// }
|
if (caller && caller !== chatLogView.itemAtIndex(chatLogView.count - 1)) {
|
||||||
// if (caller && caller !== chatLogView.itemAtIndex(chatLogView.count - 1)) {
|
// If we have a caller, only accept its request if it's the last message
|
||||||
// // If we have a caller, only accept its request if it's the last message
|
return false
|
||||||
// return false
|
}
|
||||||
// }
|
// Call this twice and with a timer since the first scroll to bottom might have happened before some stuff loads
|
||||||
// // Call this twice and with a timer since the first scroll to bottom might have happened before some stuff loads
|
// meaning that the scroll will not actually be at the bottom on switch
|
||||||
// // meaning that the scroll will not actually be at the bottom on switch
|
// Add a small delay because images, even though they say they say they are loaed, they aren't shown yet
|
||||||
// // Add a small delay because images, even though they say they say they are loaed, they aren't shown yet
|
Qt.callLater(chatLogView.positionViewAtBeginning)
|
||||||
// Qt.callLater(chatLogView.positionViewAtBeginning)
|
timer.setTimeout(function() {
|
||||||
// timer.setTimeout(function() {
|
Qt.callLater(chatLogView.positionViewAtBeginning)
|
||||||
// Qt.callLater(chatLogView.positionViewAtBeginning)
|
}, 100);
|
||||||
// }, 100);
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,24 +221,24 @@ Item {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Connections {
|
Connections {
|
||||||
|
target: messageStore.messageModule
|
||||||
|
|
||||||
|
onMessageSuccessfullySent: {
|
||||||
|
chatLogView.scrollToBottom(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
onSendingMessageFailed: {
|
||||||
|
sendingMsgFailedPopup.open();
|
||||||
|
}
|
||||||
|
|
||||||
// Not Refactored Yet
|
// Not Refactored Yet
|
||||||
// target: root.store.chatsModelInst.messageView
|
|
||||||
|
|
||||||
// onSendingMessageSuccess: {
|
|
||||||
// chatLogView.scrollToBottom(true)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// onSendingMessageFailed: {
|
|
||||||
// sendingMsgFailedPopup.open();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// onNewMessagePushed: {
|
// onNewMessagePushed: {
|
||||||
// if (!chatLogView.scrollToBottom()) {
|
// if (!chatLogView.scrollToBottom()) {
|
||||||
// newMessages++
|
// newMessages++
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
}
|
||||||
|
|
||||||
// Connections {
|
// Connections {
|
||||||
// Not Refactored Yet
|
// Not Refactored Yet
|
||||||
|
@ -274,24 +297,15 @@ Item {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Not Refactored Yet
|
onContentYChanged: {
|
||||||
// property var loadMsgs : Backpressure.oneInTime(chatLogView, 500, function() {
|
scrollDownButton.visible = contentHeight - (scrollY + height) > 400
|
||||||
// if(!messages.initialMessagesLoaded || messages.loadingHistoryMessages)
|
let loadMore = scrollDownButton.visible && scrollY < 500
|
||||||
// return
|
if(loadMore){
|
||||||
|
messageStore.loadMoreMessages()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// root.store.chatsModelInst.messageView.loadMoreMessages(chatId);
|
model: messageStore.messagesModel
|
||||||
// });
|
|
||||||
|
|
||||||
// onContentYChanged: {
|
|
||||||
// scrollDownButton.visible = (contentHeight - (scrollY + height) > 400)
|
|
||||||
// if(scrollDownButton.visible && scrollY < 500){
|
|
||||||
// loadMsgs();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
model: messageStore.messageModule.model
|
|
||||||
section.property: "sectionIdentifier"
|
|
||||||
section.criteria: ViewSection.FullString
|
|
||||||
|
|
||||||
// Not Refactored Yet
|
// Not Refactored Yet
|
||||||
//Component.onCompleted: scrollToBottom(true)
|
//Component.onCompleted: scrollToBottom(true)
|
||||||
|
@ -330,11 +344,11 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MessageDialog {
|
MessageDialog {
|
||||||
// id: sendingMsgFailedPopup
|
id: sendingMsgFailedPopup
|
||||||
// standardButtons: StandardButton.Ok
|
standardButtons: StandardButton.Ok
|
||||||
// //% "Failed to send message."
|
//% "Failed to send message."
|
||||||
// text: qsTrId("failed-to-send-message-")
|
text: qsTrId("failed-to-send-message-")
|
||||||
// icon: StandardIcon.Critical
|
icon: StandardIcon.Critical
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue