feature(@desktop/chat): implement search results for communities, channels
Added a part for fetching messages from multiple chats/channels/communities. Fixes: #2934
This commit is contained in:
parent
4f8b072f10
commit
e2628338de
|
@ -1,7 +1,6 @@
|
|||
import NimQml, Tables
|
||||
import ../../../status/chat/[chat, message]
|
||||
import ../../../status/status
|
||||
import ../../../status/ens
|
||||
import ../../../status/accounts
|
||||
import strutils
|
||||
|
||||
|
@ -39,19 +38,6 @@ QtObject:
|
|||
result.status = status
|
||||
result.setup()
|
||||
|
||||
proc userNameOrAlias(self: ChannelsList, pubKey: string): string =
|
||||
if self.status.chat.contacts.hasKey(pubKey):
|
||||
return ens.userNameOrAlias(self.status.chat.contacts[pubKey])
|
||||
generateAlias(pubKey)
|
||||
|
||||
proc chatName(self: ChannelsList, chatItem: Chat): string =
|
||||
if not chatItem.chatType.isOneToOne: return chatItem.name
|
||||
if self.status.chat.contacts.hasKey(chatItem.id) and self.status.chat.contacts[chatItem.id].hasNickname():
|
||||
return self.status.chat.contacts[chatItem.id].localNickname
|
||||
if chatItem.ensName != "":
|
||||
return "@" & userName(chatItem.ensName).userName(true)
|
||||
return self.userNameOrAlias(chatItem.id)
|
||||
|
||||
method rowCount*(self: ChannelsList, index: QModelIndex = nil): int = self.chats.len
|
||||
|
||||
proc renderBlock(self: ChannelsList, message: Message): string
|
||||
|
@ -66,7 +52,7 @@ QtObject:
|
|||
|
||||
let chatItemRole = role.ChannelsRoles
|
||||
case chatItemRole:
|
||||
of ChannelsRoles.Name: result = newQVariant(self.chatName(chatItem))
|
||||
of ChannelsRoles.Name: result = newQVariant(self.status.chat.chatName(chatItem))
|
||||
of ChannelsRoles.Timestamp: result = newQVariant($chatItem.timestamp)
|
||||
of ChannelsRoles.LastMessage: result = newQVariant(self.renderBlock(chatItem.lastMessage))
|
||||
of ChannelsRoles.ContentType: result = newQVariant(chatItem.lastMessage.contentType.int)
|
||||
|
@ -136,6 +122,13 @@ QtObject:
|
|||
if chat.id == chatId:
|
||||
return chat
|
||||
|
||||
proc getChannelById*(self: ChannelsList, chatId: string, found: var bool): Chat =
|
||||
found = false
|
||||
for chat in self.chats:
|
||||
if chat.id == chatId:
|
||||
found = true
|
||||
return chat
|
||||
|
||||
proc getChannelByName*(self: ChannelsList, name: string): Chat =
|
||||
for chat in self.chats:
|
||||
if chat.name == name:
|
||||
|
@ -213,7 +206,7 @@ QtObject:
|
|||
|
||||
proc renderInline(self: ChannelsList, elem: TextItem): string =
|
||||
case elem.textType:
|
||||
of "mention": result = self.userNameOrAlias(elem.literal)
|
||||
of "mention": result = self.status.chat.userNameOrAlias(elem.literal)
|
||||
of "link": result = elem.destination
|
||||
else: result = escape_html(elem.literal)
|
||||
|
||||
|
|
|
@ -313,4 +313,15 @@ QtObject:
|
|||
return
|
||||
self.communities[comIndex].recalculateMentions()
|
||||
let index = self.createIndex(comIndex, 0, nil)
|
||||
self.dataChanged(index, index, @[CommunityRoles.UnviewedMentionsCount.int])
|
||||
self.dataChanged(index, index, @[CommunityRoles.UnviewedMentionsCount.int])
|
||||
|
||||
proc getChannelByIdAndBelongingCommunity*(self: CommunityList, chatId: string,
|
||||
chat: var Chat, community: var Community): bool =
|
||||
for co in self.communities:
|
||||
for ch in co.chats:
|
||||
if ch.id == chatId:
|
||||
community = co
|
||||
chat = ch
|
||||
return true
|
||||
|
||||
return false
|
|
@ -0,0 +1,6 @@
|
|||
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"
|
|
@ -0,0 +1,82 @@
|
|||
import json, strformat
|
||||
|
||||
import ../../../../status/chat/[chat]
|
||||
import ../../../../status/[status]
|
||||
|
||||
import location_menu_sub_model, location_menu_sub_item
|
||||
|
||||
type
|
||||
MessageSearchLocationMenuItem* = object
|
||||
value: string
|
||||
title: string
|
||||
imageSource: string
|
||||
iconName: string
|
||||
iconColor: string
|
||||
isIdenticon: bool
|
||||
subItems: MessageSearchLocationMenuSubModel
|
||||
|
||||
proc initMessageSearchLocationMenuItem*(status: Status,
|
||||
value, title, imageSource: string,
|
||||
iconName, iconColor: string = "",
|
||||
isIdenticon: bool = true): MessageSearchLocationMenuItem =
|
||||
result.value = value
|
||||
result.title = title
|
||||
result.imageSource = imageSource
|
||||
result.iconName = iconName
|
||||
result.iconColor = iconColor
|
||||
result.isIdenticon = isIdenticon
|
||||
result.subItems = newMessageSearchLocationMenuSubModel(status)
|
||||
|
||||
proc `$`*(self: MessageSearchLocationMenuItem): string =
|
||||
result = fmt"""MenuItem(
|
||||
value: {self.value},
|
||||
title: {self.title},
|
||||
isIdenticon: {self.isIdenticon},
|
||||
iconName: {self.iconName},
|
||||
iconColor: {self.iconColor},
|
||||
imageSource:{self.imageSource}
|
||||
subItems:[
|
||||
{$self.subItems}
|
||||
]"""
|
||||
|
||||
proc getValue*(self: MessageSearchLocationMenuItem): string =
|
||||
return self.value
|
||||
|
||||
proc getTitle*(self: MessageSearchLocationMenuItem): string =
|
||||
return self.title
|
||||
|
||||
proc getImageSource*(self: MessageSearchLocationMenuItem): string =
|
||||
return self.imageSource
|
||||
|
||||
proc getIconName*(self: MessageSearchLocationMenuItem): string =
|
||||
return self.iconName
|
||||
|
||||
proc getIconColor*(self: MessageSearchLocationMenuItem): string =
|
||||
return self.iconColor
|
||||
|
||||
proc getIsIdenticon*(self: MessageSearchLocationMenuItem): bool =
|
||||
return self.isIdenticon
|
||||
|
||||
proc getSubItems*(self: MessageSearchLocationMenuItem):
|
||||
MessageSearchLocationMenuSubModel =
|
||||
self.subItems
|
||||
|
||||
proc prepareSubItems*(self: MessageSearchLocationMenuItem, chats: seq[Chat],
|
||||
isCommunityChannel: bool) =
|
||||
self.subItems.prepareItems(chats, isCommunityChannel)
|
||||
|
||||
proc getLocationSubItemForChatId*(self: MessageSearchLocationMenuItem,
|
||||
chatId: string, found: var bool): MessageSearchLocationMenuSubItem =
|
||||
self.subItems.getLocationSubItemForChatId(chatId, found)
|
||||
|
||||
proc toJsonNode*(self: MessageSearchLocationMenuItem): JsonNode =
|
||||
result = %* {
|
||||
"value": self.value,
|
||||
"title": self.title,
|
||||
"imageSource": self.imageSource,
|
||||
"iconName": self.iconName,
|
||||
"iconColor": self.iconColor,
|
||||
"isIdenticon": self.isIdenticon
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
import NimQml, Tables, strutils
|
||||
|
||||
import ../../../../status/chat/[chat]
|
||||
import ../../../../status/[status]
|
||||
|
||||
import location_menu_item, location_menu_sub_item, constants
|
||||
|
||||
type
|
||||
MessageSearchLocationMenuModelRole {.pure.} = enum
|
||||
Value = UserRole + 1
|
||||
Title
|
||||
ImageSource
|
||||
IconName
|
||||
IconColor
|
||||
IsIdenticon
|
||||
SubItems
|
||||
|
||||
QtObject:
|
||||
type
|
||||
MessageSearchLocationMenuModel* = ref object of QAbstractListModel
|
||||
items: seq[MessageSearchLocationMenuItem]
|
||||
|
||||
proc delete(self: MessageSearchLocationMenuModel) =
|
||||
self.QAbstractListModel.delete
|
||||
|
||||
proc setup(self: MessageSearchLocationMenuModel) =
|
||||
self.QAbstractListModel.setup
|
||||
|
||||
proc newMessageSearchLocationMenuModel*(): MessageSearchLocationMenuModel =
|
||||
new(result, delete)
|
||||
result.setup()
|
||||
|
||||
method rowCount(self: MessageSearchLocationMenuModel,
|
||||
index: QModelIndex = nil): int =
|
||||
|
||||
return self.items.len
|
||||
|
||||
method roleNames(self: MessageSearchLocationMenuModel): Table[int, string] =
|
||||
{
|
||||
MessageSearchLocationMenuModelRole.Value.int:"value",
|
||||
MessageSearchLocationMenuModelRole.Title.int:"title",
|
||||
MessageSearchLocationMenuModelRole.ImageSource.int:"imageSource",
|
||||
MessageSearchLocationMenuModelRole.IconName.int:"iconName",
|
||||
MessageSearchLocationMenuModelRole.IconColor.int:"iconColor",
|
||||
MessageSearchLocationMenuModelRole.IsIdenticon.int:"isIdenticon",
|
||||
MessageSearchLocationMenuModelRole.SubItems.int:"subItems"
|
||||
}.toTable
|
||||
|
||||
method data(self: MessageSearchLocationMenuModel, 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.MessageSearchLocationMenuModelRole
|
||||
|
||||
case enumRole:
|
||||
of MessageSearchLocationMenuModelRole.Value:
|
||||
result = newQVariant(item.getValue)
|
||||
of MessageSearchLocationMenuModelRole.Title:
|
||||
result = newQVariant(item.getTitle)
|
||||
of MessageSearchLocationMenuModelRole.ImageSource:
|
||||
result = newQVariant(item.getImageSource)
|
||||
of MessageSearchLocationMenuModelRole.IconName:
|
||||
result = newQVariant(item.getIconName)
|
||||
of MessageSearchLocationMenuModelRole.IconColor:
|
||||
result = newQVariant(item.getIconColor)
|
||||
of MessageSearchLocationMenuModelRole.IsIdenticon:
|
||||
result = newQVariant(item.getIsIdenticon)
|
||||
of MessageSearchLocationMenuModelRole.SubItems:
|
||||
result = newQVariant(item.getSubItems)
|
||||
|
||||
proc prepareLocationMenu*(self: MessageSearchLocationMenuModel, status: Status,
|
||||
chats: seq[Chat], communities: seq[Community]) =
|
||||
|
||||
self.beginResetModel()
|
||||
|
||||
self.items = @[]
|
||||
var item = initMessageSearchLocationMenuItem(status,
|
||||
SEARCH_MENU_LOCATION_CHAT_SECTION_NAME,
|
||||
SEARCH_MENU_LOCATION_CHAT_SECTION_NAME, "", "chat", "", false)
|
||||
item.prepareSubItems(chats, false)
|
||||
self.items.add(item)
|
||||
|
||||
for co in communities:
|
||||
item = initMessageSearchLocationMenuItem(status, co.id, co.name,
|
||||
co.communityImage.thumbnail, "", co.communityColor,
|
||||
co.communityImage.thumbnail.len == 0)
|
||||
item.prepareSubItems(co.chats, true)
|
||||
self.items.add(item)
|
||||
|
||||
self.endResetModel()
|
||||
|
||||
proc getLocationItemForCommunityId*(self: MessageSearchLocationMenuModel,
|
||||
communityId: string, found: var bool): MessageSearchLocationMenuItem =
|
||||
|
||||
found = false
|
||||
for i in self.items:
|
||||
if (i.getValue() == communityId):
|
||||
found = true
|
||||
return i
|
||||
|
||||
proc getLocationSubItemForChatId*(self: MessageSearchLocationMenuModel,
|
||||
chatId: string, found: var bool): MessageSearchLocationMenuSubItem =
|
||||
|
||||
for i in self.items:
|
||||
let subItem = i.getLocationSubItemForChatId(chatId, found)
|
||||
if (found):
|
||||
return subItem
|
|
@ -0,0 +1,59 @@
|
|||
import json, strformat
|
||||
|
||||
type
|
||||
MessageSearchLocationMenuSubItem* = object
|
||||
value: string
|
||||
text: string
|
||||
imageSource: string
|
||||
iconName: string
|
||||
iconColor: string
|
||||
isIdenticon: bool
|
||||
|
||||
proc initMessageSearchLocationMenuSubItem*(value, text, imageSource: string,
|
||||
iconName, iconColor: string = "",
|
||||
isIdenticon: bool = true): MessageSearchLocationMenuSubItem =
|
||||
result.value = value
|
||||
result.text = text
|
||||
result.imageSource = imageSource
|
||||
result.iconName = iconName
|
||||
result.iconColor = iconColor
|
||||
result.isIdenticon = isIdenticon
|
||||
|
||||
proc `$`*(self: MessageSearchLocationMenuSubItem): string =
|
||||
result = fmt"""MenuSubItem:
|
||||
value: {self.value},
|
||||
text: {self.text},
|
||||
isIdenticon: {self.isIdenticon},
|
||||
iconName: {self.iconName},
|
||||
iconColor: {self.iconColor},
|
||||
imageSource:{self.imageSource}"""
|
||||
|
||||
proc getValue*(self: MessageSearchLocationMenuSubItem): string =
|
||||
return self.value
|
||||
|
||||
proc getText*(self: MessageSearchLocationMenuSubItem): string =
|
||||
return self.text
|
||||
|
||||
proc getImageSource*(self: MessageSearchLocationMenuSubItem): string =
|
||||
return self.imageSource
|
||||
|
||||
proc getIconName*(self: MessageSearchLocationMenuSubItem): string =
|
||||
return self.iconName
|
||||
|
||||
proc getIconColor*(self: MessageSearchLocationMenuSubItem): string =
|
||||
return self.iconColor
|
||||
|
||||
proc getIsIdenticon*(self: MessageSearchLocationMenuSubItem): bool =
|
||||
return self.isIdenticon
|
||||
|
||||
proc toJsonNode*(self: MessageSearchLocationMenuSubItem): JsonNode =
|
||||
result = %* {
|
||||
"value": self.value,
|
||||
"text": self.text,
|
||||
"imageSource": self.imageSource,
|
||||
"iconName": self.iconName,
|
||||
"iconColor": self.iconColor,
|
||||
"isIdenticon": self.isIdenticon
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
import NimQml, Tables, strutils, strformat
|
||||
|
||||
import ../../../../status/chat/[chat]
|
||||
import ../../../../status/[status]
|
||||
|
||||
import location_menu_sub_item
|
||||
|
||||
type
|
||||
MessageSearchLocationMenuSubModelRole {.pure.} = enum
|
||||
Value = UserRole + 1
|
||||
Text
|
||||
ImageSource
|
||||
IconName
|
||||
IconColor
|
||||
IsIdenticon
|
||||
|
||||
QtObject:
|
||||
type
|
||||
MessageSearchLocationMenuSubModel* = ref object of QAbstractListModel
|
||||
status: Status
|
||||
items: seq[MessageSearchLocationMenuSubItem]
|
||||
|
||||
proc delete(self: MessageSearchLocationMenuSubModel) =
|
||||
self.QAbstractListModel.delete
|
||||
|
||||
proc setup(self: MessageSearchLocationMenuSubModel) =
|
||||
self.QAbstractListModel.setup
|
||||
|
||||
proc newMessageSearchLocationMenuSubModel*(status: Status):
|
||||
MessageSearchLocationMenuSubModel =
|
||||
new(result, delete)
|
||||
result.status = status
|
||||
result.setup()
|
||||
|
||||
proc `$`*(self: MessageSearchLocationMenuSubModel): string =
|
||||
for i in 0 ..< self.items.len:
|
||||
result &= fmt"""
|
||||
[{i}]:({$self.items[i]})
|
||||
"""
|
||||
|
||||
proc countChanged*(self: MessageSearchLocationMenuSubModel) {.signal.}
|
||||
|
||||
proc count*(self: MessageSearchLocationMenuSubModel): int {.slot.} =
|
||||
self.items.len
|
||||
|
||||
QtProperty[int] count:
|
||||
read = count
|
||||
notify = countChanged
|
||||
|
||||
method rowCount(self: MessageSearchLocationMenuSubModel,
|
||||
index: QModelIndex = nil): int =
|
||||
return self.items.len
|
||||
|
||||
method roleNames(self: MessageSearchLocationMenuSubModel):
|
||||
Table[int, string] =
|
||||
{
|
||||
MessageSearchLocationMenuSubModelRole.Value.int:"value",
|
||||
MessageSearchLocationMenuSubModelRole.Text.int:"text",
|
||||
MessageSearchLocationMenuSubModelRole.ImageSource.int:"imageSource",
|
||||
MessageSearchLocationMenuSubModelRole.IconName.int:"iconName",
|
||||
MessageSearchLocationMenuSubModelRole.IconColor.int:"iconColor",
|
||||
MessageSearchLocationMenuSubModelRole.IsIdenticon.int:"isIdenticon"
|
||||
}.toTable
|
||||
|
||||
method data(self: MessageSearchLocationMenuSubModel, 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.MessageSearchLocationMenuSubModelRole
|
||||
|
||||
case enumRole:
|
||||
of MessageSearchLocationMenuSubModelRole.Value:
|
||||
result = newQVariant(item.getValue)
|
||||
of MessageSearchLocationMenuSubModelRole.Text:
|
||||
result = newQVariant(item.getText)
|
||||
of MessageSearchLocationMenuSubModelRole.ImageSource:
|
||||
result = newQVariant(item.getImageSource)
|
||||
of MessageSearchLocationMenuSubModelRole.IconName:
|
||||
result = newQVariant(item.getIconName)
|
||||
of MessageSearchLocationMenuSubModelRole.IconColor:
|
||||
result = newQVariant(item.getIconColor)
|
||||
of MessageSearchLocationMenuSubModelRole.IsIdenticon:
|
||||
result = newQVariant(item.getIsIdenticon)
|
||||
|
||||
proc prepareItems*(self: MessageSearchLocationMenuSubModel, chats: seq[Chat],
|
||||
isCommunityChannel: bool) =
|
||||
self.beginResetModel()
|
||||
|
||||
self.items = @[]
|
||||
|
||||
for c in chats:
|
||||
var text = self.status.chat.chatName(c)
|
||||
if (isCommunityChannel):
|
||||
self.items.add(initMessageSearchLocationMenuSubItem(c.id, text, "",
|
||||
"channel", c.color, false))
|
||||
else:
|
||||
if (text.endsWith(".stateofus.eth")):
|
||||
text = text[0 .. ^15]
|
||||
|
||||
self.items.add(initMessageSearchLocationMenuSubItem(c.id, text,
|
||||
c.identicon, "", c.color, c.identicon.len == 0))
|
||||
|
||||
self.endResetModel()
|
||||
|
||||
proc getLocationSubItemForChatId*(self: MessageSearchLocationMenuSubModel,
|
||||
chatId: string, found: var bool): MessageSearchLocationMenuSubItem =
|
||||
|
||||
found = false
|
||||
for i in self.items:
|
||||
if (i.getValue() == chatId):
|
||||
found = true
|
||||
return i
|
|
@ -0,0 +1,80 @@
|
|||
import strformat
|
||||
|
||||
type SearchResultItem* = object
|
||||
itemId: string
|
||||
content: string
|
||||
time: string
|
||||
titleId: string
|
||||
title: string
|
||||
sectionName: string
|
||||
isLetterIdenticon: bool
|
||||
badgeImage: string
|
||||
badgePrimaryText: string
|
||||
badgeSecondaryText: string
|
||||
badgeIdenticonColor: string
|
||||
|
||||
proc initSearchResultItem*(itemId, content, time, titleId, title,
|
||||
sectionName: string,
|
||||
isLetterIdenticon: bool = false,
|
||||
badgeImage, badgePrimaryText, badgeSecondaryText,
|
||||
badgeIdenticonColor: string = ""): SearchResultItem =
|
||||
|
||||
result.itemId = itemId
|
||||
result.content = content
|
||||
result.time = time
|
||||
result.titleId = titleId
|
||||
result.title = title
|
||||
result.sectionName = sectionName
|
||||
result.isLetterIdenticon = isLetterIdenticon
|
||||
result.badgeImage = badgeImage
|
||||
result.badgePrimaryText = badgePrimaryText
|
||||
result.badgeSecondaryText = badgeSecondaryText
|
||||
result.badgeIdenticonColor = badgeIdenticonColor
|
||||
|
||||
proc `$`*(self: SearchResultItem): string =
|
||||
result = "MessageSearchResultItem("
|
||||
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"isLetterIdenticon:{self.isLetterIdenticon}"
|
||||
result &= fmt"badgeImage:{self.badgeImage}"
|
||||
result &= fmt"badgePrimaryText:{self.badgePrimaryText}"
|
||||
result &= fmt"badgeSecondaryText:{self.badgeSecondaryText}"
|
||||
result &= fmt"badgeIdenticonColor:{self.badgeIdenticonColor}"
|
||||
result &= ")"
|
||||
|
||||
method getItemId*(self: SearchResultItem): string {.base.} =
|
||||
return self.itemId
|
||||
|
||||
method getContent*(self: SearchResultItem): string {.base.} =
|
||||
return self.content
|
||||
|
||||
method getTime*(self: SearchResultItem): string {.base.} =
|
||||
return self.time
|
||||
|
||||
method getTitleId*(self: SearchResultItem): string {.base.} =
|
||||
return self.titleId
|
||||
|
||||
method getTitle*(self: SearchResultItem): string {.base.} =
|
||||
return self.title
|
||||
|
||||
method getSectionName*(self: SearchResultItem): string {.base.} =
|
||||
return self.sectionName
|
||||
|
||||
method getIsLetterIdentIcon*(self: SearchResultItem): bool {.base.} =
|
||||
return self.isLetterIdenticon
|
||||
|
||||
method getBadgeImage*(self: SearchResultItem): string {.base.} =
|
||||
return self.badgeImage
|
||||
|
||||
method getBadgePrimaryText*(self: SearchResultItem): string {.base.} =
|
||||
return self.badgePrimaryText
|
||||
|
||||
method getBadgeSecondaryText*(self: SearchResultItem): string {.base.} =
|
||||
return self.badgeSecondaryText
|
||||
|
||||
method getBadgeIdenticonColor*(self: SearchResultItem): string {.base.} =
|
||||
return self.badgeIdenticonColor
|
|
@ -0,0 +1,112 @@
|
|||
import NimQml, Tables, strutils
|
||||
|
||||
import result_item
|
||||
|
||||
type
|
||||
MessageSearchResultModelRole {.pure.} = enum
|
||||
ItemId = UserRole + 1
|
||||
Content
|
||||
Time
|
||||
TitleId
|
||||
Title
|
||||
SectionName
|
||||
IsLetterIdenticon
|
||||
BadgeImage
|
||||
BadgePrimaryText
|
||||
BadgeSecondaryText
|
||||
BadgeIdenticonColor
|
||||
|
||||
QtObject:
|
||||
type
|
||||
MessageSearchResultModel* = ref object of QAbstractListModel
|
||||
resultList: seq[SearchResultItem]
|
||||
|
||||
proc delete(self: MessageSearchResultModel) =
|
||||
self.QAbstractListModel.delete
|
||||
|
||||
proc setup(self: MessageSearchResultModel) =
|
||||
self.QAbstractListModel.setup
|
||||
|
||||
proc newMessageSearchResultModel*(): MessageSearchResultModel =
|
||||
new(result, delete)
|
||||
result.setup()
|
||||
|
||||
#################################################
|
||||
# Properties
|
||||
#################################################
|
||||
|
||||
proc countChanged*(self: MessageSearchResultModel) {.signal.}
|
||||
|
||||
proc count*(self: MessageSearchResultModel): int {.slot.} =
|
||||
self.resultList.len
|
||||
|
||||
QtProperty[int] count:
|
||||
read = count
|
||||
notify = countChanged
|
||||
|
||||
method rowCount(self: MessageSearchResultModel, index: QModelIndex = nil): int =
|
||||
return self.resultList.len
|
||||
|
||||
method roleNames(self: MessageSearchResultModel): Table[int, string] =
|
||||
{
|
||||
MessageSearchResultModelRole.ItemId.int:"itemId",
|
||||
MessageSearchResultModelRole.Content.int:"content",
|
||||
MessageSearchResultModelRole.Time.int:"time",
|
||||
MessageSearchResultModelRole.TitleId.int:"titleId",
|
||||
MessageSearchResultModelRole.Title.int:"title",
|
||||
MessageSearchResultModelRole.SectionName.int:"sectionName",
|
||||
MessageSearchResultModelRole.IsLetterIdenticon.int:"isLetterIdenticon",
|
||||
MessageSearchResultModelRole.BadgeImage.int:"badgeImage",
|
||||
MessageSearchResultModelRole.BadgePrimaryText.int:"badgePrimaryText",
|
||||
MessageSearchResultModelRole.BadgeSecondaryText.int:"badgeSecondaryText",
|
||||
MessageSearchResultModelRole.BadgeIdenticonColor.int:"badgeIdenticonColor"
|
||||
}.toTable
|
||||
|
||||
method data(self: MessageSearchResultModel, 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.MessageSearchResultModelRole
|
||||
|
||||
case enumRole:
|
||||
of MessageSearchResultModelRole.ItemId:
|
||||
result = newQVariant(item.getItemId)
|
||||
of MessageSearchResultModelRole.Content:
|
||||
result = newQVariant(item.getContent)
|
||||
of MessageSearchResultModelRole.Time:
|
||||
result = newQVariant(item.getTime)
|
||||
of MessageSearchResultModelRole.TitleId:
|
||||
result = newQVariant(item.getTitleId)
|
||||
of MessageSearchResultModelRole.Title:
|
||||
result = newQVariant(item.getTitle)
|
||||
of MessageSearchResultModelRole.SectionName:
|
||||
result = newQVariant(item.getSectionName)
|
||||
of MessageSearchResultModelRole.IsLetterIdenticon:
|
||||
result = newQVariant(item.getIsLetterIdentIcon)
|
||||
of MessageSearchResultModelRole.BadgeImage:
|
||||
result = newQVariant(item.getBadgeImage)
|
||||
of MessageSearchResultModelRole.BadgePrimaryText:
|
||||
result = newQVariant(item.getBadgePrimaryText)
|
||||
of MessageSearchResultModelRole.BadgeSecondaryText:
|
||||
result = newQVariant(item.getBadgeSecondaryText)
|
||||
of MessageSearchResultModelRole.BadgeIdenticonColor:
|
||||
result = newQVariant(item.getBadgeIdenticonColor)
|
||||
|
||||
proc add*(self: MessageSearchResultModel, item: SearchResultItem) =
|
||||
self.beginInsertRows(newQModelIndex(), self.resultList.len, self.resultList.len)
|
||||
self.resultList.add(item)
|
||||
self.endInsertRows()
|
||||
|
||||
proc set*(self: MessageSearchResultModel, items: seq[SearchResultItem]) =
|
||||
self.beginResetModel()
|
||||
self.resultList = items
|
||||
self.endResetModel()
|
||||
|
||||
proc clear*(self: MessageSearchResultModel) =
|
||||
self.beginResetModel()
|
||||
self.resultList = @[]
|
||||
self.endResetModel()
|
|
@ -0,0 +1,265 @@
|
|||
import NimQml, Tables, json, strutils, chronicles
|
||||
|
||||
import result_model, result_item, location_menu_model, location_menu_item, location_menu_sub_item
|
||||
import constants as sr_constants
|
||||
|
||||
import ../../../../status/[status, types]
|
||||
import ../../../../status/chat/[message, chat]
|
||||
import ../../../../status/libstatus/[settings]
|
||||
import ../communities
|
||||
import ../channel
|
||||
import ../chat_item
|
||||
import ../channels_list
|
||||
import ../community_list
|
||||
|
||||
logScope:
|
||||
topics = "search-messages-view-controller"
|
||||
|
||||
type ResultItemInfo = object
|
||||
communityId*: string
|
||||
channelId*: string
|
||||
messageId*: string
|
||||
|
||||
method isEmpty*(self: ResultItemInfo): bool {.base.} =
|
||||
self.communityId.len == 0 and
|
||||
self.channelId.len == 0 and
|
||||
self.messageId.len == 0
|
||||
|
||||
QtObject:
|
||||
type MessageSearchViewController* = ref object of QObject
|
||||
status: Status
|
||||
channelView: ChannelView
|
||||
communitiesView: CommunitiesView
|
||||
resultItems: Table[string, ResultItemInfo] # [resuiltItemId, ResultItemInfo]
|
||||
|
||||
messageSearchResultModel: MessageSearchResultModel
|
||||
messageSearchLocationMenuModel: MessageSearchLocationMenuModel
|
||||
meassgeSearchTerm: string
|
||||
meassgeSearchLocation: string
|
||||
meassgeSearchSubLocation: string
|
||||
|
||||
proc setup(self: MessageSearchViewController) =
|
||||
self.QObject.setup
|
||||
|
||||
proc delete*(self: MessageSearchViewController) =
|
||||
self.messageSearchResultModel.delete
|
||||
self.messageSearchLocationMenuModel.delete
|
||||
self.resultItems.clear
|
||||
self.QObject.delete
|
||||
|
||||
proc newMessageSearchViewController*(status: Status, channelView: ChannelView,
|
||||
communitiesView: CommunitiesView): MessageSearchViewController =
|
||||
new(result, delete)
|
||||
result.status = status
|
||||
result.channelView = channelView
|
||||
result.communitiesView = communitiesView
|
||||
result.resultItems = initTable[string, ResultItemInfo]()
|
||||
result.messageSearchResultModel = newMessageSearchResultModel()
|
||||
result.messageSearchLocationMenuModel = newMessageSearchLocationMenuModel()
|
||||
result.setup
|
||||
|
||||
proc getMessageSearchResultModel*(self: MessageSearchViewController):
|
||||
QVariant {.slot.} =
|
||||
newQVariant(self.messageSearchResultModel)
|
||||
|
||||
QtProperty[QVariant] resultModel:
|
||||
read = getMessageSearchResultModel
|
||||
|
||||
proc getMessageSearchLocationMenuModel*(self: MessageSearchViewController):
|
||||
QVariant {.slot.} =
|
||||
newQVariant(self.messageSearchLocationMenuModel)
|
||||
|
||||
QtProperty[QVariant] locationMenuModel:
|
||||
read = getMessageSearchLocationMenuModel
|
||||
|
||||
proc getSearchLocationObject*(self: MessageSearchViewController): string {.slot.} =
|
||||
## This method returns location and subLocation with their details so we
|
||||
## may set initial search location on the side of qml.
|
||||
var found = false
|
||||
let subItem = self.messageSearchLocationMenuModel.getLocationSubItemForChatId(
|
||||
self.channelView.activeChannel.id, found
|
||||
)
|
||||
|
||||
var jsonObject = %* {
|
||||
"location": "",
|
||||
"subLocation": ""
|
||||
}
|
||||
|
||||
if(found):
|
||||
jsonObject["subLocation"] = subItem.toJsonNode()
|
||||
|
||||
if(self.channelView.activeChannel.communityId.len == 0):
|
||||
jsonObject["location"] = %* {
|
||||
"value":sr_constants.SEARCH_MENU_LOCATION_CHAT_SECTION_NAME,
|
||||
"title":sr_constants.SEARCH_MENU_LOCATION_CHAT_SECTION_NAME
|
||||
}
|
||||
else:
|
||||
let item = self.messageSearchLocationMenuModel.getLocationItemForCommunityId(
|
||||
self.channelView.activeChannel.communityId, found
|
||||
)
|
||||
|
||||
if(found):
|
||||
jsonObject["location"] = item.toJsonNode()
|
||||
|
||||
result = Json.encode(jsonObject)
|
||||
|
||||
proc prepareLocationMenuModel*(self: MessageSearchViewController)
|
||||
{.slot.} =
|
||||
self.messageSearchLocationMenuModel.prepareLocationMenu(
|
||||
self.status,
|
||||
self.channelView.chats.chats,
|
||||
self.communitiesView.joinedCommunityList.communities)
|
||||
|
||||
proc setSearchLocation*(self: MessageSearchViewController, location: string = "",
|
||||
subLocation: string = "") {.slot.} =
|
||||
## Setting location and subLocation to an empty string means we're
|
||||
## searching in all available chats/channels/communities.
|
||||
self.meassgeSearchLocation = location
|
||||
self.meassgeSearchSubLocation = subLocation
|
||||
|
||||
proc searchMessages*(self: MessageSearchViewController, searchTerm: string)
|
||||
{.slot.} =
|
||||
self.meassgeSearchTerm = searchTerm
|
||||
self.resultItems.clear
|
||||
|
||||
if (self.meassgeSearchTerm.len == 0):
|
||||
self.messageSearchResultModel.clear()
|
||||
return
|
||||
|
||||
var chats: seq[string]
|
||||
var communities: seq[string]
|
||||
|
||||
if (self.meassgeSearchSubLocation.len > 0):
|
||||
chats.add(self.meassgeSearchSubLocation)
|
||||
elif (self.meassgeSearchLocation.len > 0):
|
||||
# If "Chat" is set for the meassgeSearchLocation that means we need to
|
||||
# search in all chats from the chat section.
|
||||
if (self.meassgeSearchLocation != sr_constants.SEARCH_MENU_LOCATION_CHAT_SECTION_NAME):
|
||||
communities.add(self.meassgeSearchLocation)
|
||||
else:
|
||||
for c in self.channelView.chats.chats:
|
||||
chats.add(c.id)
|
||||
|
||||
if (communities.len == 0 and chats.len == 0):
|
||||
for c in self.channelView.chats.chats:
|
||||
chats.add(c.id)
|
||||
|
||||
for co in self.communitiesView.joinedCommunityList.communities:
|
||||
communities.add(co.id)
|
||||
|
||||
self.status.chat.asyncSearchMessages(communities, chats,
|
||||
self.meassgeSearchTerm, false)
|
||||
|
||||
proc onSearchMessagesLoaded*(self: MessageSearchViewController,
|
||||
messages: seq[Message]) =
|
||||
|
||||
self.resultItems.clear
|
||||
|
||||
var items: seq[SearchResultItem]
|
||||
var channels: seq[SearchResultItem]
|
||||
let myPublicKey = getSetting[string](Setting.PublicKey, "0x0")
|
||||
|
||||
# Add communities
|
||||
for co in self.communitiesView.joinedCommunityList.communities:
|
||||
if(self.meassgeSearchLocation.len == 0 and
|
||||
co.name.toLower.startsWith(self.meassgeSearchTerm.toLower)):
|
||||
let item = initSearchResultItem(co.id, "", "", co.id, co.name,
|
||||
sr_constants.SEARCH_RESULT_COMMUNITIES_SECTION_NAME, false,
|
||||
co.communityImage.thumbnail, "", "", co.communityColor)
|
||||
|
||||
self.resultItems.add(co.id, ResultItemInfo(communityId: co.id))
|
||||
items.add(item)
|
||||
|
||||
# Add channels
|
||||
if(self.meassgeSearchSubLocation.len == 0 and
|
||||
self.meassgeSearchLocation.len == 0 or
|
||||
self.meassgeSearchLocation == co.name):
|
||||
for c in co.chats:
|
||||
let chatName = self.status.chat.chatName(c)
|
||||
var chatNameRaw = chatName
|
||||
if(chatName.startsWith("@")):
|
||||
chatNameRaw = chatName[1 ..^ 1]
|
||||
|
||||
if(chatNameRaw.toLower.startsWith(self.meassgeSearchTerm.toLower)):
|
||||
let item = initSearchResultItem(c.id, "", "", c.id, chatName,
|
||||
sr_constants.SEARCH_RESULT_CHANNELS_SECTION_NAME,
|
||||
c.identicon.len > 0, c.identicon, "", "", c.color)
|
||||
|
||||
self.resultItems.add(c.id, ResultItemInfo(communityId: co.id,
|
||||
channelId: c.id))
|
||||
channels.add(item)
|
||||
|
||||
# Add chats
|
||||
if(self.meassgeSearchLocation.len == 0 or
|
||||
self.meassgeSearchLocation == sr_constants.SEARCH_RESULT_CHATS_SECTION_NAME):
|
||||
for c in self.channelView.chats.chats:
|
||||
let chatName = self.status.chat.chatName(c)
|
||||
var chatNameRaw = chatName
|
||||
if(chatName.startsWith("@")):
|
||||
chatNameRaw = chatName[1 ..^ 1]
|
||||
|
||||
if(chatNameRaw.toLower.startsWith(self.meassgeSearchTerm.toLower)):
|
||||
let item = initSearchResultItem(c.id, "", "", c.id, chatName,
|
||||
sr_constants.SEARCH_RESULT_CHATS_SECTION_NAME,
|
||||
c.identicon.len > 0, c.identicon, "", "", c.color)
|
||||
|
||||
self.resultItems.add(c.id, ResultItemInfo(communityId: "",
|
||||
channelId: c.id))
|
||||
items.add(item)
|
||||
|
||||
# Add channels in order as requested by design
|
||||
items.add(channels)
|
||||
|
||||
# Add messages
|
||||
for m in messages:
|
||||
if (m.contentType != ContentType.Message):
|
||||
continue
|
||||
|
||||
var found = false
|
||||
var chat = self.channelView.chats.getChannelById(m.chatId, found)
|
||||
if (found):
|
||||
var channel = self.status.chat.chatName(chat)
|
||||
if (channel.endsWith(".stateofus.eth")):
|
||||
channel = channel[0 .. ^15]
|
||||
|
||||
var alias = self.status.chat.userNameOrAlias(m.fromAuthor, true)
|
||||
if (myPublicKey == m.fromAuthor):
|
||||
alias = "You"
|
||||
|
||||
let item = initSearchResultItem(m.id, m.text, m.timestamp, m.fromAuthor,
|
||||
alias, sr_constants.SEARCH_RESULT_MESSAGES_SECTION_NAME,
|
||||
chat.identicon.len == 0, chat.identicon, channel, "", chat.color)
|
||||
|
||||
self.resultItems.add(m.id, ResultItemInfo(communityId: "",
|
||||
channelId: chat.id, messageId: m.id))
|
||||
items.add(item)
|
||||
else:
|
||||
var community: Community
|
||||
if (self.communitiesView.joinedCommunityList.
|
||||
getChannelByIdAndBelongingCommunity(m.chatId, chat, community)):
|
||||
|
||||
var channel = self.status.chat.chatName(chat)
|
||||
if (not channel.startsWith("#")):
|
||||
channel = "#" & channel
|
||||
if (channel.endsWith(".stateofus.eth")):
|
||||
channel = channel[0 .. ^15]
|
||||
|
||||
var alias = self.status.chat.userNameOrAlias(m.fromAuthor, true)
|
||||
if (myPublicKey == m.fromAuthor):
|
||||
alias = "You"
|
||||
|
||||
let item = initSearchResultItem(m.id, m.text, m.timestamp, m.fromAuthor,
|
||||
m.alias, sr_constants.SEARCH_RESULT_MESSAGES_SECTION_NAME,
|
||||
community.communityImage.thumbnail.len == 0,
|
||||
community.communityImage.thumbnail, community.name, channel,
|
||||
community.communityColor)
|
||||
|
||||
self.resultItems.add(m.id, ResultItemInfo(communityId: community.id,
|
||||
channelId: chat.id, messageId: m.id))
|
||||
items.add(item)
|
||||
|
||||
self.messageSearchResultModel.set(items)
|
||||
|
||||
proc getItemInfo*(self: MessageSearchViewController, itemId: string):
|
||||
ResultItemInfo =
|
||||
self.resultItems.getOrDefault(itemId)
|
|
@ -12,7 +12,7 @@ import contacts
|
|||
import chat/[chat, message]
|
||||
import tasks/[qt, task_runner_impl]
|
||||
import signals/messages
|
||||
import ens
|
||||
import ens, accounts
|
||||
|
||||
logScope:
|
||||
topics = "chat-model"
|
||||
|
@ -621,6 +621,9 @@ QtObject:
|
|||
return self.channels[chatId].communityId
|
||||
|
||||
proc asyncSearchMessages*(self: ChatModel, 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):
|
||||
info "empty channel id set for fetching more messages"
|
||||
return
|
||||
|
@ -628,8 +631,8 @@ QtObject:
|
|||
if (searchTerm.len == 0):
|
||||
return
|
||||
|
||||
let arg = AsyncSearchMessageTaskArg(
|
||||
tptr: cast[ByteAddress](asyncSearchMessagesTask),
|
||||
let arg = AsyncSearchMessagesInChatTaskArg(
|
||||
tptr: cast[ByteAddress](asyncSearchMessagesInChatTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: "onAsyncSearchMessages",
|
||||
chatId: chatId,
|
||||
|
@ -638,6 +641,29 @@ QtObject:
|
|||
)
|
||||
self.tasks.threadpool.start(arg)
|
||||
|
||||
proc asyncSearchMessages*(self: ChatModel, communityIds: seq[string], chatIds: seq[string], searchTerm: string, caseSensitive: bool) =
|
||||
## Asynchronous search for messages which contain the searchTerm and belong
|
||||
## to either any chat/channel from chatIds array or any channel of community
|
||||
## from communityIds array.
|
||||
|
||||
if (communityIds.len == 0 and chatIds.len == 0):
|
||||
info "either community ids or chat ids or both must be set"
|
||||
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.tasks.threadpool.start(arg)
|
||||
|
||||
proc onAsyncSearchMessages*(self: ChatModel, response: string) {.slot.} =
|
||||
let responseObj = response.parseJson
|
||||
if (responseObj.kind != JObject):
|
||||
|
@ -774,3 +800,31 @@ QtObject:
|
|||
self.events.emit("messagesLoaded", MsgsLoadedArgs(chatId: chatId, messages: messages))
|
||||
self.events.emit("reactionsLoaded", ReactionsLoadedArgs(reactions: reactions))
|
||||
self.events.emit("pinnedMessagesLoaded", MsgsLoadedArgs(chatId: chatId, messages: pinnedMessages))
|
||||
|
||||
proc userNameOrAlias*(self: ChatModel, pubKey: string,
|
||||
prettyForm: bool = false): string =
|
||||
## Returns ens name or alias, in case if prettyForm is true and ens name
|
||||
## ends with ".stateofus.eth" that part will be removed.
|
||||
var alias: string
|
||||
if self.contacts.hasKey(pubKey):
|
||||
alias = ens.userNameOrAlias(self.contacts[pubKey])
|
||||
else:
|
||||
alias = generateAlias(pubKey)
|
||||
|
||||
if (prettyForm and alias.endsWith(".stateofus.eth")):
|
||||
alias = alias[0 .. ^15]
|
||||
|
||||
return alias
|
||||
|
||||
proc chatName*(self: ChatModel, chatItem: Chat): string =
|
||||
if (not chatItem.chatType.isOneToOne):
|
||||
return chatItem.name
|
||||
|
||||
if (self.contacts.hasKey(chatItem.id) and
|
||||
self.contacts[chatItem.id].hasNickname()):
|
||||
return self.contacts[chatItem.id].localNickname
|
||||
|
||||
if chatItem.ensName != "":
|
||||
return "@" & userName(chatItem.ensName).userName(true)
|
||||
|
||||
return self.userNameOrAlias(chatItem.id)
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
#################################################
|
||||
# Async search messages by term
|
||||
#################################################
|
||||
type
|
||||
AsyncSearchMessageTaskArg = ref object of QObjectTaskArg
|
||||
chatId: string
|
||||
type
|
||||
AsyncSearchMessagesTaskArg = ref object of QObjectTaskArg
|
||||
searchTerm: string
|
||||
caseSensitive: bool
|
||||
|
||||
const asyncSearchMessagesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let arg = decode[AsyncSearchMessageTaskArg](argEncoded)
|
||||
#################################################
|
||||
# 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)
|
||||
var messages: JsonNode
|
||||
var success: bool
|
||||
let response = status_chat.asyncSearchMessages(arg.chatId, arg.searchTerm, arg.caseSensitive, success)
|
||||
|
@ -22,6 +25,30 @@ const asyncSearchMessagesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall
|
|||
}
|
||||
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)
|
||||
var messages: JsonNode
|
||||
var success: bool
|
||||
let response = status_chat.asyncSearchMessages(arg.communityIds, arg.chatIds, arg.searchTerm, arg.caseSensitive, success)
|
||||
|
||||
if(success):
|
||||
messages = response.parseJson()["result"]
|
||||
|
||||
let responseJson = %*{
|
||||
"communityIds": arg.communityIds,
|
||||
"chatIds": arg.chatIds,
|
||||
"messages": messages
|
||||
}
|
||||
arg.finish(responseJson)
|
||||
|
||||
#################################################
|
||||
# Async load messages
|
||||
#################################################
|
||||
|
|
|
@ -246,7 +246,6 @@ proc recalculateUnviewedMessages*(community: var Community) =
|
|||
community.unviewedMessagesCount = total
|
||||
|
||||
proc recalculateMentions*(community: var Community) =
|
||||
echo "(recalculateMentions) chatId: ", community.id, " before: ", community.unviewedMentionsCount
|
||||
var total = 0
|
||||
for chat in community.chats:
|
||||
total += chat.unviewedMentionsCount
|
||||
|
|
|
@ -569,7 +569,15 @@ proc unreadActivityCenterNotificationsCount*(): int =
|
|||
proc asyncSearchMessages*(chatId: string, searchTerm: string, caseSensitive: bool, success: var bool): string =
|
||||
success = true
|
||||
try:
|
||||
result = callPrivateRPC("allChatMessagesWhichMatchTerm".prefix, %* [chatId, searchTerm, caseSensitive])
|
||||
result = callPrivateRPC("allMessagesFromChatWhichMatchTerm".prefix, %* [chatId, searchTerm, caseSensitive])
|
||||
except RpcException as e:
|
||||
success = false
|
||||
result = e.msg
|
||||
|
||||
proc asyncSearchMessages*(communityIds: seq[string], chatIds: seq[string], searchTerm: string, caseSensitive: bool, success: var bool): string =
|
||||
success = true
|
||||
try:
|
||||
result = callPrivateRPC("allMessagesFromChatsAndCommunitiesWhichMatchTerm".prefix, %* [communityIds, chatIds, searchTerm, caseSensitive])
|
||||
except RpcException as e:
|
||||
success = false
|
||||
result = e.msg
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 38b0207055f81c853830320e8b20e9532e84973b
|
||||
Subproject commit c68ee3dbe412d8af7cab079b1cb67e0a2a3bf155
|
|
@ -1,8 +1,8 @@
|
|||
import QtQuick 2.13
|
||||
import Qt.labs.platform 1.1
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import QtGraphicalEffects 1.0
|
||||
import Qt.labs.platform 1.1
|
||||
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Components 0.1
|
||||
|
@ -165,6 +165,107 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
StatusSearchLocationMenu {
|
||||
id: searchPopupMenu
|
||||
searchPopup: searchPopup
|
||||
locatioModel: chatsModel.messageSearchViewController.locationMenuModel
|
||||
|
||||
onItemClicked: {
|
||||
chatsModel.messageSearchViewController.setSearchLocation(firstLevelItemValue, secondLevelItemValue)
|
||||
if(searchPopup.searchText !== "")
|
||||
searchMessages(searchPopup.searchText)
|
||||
}
|
||||
}
|
||||
|
||||
property var searchMessages: Backpressure.debounce(searchPopup, 400, function (value) {
|
||||
chatsModel.messageSearchViewController.searchMessages(value)
|
||||
})
|
||||
|
||||
StatusSearchPopup {
|
||||
id: searchPopup
|
||||
|
||||
noResultsLabel: qsTr("No results")
|
||||
defaultSearchLocationText: qsTr("Anywhere")
|
||||
|
||||
searchOptionsPopupMenu: searchPopupMenu
|
||||
searchResults: chatsModel.messageSearchViewController.resultModel
|
||||
onSearchTextChanged: {
|
||||
searchMessages(searchPopup.searchText);
|
||||
}
|
||||
onAboutToHide: {
|
||||
if (searchPopupMenu.visible) {
|
||||
searchPopupMenu.close();
|
||||
}
|
||||
//clear menu
|
||||
for (var i = 2; i <= searchPopupMenu.count; i++) {
|
||||
searchPopupMenu.removeItem(searchPopupMenu.takeItem(i));
|
||||
}
|
||||
}
|
||||
onClosed: {
|
||||
searchPopupMenu.dismiss();
|
||||
}
|
||||
onOpened: {
|
||||
searchPopup.resetSearchSelection();
|
||||
chatsModel.messageSearchViewController.prepareLocationMenuModel()
|
||||
|
||||
const jsonObj = chatsModel.messageSearchViewController.getSearchLocationObject()
|
||||
|
||||
if (!jsonObj) {
|
||||
return
|
||||
}
|
||||
|
||||
let obj = JSON.parse(jsonObj)
|
||||
if (obj.location === "") {
|
||||
if(obj.subLocation === "") {
|
||||
chatsModel.messageSearchViewController.setSearchLocation("", "")
|
||||
}
|
||||
else {
|
||||
searchPopup.setSearchSelection(obj.subLocation.text,
|
||||
"",
|
||||
obj.subLocation.imageSource,
|
||||
obj.subLocation.isIdenticon,
|
||||
obj.subLocation.iconName,
|
||||
obj.subLocation.identiconColor)
|
||||
|
||||
chatsModel.messageSearchViewController.setSearchLocation("", obj.subLocation.value)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (obj.location.title === "Chat") {
|
||||
searchPopup.setSearchSelection(obj.subLocation.text,
|
||||
"",
|
||||
obj.subLocation.imageSource,
|
||||
obj.subLocation.isIdenticon,
|
||||
obj.subLocation.iconName,
|
||||
obj.subLocation.identiconColor)
|
||||
|
||||
chatsModel.messageSearchViewController.setSearchLocation(obj.location.value, obj.subLocation.value)
|
||||
}
|
||||
else {
|
||||
searchPopup.setSearchSelection(obj.location.title,
|
||||
obj.subLocation.text,
|
||||
obj.location.imageSource,
|
||||
obj.location.isIdenticon,
|
||||
obj.location.iconName,
|
||||
obj.location.identiconColor)
|
||||
|
||||
chatsModel.messageSearchViewController.setSearchLocation(obj.location.value, obj.subLocation.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
onResultItemClicked: {
|
||||
searchPopup.close()
|
||||
|
||||
chatsModel.switchToSearchedItem(itemId)
|
||||
}
|
||||
|
||||
onResultItemTitleClicked: {
|
||||
const pk = titleId
|
||||
const userProfileImage = appMain.getProfileImage(pk)
|
||||
return openProfilePopup(chatsModel.userNameOrAlias(pk), pk, userProfileImage || utilsModel.generateIdenticon(pk))
|
||||
}
|
||||
}
|
||||
|
||||
StackLayout {
|
||||
anchors.fill: parent
|
||||
currentIndex: chatsModel.channelView.activeChannelIndex > -1 && chatGroupsListViewCount > 0 ? 0 : 1
|
||||
|
@ -253,9 +354,6 @@ Item {
|
|||
notificationCount: chatsModel.activityNotificationList.unreadCount
|
||||
|
||||
onSearchButtonClicked: searchPopup.open()
|
||||
SearchPopup {
|
||||
id: searchPopup
|
||||
}
|
||||
|
||||
onMembersButtonClicked: showUsers = !showUsers
|
||||
onNotificationButtonClicked: activityCenter.open()
|
||||
|
|
|
@ -1,227 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
import "../../../../shared/status"
|
||||
import "../ChatColumn"
|
||||
|
||||
Popup {
|
||||
property string chatId: chatsModel.channelView.activeChannel.id
|
||||
|
||||
id: popup
|
||||
modal: true
|
||||
|
||||
Overlay.modal: Rectangle {
|
||||
color: Qt.rgba(0, 0, 0, 0.4)
|
||||
}
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||
parent: Overlay.overlay
|
||||
x: Math.round(((parent ? parent.width : 0) - width) / 2)
|
||||
y: Math.round(((parent ? parent.height : 0) - height) / 2)
|
||||
width: 690
|
||||
height: {
|
||||
const noResultHeight = 122
|
||||
let minHeight = 560
|
||||
const maxHeight = parent.height - 200
|
||||
|
||||
if (!searchResultContent.visible) {
|
||||
return noResultHeight
|
||||
}
|
||||
|
||||
if (minHeight > maxHeight) {
|
||||
return maxHeight
|
||||
}
|
||||
|
||||
if (listView.height < minHeight - noResultHeight) {
|
||||
return minHeight
|
||||
}
|
||||
if (listView.height > maxHeight - noResultHeight) {
|
||||
return maxHeight
|
||||
}
|
||||
}
|
||||
background: Rectangle {
|
||||
color: Style.current.background
|
||||
radius: 16
|
||||
}
|
||||
onOpened: {
|
||||
popupOpened = true
|
||||
searchInput.forceActiveFocus(Qt.MouseFocusReason)
|
||||
}
|
||||
onClosed: {
|
||||
popupOpened = false
|
||||
}
|
||||
padding: 0
|
||||
|
||||
Connections {
|
||||
target: chatsModel.channelView
|
||||
onActiveChannelChanged: {
|
||||
searchInput.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: searchHeader
|
||||
width: parent.width
|
||||
height: 64
|
||||
|
||||
SVGImage {
|
||||
id: searchImage
|
||||
source: "../../../img/search.svg"
|
||||
width: 40
|
||||
height: 40
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
property var searchMessages: Backpressure.debounce(searchInput, 400, function (value) {
|
||||
chatsModel.messageView.searchMessages(value)
|
||||
})
|
||||
|
||||
StyledTextField {
|
||||
id: searchInput
|
||||
anchors.left: searchImage.right
|
||||
anchors.leftMargin: Style.current.smallPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.padding
|
||||
//% "Search"
|
||||
placeholderText: qsTrId("search")
|
||||
placeholderTextColor: Style.current.secondaryText
|
||||
selectByMouse: true
|
||||
font.pixelSize: 28
|
||||
background: Rectangle {
|
||||
color: Style.current.transparent
|
||||
}
|
||||
onTextChanged: {
|
||||
searchHeader.searchMessages(searchInput.text)
|
||||
}
|
||||
}
|
||||
|
||||
Separator {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.topMargin: 0
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: channelBadge
|
||||
color: Style.current.inputBackground
|
||||
border.width: 0
|
||||
radius: Style.current.radius
|
||||
height: 32
|
||||
width: childrenRect.width + 2 * inText.anchors.leftMargin
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.top: searchHeader.bottom
|
||||
anchors.topMargin: 12
|
||||
|
||||
StyledText {
|
||||
id: inText
|
||||
//% "In:"
|
||||
text: qsTrId("in-")
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 12
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
BadgeContent {
|
||||
chatId: popup.chatId
|
||||
name: Utils.removeStatusEns(chatsModel.channelView.activeChannel.name)
|
||||
identicon: chatsModel.channelView.activeChannel.identicon
|
||||
communityId: chatsModel.channelView.activeChannel.communityId
|
||||
anchors.left: inText.right
|
||||
anchors.leftMargin: 4
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
hideSecondIcon: true
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: searchResultContent
|
||||
visible: chatsModel.messageView.searchResultMessageModel.count > 0
|
||||
width: parent.width
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: channelBadge.bottom
|
||||
anchors.topMargin: visible ? 13 : 0
|
||||
|
||||
Separator {
|
||||
id: sep2
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 0
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: sectionTitle
|
||||
//% "Messages"
|
||||
text: qsTrId("messages")
|
||||
font.pixelSize: 15
|
||||
color: Style.current.secondaryText
|
||||
anchors.top: sep2.bottom
|
||||
anchors.topMargin: Style.current.smallPadding
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.bigPadding
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: scrollView
|
||||
anchors.top: sectionTitle.bottom
|
||||
anchors.topMargin: 4
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: Style.current.smallPadding
|
||||
width: parent.width
|
||||
clip: true
|
||||
|
||||
ListView{
|
||||
id: listView
|
||||
model: chatsModel.messageView.searchResultMessageModel
|
||||
|
||||
delegate: Message {
|
||||
|
||||
anchors.right: undefined
|
||||
messageId: model.messageId
|
||||
fromAuthor: model.fromAuthor
|
||||
chatId: model.chatId
|
||||
userName: model.userName
|
||||
alias: model.alias
|
||||
localName: model.localName
|
||||
message: model.message
|
||||
plainText: model.plainText
|
||||
identicon: model.identicon
|
||||
isCurrentUser: model.isCurrentUser
|
||||
timestamp: model.timestamp
|
||||
sticker: model.sticker
|
||||
contentType: model.contentType
|
||||
outgoingStatus: model.outgoingStatus
|
||||
responseTo: model.responseTo
|
||||
imageClick: imagePopup.openPopup.bind(imagePopup)
|
||||
linkUrls: model.linkUrls
|
||||
communityId: model.communityId
|
||||
hasMention: model.hasMention
|
||||
stickerPackId: model.stickerPackId
|
||||
pinnedBy: model.pinnedBy
|
||||
pinnedMessage: model.isPinned
|
||||
activityCenterMessage: true
|
||||
clickMessage: function (isProfileClick) {
|
||||
if (isProfileClick) {
|
||||
const pk = model.fromAuthor
|
||||
const userProfileImage = appMain.getProfileImage(pk)
|
||||
return openProfilePopup(chatsModel.userNameOrAlias(pk), pk, userProfileImage || utilsModel.generateIdenticon(pk))
|
||||
}
|
||||
|
||||
popup.close()
|
||||
|
||||
if(chatsModel.messageView.isMessageDisplayed(model.messageId))
|
||||
positionAtMessage(model.messageId)
|
||||
else
|
||||
chatsModel.messageView.loadMessagesUntillMessageWithIdIsLoaded(model.messageId)
|
||||
}
|
||||
|
||||
prevMessageIndex: -1
|
||||
prevMsgTimestamp: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,13 +21,13 @@ Item {
|
|||
owner = backpressure;
|
||||
}
|
||||
|
||||
owner.Component.onDestruction.connect(cleanup);
|
||||
owner.Component.destruction.connect(cleanup);
|
||||
|
||||
var obj = Qt.createQmlObject('import QtQuick 2.0; Timer {running: false; repeat: false; interval: ' + timeout + '}', backpressure, "setTimeout");
|
||||
obj.triggered.connect(function() {
|
||||
callback();
|
||||
obj.destroy();
|
||||
owner.Component.onDestruction.disconnect(cleanup);
|
||||
owner.Component.destruction.disconnect(cleanup);
|
||||
delete _timers[tid];
|
||||
});
|
||||
obj.running = true;
|
||||
|
|
|
@ -179,7 +179,6 @@ DISTFILES += \
|
|||
app/AppLayouts/Chat/components/InviteFriendsPopup.qml \
|
||||
app/AppLayouts/Chat/components/MessageContextMenu.qml \
|
||||
app/AppLayouts/Chat/components/NicknamePopup.qml \
|
||||
app/AppLayouts/Chat/components/SearchPopup.qml \
|
||||
app/AppLayouts/Chat/components/SuggestedChannels.qml \
|
||||
app/AppLayouts/Chat/components/GroupInfoPopup.qml \
|
||||
app/AppLayouts/Chat/data/channelList.js \
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit c18777749a2574d743ca2b7e5abc654152154f00
|
||||
Subproject commit 2bffa67581c4e7546cfb5784f187b85bc6f97388
|
|
@ -1 +1 @@
|
|||
Subproject commit 2b6e50491786ae0d61a97f99edda27b70364838a
|
||||
Subproject commit f4463f3955a96e162e9881b73ba02f819e0374a4
|
|
@ -1 +1 @@
|
|||
Subproject commit 936a4fa8abd452c65cfd9d40f5ac261b3260a47e
|
||||
Subproject commit 3a2026ebbc8a98ef1ffe15693685feea38286552
|
|
@ -1 +1 @@
|
|||
Subproject commit 9f478db7ad06d0e84e43edf30c52ff586a9d266c
|
||||
Subproject commit 33747cc283b65603565c008b522a0148c5645128
|
Loading…
Reference in New Issue