feature(@desktop/chat): support jumping to search result message that is not currently loaded in memory

Changes made here were necessary in order to have good base to requested develop feature.

- duplicated methods are removed
- loading messages either on start (initial loading) or loading more messages requested by
  scrolling in the chat view is now done in separate thread (asynchronous) instead as it was earlier,
  done in the main thread
- new file is added for storing async tasks for chat part only
- ChatModel (from status/chat.nim) is QObject instance now, so it may handle async job in a slot
- a job requested from the view is done in separate thread and view is notified about the changes
  using signal/slot mechanism

This is not refactoring, but minimal update to have clear situation for further changes

Fixes: #3005
This commit is contained in:
Sale Djenic 2021-07-22 14:41:45 +02:00 committed by Iuri Matias
parent 74d868ab92
commit e62465d86f
9 changed files with 787 additions and 798 deletions

View File

@ -12,7 +12,7 @@ proc handleChatEvents(self: ChatController) =
# Display already saved messages # Display already saved messages
self.status.events.on("messagesLoaded") do(e:Args): self.status.events.on("messagesLoaded") do(e:Args):
let evArgs = MsgsLoadedArgs(e) let evArgs = MsgsLoadedArgs(e)
self.view.pushMessages(evArgs.messages) self.view.onMessagesLoaded(evArgs.messages)
for statusUpdate in evArgs.statusUpdates: for statusUpdate in evArgs.statusUpdates:
self.view.communities.updateMemberVisibility(statusUpdate) self.view.communities.updateMemberVisibility(statusUpdate)
@ -22,6 +22,8 @@ proc handleChatEvents(self: ChatController) =
# Display already pinned messages # Display already pinned messages
self.status.events.on("pinnedMessagesLoaded") do(e:Args): self.status.events.on("pinnedMessagesLoaded") do(e:Args):
self.view.pushPinnedMessages(MsgsLoadedArgs(e).messages) self.view.pushPinnedMessages(MsgsLoadedArgs(e).messages)
self.status.events.on("searchMessagesLoaded") do(e:Args):
self.view.onSearchMessagesLoaded(MsgsLoadedArgs(e).messages)
self.status.events.on("activityCenterNotificationsLoaded") do(e:Args): self.status.events.on("activityCenterNotificationsLoaded") do(e:Args):
let notifications = ActivityCenterNotificationsArgs(e).activityCenterNotifications let notifications = ActivityCenterNotificationsArgs(e).activityCenterNotifications
@ -114,8 +116,7 @@ proc handleChatEvents(self: ChatController) =
if channel.chat.chatType == status_chat.ChatType.CommunityChat: if channel.chat.chatType == status_chat.ChatType.CommunityChat:
self.view.communities.updateCommunityChat(channel.chat) self.view.communities.updateCommunityChat(channel.chat)
self.view.asyncMessageLoad(channel.chat.id) self.status.chat.loadInitialMessagesForChannel(channel.chat.id)
self.status.events.on("chatsLoaded") do(e:Args): self.status.events.on("chatsLoaded") do(e:Args):
self.view.calculateUnreadMessages() self.view.calculateUnreadMessages()
self.view.setActiveChannelByIndex(0) self.view.setActiveChannelByIndex(0)
@ -138,8 +139,8 @@ proc handleChatEvents(self: ChatController) =
elif channel.chat.chatType != ChatType.Profile: elif channel.chat.chatType != ChatType.Profile:
discard self.view.channelView.chats.addChatItemToList(channel.chat) discard self.view.channelView.chats.addChatItemToList(channel.chat)
self.view.setActiveChannel(channel.chat.id) self.view.setActiveChannel(channel.chat.id)
self.status.chat.chatMessages(channel.chat.id)
self.status.chat.chatReactions(channel.chat.id) self.status.chat.loadInitialMessagesForChannel(channel.chat.id)
self.status.chat.statusUpdates() self.status.chat.statusUpdates()
self.status.events.on("channelLeft") do(e: Args): self.status.events.on("channelLeft") do(e: Args):

View File

@ -266,7 +266,7 @@ QtObject:
proc pushChatItem*(self: ChatsView, chatItem: Chat) = proc pushChatItem*(self: ChatsView, chatItem: Chat) =
discard self.channelView.chats.addChatItemToList(chatItem) discard self.channelView.chats.addChatItemToList(chatItem)
self.messageView.messagePushed(self.messageView.messageList[chatItem.id].messages.len - 1) self.messageView.messagePushed(self.messageView.messageList[chatItem.id].count - 1)
proc setTimelineChat*(self: ChatsView, chatItem: Chat) = proc setTimelineChat*(self: ChatsView, chatItem: Chat) =
self.timelineChat = chatItem self.timelineChat = chatItem
@ -438,6 +438,12 @@ QtObject:
let task = RequestMessagesTaskArg( `method`: "requestMoreMessages", chatId: self.channelView.activeChannel.id) let task = RequestMessagesTaskArg( `method`: "requestMoreMessages", chatId: self.channelView.activeChannel.id)
mailserverWorker.start(task) mailserverWorker.start(task)
proc onMessagesLoaded*(self: ChatsView, messages: var seq[Message]) =
self.messageView.onMessagesLoaded(messages)
proc onSearchMessagesLoaded*(self: ChatsView, messages: seq[Message]) =
self.messageView.onSearchMessagesLoaded(messages)
proc pushMessages*(self: ChatsView, messages: var seq[Message]) = proc pushMessages*(self: ChatsView, messages: var seq[Message]) =
self.messageView.pushMessages(messages) self.messageView.pushMessages(messages)
@ -469,9 +475,6 @@ QtObject:
proc clearMessages*(self: ChatsView, id: string) = proc clearMessages*(self: ChatsView, id: string) =
self.messageView.clearMessages(id) self.messageView.clearMessages(id)
proc asyncMessageLoad*(self: ChatsView, chatId: string) {.slot.} =
self.messageView.asyncMessageLoad(chatId)
proc calculateUnreadMessages*(self: ChatsView) = proc calculateUnreadMessages*(self: ChatsView) =
self.messageView.calculateUnreadMessages() self.messageView.calculateUnreadMessages()

View File

@ -404,3 +404,11 @@ QtObject:
QtProperty[QVariant] userList: QtProperty[QVariant] userList:
read = getUserList read = getUserList
proc getMessageIdWhichReplacedMessageWithId*(self: ChatMessageList, replacedMessageId: string): string =
## Returns id of the message which replaced a message with a certain id.
## Returns message id as a string or an empty string if there is no such message.
for message in self.messages:
if (message.replace == replacedMessageId):
return message.id

View File

@ -3,7 +3,6 @@ import NimQml, Tables, json, sequtils, chronicles, times, re, sugar, strutils, o
import ../../../status/[status, contacts, types, mailservers] import ../../../status/[status, contacts, types, mailservers]
import ../../../status/signals/types as signal_types import ../../../status/signals/types as signal_types
import ../../../status/ens as status_ens import ../../../status/ens as status_ens
import ../../../status/chat as status_chat
import ../../../status/messages as status_messages import ../../../status/messages as status_messages
import ../../../status/utils as status_utils import ../../../status/utils as status_utils
import ../../../status/chat/[chat, message] import ../../../status/chat/[chat, message]
@ -20,47 +19,6 @@ type
ChatViewRoles {.pure.} = enum ChatViewRoles {.pure.} = enum
MessageList = UserRole + 1 MessageList = UserRole + 1
type
AsyncMessageLoadTaskArg = ref object of QObjectTaskArg
chatId: string
const asyncMessageLoadTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[AsyncMessageLoadTaskArg](argEncoded)
var messages: JsonNode
var msgCallSuccess: bool
let msgCallResult = status_chat.rpcChatMessages(arg.chatId, newJString(""), 20, msgCallSuccess)
if(msgCallSuccess):
messages = msgCallResult.parseJson()["result"]
var reactions: JsonNode
var reactionsCallSuccess: bool
let reactionsCallResult = status_chat.rpcReactions(arg.chatId, newJString(""), 20, reactionsCallSuccess)
if(reactionsCallSuccess):
reactions = reactionsCallResult.parseJson()["result"]
var pinnedMessages: JsonNode
var pinnedMessagesCallSuccess: bool
let pinnedMessagesCallResult = status_chat.rpcPinnedChatMessages(arg.chatId, newJString(""), 20, pinnedMessagesCallSuccess)
if(pinnedMessagesCallSuccess):
pinnedMessages = pinnedMessagesCallResult.parseJson()["result"]
let responseJson = %*{
"chatId": arg.chatId,
"messages": messages,
"reactions": reactions,
"pinnedMessages": pinnedMessages
}
arg.finish(responseJson)
proc asyncMessageLoad[T](self: T, slot: string, chatId: string) =
let arg = AsyncMessageLoadTaskArg(
tptr: cast[ByteAddress](asyncMessageLoadTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot,
chatId: chatId
)
self.status.tasks.threadpool.start(arg)
QtObject: QtObject:
type MessageView* = ref object of QAbstractListModel type MessageView* = ref object of QAbstractListModel
status: Status status: Status
@ -243,12 +201,12 @@ QtObject:
self.messageList[timelineChatId].add(msg) self.messageList[timelineChatId].add(msg)
# if self.channelView.activeChannel.id == timelineChatId: self.activeChannelChanged() # if self.channelView.activeChannel.id == timelineChatId: self.activeChannelChanged()
if self.channelView.activeChannel.id == timelineChatId: self.channelView.activeChannelChanged() if self.channelView.activeChannel.id == timelineChatId: self.channelView.activeChannelChanged()
msgIndex = self.messageList[timelineChatId].messages.len - 1 msgIndex = self.messageList[timelineChatId].count - 1
else: else:
self.messageList[msg.chatId].add(msg) self.messageList[msg.chatId].add(msg)
if self.pinnedMessagesList[msg.chatId].contains(msg): if self.pinnedMessagesList[msg.chatId].contains(msg):
self.pinnedMessagesList[msg.chatId].add(msg) self.pinnedMessagesList[msg.chatId].add(msg)
msgIndex = self.messageList[msg.chatId].messages.len - 1 msgIndex = self.messageList[msg.chatId].count - 1
self.messagePushed(msgIndex) self.messagePushed(msgIndex)
if self.channelOpenTime.getOrDefault(msg.chatId, high(int64)) < msg.timestamp.parseFloat.fromUnixFloat.toUnix: if self.channelOpenTime.getOrDefault(msg.chatId, high(int64)) < msg.timestamp.parseFloat.fromUnixFloat.toUnix:
var channel = self.channelView.chats.getChannelById(msg.chatId) var channel = self.channelView.chats.getChannelById(msg.chatId)
@ -304,50 +262,15 @@ QtObject:
proc messagesLoaded*(self: MessageView) {.signal.} proc messagesLoaded*(self: MessageView) {.signal.}
proc loadMoreMessages*(self: MessageView) {.slot.} = proc loadMoreMessages*(self: MessageView) {.slot.} =
trace "Loading more messages", chaId = self.channelView.activeChannel.id let channelId = self.channelView.activeChannel.id
self.status.chat.chatMessages(self.channelView.activeChannel.id, false) self.status.chat.loadMoreMessagesForChannel(channelId)
self.status.chat.chatReactions(self.channelView.activeChannel.id, false)
self.status.chat.statusUpdates()
self.messagesLoaded();
proc loadMoreMessagesWithIndex*(self: MessageView, channelIndex: int) {.slot.} = proc onMessagesLoaded*(self: MessageView, messages: var seq[Message]) =
if (self.channelView.chats.chats.len == 0): return self.pushMessages(messages)
let selectedChannel = self.channelView.getChannel(channelIndex)
if (selectedChannel == nil): return
trace "Loading more messages", chaId = selectedChannel.id
self.status.chat.chatMessages(selectedChannel.id, false)
self.status.chat.chatReactions(selectedChannel.id, false)
self.status.chat.statusUpdates()
self.messagesLoaded(); self.messagesLoaded();
proc loadingMessagesChanged*(self: MessageView, value: bool) {.signal.} proc loadingMessagesChanged*(self: MessageView, value: bool) {.signal.}
proc asyncMessageLoad*(self: MessageView, chatId: string) {.slot.} =
self.asyncMessageLoad("asyncMessageLoaded", chatId)
proc asyncMessageLoaded*(self: MessageView, rpcResponse: string) {.slot.} =
let
rpcResponseObj = rpcResponse.parseJson
chatId = rpcResponseObj{"chatId"}.getStr
if chatId == "": # .getStr() returns "" when field is not found
return
let messages = rpcResponseObj{"messages"}
if(messages != nil and messages.kind != JNull):
let chatMessages = status_chat.parseChatMessagesResponse(messages)
self.status.chat.chatMessages(chatId, true, chatMessages[0], chatMessages[1])
let rxns = rpcResponseObj{"reactions"}
if(rxns != nil and rxns.kind != JNull):
let reactions = status_chat.parseReactionsResponse(chatId, rxns)
self.status.chat.chatReactions(chatId, true, reactions[0], reactions[1])
let pinnedMsgs = rpcResponseObj{"pinnedMessages"}
if(pinnedMsgs != nil and pinnedMsgs.kind != JNull):
let pinnedMessages = status_chat.parseChatPinnedMessagesResponse(pinnedMsgs)
self.status.chat.pinnedMessagesByChatID(chatId, pinnedMessages[0], pinnedMessages[1])
proc hideLoadingIndicator*(self: MessageView) {.slot.} = proc hideLoadingIndicator*(self: MessageView) {.slot.} =
self.loadingMessages = false self.loadingMessages = false
self.loadingMessagesChanged(false) self.loadingMessagesChanged(false)
@ -410,13 +333,12 @@ QtObject:
discard self.pinnedMessagesList[channelId].deleteMessage(messageId) discard self.pinnedMessagesList[channelId].deleteMessage(messageId)
self.hideMessage(messageId) self.hideMessage(messageId)
proc deleteMessageWhichReplacedMessageWithId*(self: MessageView, channelId: string, messageId: string): bool = proc deleteMessageWhichReplacedMessageWithId*(self: MessageView, channelId: string, replacedMessageId: string): bool =
var msgIdToBeDeleted: string ## Deletes a message which replaced a message with id "replacedMessageId" from
for message in self.messageList[channelId].messages: ## a channel with id "channelId" and returns true if such message is successfully
if (message.replace == messageId): ## deleted, otherwise returns false.
msgIdToBeDeleted = message.id
break
let msgIdToBeDeleted = self.messageList[channelId].getMessageIdWhichReplacedMessageWithId(replacedMessageId)
if (msgIdToBeDeleted.len == 0): if (msgIdToBeDeleted.len == 0):
return false return false
@ -500,26 +422,6 @@ QtObject:
QtProperty[QVariant] searchResultMessageModel: QtProperty[QVariant] searchResultMessageModel:
read = getSearchResultMessageModel read = getSearchResultMessageModel
proc onSearchMessages*(self: MessageView, response: string) {.slot.} =
let responseObj = response.parseJson
if (responseObj.kind != JObject):
error "search messages response is not an json object"
return
let chatId = if(responseObj.contains("chatId")): responseObj{"chatId"}.getStr else : ""
if (chatId.len == 0):
error "search messages response either doesn't contain chatId or it is empty"
return
let messagesObj = if(responseObj.contains("messages")): responseObj{"messages"} else: newJObject()
if (messagesObj.kind != JObject):
error "search messages response either doesn't contain messages object or it is empty"
return
let (cursor, messages) = status_chat.parseChatMessagesResponse(messagesObj)
self.searchResultMessageModel.setFilteredMessages(messages)
proc searchMessages*(self: MessageView, searchTerm: string) {.slot.} = proc searchMessages*(self: MessageView, searchTerm: string) {.slot.} =
if (searchTerm.len == 0): if (searchTerm.len == 0):
self.searchResultMessageModel.clear(false) self.searchResultMessageModel.clear(false)
@ -529,10 +431,8 @@ QtObject:
# later when we decide to apply message search over multiple channels MessageListProxyModel # later when we decide to apply message search over multiple channels MessageListProxyModel
# will be updated to support setting list of sourcer messages. # will be updated to support setting list of sourcer messages.
let chatId = self.channelView.activeChannel.id let chatId = self.channelView.activeChannel.id
let slot = SlotArg( self.status.chat.asyncSearchMessages(chatId, searchTerm, false)
vptr: cast[ByteAddress](self.vptr),
slot: "onSearchMessages"
)
self.status.chat.asyncSearchMessages(slot, chatId, searchTerm, false) proc onSearchMessagesLoaded*(self: MessageView, messages: seq[Message]) =
self.searchResultMessageModel.setFilteredMessages(messages)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,87 @@
#################################################
# Async search messages by term
#################################################
type
AsyncSearchMessageTaskArg = ref object of QObjectTaskArg
chatId: string
searchTerm: string
caseSensitive: bool
const asyncSearchMessagesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[AsyncSearchMessageTaskArg](argEncoded)
var messages: JsonNode
var success: bool
let response = status_chat.asyncSearchMessages(arg.chatId, arg.searchTerm, arg.caseSensitive, success)
if(success):
messages = response.parseJson()["result"]
let responseJson = %*{
"chatId": arg.chatId,
"messages": messages
}
arg.finish(responseJson)
#################################################
# Async load messages
#################################################
type
AsyncFetchChatMessagesTaskArg = ref object of QObjectTaskArg
chatId: string
chatCursor: string
emojiCursor: string
pinnedMsgCursor: string
limit: int
const asyncFetchChatMessagesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[AsyncFetchChatMessagesTaskArg](argEncoded)
# handle messages
var chatMessagesObj: JsonNode
var chatCursor: string
var success: bool
var response = status_chat.fetchChatMessages(arg.chatId, arg.chatCursor, arg.limit, success)
var responseObj = response.parseJson()
if(success):
var resultObj: JsonNode
if (responseObj.getProp("result", resultObj)):
discard resultObj.getProp("cursor", chatCursor)
discard resultObj.getProp("messages", chatMessagesObj)
# handle reactions
var reactionsObj: JsonNode
var reactionsCursor: string
response = status_chat.rpcReactions(arg.chatId, arg.emojiCursor, arg.limit, success)
responseObj = response.parseJson()
if(success):
var resultObj: JsonNode
if (responseObj.getProp("result", resultObj)):
discard resultObj.getProp("cursor", reactionsCursor)
reactionsObj = resultObj
# handle pinned messages
var pinnedMsgObj: JsonNode
var pinnedMsgCursor: string
response = status_chat.rpcPinnedChatMessages(arg.chatId, arg.pinnedMsgCursor, arg.limit, success)
responseObj = response.parseJson()
if(success):
var resultObj: JsonNode
if (responseObj.getProp("result", resultObj)):
discard resultObj.getProp("cursor", pinnedMsgCursor)
discard resultObj.getProp("pinnedMessages", pinnedMsgObj)
let responseJson = %*{
"chatId": arg.chatId,
"messages": chatMessagesObj,
"messagesCursor": chatCursor,
"reactions": reactionsObj,
"reactionsCursor": reactionsCursor,
"pinnedMessages": pinnedMsgObj,
"pinnedMessagesCursor": pinnedMsgCursor
}
arg.finish(responseJson)

View File

@ -9,73 +9,36 @@ proc formatChatUpdate(response: JsonNode): (seq[Chat], seq[Message]) =
chats.add(jsonChat.toChat) chats.add(jsonChat.toChat)
result = (chats, messages) result = (chats, messages)
proc processChatUpdate(self: ChatModel, response: JsonNode): (seq[Chat], seq[Message]) = # This may be moved later to some common file.
var chats: seq[Chat] = @[] # We may create a macro for these template procedures aslo.
var messages: seq[Message] = @[] template getProp(obj: JsonNode, prop: string, value: var typedesc[int]): bool =
if response{"result"}{"messages"} != nil: var success = false
for jsonMsg in response["result"]["messages"]: if (obj.kind == JObject and obj.contains(prop)):
messages.add(jsonMsg.toMessage()) value = obj[prop].getInt
if response{"result"}{"chats"} != nil: success = true
for jsonChat in response["result"]["chats"]:
let chat = jsonChat.toChat
self.channels[chat.id] = chat
chats.add(chat)
result = (chats, messages)
proc emitUpdate(self: ChatModel, response: string) = success
var (chats, messages) = self.processChatUpdate(parseJson(response))
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats, contacts: @[]))
proc removeFiltersByChatId(self: ChatModel, chatId: string, filters: JsonNode) template getProp(obj: JsonNode, prop: string, value: var typedesc[string]): bool =
var success = false
if (obj.kind == JObject and obj.contains(prop)):
value = obj[prop].getStr
success = true
proc removeChatFilters(self: ChatModel, chatId: string) = success
# TODO: this code should be handled by status-go / stimbus instead of the client
# Clients should not have to care about filters. For more info about filters:
# https://github.com/status-im/specs/blob/master/docs/stable/3-whisper-usage.md#keys-management
let filters = parseJson(status_chat.loadFilters(@[]))["result"]
case self.channels[chatId].chatType template getProp(obj: JsonNode, prop: string, value: var typedesc[float]): bool =
of ChatType.Public: var success = false
for filter in filters: if (obj.kind == JObject and obj.contains(prop)):
if filter["chatId"].getStr == chatId: value = obj[prop].getFloat
status_chat.removeFilters(chatId, filter["filterId"].getStr) success = true
of ChatType.OneToOne, ChatType.Profile:
# Check if user does not belong to any active chat group
var inGroup = false
for channel in self.channels.values:
if channel.isActive and channel.id != chatId and channel.chatType == ChatType.PrivateGroupChat:
inGroup = true
break
if not inGroup: self.removeFiltersByChatId(chatId, filters)
of ChatType.PrivateGroupChat:
for member in self.channels[chatId].members:
# Check that any of the members are not in other active group chats, or that you dont have a one-to-one open.
var hasConversation = false
for channel in self.channels.values:
if (channel.isActive and channel.chatType == ChatType.OneToOne and channel.id == member.id) or
(channel.isActive and channel.id != chatId and channel.chatType == ChatType.PrivateGroupChat and channel.isMember(member.id)):
hasConversation = true
break
if not hasConversation: self.removeFiltersByChatId(member.id, filters)
else:
error "Unknown chat type removed", chatId
proc removeFiltersByChatId(self: ChatModel, chatId: string, filters: JsonNode) = success
var partitionedTopic = ""
for filter in filters:
# Contact code filter should be removed
if filter["identity"].getStr == chatId and filter["chatId"].getStr.endsWith("-contact-code"):
status_chat.removeFilters(chatId, filter["filterId"].getStr)
# Remove partitioned topic if no other user in an active group chat or one-to-one is from the template getProp(obj: JsonNode, prop: string, value: var typedesc[JsonNode]): bool =
# same partitioned topic var success = false
if filter["identity"].getStr == chatId and filter["chatId"].getStr.startsWith("contact-discovery-"): if (obj.kind == JObject and obj.contains(prop)):
partitionedTopic = filter["topic"].getStr value = obj[prop]
var samePartitionedTopic = false success = true
for f in filters.filterIt(it["topic"].getStr == partitionedTopic and it["filterId"].getStr != filter["filterId"].getStr):
let fIdentity = f["identity"].getStr; success
if self.channels.hasKey(fIdentity) and self.channels[fIdentity].isActive:
samePartitionedTopic = true
break
if not samePartitionedTopic:
status_chat.removeFilters(chatId, filter["filterId"].getStr)

View File

@ -73,14 +73,6 @@ proc loadChats*(): seq[Chat] =
result.add(chat) result.add(chat)
result.sort(sortChats) result.sort(sortChats)
proc parseChatMessagesResponse*(rpcResult: JsonNode): (string, seq[Message]) =
var messages: seq[Message] = @[]
let messagesObj = rpcResult{"messages"}
if(messagesObj != nil and messagesObj.kind != JNull):
for jsonMsg in messagesObj:
messages.add(jsonMsg.toMessage())
return (rpcResult{"cursor"}.getStr, messages)
proc parseActivityCenterNotifications*(rpcResult: JsonNode): (string, seq[ActivityCenterNotification]) = proc parseActivityCenterNotifications*(rpcResult: JsonNode): (string, seq[ActivityCenterNotification]) =
var notifs: seq[ActivityCenterNotification] = @[] var notifs: seq[ActivityCenterNotification] = @[]
var msg: Message var msg: Message
@ -95,7 +87,7 @@ proc statusUpdates*(): seq[StatusUpdate] =
for jsonStatusUpdate in rpcResult["statusUpdates"]: for jsonStatusUpdate in rpcResult["statusUpdates"]:
result.add(jsonStatusUpdate.toStatusUpdate) result.add(jsonStatusUpdate.toStatusUpdate)
proc rpcChatMessages*(chatId: string, cursorVal: JsonNode, limit: int, success: var bool): string = proc fetchChatMessages*(chatId: string, cursorVal: string, limit: int, success: var bool): string =
success = true success = true
try: try:
result = callPrivateRPC("chatMessages".prefix, %* [chatId, cursorVal, limit]) result = callPrivateRPC("chatMessages".prefix, %* [chatId, cursorVal, limit])
@ -103,26 +95,6 @@ proc rpcChatMessages*(chatId: string, cursorVal: JsonNode, limit: int, success:
success = false success = false
result = e.msg result = e.msg
proc chatMessages*(chatId: string, cursor: string = ""): (string, seq[Message]) =
var cursorVal: JsonNode
if cursor == "":
cursorVal = newJNull()
else:
cursorVal = newJString(cursor)
var success: bool
let callResult = rpcChatMessages(chatId, cursorVal, 20, success)
if success:
result = parseChatMessagesResponse(callResult.parseJson()["result"])
proc parseReactionsResponse*(chatId: string, rpcResult: JsonNode): (string, seq[Reaction]) =
var reactions: seq[Reaction] = @[]
if rpcResult != nil and rpcResult.kind != JNull and rpcResult.len != 0:
for jsonMsg in rpcResult:
reactions.add(jsonMsg.toReaction)
return (rpcResult{"cursor"}.getStr, reactions)
proc editMessage*(messageId: string, msg: string): string = proc editMessage*(messageId: string, msg: string): string =
callPrivateRPC("editMessage".prefix, %* [ callPrivateRPC("editMessage".prefix, %* [
{ {
@ -134,7 +106,7 @@ proc editMessage*(messageId: string, msg: string): string =
proc deleteMessageAndSend*(messageId: string): string = proc deleteMessageAndSend*(messageId: string): string =
callPrivateRPC("deleteMessageAndSend".prefix, %* [messageId]) callPrivateRPC("deleteMessageAndSend".prefix, %* [messageId])
proc rpcReactions*(chatId: string, cursorVal: JsonNode, limit: int, success: var bool): string = proc rpcReactions*(chatId: string, cursorVal: string, limit: int, success: var bool): string =
success = true success = true
try: try:
result = callPrivateRPC("emojiReactionsByChatID".prefix, %* [chatId, cursorVal, limit]) result = callPrivateRPC("emojiReactionsByChatID".prefix, %* [chatId, cursorVal, limit])
@ -142,19 +114,6 @@ proc rpcReactions*(chatId: string, cursorVal: JsonNode, limit: int, success: var
success = false success = false
result = e.msg result = e.msg
proc getEmojiReactionsByChatId*(chatId: string, cursor: string = ""): (string, seq[Reaction]) =
var cursorVal: JsonNode
if cursor == "":
cursorVal = newJNull()
else:
cursorVal = newJString(cursor)
var success: bool
let rpcResult = rpcReactions(chatId, cursorVal, 20, success)
if success:
result = parseReactionsResponse(chatId, rpcResult.parseJson()["result"])
proc addEmojiReaction*(chatId: string, messageId: string, emojiId: int): seq[Reaction] = proc addEmojiReaction*(chatId: string, messageId: string, emojiId: int): seq[Reaction] =
let rpcResult = parseJson(callPrivateRPC("sendEmojiReaction".prefix, %* [chatId, messageId, emojiId]))["result"] let rpcResult = parseJson(callPrivateRPC("sendEmojiReaction".prefix, %* [chatId, messageId, emojiId]))["result"]
@ -552,18 +511,7 @@ proc banUserFromCommunity*(pubKey: string, communityId: string): string =
proc setCommunityMuted*(communityId: string, muted: bool) = proc setCommunityMuted*(communityId: string, muted: bool) =
discard callPrivateRPC("setCommunityMuted".prefix, %*[communityId, muted]) discard callPrivateRPC("setCommunityMuted".prefix, %*[communityId, muted])
proc parseChatPinnedMessagesResponse*(rpcResult: JsonNode): (string, seq[Message]) = proc rpcPinnedChatMessages*(chatId: string, cursorVal: string, limit: int, success: var bool): string =
var messages: seq[Message] = @[]
let messagesObj = rpcResult{"pinnedMessages"}
if(messagesObj != nil and messagesObj.kind != JNull):
var msg: Message
for jsonMsg in messagesObj:
msg = jsonMsg["message"].toMessage()
msg.pinnedBy = $jsonMsg{"pinnedBy"}.getStr
messages.add(msg)
return (rpcResult{"cursor"}.getStr, messages)
proc rpcPinnedChatMessages*(chatId: string, cursorVal: JsonNode, limit: int, success: var bool): string =
success = true success = true
try: try:
result = callPrivateRPC("chatPinnedMessages".prefix, %* [chatId, cursorVal, limit]) result = callPrivateRPC("chatPinnedMessages".prefix, %* [chatId, cursorVal, limit])
@ -572,19 +520,6 @@ proc rpcPinnedChatMessages*(chatId: string, cursorVal: JsonNode, limit: int, suc
success = false success = false
result = e.msg result = e.msg
proc pinnedMessagesByChatID*(chatId: string, cursor: string): (string, seq[Message]) =
var cursorVal: JsonNode
if cursor == "":
cursorVal = newJNull()
else:
cursorVal = newJString(cursor)
var success: bool
let callResult = rpcPinnedChatMessages(chatId, cursorVal, 20, success)
if success:
result = parseChatPinnedMessagesResponse(callResult.parseJson()["result"])
proc setPinMessage*(messageId: string, chatId: string, pinned: bool) = proc setPinMessage*(messageId: string, chatId: string, pinned: bool) =
discard callPrivateRPC("sendPinMessage".prefix, %*[{ discard callPrivateRPC("sendPinMessage".prefix, %*[{
"message_id": messageId, "message_id": messageId,

View File

@ -9,10 +9,6 @@ type
vptr*: ByteAddress vptr*: ByteAddress
slot*: string slot*: string
SlotArg* = ref object of RootObj
vptr*: ByteAddress
slot*: string
proc finish*[T](arg: QObjectTaskArg, payload: T) = proc finish*[T](arg: QObjectTaskArg, payload: T) =
signal_handler(cast[pointer](arg.vptr), Json.encode(payload), arg.slot) signal_handler(cast[pointer](arg.vptr), Json.encode(payload), arg.slot)