feature(@desktop/chat): implement search on sqlcipher (status-go side)
Searching messages by some term for a specific channel is added on the side of status-go and an appropriate part on the side of nim is developed accordingly. Fixes: #2912
This commit is contained in:
parent
1573d7b928
commit
f0c605936d
|
@ -20,9 +20,14 @@ QtObject:
|
|||
result.setup(status)
|
||||
|
||||
proc setSourceMessages*(self: MessageListProxyModel, messages: seq[Message]) =
|
||||
## Sets source messages which will be filtered.
|
||||
self.sourceMessages = messages
|
||||
|
||||
proc setFilter*(self: MessageListProxyModel, filter: string, caseSensitive: bool) =
|
||||
## This proc filters messages that contain passed "filter" from the previously
|
||||
## set source model, respecting case sensitivity or not, based on the passed
|
||||
## "caseSensitive" parameter.
|
||||
|
||||
self.clear(false)
|
||||
|
||||
if (filter.len == 0):
|
||||
|
@ -40,3 +45,10 @@ QtObject:
|
|||
return
|
||||
|
||||
self.add(matchedMessages)
|
||||
|
||||
proc setFilteredMessages*(self: MessageListProxyModel, messages: seq[Message]) =
|
||||
## Sets filtered messages. Messages passed to this proc will be immediately
|
||||
## added to the view model and displayed to user. Method is convenient for
|
||||
## displaying already filtered messages (for example filtered response from DB)
|
||||
self.clear(false)
|
||||
self.add(messages)
|
|
@ -13,9 +13,6 @@ import ../../../status/tasks/marathon/mailserver/worker
|
|||
|
||||
import communities, chat_item, channels_list, communities, community_list, message_list, channel, message_item, message_list_proxy
|
||||
|
||||
# TODO: remove me
|
||||
import ../../../status/libstatus/chat as libstatus_chat
|
||||
|
||||
logScope:
|
||||
topics = "messages-view"
|
||||
|
||||
|
@ -338,7 +335,7 @@ QtObject:
|
|||
|
||||
let messages = rpcResponseObj{"messages"}
|
||||
if(messages != nil and messages.kind != JNull):
|
||||
let chatMessages = libstatus_chat.parseChatMessagesResponse(messages)
|
||||
let chatMessages = status_chat.parseChatMessagesResponse(messages)
|
||||
self.status.chat.chatMessages(chatId, true, chatMessages[0], chatMessages[1])
|
||||
|
||||
let rxns = rpcResponseObj{"reactions"}
|
||||
|
@ -348,7 +345,7 @@ QtObject:
|
|||
|
||||
let pinnedMsgs = rpcResponseObj{"pinnedMessages"}
|
||||
if(pinnedMsgs != nil and pinnedMsgs.kind != JNull):
|
||||
let pinnedMessages = libstatus_chat.parseChatPinnedMessagesResponse(pinnedMsgs)
|
||||
let pinnedMessages = status_chat.parseChatPinnedMessagesResponse(pinnedMsgs)
|
||||
self.status.chat.pinnedMessagesByChatID(chatId, pinnedMessages[0], pinnedMessages[1])
|
||||
|
||||
proc hideLoadingIndicator*(self: MessageView) {.slot.} =
|
||||
|
@ -503,10 +500,38 @@ 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.} =
|
||||
# channelId is used here only to support message search in currently selected channel
|
||||
if (searchTerm.len == 0):
|
||||
self.searchResultMessageModel.clear(false)
|
||||
return
|
||||
|
||||
# chatId is used here only to support message search in currently selected channel
|
||||
# later when we decide to apply message search over multiple channels MessageListProxyModel
|
||||
# will be updated to support setting list of sourcer messages.
|
||||
let channelId = self.channelView.activeChannel.id
|
||||
self.searchResultMessageModel.setSourceMessages(self.messageList[channelId].messages)
|
||||
self.searchResultMessageModel.setFilter(searchTerm, false)
|
||||
let chatId = self.channelView.activeChannel.id
|
||||
let slot = SlotArg(
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: "onSearchMessages"
|
||||
)
|
||||
|
||||
self.status.chat.asyncSearchMessages(slot, chatId, searchTerm, false)
|
||||
|
|
|
@ -9,6 +9,7 @@ import ../eventemitter
|
|||
import profile/profile
|
||||
import contacts
|
||||
import chat/[chat, message]
|
||||
import tasks/[qt, task_runner_impl]
|
||||
import signals/messages
|
||||
import ens
|
||||
|
||||
|
@ -56,6 +57,7 @@ type
|
|||
ChatModel* = ref object
|
||||
publicKey*: string
|
||||
events*: EventEmitter
|
||||
tasks*: TaskRunner
|
||||
communitiesToFetch*: seq[string]
|
||||
mailserverReady*: bool
|
||||
contacts*: Table[string, Profile]
|
||||
|
@ -77,9 +79,10 @@ type
|
|||
|
||||
include chat/utils
|
||||
|
||||
proc newChatModel*(events: EventEmitter): ChatModel =
|
||||
proc newChatModel*(events: EventEmitter, tasks: TaskRunner): ChatModel =
|
||||
result = ChatModel()
|
||||
result.events = events
|
||||
result.tasks = tasks
|
||||
result.mailserverReady = false
|
||||
result.communitiesToFetch = @[]
|
||||
result.contacts = initTable[string, Profile]()
|
||||
|
@ -634,3 +637,44 @@ proc parseReactionsResponse*(chatId: string, rpcResult: JsonNode): (string, seq[
|
|||
|
||||
proc parseChatMessagesResponse*(rpcResult: JsonNode): (string, seq[Message]) =
|
||||
result = status_chat.parseChatMessagesResponse(rpcResult)
|
||||
|
||||
# This is still not proper place for such methods, but anyway better then calling
|
||||
# them directly from the view part. Such methods should be placed in appropriate
|
||||
# utils file and may be used on many places from there.
|
||||
proc parseChatPinnedMessagesResponse*(rpcResult: JsonNode): (string, seq[Message]) =
|
||||
result = status_chat.parseChatPinnedMessagesResponse(rpcResult)
|
||||
|
||||
#################################################
|
||||
# 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)
|
||||
|
||||
proc asyncSearchMessages*(self: ChatModel, slot: SlotArg, chatId: string, searchTerm: string, caseSensitive: bool) =
|
||||
let arg = AsyncSearchMessageTaskArg(
|
||||
tptr: cast[ByteAddress](asyncSearchMessagesTask),
|
||||
vptr: slot.vptr,
|
||||
slot: slot.slot,
|
||||
chatId: chatId,
|
||||
searchTerm: searchTerm,
|
||||
caseSensitive: caseSensitive
|
||||
)
|
||||
self.tasks.threadpool.start(arg)
|
|
@ -630,3 +630,11 @@ proc unreadActivityCenterNotificationsCount*(): int =
|
|||
|
||||
if rpcResult{"result"}.kind != JNull:
|
||||
return rpcResult["result"].getInt
|
||||
|
||||
proc asyncSearchMessages*(chatId: string, searchTerm: string, caseSensitive: bool, success: var bool): string =
|
||||
success = true
|
||||
try:
|
||||
result = callPrivateRPC("allChatMessagesWhichMatchTerm".prefix, %* [chatId, searchTerm, caseSensitive])
|
||||
except RpcException as e:
|
||||
success = false
|
||||
result = e.msg
|
|
@ -33,7 +33,7 @@ proc newStatusInstance*(fleetConfig: string): Status =
|
|||
result.tasks = newTaskRunner()
|
||||
result.events = createEventEmitter()
|
||||
result.fleet = fleet.newFleetModel(fleetConfig)
|
||||
result.chat = chat.newChatModel(result.events)
|
||||
result.chat = chat.newChatModel(result.events, result.tasks)
|
||||
result.accounts = accounts.newAccountModel(result.events)
|
||||
result.wallet = wallet.newWalletModel(result.events)
|
||||
result.wallet.initEvents()
|
||||
|
|
|
@ -9,6 +9,10 @@ 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)
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 7e03daeaf9bf4e1614d5507400b3d9a0ac016bc0
|
||||
Subproject commit 6c67aaf3a3031af92931dce9673418e81f80749f
|
|
@ -218,8 +218,6 @@ Popup {
|
|||
prevMessageIndex: -1
|
||||
prevMsgTimestamp: ""
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit a838753904615610667e704efec5864e0d27e7c2
|
||||
Subproject commit eaa394d711f757b859030349aeaf41a10e1bad2b
|
|
@ -1 +1 @@
|
|||
Subproject commit f4463f3955a96e162e9881b73ba02f819e0374a4
|
||||
Subproject commit f1ec58561d7b48f6c8b664f3806fd8a113164fba
|
|
@ -1 +1 @@
|
|||
Subproject commit 8ca765ca11a9fde6a43d60289afcea312d3e6ed8
|
||||
Subproject commit 65fa68c244fa52037de652520755430ec65c77df
|
|
@ -1 +1 @@
|
|||
Subproject commit 0aab3e238bb903547e580793d8eca8740909cd57
|
||||
Subproject commit c4a71f813a783e310b1c8ca59d2390a8d76f6a0c
|
Loading…
Reference in New Issue