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)
|
||||
if(self.chatId != args.chat.id):
|
||||
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):
|
||||
let args = MessagePinUnpinArgs(e)
|
||||
|
@ -100,6 +106,9 @@ method getOneToOneChatNameAndImage*(self: Controller): tuple[name: string, image
|
|||
method belongsToCommunity*(self: Controller): bool =
|
||||
return self.belongsToCommunity
|
||||
|
||||
method loadMoreMessages*(self: Controller) =
|
||||
self.messageService.asyncLoadMoreMessagesForChat(self.chatId)
|
||||
|
||||
method addReaction*(self: Controller, messageId: string, emojiId: int) =
|
||||
self.messageService.addReaction(self.chatId, messageId, emojiId)
|
||||
|
||||
|
|
|
@ -31,6 +31,9 @@ method getOneToOneChatNameAndImage*(self: AccessInterface): tuple[name: string,
|
|||
method belongsToCommunity*(self: AccessInterface): bool {.base.} =
|
||||
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.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
|
|
|
@ -81,44 +81,73 @@ method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: se
|
|||
pinnedMessages: seq[PinnedMessageDto]) =
|
||||
var viewItems: seq[Item]
|
||||
|
||||
for m in messages:
|
||||
let sender = self.controller.getContactById(m.`from`)
|
||||
let senderDisplayName = sender.userNameOrAlias()
|
||||
let amISender = m.`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
|
||||
if(messages.len > 0):
|
||||
for m in messages:
|
||||
let sender = self.controller.getContactById(m.`from`)
|
||||
let senderDisplayName = sender.userNameOrAlias()
|
||||
let amISender = m.`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(m.id, m.responseTo, m.`from`, senderDisplayName, sender.localNickname, senderIcon,
|
||||
isSenderIconIdenticon, amISender, m.outgoingStatus, m.text, m.image, m.seen, m.timestamp, m.contentType.ContentType,
|
||||
m.messageType)
|
||||
var item = initItem(m.id, m.responseTo, m.`from`, senderDisplayName, sender.localNickname, senderIcon,
|
||||
isSenderIconIdenticon, amISender, m.outgoingStatus, m.text, m.image, m.seen, m.timestamp, m.contentType.ContentType,
|
||||
m.messageType)
|
||||
|
||||
for r in reactions:
|
||||
if(r.messageId == m.id):
|
||||
var emojiIdAsEnum: EmojiId
|
||||
if(message_reaction_item.toEmojiIdAsEnum(r.emojiId, emojiIdAsEnum)):
|
||||
let userWhoAddedThisReaction = self.controller.getContactById(r.`from`)
|
||||
let didIReactWithThisEmoji = userWhoAddedThisReaction.id == singletonInstance.userProfile.getPubKey()
|
||||
item.addReaction(emojiIdAsEnum, didIReactWithThisEmoji, userWhoAddedThisReaction.id,
|
||||
userWhoAddedThisReaction.userNameOrAlias(), r.id)
|
||||
else:
|
||||
error "wrong emoji id found when loading messages"
|
||||
for r in reactions:
|
||||
if(r.messageId == m.id):
|
||||
var emojiIdAsEnum: EmojiId
|
||||
if(message_reaction_item.toEmojiIdAsEnum(r.emojiId, emojiIdAsEnum)):
|
||||
let userWhoAddedThisReaction = self.controller.getContactById(r.`from`)
|
||||
let didIReactWithThisEmoji = userWhoAddedThisReaction.id == singletonInstance.userProfile.getPubKey()
|
||||
item.addReaction(emojiIdAsEnum, didIReactWithThisEmoji, userWhoAddedThisReaction.id,
|
||||
userWhoAddedThisReaction.userNameOrAlias(), r.id)
|
||||
else:
|
||||
error "wrong emoji id found when loading messages"
|
||||
|
||||
for p in pinnedMessages:
|
||||
if(p.message.id == m.id):
|
||||
item.pinned = true
|
||||
for p in pinnedMessages:
|
||||
if(p.message.id == m.id):
|
||||
item.pinned = true
|
||||
|
||||
# messages are sorted from the most recent to the least recent one
|
||||
viewItems.add(item)
|
||||
# messages are sorted from the most recent to the least recent one
|
||||
viewItems.add(item)
|
||||
|
||||
# ChatIdentifier message will be always the first message (the oldest one)
|
||||
viewItems.add(self.createChatIdentifierItem())
|
||||
# Delete the old ChatIdentifier message first
|
||||
self.view.model().removeItem(CHAT_IDENTIFIER_MESSAGE_ID)
|
||||
# Add new loaded messages
|
||||
self.view.model().prependItems(viewItems)
|
||||
# ChatIdentifier message will be always the first message (the oldest one)
|
||||
viewItems.add(self.createChatIdentifierItem())
|
||||
# Delete the old ChatIdentifier message first
|
||||
self.view.model().removeItem(CHAT_IDENTIFIER_MESSAGE_ID)
|
||||
# Add new loaded messages
|
||||
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) =
|
||||
var emojiIdAsEnum: EmojiId
|
||||
|
|
|
@ -11,4 +11,10 @@ method onReactionRemoved*(self: AccessInterface, messageId: string, emojiId: int
|
|||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onPinUnpinMessage*(self: AccessInterface, messageId: string, pin: bool) {.base.} =
|
||||
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.} =
|
||||
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.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ QtObject:
|
|||
delegate: io_interface.AccessInterface
|
||||
model: Model
|
||||
modelVariant: QVariant
|
||||
initialMessagesLoaded: bool
|
||||
loadingHistoryMessagesInProgress: bool
|
||||
|
||||
proc delete*(self: View) =
|
||||
self.model.delete
|
||||
|
@ -20,6 +22,8 @@ QtObject:
|
|||
result.delegate = delegate
|
||||
result.model = newModel()
|
||||
result.modelVariant = newQVariant(result.model)
|
||||
result.initialMessagesLoaded = false
|
||||
result.loadingHistoryMessagesInProgress = false
|
||||
|
||||
proc load*(self: View) =
|
||||
self.delegate.viewDidLoad()
|
||||
|
@ -29,7 +33,6 @@ QtObject:
|
|||
|
||||
proc getModel(self: View): QVariant {.slot.} =
|
||||
return self.modelVariant
|
||||
|
||||
QtProperty[QVariant] model:
|
||||
read = getModel
|
||||
|
||||
|
@ -64,4 +67,42 @@ QtObject:
|
|||
return self.delegate.amIChatAdmin()
|
||||
|
||||
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
|
||||
|
||||
if(viewItems.len == 0):
|
||||
return
|
||||
self.view.pinnedModel().prependItems(viewItems)
|
||||
|
||||
method unpinMessage*(self: Module, messageId: string) =
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import Tables, json, strformat
|
||||
import json, strformat
|
||||
import ../../../app_service/common/types
|
||||
|
||||
export types.ContentType
|
||||
|
|
|
@ -158,6 +158,20 @@ QtObject:
|
|||
self.endInsertRows()
|
||||
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) =
|
||||
let parentModelIndex = newQModelIndex()
|
||||
defer: parentModelIndex.delete
|
||||
|
@ -167,6 +181,15 @@ QtObject:
|
|||
self.endInsertRows()
|
||||
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) =
|
||||
let ind = self.findIndexForMessageId(messageId)
|
||||
if(ind == -1):
|
||||
|
|
|
@ -111,8 +111,8 @@ QtObject:
|
|||
proc processMessageUpdateAfterSend*(self: Service, response: RpcResponse[JsonNode]): (seq[ChatDto], seq[MessageDto]) =
|
||||
result = self.parseChatResponse(response)
|
||||
var (chats, messages) = result
|
||||
if chats.len == 0 and messages.len == 0:
|
||||
self.events.emit(SIGNAL_SENDING_FAILED, Args())
|
||||
if chats.len == 0 or messages.len == 0:
|
||||
error "no chats or messages in the parsed response"
|
||||
return
|
||||
|
||||
# This fixes issue#3490
|
||||
|
@ -251,7 +251,9 @@ QtObject:
|
|||
preferredUsername,
|
||||
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:
|
||||
error "Error sending message", msg = e.msg
|
||||
|
||||
|
|
|
@ -14,34 +14,37 @@ type
|
|||
const asyncFetchChatMessagesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let arg = decode[AsyncFetchChatMessagesTaskArg](argEncoded)
|
||||
|
||||
var responseJson = %*{
|
||||
"chatId": arg.chatId
|
||||
}
|
||||
|
||||
# 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)
|
||||
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
|
||||
|
||||
# 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)
|
||||
if(arg.pinnedMsgCursor != CURSOR_VALUE_IGNORE):
|
||||
var pinnedMsgArr: JsonNode
|
||||
var pinnedMsgCursor: JsonNode
|
||||
let pinnedMsgsResponse = status_go.fetchPinnedMessages(arg.chatId, arg.pinnedMsgCursor, arg.limit)
|
||||
discard pinnedMsgsResponse.result.getProp("cursor", pinnedMsgCursor)
|
||||
discard pinnedMsgsResponse.result.getProp("pinnedMessages", pinnedMsgArr)
|
||||
responseJson["pinnedMessages"] = pinnedMsgArr
|
||||
responseJson["pinnedMessagesCursor"] = pinnedMsgCursor
|
||||
|
||||
# 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
|
||||
}
|
||||
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["pinnedMessages"] = reactionsArr
|
||||
|
||||
arg.finish(responseJson)
|
||||
|
||||
|
|
|
@ -13,12 +13,11 @@ export message_dto
|
|||
export pinned_msg_dto
|
||||
export reaction_dto
|
||||
|
||||
include async_tasks
|
||||
|
||||
logScope:
|
||||
topics = "messages-service"
|
||||
|
||||
const MESSAGES_PER_PAGE = 20
|
||||
const CURSOR_VALUE_IGNORE = "ignore"
|
||||
|
||||
# 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
|
||||
|
@ -29,6 +28,8 @@ const SIGNAL_MESSAGES_MARKED_AS_READ* = "new-messagesMarkedAsRead"
|
|||
const SIGNAL_MESSAGE_REACTION_ADDED* = "new-messageReactionAdded"
|
||||
const SIGNAL_MESSAGE_REACTION_REMOVED* = "new-messageReactionRemoved"
|
||||
|
||||
include async_tasks
|
||||
|
||||
type
|
||||
SearchMessagesLoadedArgs* = ref object of Args
|
||||
messages*: seq[MessageDto]
|
||||
|
@ -59,7 +60,9 @@ QtObject:
|
|||
events: EventEmitter
|
||||
threadpool: ThreadPool
|
||||
msgCursor: Table[string, string]
|
||||
lastUsedMsgCursor: Table[string, string]
|
||||
pinnedMsgCursor: Table[string, string]
|
||||
lastUsedPinnedMsgCursor: Table[string, string]
|
||||
numOfPinnedMessagesPerChat: Table[string, int] # [chat_id, num_of_pinned_messages]
|
||||
|
||||
proc delete*(self: Service) =
|
||||
|
@ -71,7 +74,9 @@ QtObject:
|
|||
result.events = events
|
||||
result.threadpool = threadpool
|
||||
result.msgCursor = initTable[string, string]()
|
||||
result.lastUsedMsgCursor = initTable[string, string]()
|
||||
result.pinnedMsgCursor = initTable[string, string]()
|
||||
result.lastUsedPinnedMsgCursor = initTable[string, string]()
|
||||
|
||||
proc initialMessagesFetched(self: Service, chatId: string): bool =
|
||||
return self.msgCursor.hasKey(chatId)
|
||||
|
@ -99,11 +104,18 @@ QtObject:
|
|||
|
||||
var chatId: string
|
||||
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
|
||||
var msgCursor: string
|
||||
if(responseObj.getProp("messagesCursor", msgCursor)):
|
||||
self.msgCursor[chatId] = msgCursor
|
||||
if(msgCursor.len > 0):
|
||||
self.msgCursor[chatId] = msgCursor
|
||||
else:
|
||||
self.msgCursor[chatId] = self.lastUsedMsgCursor[chatId]
|
||||
|
||||
var messagesArr: JsonNode
|
||||
var messages: seq[MessageDto]
|
||||
|
@ -113,7 +125,10 @@ QtObject:
|
|||
# handling pinned messages
|
||||
var pinnedMsgCursor: string
|
||||
if(responseObj.getProp("pinnedMessagesCursor", pinnedMsgCursor)):
|
||||
self.pinnedMsgCursor[chatId] = pinnedMsgCursor
|
||||
if(pinnedMsgCursor.len > 0):
|
||||
self.pinnedMsgCursor[chatId] = pinnedMsgCursor
|
||||
else:
|
||||
self.pinnedMsgCursor[chatId] = self.lastUsedPinnedMsgCursor[chatId]
|
||||
|
||||
var pinnedMsgArr: JsonNode
|
||||
var pinnedMessages: seq[PinnedMessageDto]
|
||||
|
@ -142,13 +157,27 @@ QtObject:
|
|||
error "empty chat id", methodName="asyncLoadMoreMessagesForChat"
|
||||
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(
|
||||
tptr: cast[ByteAddress](asyncFetchChatMessagesTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: "onAsyncLoadMoreMessagesForChat",
|
||||
chatId: chatId,
|
||||
msgCursor: self.getCurrentMessageCursor(chatId),
|
||||
pinnedMsgCursor: self.getCurrentPinnedMessageCursor(chatId),
|
||||
msgCursor: msgCursor,
|
||||
pinnedMsgCursor: pinnedMsgCursor,
|
||||
limit: MESSAGES_PER_PAGE
|
||||
)
|
||||
|
||||
|
|
|
@ -5,6 +5,16 @@ QtObject {
|
|||
id: root
|
||||
|
||||
property var messageModule
|
||||
property var messagesModel: messageModule.model
|
||||
|
||||
function loadMoreMessages () {
|
||||
if(!messageModule)
|
||||
return
|
||||
if(!messageModule.initialMessagesLoaded || messageModule.loadingHistoryMessagesInProgress)
|
||||
return
|
||||
|
||||
messageModule.loadMoreMessages()
|
||||
}
|
||||
|
||||
function getMessageByIdAsJson (id) {
|
||||
if(!messageModule)
|
||||
|
|
|
@ -36,9 +36,33 @@ Item {
|
|||
property int countOnStartUp: 0
|
||||
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 {
|
||||
id: chatLogView
|
||||
anchors.fill: parent
|
||||
anchors.top: loadingMessagesIndicator.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: 0
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
clip: true
|
||||
|
@ -92,9 +116,9 @@ Item {
|
|||
// }
|
||||
// }
|
||||
|
||||
// ScrollBar.vertical: ScrollBar {
|
||||
// visible: chatLogView.visibleArea.heightRatio < 1
|
||||
// }
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
visible: chatLogView.visibleArea.heightRatio < 1
|
||||
}
|
||||
|
||||
// Connections {
|
||||
// id: contentHeightConnection
|
||||
|
@ -112,80 +136,79 @@ Item {
|
|||
id: timer
|
||||
}
|
||||
|
||||
// Button {
|
||||
// readonly property int buttonPadding: 5
|
||||
Button {
|
||||
readonly property int buttonPadding: 5
|
||||
|
||||
// id: scrollDownButton
|
||||
// visible: false
|
||||
// height: 32
|
||||
// width: nbMessages.width + arrowImage.width + 2 * Style.current.halfPadding + (nbMessages.visible ? scrollDownButton.buttonPadding : 0)
|
||||
// anchors.bottom: parent.bottom
|
||||
// anchors.right: parent.right
|
||||
// anchors.rightMargin: Style.current.padding
|
||||
// background: Rectangle {
|
||||
// color: Style.current.buttonSecondaryColor
|
||||
// border.width: 0
|
||||
// radius: 16
|
||||
// }
|
||||
// onClicked: {
|
||||
// newMessages = 0
|
||||
// scrollDownButton.visible = false
|
||||
// chatLogView.scrollToBottom(true)
|
||||
// }
|
||||
id: scrollDownButton
|
||||
visible: false
|
||||
height: 32
|
||||
width: nbMessages.width + arrowImage.width + 2 * Style.current.halfPadding + (nbMessages.visible ? scrollDownButton.buttonPadding : 0)
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.padding
|
||||
background: Rectangle {
|
||||
color: Style.current.buttonSecondaryColor
|
||||
border.width: 0
|
||||
radius: 16
|
||||
}
|
||||
onClicked: {
|
||||
newMessages = 0
|
||||
scrollDownButton.visible = false
|
||||
chatLogView.scrollToBottom(true)
|
||||
}
|
||||
|
||||
// StyledText {
|
||||
// id: nbMessages
|
||||
// visible: newMessages > 0
|
||||
// width: visible ? implicitWidth : 0
|
||||
// text: newMessages
|
||||
// anchors.verticalCenter: parent.verticalCenter
|
||||
// anchors.left: parent.left
|
||||
// color: Style.current.pillButtonTextColor
|
||||
// font.pixelSize: 15
|
||||
// anchors.leftMargin: Style.current.halfPadding
|
||||
// }
|
||||
StyledText {
|
||||
id: nbMessages
|
||||
visible: newMessages > 0
|
||||
width: visible ? implicitWidth : 0
|
||||
text: newMessages
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
color: Style.current.pillButtonTextColor
|
||||
font.pixelSize: 15
|
||||
anchors.leftMargin: Style.current.halfPadding
|
||||
}
|
||||
|
||||
// SVGImage {
|
||||
// id: arrowImage
|
||||
// width: 24
|
||||
// height: 24
|
||||
// anchors.verticalCenter: parent.verticalCenter
|
||||
// anchors.left: nbMessages.right
|
||||
// source: Style.svg("leave_chat")
|
||||
// anchors.leftMargin: nbMessages.visible ? scrollDownButton.buttonPadding : 0
|
||||
// rotation: -90
|
||||
SVGImage {
|
||||
id: arrowImage
|
||||
width: 24
|
||||
height: 24
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: nbMessages.right
|
||||
source: Style.svg("leave_chat")
|
||||
anchors.leftMargin: nbMessages.visible ? scrollDownButton.buttonPadding : 0
|
||||
rotation: -90
|
||||
|
||||
// ColorOverlay {
|
||||
// anchors.fill: parent
|
||||
// source: parent
|
||||
// color: Style.current.pillButtonTextColor
|
||||
// }
|
||||
// }
|
||||
ColorOverlay {
|
||||
anchors.fill: parent
|
||||
source: parent
|
||||
color: Style.current.pillButtonTextColor
|
||||
}
|
||||
}
|
||||
|
||||
// MouseArea {
|
||||
// cursorShape: Qt.PointingHandCursor
|
||||
// anchors.fill: parent
|
||||
// onPressed: mouse.accepted = false
|
||||
// }
|
||||
// }
|
||||
MouseArea {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
onPressed: mouse.accepted = false
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToBottom(force, caller) {
|
||||
// Not Refactored Yet
|
||||
// if (!force && !chatLogView.atYEnd) {
|
||||
// // User has scrolled up, we don't want to scroll back
|
||||
// return false
|
||||
// }
|
||||
// if (caller && caller !== chatLogView.itemAtIndex(chatLogView.count - 1)) {
|
||||
// // If we have a caller, only accept its request if it's the last message
|
||||
// return false
|
||||
// }
|
||||
// // 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
|
||||
// // Add a small delay because images, even though they say they say they are loaed, they aren't shown yet
|
||||
// Qt.callLater(chatLogView.positionViewAtBeginning)
|
||||
// timer.setTimeout(function() {
|
||||
// Qt.callLater(chatLogView.positionViewAtBeginning)
|
||||
// }, 100);
|
||||
if (!force && !chatLogView.atYEnd) {
|
||||
// User has scrolled up, we don't want to scroll back
|
||||
return false
|
||||
}
|
||||
if (caller && caller !== chatLogView.itemAtIndex(chatLogView.count - 1)) {
|
||||
// If we have a caller, only accept its request if it's the last message
|
||||
return false
|
||||
}
|
||||
// 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
|
||||
// Add a small delay because images, even though they say they say they are loaed, they aren't shown yet
|
||||
Qt.callLater(chatLogView.positionViewAtBeginning)
|
||||
timer.setTimeout(function() {
|
||||
Qt.callLater(chatLogView.positionViewAtBeginning)
|
||||
}, 100);
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -198,24 +221,24 @@ Item {
|
|||
// }
|
||||
// }
|
||||
|
||||
// Connections {
|
||||
// Not Refactored Yet
|
||||
// target: root.store.chatsModelInst.messageView
|
||||
Connections {
|
||||
target: messageStore.messageModule
|
||||
|
||||
// onSendingMessageSuccess: {
|
||||
// chatLogView.scrollToBottom(true)
|
||||
// }
|
||||
onMessageSuccessfullySent: {
|
||||
chatLogView.scrollToBottom(true)
|
||||
}
|
||||
|
||||
// onSendingMessageFailed: {
|
||||
// sendingMsgFailedPopup.open();
|
||||
// }
|
||||
onSendingMessageFailed: {
|
||||
sendingMsgFailedPopup.open();
|
||||
}
|
||||
|
||||
// Not Refactored Yet
|
||||
// onNewMessagePushed: {
|
||||
// if (!chatLogView.scrollToBottom()) {
|
||||
// newMessages++
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// Connections {
|
||||
// Not Refactored Yet
|
||||
|
@ -274,24 +297,15 @@ Item {
|
|||
// }
|
||||
// }
|
||||
|
||||
// Not Refactored Yet
|
||||
// property var loadMsgs : Backpressure.oneInTime(chatLogView, 500, function() {
|
||||
// if(!messages.initialMessagesLoaded || messages.loadingHistoryMessages)
|
||||
// return
|
||||
onContentYChanged: {
|
||||
scrollDownButton.visible = contentHeight - (scrollY + height) > 400
|
||||
let loadMore = scrollDownButton.visible && scrollY < 500
|
||||
if(loadMore){
|
||||
messageStore.loadMoreMessages()
|
||||
}
|
||||
}
|
||||
|
||||
// root.store.chatsModelInst.messageView.loadMoreMessages(chatId);
|
||||
// });
|
||||
|
||||
// onContentYChanged: {
|
||||
// scrollDownButton.visible = (contentHeight - (scrollY + height) > 400)
|
||||
// if(scrollDownButton.visible && scrollY < 500){
|
||||
// loadMsgs();
|
||||
// }
|
||||
// }
|
||||
|
||||
model: messageStore.messageModule.model
|
||||
section.property: "sectionIdentifier"
|
||||
section.criteria: ViewSection.FullString
|
||||
model: messageStore.messagesModel
|
||||
|
||||
// Not Refactored Yet
|
||||
//Component.onCompleted: scrollToBottom(true)
|
||||
|
@ -330,11 +344,11 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
// MessageDialog {
|
||||
// id: sendingMsgFailedPopup
|
||||
// standardButtons: StandardButton.Ok
|
||||
// //% "Failed to send message."
|
||||
// text: qsTrId("failed-to-send-message-")
|
||||
// icon: StandardIcon.Critical
|
||||
// }
|
||||
MessageDialog {
|
||||
id: sendingMsgFailedPopup
|
||||
standardButtons: StandardButton.Ok
|
||||
//% "Failed to send message."
|
||||
text: qsTrId("failed-to-send-message-")
|
||||
icon: StandardIcon.Critical
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue