feat: load messages on scroll to top, and fix last message scrolling

This commit is contained in:
Richard Ramos 2020-06-08 15:25:46 -04:00 committed by Iuri Matias
parent b5b02cfd57
commit f3ff229bf8
4 changed files with 71 additions and 12 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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 =

View File

@ -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