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:
Sale Djenic 2021-08-06 17:31:42 +02:00 committed by Iuri Matias
parent 4f8b072f10
commit e2628338de
23 changed files with 1065 additions and 269 deletions

View File

@ -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)

View File

@ -313,4 +313,15 @@ QtObject:
return return
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

View File

@ -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"

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -1,14 +1,17 @@
################################################# type
# Async search messages by term AsyncSearchMessagesTaskArg = ref object of QObjectTaskArg
#################################################
type
AsyncSearchMessageTaskArg = 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
################################################# #################################################

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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: ""
}
}
}
}
}

View File

@ -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;

View File

@ -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 \

2
vendor/DOtherSide vendored

@ -1 +1 @@
Subproject commit c18777749a2574d743ca2b7e5abc654152154f00 Subproject commit 2bffa67581c4e7546cfb5784f187b85bc6f97388

@ -1 +1 @@
Subproject commit 2b6e50491786ae0d61a97f99edda27b70364838a Subproject commit f4463f3955a96e162e9881b73ba02f819e0374a4

2
vendor/nimqml vendored

@ -1 +1 @@
Subproject commit 936a4fa8abd452c65cfd9d40f5ac261b3260a47e Subproject commit 3a2026ebbc8a98ef1ffe15693685feea38286552

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit 9f478db7ad06d0e84e43edf30c52ff586a9d266c Subproject commit 33747cc283b65603565c008b522a0148c5645128