mirror of
https://github.com/status-im/status-desktop.git
synced 2025-02-26 21:41:11 +00:00
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:
parent
74d868ab92
commit
e62465d86f
@ -12,7 +12,7 @@ proc handleChatEvents(self: ChatController) =
|
||||
# Display already saved messages
|
||||
self.status.events.on("messagesLoaded") do(e:Args):
|
||||
let evArgs = MsgsLoadedArgs(e)
|
||||
self.view.pushMessages(evArgs.messages)
|
||||
self.view.onMessagesLoaded(evArgs.messages)
|
||||
for statusUpdate in evArgs.statusUpdates:
|
||||
self.view.communities.updateMemberVisibility(statusUpdate)
|
||||
|
||||
@ -22,6 +22,8 @@ proc handleChatEvents(self: ChatController) =
|
||||
# Display already pinned messages
|
||||
self.status.events.on("pinnedMessagesLoaded") do(e:Args):
|
||||
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):
|
||||
let notifications = ActivityCenterNotificationsArgs(e).activityCenterNotifications
|
||||
@ -114,8 +116,7 @@ proc handleChatEvents(self: ChatController) =
|
||||
|
||||
if channel.chat.chatType == status_chat.ChatType.CommunityChat:
|
||||
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.view.calculateUnreadMessages()
|
||||
self.view.setActiveChannelByIndex(0)
|
||||
@ -138,8 +139,8 @@ proc handleChatEvents(self: ChatController) =
|
||||
elif channel.chat.chatType != ChatType.Profile:
|
||||
discard self.view.channelView.chats.addChatItemToList(channel.chat)
|
||||
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.events.on("channelLeft") do(e: Args):
|
||||
|
@ -266,7 +266,7 @@ QtObject:
|
||||
|
||||
proc pushChatItem*(self: ChatsView, chatItem: Chat) =
|
||||
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) =
|
||||
self.timelineChat = chatItem
|
||||
@ -438,6 +438,12 @@ QtObject:
|
||||
let task = RequestMessagesTaskArg( `method`: "requestMoreMessages", chatId: self.channelView.activeChannel.id)
|
||||
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]) =
|
||||
self.messageView.pushMessages(messages)
|
||||
|
||||
@ -469,9 +475,6 @@ QtObject:
|
||||
proc clearMessages*(self: ChatsView, id: string) =
|
||||
self.messageView.clearMessages(id)
|
||||
|
||||
proc asyncMessageLoad*(self: ChatsView, chatId: string) {.slot.} =
|
||||
self.messageView.asyncMessageLoad(chatId)
|
||||
|
||||
proc calculateUnreadMessages*(self: ChatsView) =
|
||||
self.messageView.calculateUnreadMessages()
|
||||
|
||||
|
@ -404,3 +404,11 @@ QtObject:
|
||||
|
||||
QtProperty[QVariant] userList:
|
||||
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
|
@ -3,7 +3,6 @@ import NimQml, Tables, json, sequtils, chronicles, times, re, sugar, strutils, o
|
||||
import ../../../status/[status, contacts, types, mailservers]
|
||||
import ../../../status/signals/types as signal_types
|
||||
import ../../../status/ens as status_ens
|
||||
import ../../../status/chat as status_chat
|
||||
import ../../../status/messages as status_messages
|
||||
import ../../../status/utils as status_utils
|
||||
import ../../../status/chat/[chat, message]
|
||||
@ -20,47 +19,6 @@ type
|
||||
ChatViewRoles {.pure.} = enum
|
||||
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:
|
||||
type MessageView* = ref object of QAbstractListModel
|
||||
status: Status
|
||||
@ -243,12 +201,12 @@ QtObject:
|
||||
self.messageList[timelineChatId].add(msg)
|
||||
# if self.channelView.activeChannel.id == timelineChatId: self.activeChannelChanged()
|
||||
if self.channelView.activeChannel.id == timelineChatId: self.channelView.activeChannelChanged()
|
||||
msgIndex = self.messageList[timelineChatId].messages.len - 1
|
||||
msgIndex = self.messageList[timelineChatId].count - 1
|
||||
else:
|
||||
self.messageList[msg.chatId].add(msg)
|
||||
if self.pinnedMessagesList[msg.chatId].contains(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)
|
||||
if self.channelOpenTime.getOrDefault(msg.chatId, high(int64)) < msg.timestamp.parseFloat.fromUnixFloat.toUnix:
|
||||
var channel = self.channelView.chats.getChannelById(msg.chatId)
|
||||
@ -304,50 +262,15 @@ QtObject:
|
||||
proc messagesLoaded*(self: MessageView) {.signal.}
|
||||
|
||||
proc loadMoreMessages*(self: MessageView) {.slot.} =
|
||||
trace "Loading more messages", chaId = self.channelView.activeChannel.id
|
||||
self.status.chat.chatMessages(self.channelView.activeChannel.id, false)
|
||||
self.status.chat.chatReactions(self.channelView.activeChannel.id, false)
|
||||
self.status.chat.statusUpdates()
|
||||
self.messagesLoaded();
|
||||
let channelId = self.channelView.activeChannel.id
|
||||
self.status.chat.loadMoreMessagesForChannel(channelId)
|
||||
|
||||
proc loadMoreMessagesWithIndex*(self: MessageView, channelIndex: int) {.slot.} =
|
||||
if (self.channelView.chats.chats.len == 0): return
|
||||
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()
|
||||
proc onMessagesLoaded*(self: MessageView, messages: var seq[Message]) =
|
||||
self.pushMessages(messages)
|
||||
self.messagesLoaded();
|
||||
|
||||
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.} =
|
||||
self.loadingMessages = false
|
||||
self.loadingMessagesChanged(false)
|
||||
@ -410,13 +333,12 @@ QtObject:
|
||||
discard self.pinnedMessagesList[channelId].deleteMessage(messageId)
|
||||
self.hideMessage(messageId)
|
||||
|
||||
proc deleteMessageWhichReplacedMessageWithId*(self: MessageView, channelId: string, messageId: string): bool =
|
||||
var msgIdToBeDeleted: string
|
||||
for message in self.messageList[channelId].messages:
|
||||
if (message.replace == messageId):
|
||||
msgIdToBeDeleted = message.id
|
||||
break
|
||||
proc deleteMessageWhichReplacedMessageWithId*(self: MessageView, channelId: string, replacedMessageId: string): bool =
|
||||
## Deletes a message which replaced a message with id "replacedMessageId" from
|
||||
## a channel with id "channelId" and returns true if such message is successfully
|
||||
## deleted, otherwise returns false.
|
||||
|
||||
let msgIdToBeDeleted = self.messageList[channelId].getMessageIdWhichReplacedMessageWithId(replacedMessageId)
|
||||
if (msgIdToBeDeleted.len == 0):
|
||||
return false
|
||||
|
||||
@ -500,26 +422,6 @@ QtObject:
|
||||
QtProperty[QVariant] searchResultMessageModel:
|
||||
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.} =
|
||||
if (searchTerm.len == 0):
|
||||
self.searchResultMessageModel.clear(false)
|
||||
@ -529,10 +431,8 @@ QtObject:
|
||||
# later when we decide to apply message search over multiple channels MessageListProxyModel
|
||||
# will be updated to support setting list of sourcer messages.
|
||||
let chatId = self.channelView.activeChannel.id
|
||||
let slot = SlotArg(
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: "onSearchMessages"
|
||||
)
|
||||
self.status.chat.asyncSearchMessages(chatId, searchTerm, false)
|
||||
|
||||
self.status.chat.asyncSearchMessages(slot, chatId, searchTerm, false)
|
||||
proc onSearchMessagesLoaded*(self: MessageView, messages: seq[Message]) =
|
||||
self.searchResultMessageModel.setFilteredMessages(messages)
|
||||
|
||||
|
1092
src/status/chat.nim
1092
src/status/chat.nim
File diff suppressed because it is too large
Load Diff
87
src/status/chat/async_tasks.nim
Normal file
87
src/status/chat/async_tasks.nim
Normal 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)
|
@ -9,73 +9,36 @@ proc formatChatUpdate(response: JsonNode): (seq[Chat], seq[Message]) =
|
||||
chats.add(jsonChat.toChat)
|
||||
result = (chats, messages)
|
||||
|
||||
proc processChatUpdate(self: ChatModel, response: JsonNode): (seq[Chat], seq[Message]) =
|
||||
var chats: seq[Chat] = @[]
|
||||
var messages: seq[Message] = @[]
|
||||
if response{"result"}{"messages"} != nil:
|
||||
for jsonMsg in response["result"]["messages"]:
|
||||
messages.add(jsonMsg.toMessage())
|
||||
if response{"result"}{"chats"} != nil:
|
||||
for jsonChat in response["result"]["chats"]:
|
||||
let chat = jsonChat.toChat
|
||||
self.channels[chat.id] = chat
|
||||
chats.add(chat)
|
||||
result = (chats, messages)
|
||||
# This may be moved later to some common file.
|
||||
# We may create a macro for these template procedures aslo.
|
||||
template getProp(obj: JsonNode, prop: string, value: var typedesc[int]): bool =
|
||||
var success = false
|
||||
if (obj.kind == JObject and obj.contains(prop)):
|
||||
value = obj[prop].getInt
|
||||
success = true
|
||||
|
||||
proc emitUpdate(self: ChatModel, response: string) =
|
||||
var (chats, messages) = self.processChatUpdate(parseJson(response))
|
||||
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats, contacts: @[]))
|
||||
success
|
||||
|
||||
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) =
|
||||
# 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"]
|
||||
success
|
||||
|
||||
case self.channels[chatId].chatType
|
||||
of ChatType.Public:
|
||||
for filter in filters:
|
||||
if filter["chatId"].getStr == chatId:
|
||||
status_chat.removeFilters(chatId, filter["filterId"].getStr)
|
||||
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 don’t 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
|
||||
template getProp(obj: JsonNode, prop: string, value: var typedesc[float]): bool =
|
||||
var success = false
|
||||
if (obj.kind == JObject and obj.contains(prop)):
|
||||
value = obj[prop].getFloat
|
||||
success = true
|
||||
|
||||
proc removeFiltersByChatId(self: ChatModel, chatId: string, filters: JsonNode) =
|
||||
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)
|
||||
success
|
||||
|
||||
# Remove partitioned topic if no other user in an active group chat or one-to-one is from the
|
||||
# same partitioned topic
|
||||
if filter["identity"].getStr == chatId and filter["chatId"].getStr.startsWith("contact-discovery-"):
|
||||
partitionedTopic = filter["topic"].getStr
|
||||
var samePartitionedTopic = false
|
||||
for f in filters.filterIt(it["topic"].getStr == partitionedTopic and it["filterId"].getStr != filter["filterId"].getStr):
|
||||
let fIdentity = f["identity"].getStr;
|
||||
if self.channels.hasKey(fIdentity) and self.channels[fIdentity].isActive:
|
||||
samePartitionedTopic = true
|
||||
break
|
||||
if not samePartitionedTopic:
|
||||
status_chat.removeFilters(chatId, filter["filterId"].getStr)
|
||||
template getProp(obj: JsonNode, prop: string, value: var typedesc[JsonNode]): bool =
|
||||
var success = false
|
||||
if (obj.kind == JObject and obj.contains(prop)):
|
||||
value = obj[prop]
|
||||
success = true
|
||||
|
||||
success
|
@ -73,14 +73,6 @@ proc loadChats*(): seq[Chat] =
|
||||
result.add(chat)
|
||||
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]) =
|
||||
var notifs: seq[ActivityCenterNotification] = @[]
|
||||
var msg: Message
|
||||
@ -95,7 +87,7 @@ proc statusUpdates*(): seq[StatusUpdate] =
|
||||
for jsonStatusUpdate in rpcResult["statusUpdates"]:
|
||||
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
|
||||
try:
|
||||
result = callPrivateRPC("chatMessages".prefix, %* [chatId, cursorVal, limit])
|
||||
@ -103,26 +95,6 @@ proc rpcChatMessages*(chatId: string, cursorVal: JsonNode, limit: int, success:
|
||||
success = false
|
||||
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 =
|
||||
callPrivateRPC("editMessage".prefix, %* [
|
||||
{
|
||||
@ -134,7 +106,7 @@ proc editMessage*(messageId: string, msg: string): string =
|
||||
proc deleteMessageAndSend*(messageId: string): string =
|
||||
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
|
||||
try:
|
||||
result = callPrivateRPC("emojiReactionsByChatID".prefix, %* [chatId, cursorVal, limit])
|
||||
@ -142,19 +114,6 @@ proc rpcReactions*(chatId: string, cursorVal: JsonNode, limit: int, success: var
|
||||
success = false
|
||||
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] =
|
||||
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) =
|
||||
discard callPrivateRPC("setCommunityMuted".prefix, %*[communityId, muted])
|
||||
|
||||
proc parseChatPinnedMessagesResponse*(rpcResult: JsonNode): (string, seq[Message]) =
|
||||
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 =
|
||||
proc rpcPinnedChatMessages*(chatId: string, cursorVal: string, limit: int, success: var bool): string =
|
||||
success = true
|
||||
try:
|
||||
result = callPrivateRPC("chatPinnedMessages".prefix, %* [chatId, cursorVal, limit])
|
||||
@ -572,19 +520,6 @@ proc rpcPinnedChatMessages*(chatId: string, cursorVal: JsonNode, limit: int, suc
|
||||
success = false
|
||||
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) =
|
||||
discard callPrivateRPC("sendPinMessage".prefix, %*[{
|
||||
"message_id": messageId,
|
||||
|
@ -9,10 +9,6 @@ type
|
||||
vptr*: ByteAddress
|
||||
slot*: string
|
||||
|
||||
SlotArg* = ref object of RootObj
|
||||
vptr*: ByteAddress
|
||||
slot*: string
|
||||
|
||||
proc finish*[T](arg: QObjectTaskArg, payload: T) =
|
||||
signal_handler(cast[pointer](arg.vptr), Json.encode(payload), arg.slot)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user