feat: introduce "fetch more messages" button to request old messages
Closes #149
This commit is contained in:
parent
464a2018d4
commit
1481f2648f
|
@ -78,7 +78,7 @@ proc handleMailserverEvents(self: ChatController) =
|
||||||
|
|
||||||
self.status.events.on("mailserverAvailable") do(e:Args):
|
self.status.events.on("mailserverAvailable") do(e:Args):
|
||||||
let mailserverTopics = self.status.mailservers.getMailserverTopics()
|
let mailserverTopics = self.status.mailservers.getMailserverTopics()
|
||||||
var fromValue: int64 = times.toUnix(times.getTime()) - 86400
|
var fromValue = times.toUnix(times.getTime()) - 86400 # today - 24 hours
|
||||||
|
|
||||||
if mailserverTopics.len > 0:
|
if mailserverTopics.len > 0:
|
||||||
fromValue = min(mailserverTopics.map(topic => topic.lastRequest))
|
fromValue = min(mailserverTopics.map(topic => topic.lastRequest))
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import NimQml, Tables, json, sequtils, chronicles, times, re, sugar, strutils, os, strformat
|
import NimQml, Tables, json, sequtils, chronicles, times, re, sugar, strutils, os, strformat
|
||||||
import ../../status/status
|
import ../../status/status
|
||||||
|
import ../../status/mailservers
|
||||||
import ../../status/libstatus/accounts/constants
|
import ../../status/libstatus/accounts/constants
|
||||||
|
import ../../status/libstatus/mailservers as status_mailservers
|
||||||
import ../../status/accounts as status_accounts
|
import ../../status/accounts as status_accounts
|
||||||
import ../../status/chat as status_chat
|
import ../../status/chat as status_chat
|
||||||
import ../../status/messages as status_messages
|
import ../../status/messages as status_messages
|
||||||
|
@ -35,6 +37,7 @@ QtObject:
|
||||||
channelOpenTime*: Table[string, int64]
|
channelOpenTime*: Table[string, int64]
|
||||||
connected: bool
|
connected: bool
|
||||||
unreadMessageCnt: int
|
unreadMessageCnt: int
|
||||||
|
oldestMessageTimestamp: int64
|
||||||
|
|
||||||
proc setup(self: ChatsView) = self.QAbstractListModel.setup
|
proc setup(self: ChatsView) = self.QAbstractListModel.setup
|
||||||
|
|
||||||
|
@ -62,6 +65,27 @@ QtObject:
|
||||||
result.pubKey = ""
|
result.pubKey = ""
|
||||||
result.setup()
|
result.setup()
|
||||||
|
|
||||||
|
proc oldestMessageTimestampChanged*(self: ChatsView) {.signal.}
|
||||||
|
|
||||||
|
proc getOldestMessageTimestamp*(self: ChatsView): QVariant {.slot.} =
|
||||||
|
newQVariant($self.oldestMessageTimestamp)
|
||||||
|
|
||||||
|
QtProperty[QVariant] oldestMsgTimestamp:
|
||||||
|
read = getOldestMessageTimestamp
|
||||||
|
notify = oldestMessageTimestampChanged
|
||||||
|
|
||||||
|
proc setLastMessageTimestamp(self: ChatsView, force = false) =
|
||||||
|
if self.status.chat.lastMessageTimestamps.hasKey(self.activeChannel.id):
|
||||||
|
if force or self.status.chat.lastMessageTimestamps[self.activeChannel.id] <= self.oldestMessageTimestamp:
|
||||||
|
self.oldestMessageTimestamp = self.status.chat.lastMessageTimestamps[self.activeChannel.id]
|
||||||
|
else:
|
||||||
|
let topics = self.status.mailservers.getMailserverTopicsByChatId(self.activeChannel.id)
|
||||||
|
if topics.len > 0:
|
||||||
|
self.oldestMessageTimestamp = topics[0].lastRequest
|
||||||
|
else:
|
||||||
|
self.oldestMessageTimestamp = times.toUnix(times.getTime())
|
||||||
|
self.oldestMessageTimestampChanged()
|
||||||
|
|
||||||
proc addStickerPackToList*(self: ChatsView, stickerPack: StickerPack, isInstalled, isBought: bool) =
|
proc addStickerPackToList*(self: ChatsView, stickerPack: StickerPack, isInstalled, isBought: bool) =
|
||||||
self.stickerPacks.addStickerPackToList(stickerPack, newStickerList(stickerPack.stickers), isInstalled, isBought)
|
self.stickerPacks.addStickerPackToList(stickerPack, newStickerList(stickerPack.stickers), isInstalled, isBought)
|
||||||
|
|
||||||
|
@ -206,6 +230,7 @@ QtObject:
|
||||||
self.status.chat.setActiveChannel(selectedChannel.id)
|
self.status.chat.setActiveChannel(selectedChannel.id)
|
||||||
discard self.status.chat.markAllChannelMessagesRead(selectedChannel.id)
|
discard self.status.chat.markAllChannelMessagesRead(selectedChannel.id)
|
||||||
self.currentSuggestions.setNewData(self.status.contacts.getContacts())
|
self.currentSuggestions.setNewData(self.status.contacts.getContacts())
|
||||||
|
self.setLastMessageTimestamp(true)
|
||||||
self.activeChannelChanged()
|
self.activeChannelChanged()
|
||||||
|
|
||||||
proc getActiveChannelIdx(self: ChatsView): QVariant {.slot.} =
|
proc getActiveChannelIdx(self: ChatsView): QVariant {.slot.} =
|
||||||
|
@ -237,6 +262,7 @@ QtObject:
|
||||||
self.activeChannel.setChatItem(self.chats.getChannel(self.chats.chats.findIndexById(channel)))
|
self.activeChannel.setChatItem(self.chats.getChannel(self.chats.chats.findIndexById(channel)))
|
||||||
discard self.status.chat.markAllChannelMessagesRead(self.activeChannel.id)
|
discard self.status.chat.markAllChannelMessagesRead(self.activeChannel.id)
|
||||||
self.currentSuggestions.setNewData(self.status.contacts.getContacts())
|
self.currentSuggestions.setNewData(self.status.contacts.getContacts())
|
||||||
|
self.setLastMessageTimestamp(true)
|
||||||
self.activeChannelChanged()
|
self.activeChannelChanged()
|
||||||
|
|
||||||
proc getActiveChannel*(self: ChatsView): QVariant {.slot.} =
|
proc getActiveChannel*(self: ChatsView): QVariant {.slot.} =
|
||||||
|
@ -373,6 +399,8 @@ QtObject:
|
||||||
trace "Loading more messages", chaId = self.activeChannel.id
|
trace "Loading more messages", chaId = self.activeChannel.id
|
||||||
self.status.chat.chatMessages(self.activeChannel.id, false)
|
self.status.chat.chatMessages(self.activeChannel.id, false)
|
||||||
self.status.chat.chatReactions(self.activeChannel.id, false)
|
self.status.chat.chatReactions(self.activeChannel.id, false)
|
||||||
|
if self.status.chat.msgCursor[self.activeChannel.id] == "":
|
||||||
|
self.setLastMessageTimestamp()
|
||||||
self.messagesLoaded();
|
self.messagesLoaded();
|
||||||
|
|
||||||
proc loadMoreMessagesWithIndex*(self: ChatsView, channelIndex: int) {.slot.} =
|
proc loadMoreMessagesWithIndex*(self: ChatsView, channelIndex: int) {.slot.} =
|
||||||
|
@ -382,6 +410,16 @@ QtObject:
|
||||||
trace "Loading more messages", chaId = selectedChannel.id
|
trace "Loading more messages", chaId = selectedChannel.id
|
||||||
self.status.chat.chatMessages(selectedChannel.id, false)
|
self.status.chat.chatMessages(selectedChannel.id, false)
|
||||||
self.status.chat.chatReactions(selectedChannel.id, false)
|
self.status.chat.chatReactions(selectedChannel.id, false)
|
||||||
|
self.setLastMessageTimestamp()
|
||||||
|
self.messagesLoaded();
|
||||||
|
|
||||||
|
proc requestMoreMessages*(self: ChatsView) {.slot.} =
|
||||||
|
let topics = self.status.mailservers.getMailserverTopicsByChatId(self.activeChannel.id).map(topic => topic.topic)
|
||||||
|
let currentOldestMessageTimestamp = self.oldestMessageTimestamp
|
||||||
|
self.oldestMessageTimestamp = self.oldestMessageTimestamp - 86400
|
||||||
|
|
||||||
|
self.status.mailservers.requestMessages(topics, self.oldestMessageTimestamp, currentOldestMessageTimestamp, true)
|
||||||
|
self.oldestMessageTimestampChanged()
|
||||||
self.messagesLoaded();
|
self.messagesLoaded();
|
||||||
|
|
||||||
proc leaveChatByIndex*(self: ChatsView, channelIndex: int) {.slot.} =
|
proc leaveChatByIndex*(self: ChatsView, channelIndex: int) {.slot.} =
|
||||||
|
|
|
@ -52,6 +52,10 @@ QtObject:
|
||||||
|
|
||||||
include message_format
|
include message_format
|
||||||
|
|
||||||
|
proc fetchMoreMessagesButton(self: ChatMessageList): Message =
|
||||||
|
result = Message()
|
||||||
|
result.contentType = ContentType.FetchMoreMessagesButton;
|
||||||
|
|
||||||
proc chatIdentifier(self: ChatMessageList, chatId:string): Message =
|
proc chatIdentifier(self: ChatMessageList, chatId:string): Message =
|
||||||
result = Message()
|
result = Message()
|
||||||
result.contentType = ContentType.ChatIdentifier;
|
result.contentType = ContentType.ChatIdentifier;
|
||||||
|
@ -59,7 +63,7 @@ QtObject:
|
||||||
|
|
||||||
proc newChatMessageList*(chatId: string, status: Status): ChatMessageList =
|
proc newChatMessageList*(chatId: string, status: Status): ChatMessageList =
|
||||||
new(result, delete)
|
new(result, delete)
|
||||||
result.messages = @[result.chatIdentifier(chatId)]
|
result.messages = @[result.chatIdentifier(chatId), result.fetchMoreMessagesButton()]
|
||||||
result.messageIndex = initTable[string, int]()
|
result.messageIndex = initTable[string, int]()
|
||||||
result.timedoutMessages = initHashSet[string]()
|
result.timedoutMessages = initHashSet[string]()
|
||||||
result.status = status
|
result.status = status
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import eventemitter, json, strutils, sequtils, tables, chronicles, sugar
|
import eventemitter, json, strutils, sequtils, tables, chronicles, sugar, times
|
||||||
import libstatus/contracts as status_contracts
|
import libstatus/contracts as status_contracts
|
||||||
import libstatus/chat as status_chat
|
import libstatus/chat as status_chat
|
||||||
import libstatus/mailservers as status_mailservers
|
import libstatus/mailservers as status_mailservers
|
||||||
|
@ -51,6 +51,7 @@ type
|
||||||
availableStickerPacks*: Table[int, StickerPack]
|
availableStickerPacks*: Table[int, StickerPack]
|
||||||
installedStickerPacks*: Table[int, StickerPack]
|
installedStickerPacks*: Table[int, StickerPack]
|
||||||
purchasedStickerPacks*: seq[int]
|
purchasedStickerPacks*: seq[int]
|
||||||
|
lastMessageTimestamps*: Table[string, int64]
|
||||||
|
|
||||||
MessageArgs* = ref object of Args
|
MessageArgs* = ref object of Args
|
||||||
id*: string
|
id*: string
|
||||||
|
@ -69,6 +70,7 @@ proc newChatModel*(events: EventEmitter): ChatModel =
|
||||||
result.availableStickerPacks = initTable[int, StickerPack]()
|
result.availableStickerPacks = initTable[int, StickerPack]()
|
||||||
result.installedStickerPacks = initTable[int, StickerPack]()
|
result.installedStickerPacks = initTable[int, StickerPack]()
|
||||||
result.purchasedStickerPacks = @[]
|
result.purchasedStickerPacks = @[]
|
||||||
|
result.lastMessageTimestamps = initTable[string, int64]()
|
||||||
|
|
||||||
|
|
||||||
proc delete*(self: ChatModel) =
|
proc delete*(self: ChatModel) =
|
||||||
|
@ -79,6 +81,15 @@ proc update*(self: ChatModel, chats: seq[Chat], messages: seq[Message], emojiRea
|
||||||
if chat.isActive:
|
if chat.isActive:
|
||||||
self.channels[chat.id] = chat
|
self.channels[chat.id] = chat
|
||||||
|
|
||||||
|
for message in messages:
|
||||||
|
let chatId = message.chatId
|
||||||
|
let ts = times.convert(Milliseconds, Seconds, message.whisperTimestamp.parseInt())
|
||||||
|
if not self.lastMessageTimestamps.hasKey(chatId):
|
||||||
|
self.lastMessageTimestamps[chatId] = ts
|
||||||
|
else:
|
||||||
|
if self.lastMessageTimestamps[chatId] > ts:
|
||||||
|
self.lastMessageTimestamps[chatId] = ts
|
||||||
|
|
||||||
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats, contacts: @[], emojiReactions: emojiReactions))
|
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats, contacts: @[], emojiReactions: emojiReactions))
|
||||||
|
|
||||||
proc hasChannel*(self: ChatModel, chatId: string): bool =
|
proc hasChannel*(self: ChatModel, chatId: string): bool =
|
||||||
|
@ -293,6 +304,12 @@ proc chatMessages*(self: ChatModel, chatId: string, initialLoad:bool = true) =
|
||||||
|
|
||||||
let messageTuple = status_chat.chatMessages(chatId, self.msgCursor[chatId])
|
let messageTuple = status_chat.chatMessages(chatId, self.msgCursor[chatId])
|
||||||
self.msgCursor[chatId] = messageTuple[0];
|
self.msgCursor[chatId] = messageTuple[0];
|
||||||
|
|
||||||
|
if messageTuple[1].len > 0:
|
||||||
|
let lastMsgIndex = messageTuple[1].len - 1
|
||||||
|
let ts = times.convert(Milliseconds, Seconds, messageTuple[1][lastMsgIndex].whisperTimestamp.parseInt())
|
||||||
|
self.lastMessageTimestamps[chatId] = ts
|
||||||
|
|
||||||
self.events.emit("messagesLoaded", MsgsLoadedArgs(messages: messageTuple[1]))
|
self.events.emit("messagesLoaded", MsgsLoadedArgs(messages: messageTuple[1]))
|
||||||
|
|
||||||
proc chatReactions*(self: ChatModel, chatId: string, initialLoad:bool = true) =
|
proc chatReactions*(self: ChatModel, chatId: string, initialLoad:bool = true) =
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import strformat
|
import strformat
|
||||||
|
|
||||||
type ContentType* {.pure.} = enum
|
type ContentType* {.pure.} = enum
|
||||||
|
FetchMoreMessagesButton = -2
|
||||||
ChatIdentifier = -1,
|
ChatIdentifier = -1,
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
Message = 1,
|
Message = 1,
|
||||||
|
|
|
@ -63,10 +63,13 @@ proc update*(peer: string) =
|
||||||
proc delete*(peer: string) =
|
proc delete*(peer: string) =
|
||||||
discard callPrivateRPC("mailservers_deleteMailserver", %* [peer])
|
discard callPrivateRPC("mailservers_deleteMailserver", %* [peer])
|
||||||
|
|
||||||
proc requestMessages*(topics: seq[string], symKeyID: string, peer: string, numberOfMessages: int, fromTimestamp: int64 = 0) =
|
proc requestMessages*(topics: seq[string], symKeyID: string, peer: string, numberOfMessages: int, fromTimestamp: int64 = 0, toTimestamp: int64 = 0, force: bool = false) =
|
||||||
var fromValue = (times.toUnix(times.getTime()) - 86400)
|
var toValue = times.toUnix(times.getTime())
|
||||||
|
var fromValue = toValue - 86400
|
||||||
if fromTimestamp != 0:
|
if fromTimestamp != 0:
|
||||||
fromValue = fromTimestamp
|
fromValue = fromTimestamp
|
||||||
|
if toTimestamp != 0:
|
||||||
|
toValue = toTimestamp
|
||||||
|
|
||||||
echo callPrivateRPC("requestMessages".prefix, %* [
|
echo callPrivateRPC("requestMessages".prefix, %* [
|
||||||
{
|
{
|
||||||
|
@ -76,7 +79,9 @@ proc requestMessages*(topics: seq[string], symKeyID: string, peer: string, numbe
|
||||||
"timeout": 30,
|
"timeout": 30,
|
||||||
"limit": numberOfMessages,
|
"limit": numberOfMessages,
|
||||||
"cursor": nil,
|
"cursor": nil,
|
||||||
"from": fromValue
|
"from": fromValue,
|
||||||
|
"to": toValue,
|
||||||
|
"force": force
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import algorithm, json, random, math, os, tables, sets, chronicles, eventemitter, sequtils, locks
|
import algorithm, json, random, math, os, tables, sets, chronicles, eventemitter, sequtils, locks, sugar
|
||||||
import libstatus/core as status_core
|
import libstatus/core as status_core
|
||||||
import libstatus/chat as status_chat
|
import libstatus/chat as status_chat
|
||||||
import libstatus/mailservers as status_mailservers
|
import libstatus/mailservers as status_mailservers
|
||||||
|
@ -128,10 +128,10 @@ proc peerSummaryChange*(self: MailserverModel, peers: seq[string]) =
|
||||||
self.nodes[peer] = MailserverStatus.Connected
|
self.nodes[peer] = MailserverStatus.Connected
|
||||||
self.events.emit("peerConnected", MailserverArg(peer: peer))
|
self.events.emit("peerConnected", MailserverArg(peer: peer))
|
||||||
|
|
||||||
proc requestMessages*(self: MailserverModel, topics: seq[string], fromValue: int64 = 0) =
|
proc requestMessages*(self: MailserverModel, topics: seq[string], fromValue: int64 = 0, toValue: int64 = 0, force: bool = false) =
|
||||||
debug "Requesting messages from", mailserver=self.selectedMailserver
|
debug "Requesting messages from", mailserver=self.selectedMailserver
|
||||||
let generatedSymKey = status_chat.generateSymKeyFromPassword()
|
let generatedSymKey = status_chat.generateSymKeyFromPassword()
|
||||||
status_mailservers.requestMessages(topics, generatedSymKey, self.selectedMailserver, 1000, fromValue)
|
status_mailservers.requestMessages(topics, generatedSymKey, self.selectedMailserver, 1000, fromValue, toValue, force)
|
||||||
|
|
||||||
proc getMailserverTopics*(self: MailserverModel): seq[MailserverTopic] =
|
proc getMailserverTopics*(self: MailserverModel): seq[MailserverTopic] =
|
||||||
let response = status_mailservers.getMailserverTopics()
|
let response = status_mailservers.getMailserverTopics()
|
||||||
|
@ -147,6 +147,10 @@ proc getMailserverTopics*(self: MailserverModel): seq[MailserverTopic] =
|
||||||
lastRequest: topic["last-request"].getInt
|
lastRequest: topic["last-request"].getInt
|
||||||
))
|
))
|
||||||
|
|
||||||
|
proc getMailserverTopicsByChatId*(self: MailserverModel, chatId: string): seq[MailServerTopic] =
|
||||||
|
result = self.getMailserverTopics()
|
||||||
|
.filter(topic => topic.chatIds.contains(chatId))
|
||||||
|
|
||||||
proc addMailserverTopic*(self: MailserverModel, topic: MailserverTopic) =
|
proc addMailserverTopic*(self: MailserverModel, topic: MailserverTopic) =
|
||||||
discard status_mailservers.addMailserverTopic(topic)
|
discard status_mailservers.addMailserverTopic(topic)
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,8 @@ Item {
|
||||||
switch(contentType) {
|
switch(contentType) {
|
||||||
case Constants.chatIdentifier:
|
case Constants.chatIdentifier:
|
||||||
return channelIdentifierComponent
|
return channelIdentifierComponent
|
||||||
|
case Constants.fetchMoreMessagesButton:
|
||||||
|
return fetchMoreMessagesButtonComponent
|
||||||
case Constants.systemMessagePrivateGroupType:
|
case Constants.systemMessagePrivateGroupType:
|
||||||
return privateGroupHeaderComponent
|
return privateGroupHeaderComponent
|
||||||
default:
|
default:
|
||||||
|
@ -83,6 +85,50 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: fetchMoreMessagesButtonComponent
|
||||||
|
Item {
|
||||||
|
id: wrapper
|
||||||
|
height: wrapper.visible ? fetchMoreButton.height + fetchDate.height + 3 + Style.current.smallPadding*2 : 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
Separator {
|
||||||
|
id: sep1
|
||||||
|
}
|
||||||
|
StyledText {
|
||||||
|
id: fetchMoreButton
|
||||||
|
font.weight: Font.Medium
|
||||||
|
font.pixelSize: 15
|
||||||
|
color: Style.current.blue
|
||||||
|
text: qsTr("↓ Fetch more messages")
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: sep1.bottom
|
||||||
|
anchors.topMargin: Style.current.smallPadding
|
||||||
|
MouseArea {
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
chatsModel.requestMoreMessages()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StyledText {
|
||||||
|
id: fetchDate
|
||||||
|
anchors.top: fetchMoreButton.bottom
|
||||||
|
anchors.topMargin: 3
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
color: Style.current.darkGrey
|
||||||
|
text: qsTr("before %1").arg(new Date(chatsModel.oldestMsgTimestamp*1000).toDateString())
|
||||||
|
}
|
||||||
|
Separator {
|
||||||
|
anchors.top: fetchDate.bottom
|
||||||
|
anchors.topMargin: Style.current.smallPadding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: channelIdentifierComponent
|
id: channelIdentifierComponent
|
||||||
ChannelIdentifier {
|
ChannelIdentifier {
|
||||||
|
|
|
@ -7,6 +7,7 @@ QtObject {
|
||||||
readonly property int chatTypePublic: 2
|
readonly property int chatTypePublic: 2
|
||||||
readonly property int chatTypePrivateGroupChat: 3
|
readonly property int chatTypePrivateGroupChat: 3
|
||||||
|
|
||||||
|
readonly property int fetchMoreMessagesButton: -2
|
||||||
readonly property int chatIdentifier: -1
|
readonly property int chatIdentifier: -1
|
||||||
readonly property int unknownContentType: 0
|
readonly property int unknownContentType: 0
|
||||||
readonly property int messageType: 1
|
readonly property int messageType: 1
|
||||||
|
|
Loading…
Reference in New Issue