feat: load messages on scroll to top, and fix last message scrolling
This commit is contained in:
parent
b5b02cfd57
commit
f3ff229bf8
|
@ -1,6 +1,7 @@
|
||||||
import NimQml
|
import NimQml
|
||||||
import Tables
|
import Tables
|
||||||
import json
|
import json
|
||||||
|
import chronicles
|
||||||
|
|
||||||
import ../../signals/types
|
import ../../signals/types
|
||||||
import ../../status/chat
|
import ../../status/chat
|
||||||
|
@ -10,6 +11,9 @@ import views/channels_list
|
||||||
import views/message_list
|
import views/message_list
|
||||||
import views/chat_item
|
import views/chat_item
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "chats-view"
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type
|
type
|
||||||
ChatsView* = ref object of QAbstractListModel
|
ChatsView* = ref object of QAbstractListModel
|
||||||
|
@ -77,15 +81,19 @@ QtObject:
|
||||||
proc upsertChannel(self: ChatsView, channel: string) =
|
proc upsertChannel(self: ChatsView, channel: string) =
|
||||||
if not self.messageList.hasKey(channel):
|
if not self.messageList.hasKey(channel):
|
||||||
self.messageList[channel] = newChatMessageList(channel)
|
self.messageList[channel] = newChatMessageList(channel)
|
||||||
|
|
||||||
|
proc messagePushed*(self: ChatsView) {.signal.}
|
||||||
|
|
||||||
proc pushMessage*(self:ChatsView, message: ChatMessage) =
|
proc pushMessage*(self:ChatsView, message: ChatMessage) =
|
||||||
self.upsertChannel(message.chatId)
|
self.upsertChannel(message.chatId)
|
||||||
self.messageList[message.chatId].add(message)
|
self.messageList[message.chatId].add(message)
|
||||||
|
self.messagePushed()
|
||||||
|
|
||||||
proc pushMessages*(self:ChatsView, messages: seq[Message]) =
|
proc pushMessages*(self:ChatsView, messages: seq[Message]) =
|
||||||
for msg in messages:
|
for msg in messages:
|
||||||
self.upsertChannel(msg.chatId)
|
self.upsertChannel(msg.chatId)
|
||||||
self.messageList[msg.chatId].add(msg.toChatMessage())
|
self.messageList[msg.chatId].add(msg.toChatMessage())
|
||||||
|
self.messagePushed()
|
||||||
|
|
||||||
proc getMessageList(self: ChatsView): QVariant {.slot.} =
|
proc getMessageList(self: ChatsView): QVariant {.slot.} =
|
||||||
self.upsertChannel(self.activeChannel.id)
|
self.upsertChannel(self.activeChannel.id)
|
||||||
|
@ -97,6 +105,7 @@ QtObject:
|
||||||
|
|
||||||
proc pushChatItem*(self: ChatsView, chatItem: ChatItem) =
|
proc pushChatItem*(self: ChatsView, chatItem: ChatItem) =
|
||||||
discard self.chats.addChatItemToList(chatItem)
|
discard self.chats.addChatItemToList(chatItem)
|
||||||
|
self.messagePushed()
|
||||||
|
|
||||||
proc sendMessage*(self: ChatsView, message: string) {.slot.} =
|
proc sendMessage*(self: ChatsView, message: string) {.slot.} =
|
||||||
discard self.status.chat.sendMessage(self.activeChannel.id, message)
|
discard self.status.chat.sendMessage(self.activeChannel.id, message)
|
||||||
|
@ -104,6 +113,13 @@ QtObject:
|
||||||
proc joinChat*(self: ChatsView, channel: string, chatTypeInt: int): int {.slot.} =
|
proc joinChat*(self: ChatsView, channel: string, chatTypeInt: int): int {.slot.} =
|
||||||
self.status.chat.join(channel, ChatType(chatTypeInt))
|
self.status.chat.join(channel, ChatType(chatTypeInt))
|
||||||
|
|
||||||
|
proc messagesLoaded*(self: ChatsView) {.signal.}
|
||||||
|
|
||||||
|
proc loadMoreMessages*(self: ChatsView) {.slot.} =
|
||||||
|
trace "Loading more messages", chaId = self.activeChannel.id
|
||||||
|
self.status.chat.chatMessages(self.activeChannel.id, false)
|
||||||
|
self.messagesLoaded();
|
||||||
|
|
||||||
proc leaveActiveChat*(self: ChatsView) {.slot.} =
|
proc leaveActiveChat*(self: ChatsView) {.slot.} =
|
||||||
self.status.chat.leave(self.activeChannel.id)
|
self.status.chat.leave(self.activeChannel.id)
|
||||||
|
|
||||||
|
|
|
@ -33,12 +33,14 @@ type
|
||||||
events*: EventEmitter
|
events*: EventEmitter
|
||||||
channels*: HashSet[string]
|
channels*: HashSet[string]
|
||||||
filters*: Table[string, string]
|
filters*: Table[string, string]
|
||||||
|
msgCursor*: Table[string, string]
|
||||||
|
|
||||||
proc newChatModel*(events: EventEmitter): ChatModel =
|
proc newChatModel*(events: EventEmitter): ChatModel =
|
||||||
result = ChatModel()
|
result = ChatModel()
|
||||||
result.events = events
|
result.events = events
|
||||||
result.channels = initHashSet[string]()
|
result.channels = initHashSet[string]()
|
||||||
result.filters = initTable[string, string]()
|
result.filters = initTable[string, string]()
|
||||||
|
result.msgCursor = initTable[string, string]()
|
||||||
|
|
||||||
proc delete*(self: ChatModel) =
|
proc delete*(self: ChatModel) =
|
||||||
discard
|
discard
|
||||||
|
@ -117,9 +119,17 @@ proc sendMessage*(self: ChatModel, chatId: string, msg: string): string =
|
||||||
self.events.emit("messageSent", MsgArgs(message: msg, chatId: chatId, payload: parsedMessage))
|
self.events.emit("messageSent", MsgArgs(message: msg, chatId: chatId, payload: parsedMessage))
|
||||||
sentMessage
|
sentMessage
|
||||||
|
|
||||||
proc chatMessages*(self: ChatModel, chatId: string) =
|
proc chatMessages*(self: ChatModel, chatId: string, initialLoad:bool = true) =
|
||||||
let msgs = status_chat.chatMessages(chatId)
|
if not self.msgCursor.hasKey(chatId):
|
||||||
self.events.emit("messagesLoaded", MsgsLoadedArgs(messages: msgs))
|
self.msgCursor[chatId] = "";
|
||||||
|
|
||||||
|
# Messages were already loaded, since cursor will
|
||||||
|
# be nil/empty if there are no more messages
|
||||||
|
if(not initialLoad and self.msgCursor[chatId] == ""): return
|
||||||
|
|
||||||
|
let messageTuple = status_chat.chatMessages(chatId, self.msgCursor[chatId])
|
||||||
|
self.msgCursor[chatId] = messageTuple[0];
|
||||||
|
self.events.emit("messagesLoaded", MsgsLoadedArgs(messages: messageTuple[1]))
|
||||||
|
|
||||||
proc markAllChannelMessagesRead*(self: ChatModel, chatId: string): JsonNode =
|
proc markAllChannelMessagesRead*(self: ChatModel, chatId: string): JsonNode =
|
||||||
var response = status_chat.markAllRead(chatId)
|
var response = status_chat.markAllRead(chatId)
|
||||||
|
|
|
@ -71,12 +71,22 @@ proc loadChats*(): seq[Chat] =
|
||||||
if chat.active and chat.chatType != ChatType.Unknown:
|
if chat.active and chat.chatType != ChatType.Unknown:
|
||||||
result.add(jsonChat.toChat)
|
result.add(jsonChat.toChat)
|
||||||
|
|
||||||
proc chatMessages*(chatId: string): seq[Message] =
|
proc chatMessages*(chatId: string, cursor: string = ""): (string, seq[Message]) =
|
||||||
result = @[]
|
var messages: seq[Message] = @[]
|
||||||
let rpcResult = parseJson(callPrivateRPC("chatMessages".prefix, %* [chatId, nil, 1000]))["result"]
|
var cursorVal: JsonNode
|
||||||
|
|
||||||
|
if cursor == "":
|
||||||
|
cursorVal = newJNull()
|
||||||
|
else:
|
||||||
|
cursorVal = newJString(cursor)
|
||||||
|
|
||||||
|
let rpcResult = parseJson(callPrivateRPC("chatMessages".prefix, %* [chatId, cursorVal, 20]))["result"]
|
||||||
|
|
||||||
if rpcResult["messages"].kind != JNull:
|
if rpcResult["messages"].kind != JNull:
|
||||||
for jsonMsg in rpcResult["messages"]:
|
for jsonMsg in rpcResult["messages"]:
|
||||||
result.add(jsonMsg.toMessage)
|
messages.add(jsonMsg.toMessage)
|
||||||
|
|
||||||
|
return (rpcResult{"cursor"}.getStr, messages)
|
||||||
|
|
||||||
# TODO this probably belongs in another file
|
# TODO this probably belongs in another file
|
||||||
proc generateSymKeyFromPassword*(): string =
|
proc generateSymKeyFromPassword*(): string =
|
||||||
|
|
|
@ -13,6 +13,7 @@ ScrollView {
|
||||||
id: scrollView
|
id: scrollView
|
||||||
|
|
||||||
property var messageList: MessagesData {}
|
property var messageList: MessagesData {}
|
||||||
|
property bool loadingMessages: false
|
||||||
|
|
||||||
contentItem: chatLogView
|
contentItem: chatLogView
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
@ -28,14 +29,36 @@ ScrollView {
|
||||||
id: chatLogView
|
id: chatLogView
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
onCountChanged: {
|
|
||||||
if (!this.atYEnd) {
|
Connections {
|
||||||
// User has scrolled up, we don't want to scroll back
|
target: chatsModel
|
||||||
return
|
onMessagesLoaded: {
|
||||||
|
loadingMessages = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onActiveChannelChanged: {
|
||||||
|
Qt.callLater( chatLogView.positionViewAtEnd )
|
||||||
|
}
|
||||||
|
|
||||||
|
onMessagePushed: {
|
||||||
|
if (!chatLogView.atYEnd) {
|
||||||
|
// User has scrolled up, we don't want to scroll back
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
Qt.callLater( chatLogView.positionViewAtEnd )
|
if(chatLogView.atYEnd)
|
||||||
|
Qt.callLater( chatLogView.positionViewAtEnd )
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onContentYChanged: {
|
||||||
|
if(atYBeginning && !loadingMessages){
|
||||||
|
loadingMessages = true;
|
||||||
|
chatsModel.loadMoreMessages();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
model: messageListDelegate
|
model: messageListDelegate
|
||||||
section.property: "fromAuthor"
|
section.property: "fromAuthor"
|
||||||
section.criteria: ViewSection.FullString
|
section.criteria: ViewSection.FullString
|
||||||
|
|
Loading…
Reference in New Issue