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 NimQml, Tables
|
||||||
import ../../../status/chat/[chat, message]
|
import ../../../status/chat/[chat, message]
|
||||||
import ../../../status/status
|
import ../../../status/status
|
||||||
import ../../../status/ens
|
|
||||||
import ../../../status/accounts
|
import ../../../status/accounts
|
||||||
import strutils
|
import strutils
|
||||||
|
|
||||||
|
@ -39,19 +38,6 @@ QtObject:
|
||||||
result.status = status
|
result.status = status
|
||||||
result.setup()
|
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
|
method rowCount*(self: ChannelsList, index: QModelIndex = nil): int = self.chats.len
|
||||||
|
|
||||||
proc renderBlock(self: ChannelsList, message: Message): string
|
proc renderBlock(self: ChannelsList, message: Message): string
|
||||||
|
@ -66,7 +52,7 @@ QtObject:
|
||||||
|
|
||||||
let chatItemRole = role.ChannelsRoles
|
let chatItemRole = role.ChannelsRoles
|
||||||
case chatItemRole:
|
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.Timestamp: result = newQVariant($chatItem.timestamp)
|
||||||
of ChannelsRoles.LastMessage: result = newQVariant(self.renderBlock(chatItem.lastMessage))
|
of ChannelsRoles.LastMessage: result = newQVariant(self.renderBlock(chatItem.lastMessage))
|
||||||
of ChannelsRoles.ContentType: result = newQVariant(chatItem.lastMessage.contentType.int)
|
of ChannelsRoles.ContentType: result = newQVariant(chatItem.lastMessage.contentType.int)
|
||||||
|
@ -136,6 +122,13 @@ QtObject:
|
||||||
if chat.id == chatId:
|
if chat.id == chatId:
|
||||||
return chat
|
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 =
|
proc getChannelByName*(self: ChannelsList, name: string): Chat =
|
||||||
for chat in self.chats:
|
for chat in self.chats:
|
||||||
if chat.name == name:
|
if chat.name == name:
|
||||||
|
@ -213,7 +206,7 @@ QtObject:
|
||||||
|
|
||||||
proc renderInline(self: ChannelsList, elem: TextItem): string =
|
proc renderInline(self: ChannelsList, elem: TextItem): string =
|
||||||
case elem.textType:
|
case elem.textType:
|
||||||
of "mention": result = self.userNameOrAlias(elem.literal)
|
of "mention": result = self.status.chat.userNameOrAlias(elem.literal)
|
||||||
of "link": result = elem.destination
|
of "link": result = elem.destination
|
||||||
else: result = escape_html(elem.literal)
|
else: result = escape_html(elem.literal)
|
||||||
|
|
||||||
|
|
|
@ -314,3 +314,14 @@ QtObject:
|
||||||
self.communities[comIndex].recalculateMentions()
|
self.communities[comIndex].recalculateMentions()
|
||||||
let index = self.createIndex(comIndex, 0, nil)
|
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 chat/[chat, message]
|
||||||
import tasks/[qt, task_runner_impl]
|
import tasks/[qt, task_runner_impl]
|
||||||
import signals/messages
|
import signals/messages
|
||||||
import ens
|
import ens, accounts
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "chat-model"
|
topics = "chat-model"
|
||||||
|
@ -621,6 +621,9 @@ QtObject:
|
||||||
return self.channels[chatId].communityId
|
return self.channels[chatId].communityId
|
||||||
|
|
||||||
proc asyncSearchMessages*(self: ChatModel, chatId: string, searchTerm: string, caseSensitive: bool) =
|
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):
|
if (chatId.len == 0):
|
||||||
info "empty channel id set for fetching more messages"
|
info "empty channel id set for fetching more messages"
|
||||||
return
|
return
|
||||||
|
@ -628,8 +631,8 @@ QtObject:
|
||||||
if (searchTerm.len == 0):
|
if (searchTerm.len == 0):
|
||||||
return
|
return
|
||||||
|
|
||||||
let arg = AsyncSearchMessageTaskArg(
|
let arg = AsyncSearchMessagesInChatTaskArg(
|
||||||
tptr: cast[ByteAddress](asyncSearchMessagesTask),
|
tptr: cast[ByteAddress](asyncSearchMessagesInChatTask),
|
||||||
vptr: cast[ByteAddress](self.vptr),
|
vptr: cast[ByteAddress](self.vptr),
|
||||||
slot: "onAsyncSearchMessages",
|
slot: "onAsyncSearchMessages",
|
||||||
chatId: chatId,
|
chatId: chatId,
|
||||||
|
@ -638,6 +641,29 @@ QtObject:
|
||||||
)
|
)
|
||||||
self.tasks.threadpool.start(arg)
|
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.} =
|
proc onAsyncSearchMessages*(self: ChatModel, response: string) {.slot.} =
|
||||||
let responseObj = response.parseJson
|
let responseObj = response.parseJson
|
||||||
if (responseObj.kind != JObject):
|
if (responseObj.kind != JObject):
|
||||||
|
@ -774,3 +800,31 @@ QtObject:
|
||||||
self.events.emit("messagesLoaded", MsgsLoadedArgs(chatId: chatId, messages: messages))
|
self.events.emit("messagesLoaded", MsgsLoadedArgs(chatId: chatId, messages: messages))
|
||||||
self.events.emit("reactionsLoaded", ReactionsLoadedArgs(reactions: reactions))
|
self.events.emit("reactionsLoaded", ReactionsLoadedArgs(reactions: reactions))
|
||||||
self.events.emit("pinnedMessagesLoaded", MsgsLoadedArgs(chatId: chatId, messages: pinnedMessages))
|
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
|
type
|
||||||
AsyncSearchMessageTaskArg = ref object of QObjectTaskArg
|
AsyncSearchMessagesTaskArg = ref object of QObjectTaskArg
|
||||||
chatId: string
|
|
||||||
searchTerm: string
|
searchTerm: string
|
||||||
caseSensitive: bool
|
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 messages: JsonNode
|
||||||
var success: bool
|
var success: bool
|
||||||
let response = status_chat.asyncSearchMessages(arg.chatId, arg.searchTerm, arg.caseSensitive, success)
|
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)
|
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
|
# Async load messages
|
||||||
#################################################
|
#################################################
|
||||||
|
|
|
@ -246,7 +246,6 @@ proc recalculateUnviewedMessages*(community: var Community) =
|
||||||
community.unviewedMessagesCount = total
|
community.unviewedMessagesCount = total
|
||||||
|
|
||||||
proc recalculateMentions*(community: var Community) =
|
proc recalculateMentions*(community: var Community) =
|
||||||
echo "(recalculateMentions) chatId: ", community.id, " before: ", community.unviewedMentionsCount
|
|
||||||
var total = 0
|
var total = 0
|
||||||
for chat in community.chats:
|
for chat in community.chats:
|
||||||
total += chat.unviewedMentionsCount
|
total += chat.unviewedMentionsCount
|
||||||
|
|
|
@ -569,7 +569,15 @@ proc unreadActivityCenterNotificationsCount*(): int =
|
||||||
proc asyncSearchMessages*(chatId: string, searchTerm: string, caseSensitive: bool, success: var bool): string =
|
proc asyncSearchMessages*(chatId: string, searchTerm: string, caseSensitive: bool, success: var bool): string =
|
||||||
success = true
|
success = true
|
||||||
try:
|
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:
|
except RpcException as e:
|
||||||
success = false
|
success = false
|
||||||
result = e.msg
|
result = e.msg
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 38b0207055f81c853830320e8b20e9532e84973b
|
Subproject commit c68ee3dbe412d8af7cab079b1cb67e0a2a3bf155
|
|
@ -1,8 +1,8 @@
|
||||||
import QtQuick 2.13
|
import QtQuick 2.13
|
||||||
|
import Qt.labs.platform 1.1
|
||||||
import QtQuick.Controls 2.13
|
import QtQuick.Controls 2.13
|
||||||
import QtQuick.Layouts 1.13
|
import QtQuick.Layouts 1.13
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
import Qt.labs.platform 1.1
|
|
||||||
|
|
||||||
import StatusQ.Core.Theme 0.1
|
import StatusQ.Core.Theme 0.1
|
||||||
import StatusQ.Components 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 {
|
StackLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
currentIndex: chatsModel.channelView.activeChannelIndex > -1 && chatGroupsListViewCount > 0 ? 0 : 1
|
currentIndex: chatsModel.channelView.activeChannelIndex > -1 && chatGroupsListViewCount > 0 ? 0 : 1
|
||||||
|
@ -253,9 +354,6 @@ Item {
|
||||||
notificationCount: chatsModel.activityNotificationList.unreadCount
|
notificationCount: chatsModel.activityNotificationList.unreadCount
|
||||||
|
|
||||||
onSearchButtonClicked: searchPopup.open()
|
onSearchButtonClicked: searchPopup.open()
|
||||||
SearchPopup {
|
|
||||||
id: searchPopup
|
|
||||||
}
|
|
||||||
|
|
||||||
onMembersButtonClicked: showUsers = !showUsers
|
onMembersButtonClicked: showUsers = !showUsers
|
||||||
onNotificationButtonClicked: activityCenter.open()
|
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 = 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");
|
var obj = Qt.createQmlObject('import QtQuick 2.0; Timer {running: false; repeat: false; interval: ' + timeout + '}', backpressure, "setTimeout");
|
||||||
obj.triggered.connect(function() {
|
obj.triggered.connect(function() {
|
||||||
callback();
|
callback();
|
||||||
obj.destroy();
|
obj.destroy();
|
||||||
owner.Component.onDestruction.disconnect(cleanup);
|
owner.Component.destruction.disconnect(cleanup);
|
||||||
delete _timers[tid];
|
delete _timers[tid];
|
||||||
});
|
});
|
||||||
obj.running = true;
|
obj.running = true;
|
||||||
|
|
|
@ -179,7 +179,6 @@ DISTFILES += \
|
||||||
app/AppLayouts/Chat/components/InviteFriendsPopup.qml \
|
app/AppLayouts/Chat/components/InviteFriendsPopup.qml \
|
||||||
app/AppLayouts/Chat/components/MessageContextMenu.qml \
|
app/AppLayouts/Chat/components/MessageContextMenu.qml \
|
||||||
app/AppLayouts/Chat/components/NicknamePopup.qml \
|
app/AppLayouts/Chat/components/NicknamePopup.qml \
|
||||||
app/AppLayouts/Chat/components/SearchPopup.qml \
|
|
||||||
app/AppLayouts/Chat/components/SuggestedChannels.qml \
|
app/AppLayouts/Chat/components/SuggestedChannels.qml \
|
||||||
app/AppLayouts/Chat/components/GroupInfoPopup.qml \
|
app/AppLayouts/Chat/components/GroupInfoPopup.qml \
|
||||||
app/AppLayouts/Chat/data/channelList.js \
|
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