refactor(app-search): app search module added
This commit is contained in:
parent
9045b0bbc4
commit
d67278b23e
|
@ -0,0 +1,41 @@
|
|||
type
|
||||
BaseItem* {.pure inheritable.} = ref object of RootObj
|
||||
value: string
|
||||
text: string
|
||||
image: string
|
||||
icon: string
|
||||
iconColor: string
|
||||
isIdenticon: bool
|
||||
|
||||
proc setup*(self: BaseItem, value, text, image, icon, iconColor: string, isIdenticon: bool) =
|
||||
self.value = value
|
||||
self.text = text
|
||||
self.image = image
|
||||
self.icon = icon
|
||||
self.iconColor = iconColor
|
||||
self.isIdenticon = isIdenticon
|
||||
|
||||
proc initBaseItem*(value, text, image, icon, iconColor: string, isIdenticon: bool): BaseItem =
|
||||
result = BaseItem()
|
||||
result.setup(value, text, image, icon, iconColor, isIdenticon)
|
||||
|
||||
proc delete*(self: BaseItem) =
|
||||
discard
|
||||
|
||||
method value*(self: BaseItem): string {.inline base.} =
|
||||
self.value
|
||||
|
||||
method text*(self: BaseItem): string {.inline base.} =
|
||||
self.text
|
||||
|
||||
method image*(self: BaseItem): string {.inline base.} =
|
||||
self.image
|
||||
|
||||
method icon*(self: BaseItem): string {.inline base.} =
|
||||
self.icon
|
||||
|
||||
method iconColor*(self: BaseItem): string {.inline base.} =
|
||||
self.iconColor
|
||||
|
||||
method isIdenticon*(self: BaseItem): bool {.inline base.} =
|
||||
self.isIdenticon
|
|
@ -0,0 +1,114 @@
|
|||
import controller_interface
|
||||
import io_interface
|
||||
|
||||
import ../../../boot/app_sections_config as conf
|
||||
import ../../../../app_service/service/chat/service as chat_service
|
||||
import ../../../../app_service/service/community/service as community_service
|
||||
import ../../../../app_service/service/message/service as message_service
|
||||
|
||||
import eventemitter
|
||||
import status/[signals]
|
||||
|
||||
export controller_interface
|
||||
|
||||
type
|
||||
Controller* = ref object of controller_interface.AccessInterface
|
||||
delegate: io_interface.AccessInterface
|
||||
events: EventEmitter
|
||||
chatService: chat_service.ServiceInterface
|
||||
communityService: community_service.ServiceInterface
|
||||
messageService: message_service.Service
|
||||
activeSectionId: string
|
||||
activeChatId: string
|
||||
searchLocation: string
|
||||
searchSubLocation: string
|
||||
searchTerm: string
|
||||
|
||||
proc newController*(delegate: io_interface.AccessInterface, events: EventEmitter,
|
||||
chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface,
|
||||
messageService: message_service.Service): Controller =
|
||||
result = Controller()
|
||||
result.delegate = delegate
|
||||
result.events = events
|
||||
result.chatService = chatService
|
||||
result.communityService = communityService
|
||||
result.messageService = messageService
|
||||
|
||||
method delete*(self: Controller) =
|
||||
discard
|
||||
|
||||
method init*(self: Controller) =
|
||||
self.events.on(SIGNAL_SEARCH_MESSAGES_LOADED) do(e:Args):
|
||||
let args = SearchMessagesLoadedArgs(e)
|
||||
self.delegate.onSearchMessagesDone(args.messages)
|
||||
|
||||
method activeSectionId*(self: Controller): string =
|
||||
return self.activeSectionId
|
||||
|
||||
method activeChatId*(self: Controller): string =
|
||||
return self.activeChatId
|
||||
|
||||
method setActiveSectionIdAndChatId*(self: Controller, sectionId: string, chatId: string) =
|
||||
self.activeSectionId = sectionId
|
||||
self.activeChatId = chatId
|
||||
|
||||
method searchTerm*(self: Controller): string =
|
||||
return self.searchTerm
|
||||
|
||||
method searchLocation*(self: Controller): string =
|
||||
return self.searchLocation
|
||||
|
||||
method searchSubLocation*(self: Controller): string =
|
||||
return self.searchSubLocation
|
||||
|
||||
method setSearchLocation*(self: Controller, location: string, subLocation: string) =
|
||||
## Setting location and subLocation to an empty string means we're
|
||||
## searching in all available chats/channels/communities.
|
||||
self.searchLocation = location
|
||||
self.searchSubLocation = subLocation
|
||||
|
||||
method getCommunities*(self: Controller): seq[CommunityDto] =
|
||||
return self.communityService.getCommunities()
|
||||
|
||||
method getCommunityById*(self: Controller, communityId: string): CommunityDto =
|
||||
return self.communityService.getCommunityById(communityId)
|
||||
|
||||
method getAllChatsForCommunity*(self: Controller, communityId: string): seq[Chat] =
|
||||
return self.communityService.getAllChats(communityId)
|
||||
|
||||
method getChatDetailsForChatTypes*(self: Controller, types: seq[ChatType]): seq[ChatDto] =
|
||||
return self.chatService.getChatsOfChatTypes(types)
|
||||
|
||||
method getChatDetails*(self: Controller, communityId, chatId: string): ChatDto =
|
||||
let fullId = communityId & chatId
|
||||
return self.chatService.getChatById(fullId)
|
||||
|
||||
method searchMessages*(self: Controller, searchTerm: string) =
|
||||
self.searchTerm = searchTerm
|
||||
|
||||
var chats: seq[string]
|
||||
var communities: seq[string]
|
||||
|
||||
if (self.searchSubLocation.len > 0):
|
||||
chats.add(self.searchSubLocation)
|
||||
elif (self.searchLocation.len > 0):
|
||||
# If "Chat" is set for the meassgeSearchLocation that means we need to search in all chats from the chat section.
|
||||
if (self.searchLocation != conf.CHAT_SECTION_ID):
|
||||
communities.add(self.searchLocation)
|
||||
else:
|
||||
let types = @[ChatType.OneToOne, ChatType.Public, ChatType.PrivateGroupChat]
|
||||
let displayedChats = self.getChatDetailsForChatTypes(types)
|
||||
for c in displayedChats:
|
||||
chats.add(c.id)
|
||||
|
||||
if (communities.len == 0 and chats.len == 0):
|
||||
let types = @[ChatType.OneToOne, ChatType.Public, ChatType.PrivateGroupChat]
|
||||
let displayedChats = self.getChatDetailsForChatTypes(types)
|
||||
for c in displayedChats:
|
||||
chats.add(c.id)
|
||||
|
||||
let communitiesIds = self.communityService.getCommunityIds()
|
||||
for cId in communitiesIds:
|
||||
communities.add(cId)
|
||||
|
||||
self.messageService.asyncSearchMessages(communities, chats, self.searchTerm, false)
|
|
@ -0,0 +1,51 @@
|
|||
import ../../../../app_service/service/chat/dto/chat
|
||||
import ../../../../app_service/service/community/dto/community
|
||||
|
||||
type
|
||||
AccessInterface* {.pure inheritable.} = ref object of RootObj
|
||||
## Abstract class for any input/interaction with this module.
|
||||
|
||||
method delete*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method init*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method activeSectionId*(self: AccessInterface): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method activeChatId*(self: AccessInterface): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method setActiveSectionIdAndChatId*(self: AccessInterface, sectionId: string, chatId: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method searchTerm*(self: AccessInterface): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method searchLocation*(self: AccessInterface): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method searchSubLocation*(self: AccessInterface): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method setSearchLocation*(self: AccessInterface, location: string, subLocation: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getCommunities*(self: AccessInterface): seq[CommunityDto] {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getCommunityById*(self: AccessInterface, communityId: string): CommunityDto {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getAllChatsForCommunity*(self: AccessInterface, communityId: string): seq[Chat] {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getChatDetailsForChatTypes*(self: AccessInterface, types: seq[ChatType]): seq[ChatDto] {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getChatDetails*(self: AccessInterface, communityId, chatId: string): ChatDto {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method searchMessages*(self: AccessInterface, searchTerm: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
|
@ -0,0 +1,11 @@
|
|||
# Defines how parent module accesses this module
|
||||
include ./private_interfaces/module_base_interface
|
||||
include ./private_interfaces/module_access_interface
|
||||
|
||||
# Defines how this module view communicates with this module
|
||||
include ./private_interfaces/module_view_delegate_interface
|
||||
|
||||
# Defines how this controller communicates with this module
|
||||
include ./private_interfaces/module_controller_delegate_interface
|
||||
|
||||
# Defines how submodules of this module communicate with this module
|
|
@ -0,0 +1,47 @@
|
|||
|
||||
import json, strformat
|
||||
import base_item, location_menu_sub_model, location_menu_sub_item
|
||||
|
||||
type
|
||||
Item* = ref object of BaseItem
|
||||
subItems: SubModel
|
||||
|
||||
proc initItem*(value, text, image, icon, iconColor: string = "", isIdenticon: bool = true): Item =
|
||||
result = Item()
|
||||
result.setup(value, text, image, icon, iconColor, isIdenticon)
|
||||
result.subItems = newSubModel()
|
||||
|
||||
proc delete*(self: Item) =
|
||||
self.subItems.delete
|
||||
self.BaseItem.delete
|
||||
|
||||
proc subItems*(self: Item): SubModel {.inline.} =
|
||||
self.subItems
|
||||
|
||||
proc `$`*(self: Item): string =
|
||||
result = fmt"""SearchMenuItem(
|
||||
value: {self.value},
|
||||
title: {self.text},
|
||||
imageSource: {self.image},
|
||||
iconName: {self.icon},
|
||||
iconColor: {self.iconColor},
|
||||
isIdenticon: {self.isIdenticon},
|
||||
subItems:[
|
||||
{$self.subItems}
|
||||
]"""
|
||||
|
||||
proc toJsonNode*(self: Item): JsonNode =
|
||||
result = %* {
|
||||
"value": self.value,
|
||||
"title": self.text,
|
||||
"imageSource": self.image,
|
||||
"iconName": self.icon,
|
||||
"iconColor": self.iconColor,
|
||||
"isIdenticon": self.isIdenticon
|
||||
}
|
||||
|
||||
proc setSubItems*(self: Item, subItems: seq[SubItem]) =
|
||||
self.subItems.setItems(subItems)
|
||||
|
||||
proc getSubItemForValue*(self: Item, value: string): SubItem =
|
||||
self.subItems.getItemForValue(value)
|
|
@ -0,0 +1,81 @@
|
|||
import NimQml, Tables, strutils
|
||||
|
||||
import location_menu_item, location_menu_sub_item
|
||||
|
||||
type
|
||||
ModelRole {.pure.} = enum
|
||||
Value = UserRole + 1
|
||||
Title
|
||||
ImageSource
|
||||
IconName
|
||||
IconColor
|
||||
IsIdenticon
|
||||
SubItems
|
||||
|
||||
QtObject:
|
||||
type
|
||||
Model* = ref object of QAbstractListModel
|
||||
items: seq[Item]
|
||||
|
||||
proc delete(self: Model) =
|
||||
for i in 0 ..< self.items.len:
|
||||
self.items[i].delete
|
||||
self.items = @[]
|
||||
self.QAbstractListModel.delete
|
||||
|
||||
proc setup(self: Model) =
|
||||
self.QAbstractListModel.setup
|
||||
|
||||
proc newModel*(): Model =
|
||||
new(result, delete)
|
||||
result.setup()
|
||||
|
||||
method rowCount(self: Model, index: QModelIndex = nil): int =
|
||||
return self.items.len
|
||||
|
||||
method roleNames(self: Model): Table[int, string] =
|
||||
{
|
||||
ModelRole.Value.int:"value",
|
||||
ModelRole.Title.int:"title",
|
||||
ModelRole.ImageSource.int:"imageSource",
|
||||
ModelRole.IconName.int:"iconName",
|
||||
ModelRole.IconColor.int:"iconColor",
|
||||
ModelRole.IsIdenticon.int:"isIdenticon",
|
||||
ModelRole.SubItems.int:"subItems"
|
||||
}.toTable
|
||||
|
||||
method data(self: Model, index: QModelIndex, role: int): QVariant =
|
||||
if (not index.isValid):
|
||||
return
|
||||
|
||||
if (index.row < 0 or index.row >= self.items.len):
|
||||
return
|
||||
|
||||
let item = self.items[index.row]
|
||||
let enumRole = role.ModelRole
|
||||
|
||||
case enumRole:
|
||||
of ModelRole.Value:
|
||||
result = newQVariant(item.value)
|
||||
of ModelRole.Title:
|
||||
result = newQVariant(item.text)
|
||||
of ModelRole.ImageSource:
|
||||
result = newQVariant(item.image)
|
||||
of ModelRole.IconName:
|
||||
result = newQVariant(item.icon)
|
||||
of ModelRole.IconColor:
|
||||
result = newQVariant(item.iconColor)
|
||||
of ModelRole.IsIdenticon:
|
||||
result = newQVariant(item.isIdenticon)
|
||||
of ModelRole.SubItems:
|
||||
result = newQVariant(item.subItems)
|
||||
|
||||
proc setItems*(self: Model, items: seq[Item]) =
|
||||
self.beginResetModel()
|
||||
self.items = items
|
||||
self.endResetModel()
|
||||
|
||||
proc getItemForValue*(self: Model, value: string): Item =
|
||||
for i in self.items:
|
||||
if (i.value == value):
|
||||
return i
|
|
@ -0,0 +1,34 @@
|
|||
import json, strformat
|
||||
import base_item
|
||||
|
||||
export base_item
|
||||
|
||||
type
|
||||
SubItem* = ref object of BaseItem
|
||||
|
||||
proc initSubItem*(value, text, image, icon, iconColor: string = "", isIdenticon: bool = true): SubItem =
|
||||
result = SubItem()
|
||||
result.setup(value, text, image, icon, iconColor, isIdenticon)
|
||||
|
||||
proc delete*(self: SubItem) =
|
||||
self.BaseItem.delete
|
||||
|
||||
proc `$`*(self: SubItem): string =
|
||||
result = fmt"""SearchMenuSubItem(
|
||||
value: {self.value},
|
||||
text: {self.text},
|
||||
imageSource: {self.image},
|
||||
iconName: {self.icon},
|
||||
iconColor: {self.iconColor},
|
||||
isIdenticon: {self.isIdenticon}
|
||||
]"""
|
||||
|
||||
proc toJsonNode*(self: SubItem): JsonNode =
|
||||
result = %* {
|
||||
"value": self.value,
|
||||
"text": self.text,
|
||||
"imageSource": self.image,
|
||||
"iconName": self.icon,
|
||||
"iconColor": self.iconColor,
|
||||
"isIdenticon": self.isIdenticon
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
import NimQml, Tables, strutils, strformat
|
||||
|
||||
import location_menu_sub_item
|
||||
|
||||
type
|
||||
SubModelRole {.pure.} = enum
|
||||
Value = UserRole + 1
|
||||
Text
|
||||
Image
|
||||
Icon
|
||||
IconColor
|
||||
IsIdenticon
|
||||
|
||||
QtObject:
|
||||
type
|
||||
SubModel* = ref object of QAbstractListModel
|
||||
items: seq[SubItem]
|
||||
|
||||
proc delete*(self: SubModel) =
|
||||
for i in 0 ..< self.items.len:
|
||||
self.items[i].delete
|
||||
self.items = @[]
|
||||
self.QAbstractListModel.delete
|
||||
|
||||
proc setup(self: SubModel) =
|
||||
self.QAbstractListModel.setup
|
||||
|
||||
proc newSubModel*(): SubModel =
|
||||
new(result, delete)
|
||||
result.setup()
|
||||
|
||||
proc `$`*(self: SubModel): string =
|
||||
for i in 0 ..< self.items.len:
|
||||
result &= fmt"""
|
||||
[{i}]:({$self.items[i]})
|
||||
"""
|
||||
|
||||
proc countChanged*(self: SubModel) {.signal.}
|
||||
|
||||
proc count*(self: SubModel): int {.slot.} =
|
||||
self.items.len
|
||||
|
||||
QtProperty[int] count:
|
||||
read = count
|
||||
notify = countChanged
|
||||
|
||||
method rowCount(self: SubModel, index: QModelIndex = nil): int =
|
||||
return self.items.len
|
||||
|
||||
method roleNames(self: SubModel): Table[int, string] =
|
||||
{
|
||||
SubModelRole.Value.int:"value",
|
||||
SubModelRole.Text.int:"text",
|
||||
SubModelRole.Image.int:"imageSource",
|
||||
SubModelRole.Icon.int:"iconName",
|
||||
SubModelRole.IconColor.int:"iconColor",
|
||||
SubModelRole.IsIdenticon.int:"isIdenticon"
|
||||
}.toTable
|
||||
|
||||
method data(self: SubModel, index: QModelIndex, role: int): QVariant =
|
||||
if (not index.isValid):
|
||||
return
|
||||
|
||||
if (index.row < 0 or index.row >= self.items.len):
|
||||
return
|
||||
|
||||
let item = self.items[index.row]
|
||||
let enumRole = role.SubModelRole
|
||||
|
||||
case enumRole:
|
||||
of SubModelRole.Value:
|
||||
result = newQVariant(item.value)
|
||||
of SubModelRole.Text:
|
||||
result = newQVariant(item.text)
|
||||
of SubModelRole.Image:
|
||||
result = newQVariant(item.image)
|
||||
of SubModelRole.Icon:
|
||||
result = newQVariant(item.icon)
|
||||
of SubModelRole.IconColor:
|
||||
result = newQVariant(item.iconColor)
|
||||
of SubModelRole.IsIdenticon:
|
||||
result = newQVariant(item.isIdenticon)
|
||||
|
||||
proc setItems*(self: SubModel, items: seq[SubItem]) =
|
||||
self.beginResetModel()
|
||||
self.items = items
|
||||
self.endResetModel()
|
||||
|
||||
proc getItemForValue*(self: SubModel, value: string): SubItem =
|
||||
for i in self.items:
|
||||
if (i.value == value):
|
||||
return i
|
|
@ -0,0 +1,205 @@
|
|||
import NimQml
|
||||
import json, strutils, chronicles
|
||||
import io_interface
|
||||
import ../io_interface as delegate_interface
|
||||
import view, controller
|
||||
import location_menu_model, location_menu_item
|
||||
import location_menu_sub_model, location_menu_sub_item
|
||||
import result_model, result_item
|
||||
import ../../shared_models/message_item
|
||||
|
||||
import ../../../boot/app_sections_config as conf
|
||||
import ../../../../app_service/service/chat/service as chat_service
|
||||
import ../../../../app_service/service/community/service as community_service
|
||||
import ../../../../app_service/service/message/service as message_service
|
||||
|
||||
import eventemitter
|
||||
|
||||
export io_interface
|
||||
|
||||
logScope:
|
||||
topics = "app-search-module"
|
||||
|
||||
# Constants used in this module
|
||||
const SEARCH_MENU_LOCATION_CHAT_SECTION_NAME = "Chat"
|
||||
const SEARCH_RESULT_COMMUNITIES_SECTION_NAME = "Communities"
|
||||
const SEARCH_RESULT_CHATS_SECTION_NAME = "Chats"
|
||||
const SEARCH_RESULT_CHANNELS_SECTION_NAME = "Channels"
|
||||
const SEARCH_RESULT_MESSAGES_SECTION_NAME = "Messages"
|
||||
|
||||
type
|
||||
Module* = ref object of io_interface.AccessInterface
|
||||
delegate: delegate_interface.AccessInterface
|
||||
view: View
|
||||
viewVariant: QVariant
|
||||
controller: controller.AccessInterface
|
||||
moduleLoaded: bool
|
||||
|
||||
proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitter, chatService: chat_service.Service,
|
||||
communityService: community_service.Service, messageService: message_service.Service):
|
||||
Module =
|
||||
result = Module()
|
||||
result.delegate = delegate
|
||||
result.view = view.newView(result)
|
||||
result.viewVariant = newQVariant(result.view)
|
||||
result.controller = controller.newController(result, events, chatService, communityService, messageService)
|
||||
result.moduleLoaded = false
|
||||
|
||||
method delete*(self: Module) =
|
||||
self.view.delete
|
||||
self.viewVariant.delete
|
||||
self.controller.delete
|
||||
|
||||
method load*(self: Module) =
|
||||
self.controller.init()
|
||||
self.view.load()
|
||||
|
||||
method isLoaded*(self: Module): bool =
|
||||
return self.moduleLoaded
|
||||
|
||||
method viewDidLoad*(self: Module) =
|
||||
self.delegate.chatSectionDidLoad()
|
||||
|
||||
method getModuleAsVariant*(self: Module): QVariant =
|
||||
return self.viewVariant
|
||||
|
||||
proc buildLocationMenuForChat(self: Module): location_menu_item.Item =
|
||||
var item = location_menu_item.initItem(conf.CHAT_SECTION_ID, SEARCH_MENU_LOCATION_CHAT_SECTION_NAME, "", "chat", "",
|
||||
false)
|
||||
|
||||
let types = @[ChatType.OneToOne, ChatType.Public, ChatType.PrivateGroupChat]
|
||||
let displayedChats = self.controller.getChatDetailsForChatTypes(types)
|
||||
|
||||
var subItems: seq[location_menu_sub_item.SubItem]
|
||||
for c in displayedChats:
|
||||
var text = if(c.name.endsWith(".stateofus.eth")): c.name[0 .. ^15] else: c.name
|
||||
let subItem = location_menu_sub_item.initSubItem(c.id, text, c.identicon, "", c.color, c.identicon.len == 0)
|
||||
subItems.add(subItem)
|
||||
|
||||
item.setSubItems(subItems)
|
||||
return item
|
||||
|
||||
proc buildLocationMenuForCommunity(self: Module, community: CommunityDto): location_menu_item.Item =
|
||||
var item = location_menu_item.initItem(community.id, community.name, community.images.thumbnail, "", community.color,
|
||||
community.images.thumbnail.len == 0)
|
||||
|
||||
var subItems: seq[location_menu_sub_item.SubItem]
|
||||
let chats = self.controller.getAllChatsForCommunity(community.id)
|
||||
for c in chats:
|
||||
let chatDto = self.controller.getChatDetails(community.id, c.id)
|
||||
let subItem = location_menu_sub_item.initSubItem(chatDto.id, chatDto.name, chatDto.identicon, "", chatDto.color,
|
||||
chatDto.identicon.len == 0)
|
||||
subItems.add(subItem)
|
||||
|
||||
item.setSubItems(subItems)
|
||||
return item
|
||||
|
||||
method prepareLocationMenuModel*(self: Module) =
|
||||
var items: seq[location_menu_item.Item]
|
||||
items.add(self.buildLocationMenuForChat())
|
||||
|
||||
let communities = self.controller.getCommunities()
|
||||
for c in communities:
|
||||
items.add(self.buildLocationMenuForCommunity(c))
|
||||
|
||||
self.view.locationMenuModel().setItems(items)
|
||||
|
||||
method onActiveChatChange*(self: Module, sectionId: string, chatId: string) =
|
||||
self.controller.setActiveSectionIdAndChatId(sectionId, chatId)
|
||||
|
||||
method setSearchLocation*(self: Module, location: string, subLocation: string) =
|
||||
self.controller.setSearchLocation(location, subLocation)
|
||||
|
||||
method getSearchLocationObject*(self: Module): string =
|
||||
## This method returns location and subLocation with their details so we
|
||||
## may set initial search location on the side of qml.
|
||||
var jsonObject = %* {
|
||||
"location": "",
|
||||
"subLocation": ""
|
||||
}
|
||||
|
||||
if(self.controller.activeSectionId().len == 0):
|
||||
return Json.encode(jsonObject)
|
||||
|
||||
let item = self.view.locationMenuModel().getItemForValue(self.controller.activeSectionId())
|
||||
if(not item.isNil):
|
||||
jsonObject["location"] = item.toJsonNode()
|
||||
|
||||
if(self.controller.activeChatId().len > 0):
|
||||
let subItem = item.getSubItemForValue(self.controller.activeChatId())
|
||||
if(not subItem.isNil):
|
||||
jsonObject["subLocation"] = subItem.toJsonNode()
|
||||
|
||||
return Json.encode(jsonObject)
|
||||
|
||||
method searchMessages*(self: Module, searchTerm: string) =
|
||||
if (searchTerm.len == 0):
|
||||
self.view.searchResultModel().clear()
|
||||
return
|
||||
|
||||
self.controller.searchMessages(searchTerm)
|
||||
|
||||
method onSearchMessagesDone*(self: Module, messages: seq[MessageDto]) =
|
||||
var items: seq[result_item.Item]
|
||||
var channels: seq[result_item.Item]
|
||||
let myPublicKey = "" # This will be updated once we add userProfile #getSetting[string](Setting.PublicKey, "0x0")
|
||||
|
||||
# Add communities
|
||||
let communities = self.controller.getCommunities()
|
||||
for co in communities:
|
||||
if(self.controller.searchLocation().len == 0 and co.name.toLower.startsWith(self.controller.searchTerm().toLower)):
|
||||
let item = result_item.initItem(co.id, "", "", co.id, co.name, SEARCH_RESULT_COMMUNITIES_SECTION_NAME,
|
||||
co.images.thumbnail, co.color, "", "", co.images.thumbnail, co.color)
|
||||
|
||||
items.add(item)
|
||||
|
||||
# Add channels
|
||||
if(self.controller.searchSubLocation().len == 0 and self.controller.searchLocation().len == 0 or
|
||||
self.controller.searchLocation() == co.name):
|
||||
for c in co.chats:
|
||||
let chatDto = self.controller.getChatDetails(co.id, c.id)
|
||||
if(c.name.toLower.startsWith(self.controller.searchTerm().toLower)):
|
||||
let item = result_item.initItem(chatDto.id, "", "", chatDto.id, chatDto.name,
|
||||
SEARCH_RESULT_CHANNELS_SECTION_NAME, chatDto.identicon, chatDto.color, "", "", chatDto.identicon, chatDto.color,
|
||||
chatDto.identicon.len > 0)
|
||||
|
||||
channels.add(item)
|
||||
|
||||
# Add chats
|
||||
if(self.controller.searchLocation().len == 0 or self.controller.searchLocation() == conf.CHAT_SECTION_ID):
|
||||
let types = @[ChatType.OneToOne, ChatType.Public, ChatType.PrivateGroupChat]
|
||||
let displayedChats = self.controller.getChatDetailsForChatTypes(types)
|
||||
|
||||
for c in displayedChats:
|
||||
if(c.name.toLower.startsWith(self.controller.searchTerm().toLower)):
|
||||
let item = result_item.initItem(c.id, "", "", c.id, c.name, SEARCH_RESULT_CHATS_SECTION_NAME, c.identicon,
|
||||
c.color, "", "", c.identicon, c.color, c.identicon.len > 0)
|
||||
|
||||
items.add(item)
|
||||
|
||||
# Add channels in order as requested by the design
|
||||
items.add(channels)
|
||||
|
||||
# Add messages
|
||||
for m in messages:
|
||||
if (m.contentType.ContentType != ContentType.Message):
|
||||
continue
|
||||
|
||||
let chatDto = self.controller.getChatDetails("", m.chatId)
|
||||
let image = if(m.image.len > 0): m.image else: m.identicon
|
||||
|
||||
if(chatDto.communityId.len == 0):
|
||||
let item = result_item.initItem(m.id, m.text, $m.timestamp, m.`from`, m.`from`,
|
||||
SEARCH_RESULT_MESSAGES_SECTION_NAME, image, "", chatDto.name, "", chatDto.identicon, chatDto.color,
|
||||
chatDto.identicon.len == 0)
|
||||
|
||||
items.add(item)
|
||||
else:
|
||||
let community = self.controller.getCommunityById(chatDto.communityId)
|
||||
let item = result_item.initItem(m.id, m.text, $m.timestamp, m.`from`, m.`from`,
|
||||
SEARCH_RESULT_MESSAGES_SECTION_NAME, image, "", community.name, chatDto.name, community.images.thumbnail,
|
||||
community.color, community.images.thumbnail.len == 0)
|
||||
|
||||
items.add(item)
|
||||
|
||||
self.view.searchResultModel().set(items)
|
|
@ -0,0 +1,16 @@
|
|||
import NimQml
|
||||
|
||||
method delete*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method load*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method isLoaded*(self: AccessInterface): bool {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getModuleAsVariant*(self: AccessInterface): QVariant {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onActiveChatChange*(self: AccessInterface, sectionId: string, chatId: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
|
@ -0,0 +1,5 @@
|
|||
type
|
||||
AccessInterface* {.pure inheritable.} = ref object of RootObj
|
||||
|
||||
# Since nim doesn't support using concepts in second level nested types we
|
||||
# define delegate interfaces within access interface.
|
|
@ -0,0 +1,4 @@
|
|||
import ../../../../../app_service/service/message/dto/message
|
||||
|
||||
method onSearchMessagesDone*(self: AccessInterface, messages: seq[MessageDto]) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
|
@ -0,0 +1,14 @@
|
|||
method viewDidLoad*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method prepareLocationMenuModel*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method setSearchLocation*(self: AccessInterface, location: string, subLocation: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getSearchLocationObject*(self: AccessInterface): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method searchMessages*(self: AccessInterface, searchTerm: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
|
@ -0,0 +1,90 @@
|
|||
import strformat
|
||||
|
||||
type Item* = object
|
||||
itemId: string
|
||||
content: string
|
||||
time: string
|
||||
titleId: string
|
||||
title: string
|
||||
sectionName: string
|
||||
image: string
|
||||
color: string
|
||||
badgePrimaryText: string
|
||||
badgeSecondaryText: string
|
||||
badgeImage: string
|
||||
badgeIconColor: string
|
||||
badgeIsLetterIdenticon: bool
|
||||
|
||||
proc initItem*(itemId, content, time, titleId, title, sectionName: string, image, color, badgePrimaryText,
|
||||
badgeSecondaryText, badgeImage, badgeIconColor: string = "", badgeIsLetterIdenticon: bool = false):
|
||||
Item =
|
||||
|
||||
result.itemId = itemId
|
||||
result.content = content
|
||||
result.time = time
|
||||
result.titleId = titleId
|
||||
result.title = title
|
||||
result.sectionName = sectionName
|
||||
result.image = image
|
||||
result.color = color
|
||||
result.badgePrimaryText = badgePrimaryText
|
||||
result.badgeSecondaryText = badgeSecondaryText
|
||||
result.badgeImage = badgeImage
|
||||
result.badgeIconColor = badgeIconColor
|
||||
result.badgeIsLetterIdenticon = badgeIsLetterIdenticon
|
||||
|
||||
proc `$`*(self: Item): string =
|
||||
result = "SearchResultItem("
|
||||
result &= fmt"itemId:{self.itemId}, "
|
||||
result &= fmt"content:{self.content}, "
|
||||
result &= fmt"time:{self.time}, "
|
||||
result &= fmt"titleId:{self.titleId}, "
|
||||
result &= fmt"title:{self.title}"
|
||||
result &= fmt"sectionName:{self.sectionName}"
|
||||
result &= fmt"image:{self.image}"
|
||||
result &= fmt"color:{self.color}"
|
||||
result &= fmt"badgePrimaryText:{self.badgePrimaryText}"
|
||||
result &= fmt"badgeSecondaryText:{self.badgeSecondaryText}"
|
||||
result &= fmt"badgeImage:{self.badgeImage}"
|
||||
result &= fmt"badgeIconColor:{self.badgeIconColor}"
|
||||
result &= fmt"badgeIsLetterIdenticon:{self.badgeIsLetterIdenticon}"
|
||||
result &= ")"
|
||||
|
||||
method itemId*(self: Item): string {.inline.} =
|
||||
return self.itemId
|
||||
|
||||
method content*(self: Item): string {.inline.} =
|
||||
return self.content
|
||||
|
||||
method time*(self: Item): string {.inline.} =
|
||||
return self.time
|
||||
|
||||
method titleId*(self: Item): string {.inline.} =
|
||||
return self.titleId
|
||||
|
||||
method title*(self: Item): string {.inline.} =
|
||||
return self.title
|
||||
|
||||
method sectionName*(self: Item): string {.inline.} =
|
||||
return self.sectionName
|
||||
|
||||
method image*(self: Item): string {.inline.} =
|
||||
return self.image
|
||||
|
||||
method color*(self: Item): string {.inline.} =
|
||||
return self.color
|
||||
|
||||
method badgePrimaryText*(self: Item): string {.inline.} =
|
||||
return self.badgePrimaryText
|
||||
|
||||
method badgeSecondaryText*(self: Item): string {.inline.} =
|
||||
return self.badgeSecondaryText
|
||||
|
||||
method badgeImage*(self: Item): string {.inline.} =
|
||||
return self.badgeImage
|
||||
|
||||
method badgeIconColor*(self: Item): string {.inline.} =
|
||||
return self.badgeIconColor
|
||||
|
||||
method badgeIsLetterIdenticon*(self: Item): bool {.inline.} =
|
||||
return self.badgeIsLetterIdenticon
|
|
@ -0,0 +1,120 @@
|
|||
import NimQml, Tables, strutils
|
||||
|
||||
import result_item
|
||||
|
||||
type
|
||||
ModelRole {.pure.} = enum
|
||||
ItemId = UserRole + 1
|
||||
Content
|
||||
Time
|
||||
TitleId
|
||||
Title
|
||||
SectionName
|
||||
Image
|
||||
Color
|
||||
BadgePrimaryText
|
||||
BadgeSecondaryText
|
||||
BadgeImage
|
||||
BadgeIconColor
|
||||
BadgeIsLetterIdenticon
|
||||
|
||||
QtObject:
|
||||
type
|
||||
Model* = ref object of QAbstractListModel
|
||||
resultList: seq[Item]
|
||||
|
||||
proc delete(self: Model) =
|
||||
self.QAbstractListModel.delete
|
||||
|
||||
proc setup(self: Model) =
|
||||
self.QAbstractListModel.setup
|
||||
|
||||
proc newModel*(): Model =
|
||||
new(result, delete)
|
||||
result.setup()
|
||||
|
||||
#################################################
|
||||
# Properties
|
||||
#################################################
|
||||
|
||||
proc countChanged*(self: Model) {.signal.}
|
||||
|
||||
proc count*(self: Model): int {.slot.} =
|
||||
self.resultList.len
|
||||
|
||||
QtProperty[int] count:
|
||||
read = count
|
||||
notify = countChanged
|
||||
|
||||
method rowCount(self: Model, index: QModelIndex = nil): int =
|
||||
return self.resultList.len
|
||||
|
||||
method roleNames(self: Model): Table[int, string] =
|
||||
{
|
||||
ModelRole.ItemId.int:"itemId",
|
||||
ModelRole.Content.int:"content",
|
||||
ModelRole.Time.int:"time",
|
||||
ModelRole.TitleId.int:"titleId",
|
||||
ModelRole.Title.int:"title",
|
||||
ModelRole.SectionName.int:"sectionName",
|
||||
ModelRole.Image.int:"image",
|
||||
ModelRole.Color.int:"color",
|
||||
ModelRole.BadgePrimaryText.int:"badgePrimaryText",
|
||||
ModelRole.BadgeSecondaryText.int:"badgeSecondaryText",
|
||||
ModelRole.BadgeImage.int:"badgeImage",
|
||||
ModelRole.BadgeIconColor.int:"badgeIconColor",
|
||||
ModelRole.BadgeIsLetterIdenticon.int:"badgeIsLetterIdenticon"
|
||||
}.toTable
|
||||
|
||||
method data(self: Model, index: QModelIndex, role: int): QVariant =
|
||||
if (not index.isValid):
|
||||
return
|
||||
|
||||
if (index.row < 0 or index.row >= self.resultList.len):
|
||||
return
|
||||
|
||||
let item = self.resultList[index.row]
|
||||
let enumRole = role.ModelRole
|
||||
|
||||
case enumRole:
|
||||
of ModelRole.ItemId:
|
||||
result = newQVariant(item.itemId)
|
||||
of ModelRole.Content:
|
||||
result = newQVariant(item.content)
|
||||
of ModelRole.Time:
|
||||
result = newQVariant(item.time)
|
||||
of ModelRole.TitleId:
|
||||
result = newQVariant(item.titleId)
|
||||
of ModelRole.Title:
|
||||
result = newQVariant(item.title)
|
||||
of ModelRole.SectionName:
|
||||
result = newQVariant(item.sectionName)
|
||||
of ModelRole.Image:
|
||||
result = newQVariant(item.image)
|
||||
of ModelRole.Color:
|
||||
result = newQVariant(item.color)
|
||||
of ModelRole.BadgePrimaryText:
|
||||
result = newQVariant(item.badgePrimaryText)
|
||||
of ModelRole.BadgeSecondaryText:
|
||||
result = newQVariant(item.badgeSecondaryText)
|
||||
of ModelRole.BadgeImage:
|
||||
result = newQVariant(item.badgeImage)
|
||||
of ModelRole.BadgeIconColor:
|
||||
result = newQVariant(item.badgeIconColor)
|
||||
of ModelRole.BadgeIsLetterIdenticon:
|
||||
result = newQVariant(item.badgeIsLetterIdentIcon)
|
||||
|
||||
proc add*(self: Model, item: Item) =
|
||||
self.beginInsertRows(newQModelIndex(), self.resultList.len, self.resultList.len)
|
||||
self.resultList.add(item)
|
||||
self.endInsertRows()
|
||||
|
||||
proc set*(self: Model, items: seq[Item]) =
|
||||
self.beginResetModel()
|
||||
self.resultList = items
|
||||
self.endResetModel()
|
||||
|
||||
proc clear*(self: Model) =
|
||||
self.beginResetModel()
|
||||
self.resultList = @[]
|
||||
self.endResetModel()
|
|
@ -0,0 +1,61 @@
|
|||
import NimQml
|
||||
import result_model, location_menu_model
|
||||
import io_interface
|
||||
|
||||
QtObject:
|
||||
type
|
||||
View* = ref object of QObject
|
||||
delegate: io_interface.AccessInterface
|
||||
searchResultModel: result_model.Model
|
||||
searchResultModelVariant: QVariant
|
||||
locationMenuModel: location_menu_model.Model
|
||||
locationMenuModelVariant: QVariant
|
||||
|
||||
proc delete*(self: View) =
|
||||
self.searchResultModel.delete
|
||||
self.searchResultModelVariant.delete
|
||||
self.locationMenuModel.delete
|
||||
self.locationMenuModelVariant.delete
|
||||
self.QObject.delete
|
||||
|
||||
proc newView*(delegate: io_interface.AccessInterface): View =
|
||||
new(result, delete)
|
||||
result.QObject.setup
|
||||
result.delegate = delegate
|
||||
result.searchResultModel = result_model.newModel()
|
||||
result.searchResultModelVariant = newQVariant(result.searchResultModel)
|
||||
result.locationMenuModel = location_menu_model.newModel()
|
||||
result.locationMenuModelVariant = newQVariant(result.locationMenuModel)
|
||||
|
||||
proc load*(self: View) =
|
||||
self.delegate.viewDidLoad()
|
||||
|
||||
proc searchResultModel*(self: View): result_model.Model =
|
||||
return self.searchResultModel
|
||||
|
||||
proc locationMenuModel*(self: View): location_menu_model.Model =
|
||||
return self.locationMenuModel
|
||||
|
||||
proc getSearchResultModel*(self: View): QVariant {.slot.} =
|
||||
return newQVariant(self.searchResultModel)
|
||||
|
||||
QtProperty[QVariant] resultModel:
|
||||
read = getSearchResultModel
|
||||
|
||||
proc getLocationMenuModel*(self: View): QVariant {.slot.} =
|
||||
newQVariant(self.locationMenuModel)
|
||||
|
||||
QtProperty[QVariant] locationMenuModel:
|
||||
read = getLocationMenuModel
|
||||
|
||||
proc prepareLocationMenuModel*(self: View) {.slot.} =
|
||||
self.delegate.prepareLocationMenuModel()
|
||||
|
||||
proc setSearchLocation*(self: View, location: string = "", subLocation: string = "") {.slot.} =
|
||||
self.delegate.setSearchLocation(location, subLocation)
|
||||
|
||||
proc getSearchLocationObject*(self: View): string {.slot.} =
|
||||
self.delegate.getSearchLocationObject()
|
||||
|
||||
proc searchMessages*(self: View, searchTerm: string) {.slot.} =
|
||||
self.delegate.searchMessages(searchTerm)
|
|
@ -8,6 +8,7 @@ import chat_section/module as chat_section_module
|
|||
import wallet_section/module as wallet_section_module
|
||||
import browser_section/module as browser_section_module
|
||||
import profile_section/module as profile_section_module
|
||||
import app_search/module as app_search_module
|
||||
|
||||
import ../../../app_service/service/keychain/service as keychain_service
|
||||
import ../../../app_service/service/chat/service as chat_service
|
||||
|
@ -46,6 +47,7 @@ type
|
|||
walletSectionModule: wallet_section_module.AccessInterface
|
||||
browserSectionModule: browser_section_module.AccessInterface
|
||||
profileSectionModule: profile_section_module.AccessInterface
|
||||
appSearchModule: app_search_module.AccessInterface
|
||||
moduleLoaded: bool
|
||||
|
||||
proc newModule*[T](
|
||||
|
@ -90,6 +92,7 @@ proc newModule*[T](
|
|||
dappPermissionsService, providerService)
|
||||
result.profileSectionModule = profile_section_module.newModule(result, events, accountsService, settingsService,
|
||||
profileService, contactsService, aboutService, languageService, mnemonicService, privacyService)
|
||||
result.appSearchModule = app_search_module.newModule(result, events, chatService, communityService, messageService)
|
||||
|
||||
method delete*[T](self: Module[T]) =
|
||||
self.chatSectionModule.delete
|
||||
|
@ -99,6 +102,7 @@ method delete*[T](self: Module[T]) =
|
|||
self.communitySectionsModule.clear
|
||||
self.walletSectionModule.delete
|
||||
self.browserSectionModule.delete
|
||||
self.appSearchModule.delete
|
||||
self.view.delete
|
||||
self.viewVariant.delete
|
||||
self.controller.delete
|
||||
|
@ -197,6 +201,7 @@ method load*[T](self: Module[T], events: EventEmitter, chatService: chat_service
|
|||
# self.timelineSectionModule.load()
|
||||
# self.nodeManagementSectionModule.load()
|
||||
self.profileSectionModule.load()
|
||||
self.appSearchModule.load()
|
||||
|
||||
# Set active section on app start
|
||||
self.setActiveSection(activeSection)
|
||||
|
@ -304,4 +309,7 @@ method getCommunitySectionModule*[T](self: Module[T], communityId: string): QVar
|
|||
return self.communitySectionsModule[communityId].getModuleAsVariant()
|
||||
|
||||
method onActiveChatChange*[T](self: Module[T], sectionId: string, chatId: string) =
|
||||
discard
|
||||
self.appSearchModule.onActiveChatChange(sectionId, chatId)
|
||||
|
||||
method getAppSearchModule*[T](self: Module[T]): QVariant =
|
||||
self.appSearchModule.getModuleAsVariant()
|
|
@ -17,4 +17,7 @@ method getChatSectionModule*(self: AccessInterface): QVariant {.base.} =
|
|||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getCommunitySectionModule*(self: AccessInterface, communityId: string): QVariant {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getAppSearchModule*(self: AccessInterface): QVariant {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
|
@ -117,4 +117,10 @@ QtObject:
|
|||
return communityVariant
|
||||
|
||||
proc getChatSectionModule*(self: View): QVariant {.slot.} =
|
||||
return self.delegate.getChatSectionModule()
|
||||
return self.delegate.getChatSectionModule()
|
||||
|
||||
proc getAppSearchModule(self: View): QVariant {.slot.} =
|
||||
return self.delegate.getAppSearchModule()
|
||||
|
||||
QtProperty[QVariant] appSearchModule:
|
||||
read = getAppSearchModule
|
|
@ -32,7 +32,7 @@ method init*(self: Service) =
|
|||
proc(x: JsonNode): CommunityDto = x.toCommunityDto())
|
||||
|
||||
for community in communities:
|
||||
self.communities[community.id] = community
|
||||
self.communities[community.id] = community
|
||||
|
||||
except Exception as e:
|
||||
let errDesription = e.msg
|
||||
|
@ -42,6 +42,13 @@ method init*(self: Service) =
|
|||
method getCommunities*(self: Service): seq[CommunityDto] =
|
||||
return toSeq(self.communities.values)
|
||||
|
||||
method getCommunityById*(self: Service, communityId: string): CommunityDto =
|
||||
if(not self.communities.hasKey(communityId)):
|
||||
error "error: requested community doesn't exists"
|
||||
return
|
||||
|
||||
return self.communities[communityId]
|
||||
|
||||
method getCommunityIds*(self: Service): seq[string] =
|
||||
return toSeq(self.communities.keys)
|
||||
|
||||
|
@ -63,7 +70,7 @@ proc sortDesc[T](t1, t2: T): int =
|
|||
|
||||
method getCategories*(self: Service, communityId: string, order: SortOrder = SortOrder.Ascending): seq[Category] =
|
||||
if(not self.communities.contains(communityId)):
|
||||
error "trying to get community for an unexisting community id"
|
||||
error "trying to get community categories for an unexisting community id"
|
||||
return
|
||||
|
||||
result = self.communities[communityId].categories
|
||||
|
@ -73,8 +80,11 @@ method getCategories*(self: Service, communityId: string, order: SortOrder = Sor
|
|||
result.sort(sortDesc[Category])
|
||||
|
||||
method getChats*(self: Service, communityId: string, categoryId = "", order = SortOrder.Ascending): seq[Chat] =
|
||||
## By default returns chats which don't belong to any category, for passed `communityId`.
|
||||
## If `categoryId` is set then only chats belonging to that category for passed `communityId` will be returned.
|
||||
## Returned chats are sorted by position following set `order` parameter.
|
||||
if(not self.communities.contains(communityId)):
|
||||
error "trying to get community for an unexisting community id"
|
||||
error "trying to get community chats for an unexisting community id"
|
||||
return
|
||||
|
||||
for chat in self.communities[communityId].chats:
|
||||
|
@ -87,3 +97,17 @@ method getChats*(self: Service, communityId: string, categoryId = "", order = So
|
|||
result.sort(sortAsc[Chat])
|
||||
else:
|
||||
result.sort(sortDesc[Chat])
|
||||
|
||||
method getAllChats*(self: Service, communityId: string, order = SortOrder.Ascending): seq[Chat] =
|
||||
## Returns all chats belonging to the community with passed `communityId`, sorted by position.
|
||||
## Returned chats are sorted by position following set `order` parameter.
|
||||
if(not self.communities.contains(communityId)):
|
||||
error "trying to get all community chats for an unexisting community id"
|
||||
return
|
||||
|
||||
result = self.communities[communityId].chats
|
||||
|
||||
if(order == SortOrder.Ascending):
|
||||
result.sort(sortAsc[Chat])
|
||||
else:
|
||||
result.sort(sortDesc[Chat])
|
|
@ -16,6 +16,9 @@ method init*(self: ServiceInterface) {.base.} =
|
|||
method getCommunities*(self: ServiceInterface): seq[CommunityDto] {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getCommunityById*(self: ServiceInterface, communityId: string): CommunityDto {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getCommunityIds*(self: ServiceInterface): seq[string] {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
|
@ -25,4 +28,7 @@ method getCategories*(self: ServiceInterface, communityId: string, order: SortOr
|
|||
|
||||
method getChats*(self: ServiceInterface, communityId: string, categoryId = "", order = SortOrder.Ascending): seq[Chat]
|
||||
{.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getAllChats*(self: ServiceInterface, communityId: string, order = SortOrder.Ascending): seq[Chat] {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
|
@ -43,4 +43,52 @@ const asyncFetchChatMessagesTask: Task = proc(argEncoded: string) {.gcsafe, nimc
|
|||
"reactions": reactionsArr
|
||||
}
|
||||
|
||||
arg.finish(responseJson)
|
||||
|
||||
#################################################
|
||||
# Async search messages
|
||||
#################################################
|
||||
|
||||
type
|
||||
AsyncSearchMessagesTaskArg = ref object of QObjectTaskArg
|
||||
searchTerm: string
|
||||
caseSensitive: bool
|
||||
|
||||
#################################################
|
||||
# Async search messages in chat with chatId by term
|
||||
#################################################
|
||||
type
|
||||
AsyncSearchMessagesInChatTaskArg = ref object of AsyncSearchMessagesTaskArg
|
||||
chatId: string
|
||||
|
||||
const asyncSearchMessagesInChatTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let arg = decode[AsyncSearchMessagesInChatTaskArg](argEncoded)
|
||||
|
||||
let response = status_go.fetchAllMessagesFromChatWhichMatchTerm(arg.chatId, arg.searchTerm, arg.caseSensitive)
|
||||
|
||||
let responseJson = %*{
|
||||
"chatId": arg.chatId,
|
||||
"messages": response.result
|
||||
}
|
||||
arg.finish(responseJson)
|
||||
|
||||
#################################################
|
||||
# Async search messages in chats/channels and communities by term
|
||||
#################################################
|
||||
type
|
||||
AsyncSearchMessagesInChatsAndCommunitiesTaskArg = ref object of AsyncSearchMessagesTaskArg
|
||||
communityIds: seq[string]
|
||||
chatIds: seq[string]
|
||||
|
||||
const asyncSearchMessagesInChatsAndCommunitiesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let arg = decode[AsyncSearchMessagesInChatsAndCommunitiesTaskArg](argEncoded)
|
||||
|
||||
let response = status_go.fetchAllMessagesFromChatsAndCommunitiesWhichMatchTerm(arg.communityIds, arg.chatIds,
|
||||
arg.searchTerm, arg.caseSensitive)
|
||||
|
||||
let responseJson = %*{
|
||||
"communityIds": arg.communityIds,
|
||||
"chatIds": arg.chatIds,
|
||||
"messages": response.result
|
||||
}
|
||||
arg.finish(responseJson)
|
|
@ -24,8 +24,12 @@ const MESSAGES_PER_PAGE = 20
|
|||
const SIGNAL_MESSAGES_LOADED* = "new-messagesLoaded" #Once we are done with refactoring we should remove "new-" from all signals
|
||||
const SIGNAL_MESSAGE_PINNED* = "new-messagePinned"
|
||||
const SIGNAL_MESSAGE_UNPINNED* = "new-messageUnpinned"
|
||||
const SIGNAL_SEARCH_MESSAGES_LOADED* = "new-searchMessagesLoaded"
|
||||
|
||||
type
|
||||
SearchMessagesLoadedArgs* = ref object of Args
|
||||
messages*: seq[MessageDto]
|
||||
|
||||
MessagesLoadedArgs* = ref object of Args
|
||||
chatId*: string
|
||||
messages*: seq[MessageDto]
|
||||
|
@ -66,7 +70,7 @@ QtObject:
|
|||
|
||||
return self.pinnedMsgCursor[chatId]
|
||||
|
||||
proc onLoadMoreMessagesForChat*(self: Service, response: string) {.slot.} =
|
||||
proc onAsyncLoadMoreMessagesForChat*(self: Service, response: string) {.slot.} =
|
||||
let responseObj = response.parseJson
|
||||
if (responseObj.kind != JObject):
|
||||
info "load more messages response is not a json object"
|
||||
|
@ -112,15 +116,15 @@ QtObject:
|
|||
self.events.emit(SIGNAL_MESSAGES_LOADED, data)
|
||||
|
||||
|
||||
proc loadMoreMessagesForChat*(self: Service, chatId: string) =
|
||||
proc asyncLoadMoreMessagesForChat*(self: Service, chatId: string) =
|
||||
if (chatId.len == 0):
|
||||
error "empty chat id", methodName="loadMoreMessagesForChat"
|
||||
error "empty chat id", methodName="asyncLoadMoreMessagesForChat"
|
||||
return
|
||||
|
||||
let arg = AsyncFetchChatMessagesTaskArg(
|
||||
tptr: cast[ByteAddress](asyncFetchChatMessagesTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: "onLoadMoreMessagesForChat",
|
||||
slot: "onAsyncLoadMoreMessagesForChat",
|
||||
chatId: chatId,
|
||||
msgCursor: self.getCurrentMessageCursor(chatId),
|
||||
pinnedMsgCursor: self.getCurrentPinnedMessageCursor(chatId),
|
||||
|
@ -129,12 +133,12 @@ QtObject:
|
|||
|
||||
self.threadpool.start(arg)
|
||||
|
||||
proc loadInitialMessagesForChat*(self: Service, chatId: string) =
|
||||
proc asyncLoadInitialMessagesForChat*(self: Service, chatId: string) =
|
||||
if(self.getCurrentMessageCursor(chatId).len > 0):
|
||||
return
|
||||
|
||||
# we're here if initial messages are not loaded yet
|
||||
self.loadMoreMessagesForChat(chatId)
|
||||
self.asyncLoadMoreMessagesForChat(chatId)
|
||||
|
||||
|
||||
proc addReaction*(self: Service, chatId: string, messageId: string, emojiId: int):
|
||||
|
@ -207,4 +211,75 @@ QtObject:
|
|||
|
||||
except Exception as e:
|
||||
result.error = e.msg
|
||||
error "error: ", methodName="getDetailsForMessage", errName = e.name, errDesription = e.msg
|
||||
error "error: ", methodName="getDetailsForMessage", errName = e.name, errDesription = e.msg
|
||||
|
||||
proc finishAsyncSearchMessagesWithError*(self: Service, errorMessage: string) =
|
||||
error "error: ", methodName="onAsyncSearchMessages", errDescription = errorMessage
|
||||
self.events.emit(SIGNAL_SEARCH_MESSAGES_LOADED, SearchMessagesLoadedArgs())
|
||||
|
||||
proc onAsyncSearchMessages*(self: Service, response: string) {.slot.} =
|
||||
let responseObj = response.parseJson
|
||||
if (responseObj.kind != JObject):
|
||||
self.finishAsyncSearchMessagesWithError("search messages response is not an json object")
|
||||
return
|
||||
|
||||
var messagesObj: JsonNode
|
||||
if (not responseObj.getProp("messages", messagesObj)):
|
||||
self.finishAsyncSearchMessagesWithError("search messages response doesn't contain messages property")
|
||||
return
|
||||
|
||||
var messagesArray: JsonNode
|
||||
if (not messagesObj.getProp("messages", messagesArray)):
|
||||
self.finishAsyncSearchMessagesWithError("search messages response doesn't contain messages array")
|
||||
return
|
||||
|
||||
if (messagesArray.kind != JArray):
|
||||
self.finishAsyncSearchMessagesWithError("expected messages json array is not of JArray type")
|
||||
return
|
||||
|
||||
var messages = map(messagesArray.getElems(), proc(x: JsonNode): MessageDto = x.toMessageDto())
|
||||
|
||||
let data = SearchMessagesLoadedArgs(messages: messages)
|
||||
self.events.emit(SIGNAL_SEARCH_MESSAGES_LOADED, data)
|
||||
|
||||
proc asyncSearchMessages*(self: Service, chatId: string, searchTerm: string, caseSensitive: bool) =
|
||||
## Asynchronous search for messages which contain the searchTerm and belong to the chat with chatId.
|
||||
if (chatId.len == 0):
|
||||
error "error: empty channel id set for fetching more messages", methodName="asyncSearchMessages"
|
||||
return
|
||||
|
||||
if (searchTerm.len == 0):
|
||||
return
|
||||
|
||||
let arg = AsyncSearchMessagesInChatTaskArg(
|
||||
tptr: cast[ByteAddress](asyncSearchMessagesInChatTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: "onAsyncSearchMessages",
|
||||
chatId: chatId,
|
||||
searchTerm: searchTerm,
|
||||
caseSensitive: caseSensitive
|
||||
)
|
||||
self.threadpool.start(arg)
|
||||
|
||||
proc asyncSearchMessages*(self: Service, communityIds: seq[string], chatIds: seq[string], searchTerm: string,
|
||||
caseSensitive: bool) =
|
||||
## Asynchronous search for messages which contain the searchTerm and belong to any chat/channel from chatIds array
|
||||
## or any channel of community from communityIds array.
|
||||
|
||||
if (communityIds.len == 0 and chatIds.len == 0):
|
||||
error "either community ids or chat ids or both must be set", methodName="asyncSearchMessages"
|
||||
return
|
||||
|
||||
if (searchTerm.len == 0):
|
||||
return
|
||||
|
||||
let arg = AsyncSearchMessagesInChatsAndCommunitiesTaskArg(
|
||||
tptr: cast[ByteAddress](asyncSearchMessagesInChatsAndCommunitiesTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: "onAsyncSearchMessages",
|
||||
communityIds: communityIds,
|
||||
chatIds: chatIds,
|
||||
searchTerm: searchTerm,
|
||||
caseSensitive: caseSensitive
|
||||
)
|
||||
self.threadpool.start(arg)
|
|
@ -52,21 +52,21 @@ StatusAppThreePanelLayout {
|
|||
StatusSearchLocationMenu {
|
||||
id: searchPopupMenu
|
||||
searchPopup: searchPopup
|
||||
locationModel: root.rootStore.chatsModelInst.messageSearchViewController.locationMenuModel
|
||||
locationModel: mainModule.appSearchModule.locationMenuModel
|
||||
|
||||
onItemClicked: {
|
||||
root.rootStore.chatsModelInst.messageSearchViewController.setSearchLocation(firstLevelItemValue, secondLevelItemValue)
|
||||
mainModule.appSearchModule.setSearchLocation(firstLevelItemValue, secondLevelItemValue)
|
||||
if(searchPopup.searchText !== "")
|
||||
searchMessages(searchPopup.searchText)
|
||||
}
|
||||
}
|
||||
|
||||
property var searchMessages: Backpressure.debounce(searchPopup, 400, function (value) {
|
||||
root.rootStore.chatsModelInst.messageSearchViewController.searchMessages(value)
|
||||
mainModule.appSearchModule.searchMessages(value)
|
||||
})
|
||||
|
||||
Connections {
|
||||
target: root.rootStore.chatsModelInst.messageSearchViewController.locationMenuModel
|
||||
target: mainModule.appSearchModule.locationMenuModel
|
||||
onModelAboutToBeReset: {
|
||||
for (var i = 2; i <= searchPopupMenu.count; i++) {
|
||||
//clear menu
|
||||
|
@ -84,7 +84,7 @@ StatusAppThreePanelLayout {
|
|||
defaultSearchLocationText: qsTr("Anywhere")
|
||||
|
||||
searchOptionsPopupMenu: searchPopupMenu
|
||||
searchResults: root.rootStore.chatsModelInst.messageSearchViewController.resultModel
|
||||
searchResults: mainModule.appSearchModule.resultModel
|
||||
|
||||
formatTimestampFn: function (ts) {
|
||||
return new Date(parseInt(ts, 10)).toLocaleString(Qt.locale(localAppSettings.locale))
|
||||
|
@ -104,9 +104,9 @@ StatusAppThreePanelLayout {
|
|||
onOpened: {
|
||||
searchPopup.resetSearchSelection();
|
||||
searchPopup.forceActiveFocus()
|
||||
root.rootStore.chatsModelInst.messageSearchViewController.prepareLocationMenuModel()
|
||||
mainModule.appSearchModule.prepareLocationMenuModel()
|
||||
|
||||
const jsonObj = root.rootStore.chatsModelInst.messageSearchViewController.getSearchLocationObject()
|
||||
const jsonObj = mainModule.appSearchModule.getSearchLocationObject()
|
||||
|
||||
if (!jsonObj) {
|
||||
return
|
||||
|
@ -115,7 +115,7 @@ StatusAppThreePanelLayout {
|
|||
let obj = JSON.parse(jsonObj)
|
||||
if (obj.location === "") {
|
||||
if(obj.subLocation === "") {
|
||||
root.rootStore.chatsModelInst.messageSearchViewController.setSearchLocation("", "")
|
||||
mainModule.appSearchModule.setSearchLocation("", "")
|
||||
}
|
||||
else {
|
||||
searchPopup.setSearchSelection(obj.subLocation.text,
|
||||
|
@ -125,7 +125,7 @@ StatusAppThreePanelLayout {
|
|||
obj.subLocation.iconName,
|
||||
obj.subLocation.identiconColor)
|
||||
|
||||
root.rootStore.chatsModelInst.messageSearchViewController.setSearchLocation("", obj.subLocation.value)
|
||||
mainModule.appSearchModule.setSearchLocation("", obj.subLocation.value)
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -137,7 +137,7 @@ StatusAppThreePanelLayout {
|
|||
obj.subLocation.iconName,
|
||||
obj.subLocation.identiconColor)
|
||||
|
||||
root.rootStore.chatsModelInst.messageSearchViewController.setSearchLocation(obj.location.value, obj.subLocation.value)
|
||||
mainModule.appSearchModule.setSearchLocation(obj.location.value, obj.subLocation.value)
|
||||
}
|
||||
else {
|
||||
searchPopup.setSearchSelection(obj.location.title,
|
||||
|
@ -147,7 +147,7 @@ StatusAppThreePanelLayout {
|
|||
obj.location.iconName,
|
||||
obj.location.identiconColor)
|
||||
|
||||
root.rootStore.chatsModelInst.messageSearchViewController.setSearchLocation(obj.location.value, obj.subLocation.value)
|
||||
mainModule.appSearchModule.setSearchLocation(obj.location.value, obj.subLocation.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue