feat(Chat): add pinned messages feature
This commit is contained in:
parent
6abba06c42
commit
b52dceb984
|
@ -18,6 +18,9 @@ proc handleChatEvents(self: ChatController) =
|
||||||
# Display emoji reactions
|
# Display emoji reactions
|
||||||
self.status.events.on("reactionsLoaded") do(e:Args):
|
self.status.events.on("reactionsLoaded") do(e:Args):
|
||||||
self.view.reactions.push(ReactionsLoadedArgs(e).reactions)
|
self.view.reactions.push(ReactionsLoadedArgs(e).reactions)
|
||||||
|
# Display already pinned messages
|
||||||
|
self.status.events.on("pinnedMessagesLoaded") do(e:Args):
|
||||||
|
self.view.pushPinnedMessages(MsgsLoadedArgs(e).messages)
|
||||||
|
|
||||||
self.status.events.on("contactUpdate") do(e: Args):
|
self.status.events.on("contactUpdate") do(e: Args):
|
||||||
var evArgs = ContactUpdateArgs(e)
|
var evArgs = ContactUpdateArgs(e)
|
||||||
|
@ -40,6 +43,8 @@ proc handleChatEvents(self: ChatController) =
|
||||||
self.view.communities.addCommunityToList(community)
|
self.view.communities.addCommunityToList(community)
|
||||||
if (evArgs.communityMembershipRequests.len > 0):
|
if (evArgs.communityMembershipRequests.len > 0):
|
||||||
self.view.communities.addMembershipRequests(evArgs.communityMembershipRequests)
|
self.view.communities.addMembershipRequests(evArgs.communityMembershipRequests)
|
||||||
|
if (evArgs.pinnedMessages.len > 0):
|
||||||
|
self.view.addPinnedMessages(evArgs.pinnedMessages)
|
||||||
|
|
||||||
self.status.events.on("channelUpdate") do(e: Args):
|
self.status.events.on("channelUpdate") do(e: Args):
|
||||||
var evArgs = ChatUpdateArgs(e)
|
var evArgs = ChatUpdateArgs(e)
|
||||||
|
|
|
@ -4,7 +4,7 @@ import
|
||||||
proc handleSignals(self: ChatController) =
|
proc handleSignals(self: ChatController) =
|
||||||
self.status.events.on(SignalType.Message.event) do(e:Args):
|
self.status.events.on(SignalType.Message.event) do(e:Args):
|
||||||
var data = MessageSignal(e)
|
var data = MessageSignal(e)
|
||||||
self.status.chat.update(data.chats, data.messages, data.emojiReactions, data.communities, data.membershipRequests)
|
self.status.chat.update(data.chats, data.messages, data.emojiReactions, data.communities, data.membershipRequests, data.pinnedMessages)
|
||||||
|
|
||||||
self.status.events.on(SignalType.DiscoverySummary.event) do(e:Args):
|
self.status.events.on(SignalType.DiscoverySummary.event) do(e:Args):
|
||||||
## Handle mailserver peers being added and removed
|
## Handle mailserver peers being added and removed
|
||||||
|
|
|
@ -65,10 +65,17 @@ const asyncMessageLoadTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.}
|
||||||
if(reactionsCallSuccess):
|
if(reactionsCallSuccess):
|
||||||
reactions = reactionsCallResult.parseJson()["result"]
|
reactions = reactionsCallResult.parseJson()["result"]
|
||||||
|
|
||||||
|
var pinnedMessages: JsonNode
|
||||||
|
var pinnedMessagesCallSuccess: bool
|
||||||
|
let pinnedMessagesCallResult = rpcPinnedChatMessages(arg.chatId, newJString(""), 20, pinnedMessagesCallSuccess)
|
||||||
|
if(reactionsCallSuccess):
|
||||||
|
pinnedMessages = pinnedMessagesCallResult.parseJson()["result"]
|
||||||
|
|
||||||
let responseJson = %*{
|
let responseJson = %*{
|
||||||
"chatId": arg.chatId,
|
"chatId": arg.chatId,
|
||||||
"messages": messages,
|
"messages": messages,
|
||||||
"reactions": reactions
|
"reactions": reactions,
|
||||||
|
"pinnedMessages": pinnedMessages
|
||||||
}
|
}
|
||||||
arg.finish(responseJson)
|
arg.finish(responseJson)
|
||||||
|
|
||||||
|
@ -104,6 +111,7 @@ QtObject:
|
||||||
currentSuggestions*: SuggestionsList
|
currentSuggestions*: SuggestionsList
|
||||||
callResult: string
|
callResult: string
|
||||||
messageList*: OrderedTable[string, ChatMessageList]
|
messageList*: OrderedTable[string, ChatMessageList]
|
||||||
|
pinnedMessagesList*: OrderedTable[string, ChatMessageList]
|
||||||
reactions*: ReactionView
|
reactions*: ReactionView
|
||||||
stickers*: StickersView
|
stickers*: StickersView
|
||||||
groups*: GroupsView
|
groups*: GroupsView
|
||||||
|
@ -129,11 +137,14 @@ QtObject:
|
||||||
self.currentSuggestions.delete
|
self.currentSuggestions.delete
|
||||||
for msg in self.messageList.values:
|
for msg in self.messageList.values:
|
||||||
msg.delete
|
msg.delete
|
||||||
|
for msg in self.pinnedMessagesList.values:
|
||||||
|
msg.delete
|
||||||
self.reactions.delete
|
self.reactions.delete
|
||||||
self.stickers.delete
|
self.stickers.delete
|
||||||
self.groups.delete
|
self.groups.delete
|
||||||
self.transactions.delete
|
self.transactions.delete
|
||||||
self.messageList = initOrderedTable[string, ChatMessageList]()
|
self.messageList = initOrderedTable[string, ChatMessageList]()
|
||||||
|
self.pinnedMessagesList = initOrderedTable[string, ChatMessageList]()
|
||||||
self.communities.delete
|
self.communities.delete
|
||||||
self.channelOpenTime = initTable[string, int64]()
|
self.channelOpenTime = initTable[string, int64]()
|
||||||
self.QAbstractListModel.delete
|
self.QAbstractListModel.delete
|
||||||
|
@ -147,6 +158,7 @@ QtObject:
|
||||||
result.contextChannel = newChatItemView(status)
|
result.contextChannel = newChatItemView(status)
|
||||||
result.currentSuggestions = newSuggestionsList()
|
result.currentSuggestions = newSuggestionsList()
|
||||||
result.messageList = initOrderedTable[string, ChatMessageList]()
|
result.messageList = initOrderedTable[string, ChatMessageList]()
|
||||||
|
result.pinnedMessagesList = initOrderedTable[string, ChatMessageList]()
|
||||||
result.reactions = newReactionView(status, result.messageList.addr, result.activeChannel)
|
result.reactions = newReactionView(status, result.messageList.addr, result.activeChannel)
|
||||||
result.stickers = newStickersView(status, result.activeChannel)
|
result.stickers = newStickersView(status, result.activeChannel)
|
||||||
result.groups = newGroupsView(status,result.activeChannel)
|
result.groups = newGroupsView(status,result.activeChannel)
|
||||||
|
@ -431,6 +443,7 @@ QtObject:
|
||||||
if not self.messageList.hasKey(channel):
|
if not self.messageList.hasKey(channel):
|
||||||
self.beginInsertRows(newQModelIndex(), self.messageList.len, self.messageList.len)
|
self.beginInsertRows(newQModelIndex(), self.messageList.len, self.messageList.len)
|
||||||
self.messageList[channel] = newChatMessageList(channel, self.status, not chat.isNil and chat.chatType != ChatType.Profile)
|
self.messageList[channel] = newChatMessageList(channel, self.status, not chat.isNil and chat.chatType != ChatType.Profile)
|
||||||
|
self.pinnedMessagesList[channel] = newChatMessageList(channel, self.status, false)
|
||||||
self.channelOpenTime[channel] = now().toTime.toUnix * 1000
|
self.channelOpenTime[channel] = now().toTime.toUnix * 1000
|
||||||
self.endInsertRows();
|
self.endInsertRows();
|
||||||
|
|
||||||
|
@ -451,6 +464,13 @@ QtObject:
|
||||||
proc isAddedContact*(self: ChatsView, id: string): bool {.slot.} =
|
proc isAddedContact*(self: ChatsView, id: string): bool {.slot.} =
|
||||||
result = self.status.contacts.isAdded(id)
|
result = self.status.contacts.isAdded(id)
|
||||||
|
|
||||||
|
proc pushPinnedMessages*(self:ChatsView, messages: var seq[Message]) =
|
||||||
|
for msg in messages.mitems:
|
||||||
|
self.upsertChannel(msg.chatId)
|
||||||
|
self.pinnedMessagesList[msg.chatId].add(msg)
|
||||||
|
# put the message as pinned in the message list
|
||||||
|
self.messageList[msg.chatId].changeMessagePinned(msg.id, true)
|
||||||
|
|
||||||
proc pushMessages*(self:ChatsView, messages: var seq[Message]) =
|
proc pushMessages*(self:ChatsView, messages: var seq[Message]) =
|
||||||
for msg in messages.mitems:
|
for msg in messages.mitems:
|
||||||
self.upsertChannel(msg.chatId)
|
self.upsertChannel(msg.chatId)
|
||||||
|
@ -532,6 +552,14 @@ QtObject:
|
||||||
read = getMessageList
|
read = getMessageList
|
||||||
notify = activeChannelChanged
|
notify = activeChannelChanged
|
||||||
|
|
||||||
|
proc getPinnedMessagesList(self: ChatsView): QVariant {.slot.} =
|
||||||
|
self.upsertChannel(self.activeChannel.id)
|
||||||
|
return newQVariant(self.pinnedMessagesList[self.activeChannel.id])
|
||||||
|
|
||||||
|
QtProperty[QVariant] pinnedMessagesList:
|
||||||
|
read = getPinnedMessagesList
|
||||||
|
notify = activeChannelChanged
|
||||||
|
|
||||||
proc pushChatItem*(self: ChatsView, chatItem: Chat) =
|
proc pushChatItem*(self: ChatsView, chatItem: Chat) =
|
||||||
discard self.chats.addChatItemToList(chatItem)
|
discard self.chats.addChatItemToList(chatItem)
|
||||||
self.messagePushed(self.messageList[chatItem.id].messages.len - 1)
|
self.messagePushed(self.messageList[chatItem.id].messages.len - 1)
|
||||||
|
@ -599,6 +627,10 @@ QtObject:
|
||||||
let reactions = parseReactionsResponse(rpcResponseObj["chatId"].getStr, rpcResponseObj["reactions"])
|
let reactions = parseReactionsResponse(rpcResponseObj["chatId"].getStr, rpcResponseObj["reactions"])
|
||||||
self.status.chat.chatReactions(rpcResponseObj["chatId"].getStr, true, reactions[0], reactions[1])
|
self.status.chat.chatReactions(rpcResponseObj["chatId"].getStr, true, reactions[0], reactions[1])
|
||||||
|
|
||||||
|
if(rpcResponseObj["pinnedMessages"].kind != JNull):
|
||||||
|
let pinnedMessages = parseChatMessagesResponse(rpcResponseObj["chatId"].getStr, rpcResponseObj["pinnedMessages"])
|
||||||
|
self.status.chat.pinnedMessagesByChatID(rpcResponseObj["chatId"].getStr, pinnedMessages[0], pinnedMessages[1])
|
||||||
|
|
||||||
proc hideLoadingIndicator*(self: ChatsView) {.slot.} =
|
proc hideLoadingIndicator*(self: ChatsView) {.slot.} =
|
||||||
self.loadingMessages = false
|
self.loadingMessages = false
|
||||||
self.loadingMessagesChanged(false)
|
self.loadingMessagesChanged(false)
|
||||||
|
@ -835,6 +867,35 @@ QtObject:
|
||||||
if(id == msg.id): return idx
|
if(id == msg.id): return idx
|
||||||
return idx
|
return idx
|
||||||
|
|
||||||
|
proc addPinMessage*(self: ChatsView, messageId: string, chatId: string) =
|
||||||
|
self.upsertChannel(chatId)
|
||||||
|
self.messageList[chatId].changeMessagePinned(messageId, true)
|
||||||
|
self.pinnedMessagesList[chatId].add(self.messageList[chatId].getMessageById(messageId))
|
||||||
|
|
||||||
|
proc removePinMessage*(self: ChatsView, messageId: string, chatId: string) =
|
||||||
|
self.upsertChannel(chatId)
|
||||||
|
self.messageList[chatId].changeMessagePinned(messageId, false)
|
||||||
|
try:
|
||||||
|
self.pinnedMessagesList[chatId].remove(messageId)
|
||||||
|
except Exception as e:
|
||||||
|
error "Error removing ", msg = e.msg
|
||||||
|
|
||||||
|
|
||||||
|
proc pinMessage*(self: ChatsView, messageId: string, chatId: string) {.slot.} =
|
||||||
|
self.status.chat.setPinMessage(messageId, chatId, true)
|
||||||
|
self.addPinMessage(messageId, chatId)
|
||||||
|
|
||||||
|
proc unPinMessage*(self: ChatsView, messageId: string, chatId: string) {.slot.} =
|
||||||
|
self.status.chat.setPinMessage(messageId, chatId, false)
|
||||||
|
self.removePinMessage(messageId, chatId)
|
||||||
|
|
||||||
|
proc addPinnedMessages*(self: ChatsView, pinnedMessages: seq[Message]) =
|
||||||
|
for pinnedMessage in pinnedMessages:
|
||||||
|
if (pinnedMessage.isPinned):
|
||||||
|
self.addPinMessage(pinnedMessage.id, pinnedMessage.localChatId)
|
||||||
|
else:
|
||||||
|
self.removePinMessage(pinnedMessage.id, pinnedMessage.localChatId)
|
||||||
|
|
||||||
proc isActiveMailserverResult(self: ChatsView, resultEncoded: string) {.slot.} =
|
proc isActiveMailserverResult(self: ChatsView, resultEncoded: string) {.slot.} =
|
||||||
let isActiveMailserverAvailable = decode[bool](resultEncoded)
|
let isActiveMailserverAvailable = decode[bool](resultEncoded)
|
||||||
if isActiveMailserverAvailable:
|
if isActiveMailserverAvailable:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import NimQml, Tables, sets, json, sugar
|
import NimQml, Tables, sets, json, sugar, chronicles
|
||||||
import ../../../status/status
|
import ../../../status/status
|
||||||
import ../../../status/accounts
|
import ../../../status/accounts
|
||||||
import ../../../status/chat
|
import ../../../status/chat
|
||||||
|
@ -37,8 +37,9 @@ type
|
||||||
CommunityId = UserRole + 27
|
CommunityId = UserRole + 27
|
||||||
HasMention = UserRole + 28
|
HasMention = UserRole + 28
|
||||||
StickerPackId = UserRole + 29
|
StickerPackId = UserRole + 29
|
||||||
GapFrom = UserRole + 30
|
IsPinned = UserRole + 30
|
||||||
GapTo = UserRole + 31
|
GapFrom = UserRole + 31
|
||||||
|
GapTo = UserRole + 32
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type
|
type
|
||||||
|
@ -124,6 +125,15 @@ QtObject:
|
||||||
method rowCount(self: ChatMessageList, index: QModelIndex = nil): int =
|
method rowCount(self: ChatMessageList, index: QModelIndex = nil): int =
|
||||||
return self.messages.len
|
return self.messages.len
|
||||||
|
|
||||||
|
proc countChanged*(self: ChatMessageList) {.signal.}
|
||||||
|
|
||||||
|
proc count*(self: ChatMessageList): int {.slot.} =
|
||||||
|
self.messages.len
|
||||||
|
|
||||||
|
QtProperty[int] count:
|
||||||
|
read = count
|
||||||
|
notify = countChanged
|
||||||
|
|
||||||
proc getReactions*(self:ChatMessageList, messageId: string):string =
|
proc getReactions*(self:ChatMessageList, messageId: string):string =
|
||||||
if not self.messageReactions.hasKey(messageId): return ""
|
if not self.messageReactions.hasKey(messageId): return ""
|
||||||
result = self.messageReactions[messageId]
|
result = self.messageReactions[messageId]
|
||||||
|
@ -161,6 +171,7 @@ QtObject:
|
||||||
of ChatMessageRoles.LinkUrls: result = newQVariant(message.linkUrls)
|
of ChatMessageRoles.LinkUrls: result = newQVariant(message.linkUrls)
|
||||||
of ChatMessageRoles.CommunityId: result = newQVariant(message.communityId)
|
of ChatMessageRoles.CommunityId: result = newQVariant(message.communityId)
|
||||||
of ChatMessageRoles.HasMention: result = newQVariant(message.hasMention)
|
of ChatMessageRoles.HasMention: result = newQVariant(message.hasMention)
|
||||||
|
of ChatMessageRoles.IsPinned: result = newQVariant(message.isPinned)
|
||||||
# Pass the command parameters as a JSON string
|
# Pass the command parameters as a JSON string
|
||||||
of ChatMessageRoles.CommandParameters: result = newQVariant($(%*{
|
of ChatMessageRoles.CommandParameters: result = newQVariant($(%*{
|
||||||
"id": message.commandParameters.id,
|
"id": message.commandParameters.id,
|
||||||
|
@ -205,6 +216,7 @@ QtObject:
|
||||||
ChatMessageRoles.CommunityId.int: "communityId",
|
ChatMessageRoles.CommunityId.int: "communityId",
|
||||||
ChatMessageRoles.Alias.int:"alias",
|
ChatMessageRoles.Alias.int:"alias",
|
||||||
ChatMessageRoles.HasMention.int:"hasMention",
|
ChatMessageRoles.HasMention.int:"hasMention",
|
||||||
|
ChatMessageRoles.IsPinned.int:"isPinned",
|
||||||
ChatMessageRoles.LocalName.int:"localName",
|
ChatMessageRoles.LocalName.int:"localName",
|
||||||
ChatMessageRoles.StickerPackId.int:"stickerPackId",
|
ChatMessageRoles.StickerPackId.int:"stickerPackId",
|
||||||
ChatMessageRoles.GapFrom.int:"gapFrom",
|
ChatMessageRoles.GapFrom.int:"gapFrom",
|
||||||
|
@ -237,9 +249,11 @@ QtObject:
|
||||||
proc add*(self: ChatMessageList, message: Message) =
|
proc add*(self: ChatMessageList, message: Message) =
|
||||||
if self.messageIndex.hasKey(message.id): return # duplicated msg
|
if self.messageIndex.hasKey(message.id): return # duplicated msg
|
||||||
|
|
||||||
|
debug "New message", chatId = self.id, id = message.id, text = message.text
|
||||||
self.beginInsertRows(newQModelIndex(), self.messages.len, self.messages.len)
|
self.beginInsertRows(newQModelIndex(), self.messages.len, self.messages.len)
|
||||||
self.messageIndex[message.id] = self.messages.len
|
self.messageIndex[message.id] = self.messages.len
|
||||||
self.messages.add(message)
|
self.messages.add(message)
|
||||||
|
self.countChanged()
|
||||||
self.endInsertRows()
|
self.endInsertRows()
|
||||||
|
|
||||||
proc add*(self: ChatMessageList, messages: seq[Message]) =
|
proc add*(self: ChatMessageList, messages: seq[Message]) =
|
||||||
|
@ -248,8 +262,19 @@ QtObject:
|
||||||
if self.messageIndex.hasKey(message.id): continue
|
if self.messageIndex.hasKey(message.id): continue
|
||||||
self.messageIndex[message.id] = self.messages.len
|
self.messageIndex[message.id] = self.messages.len
|
||||||
self.messages.add(message)
|
self.messages.add(message)
|
||||||
|
self.countChanged()
|
||||||
self.endInsertRows()
|
self.endInsertRows()
|
||||||
|
|
||||||
|
proc remove*(self: ChatMessageList, messageId: string) =
|
||||||
|
if not self.messageIndex.hasKey(messageId): return
|
||||||
|
|
||||||
|
let index = self.getMessageIndex(messageId)
|
||||||
|
self.beginRemoveRows(newQModelIndex(), index, index)
|
||||||
|
self.messages.delete(index)
|
||||||
|
self.messageIndex.del(messageId)
|
||||||
|
self.countChanged()
|
||||||
|
self.endRemoveRows()
|
||||||
|
|
||||||
proc getMessageById*(self: ChatMessageList, messageId: string): Message =
|
proc getMessageById*(self: ChatMessageList, messageId: string): Message =
|
||||||
if (not self.messageIndex.hasKey(messageId)): return
|
if (not self.messageIndex.hasKey(messageId)): return
|
||||||
return self.messages[self.messageIndex[messageId]]
|
return self.messages[self.messageIndex[messageId]]
|
||||||
|
@ -269,6 +294,16 @@ QtObject:
|
||||||
let bottomRight = self.createIndex(msgIdx, 0, nil)
|
let bottomRight = self.createIndex(msgIdx, 0, nil)
|
||||||
self.dataChanged(topLeft, bottomRight, @[ChatMessageRoles.EmojiReactions.int])
|
self.dataChanged(topLeft, bottomRight, @[ChatMessageRoles.EmojiReactions.int])
|
||||||
|
|
||||||
|
proc changeMessagePinned*(self: ChatMessageList, messageId: string, pinned: bool) =
|
||||||
|
if not self.messageIndex.hasKey(messageId): return
|
||||||
|
let msgIdx = self.messageIndex[messageId]
|
||||||
|
var message = self.messages[msgIdx]
|
||||||
|
message.isPinned = pinned
|
||||||
|
self.messages[msgIdx] = message
|
||||||
|
let topLeft = self.createIndex(msgIdx, 0, nil)
|
||||||
|
let bottomRight = self.createIndex(msgIdx, 0, nil)
|
||||||
|
self.dataChanged(topLeft, bottomRight, @[ChatMessageRoles.IsPinned.int])
|
||||||
|
|
||||||
proc markMessageAsSent*(self: ChatMessageList, messageId: string)=
|
proc markMessageAsSent*(self: ChatMessageList, messageId: string)=
|
||||||
let topLeft = self.createIndex(0, 0, nil)
|
let topLeft = self.createIndex(0, 0, nil)
|
||||||
let bottomRight = self.createIndex(self.messages.len, 0, nil)
|
let bottomRight = self.createIndex(self.messages.len, 0, nil)
|
||||||
|
|
|
@ -24,6 +24,7 @@ type
|
||||||
ChatUpdateArgs* = ref object of Args
|
ChatUpdateArgs* = ref object of Args
|
||||||
chats*: seq[Chat]
|
chats*: seq[Chat]
|
||||||
messages*: seq[Message]
|
messages*: seq[Message]
|
||||||
|
pinnedMessages*: seq[Message]
|
||||||
contacts*: seq[Profile]
|
contacts*: seq[Profile]
|
||||||
emojiReactions*: seq[Reaction]
|
emojiReactions*: seq[Reaction]
|
||||||
communities*: seq[Community]
|
communities*: seq[Community]
|
||||||
|
@ -56,6 +57,7 @@ type
|
||||||
contacts*: Table[string, Profile]
|
contacts*: Table[string, Profile]
|
||||||
channels*: Table[string, Chat]
|
channels*: Table[string, Chat]
|
||||||
msgCursor*: Table[string, string]
|
msgCursor*: Table[string, string]
|
||||||
|
pinnedMsgCursor*: Table[string, string]
|
||||||
emojiCursor*: Table[string, string]
|
emojiCursor*: Table[string, string]
|
||||||
lastMessageTimestamps*: Table[string, int64]
|
lastMessageTimestamps*: Table[string, int64]
|
||||||
|
|
||||||
|
@ -73,6 +75,7 @@ proc newChatModel*(events: EventEmitter): ChatModel =
|
||||||
result.contacts = initTable[string, Profile]()
|
result.contacts = initTable[string, Profile]()
|
||||||
result.channels = initTable[string, Chat]()
|
result.channels = initTable[string, Chat]()
|
||||||
result.msgCursor = initTable[string, string]()
|
result.msgCursor = initTable[string, string]()
|
||||||
|
result.pinnedMsgCursor = initTable[string, string]()
|
||||||
result.emojiCursor = initTable[string, string]()
|
result.emojiCursor = initTable[string, string]()
|
||||||
result.lastMessageTimestamps = initTable[string, int64]()
|
result.lastMessageTimestamps = initTable[string, int64]()
|
||||||
|
|
||||||
|
@ -99,7 +102,7 @@ proc cleanSpamChatGroups(self: ChatModel, chats: seq[Chat], contacts: seq[Profil
|
||||||
else:
|
else:
|
||||||
result.add(chat)
|
result.add(chat)
|
||||||
|
|
||||||
proc update*(self: ChatModel, chats: seq[Chat], messages: seq[Message], emojiReactions: seq[Reaction], communities: seq[Community], communityMembershipRequests: seq[CommunityMembershipRequest]) =
|
proc update*(self: ChatModel, chats: seq[Chat], messages: seq[Message], emojiReactions: seq[Reaction], communities: seq[Community], communityMembershipRequests: seq[CommunityMembershipRequest], pinnedMessages: seq[Message]) =
|
||||||
var contacts = getAddedContacts()
|
var contacts = getAddedContacts()
|
||||||
|
|
||||||
# Automatically decline chat group invitations if admin is not a contact
|
# Automatically decline chat group invitations if admin is not a contact
|
||||||
|
@ -118,7 +121,7 @@ proc update*(self: ChatModel, chats: seq[Chat], messages: seq[Message], emojiRea
|
||||||
if self.lastMessageTimestamps[chatId] > ts:
|
if self.lastMessageTimestamps[chatId] > ts:
|
||||||
self.lastMessageTimestamps[chatId] = ts
|
self.lastMessageTimestamps[chatId] = ts
|
||||||
|
|
||||||
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chatList, contacts: @[], emojiReactions: emojiReactions, communities: communities, communityMembershipRequests: communityMembershipRequests))
|
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chatList, contacts: @[], emojiReactions: emojiReactions, communities: communities, communityMembershipRequests: communityMembershipRequests, pinnedMessages: pinnedMessages))
|
||||||
|
|
||||||
proc hasChannel*(self: ChatModel, chatId: string): bool =
|
proc hasChannel*(self: ChatModel, chatId: string): bool =
|
||||||
self.channels.hasKey(chatId)
|
self.channels.hasKey(chatId)
|
||||||
|
@ -504,3 +507,20 @@ proc pendingRequestsToJoinForCommunity*(self: ChatModel, communityKey: string):
|
||||||
|
|
||||||
proc myPendingRequestsToJoin*(self: ChatModel): seq[CommunityMembershipRequest] =
|
proc myPendingRequestsToJoin*(self: ChatModel): seq[CommunityMembershipRequest] =
|
||||||
result = status_chat.myPendingRequestsToJoin()
|
result = status_chat.myPendingRequestsToJoin()
|
||||||
|
|
||||||
|
proc setPinMessage*(self: ChatModel, messageId: string, chatId: string, pinned: bool) =
|
||||||
|
status_chat.setPinMessage(messageId, chatId, pinned)
|
||||||
|
|
||||||
|
proc pinnedMessagesByChatID*(self: ChatModel, chatId: string): seq[Message] =
|
||||||
|
if not self.pinnedMsgCursor.hasKey(chatId):
|
||||||
|
self.pinnedMsgCursor[chatId] = "";
|
||||||
|
|
||||||
|
let messageTuple = status_chat.pinnedMessagesByChatID(chatId, self.pinnedMsgCursor[chatId])
|
||||||
|
self.pinnedMsgCursor[chatId] = messageTuple[0];
|
||||||
|
|
||||||
|
result = messageTuple[1]
|
||||||
|
|
||||||
|
proc pinnedMessagesByChatID*(self: ChatModel, chatId: string, cursor: string = "", pinnedMessages: seq[Message]) =
|
||||||
|
self.msgCursor[chatId] = cursor
|
||||||
|
|
||||||
|
self.events.emit("pinnedMessagesLoaded", MsgsLoadedArgs(messages: pinnedMessages))
|
||||||
|
|
|
@ -68,6 +68,7 @@ type Message* = object
|
||||||
communityId*: string
|
communityId*: string
|
||||||
audioDurationMs*: int
|
audioDurationMs*: int
|
||||||
hasMention*: bool
|
hasMention*: bool
|
||||||
|
isPinned*: bool
|
||||||
|
|
||||||
type Reaction* = object
|
type Reaction* = object
|
||||||
id*: string
|
id*: string
|
||||||
|
|
|
@ -454,3 +454,31 @@ proc banUserFromCommunity*(pubKey: string, communityId: string): string =
|
||||||
"communityId": communityId,
|
"communityId": communityId,
|
||||||
"user": pubKey
|
"user": pubKey
|
||||||
}])
|
}])
|
||||||
|
|
||||||
|
proc rpcPinnedChatMessages*(chatId: string, cursorVal: JsonNode, limit: int, success: var bool): string =
|
||||||
|
success = true
|
||||||
|
try:
|
||||||
|
result = callPrivateRPC("chatPinnedMessages".prefix, %* [chatId, cursorVal, limit])
|
||||||
|
except RpcException as e:
|
||||||
|
success = false
|
||||||
|
result = e.msg
|
||||||
|
|
||||||
|
proc pinnedMessagesByChatID*(chatId: string, cursor: string): (string, seq[Message]) =
|
||||||
|
var cursorVal: JsonNode
|
||||||
|
|
||||||
|
if cursor == "":
|
||||||
|
cursorVal = newJNull()
|
||||||
|
else:
|
||||||
|
cursorVal = newJString(cursor)
|
||||||
|
|
||||||
|
var success: bool
|
||||||
|
let callResult = rpcPinnedChatMessages(chatId, cursorVal, 20, success)
|
||||||
|
if success:
|
||||||
|
result = parseChatMessagesResponse(chatId, callResult.parseJson()["result"])
|
||||||
|
|
||||||
|
proc setPinMessage*(messageId: string, chatId: string, pinned: bool) =
|
||||||
|
discard callPrivateRPC("sendPinMessage".prefix, %*[{
|
||||||
|
"message_id": messageId,
|
||||||
|
"pinned": pinned,
|
||||||
|
"chat_id": chatId
|
||||||
|
}])
|
|
@ -1,7 +1,7 @@
|
||||||
import json, json, options, json_serialization, stint, chronicles
|
import json, json, options, json_serialization, stint, chronicles
|
||||||
import core, types, utils, strutils, strformat
|
import core, types, utils, strutils, strformat
|
||||||
import utils
|
import utils
|
||||||
from status_go import validateMnemonic, startWallet
|
from status_go import validateMnemonic#, startWallet
|
||||||
import ../wallet/account
|
import ../wallet/account
|
||||||
import web3/ethtypes
|
import web3/ethtypes
|
||||||
import ./types
|
import ./types
|
||||||
|
|
|
@ -63,10 +63,29 @@ proc fromEvent*(event: JsonNode): Signal =
|
||||||
signal.communities.add(jsonCommunity.toCommunity)
|
signal.communities.add(jsonCommunity.toCommunity)
|
||||||
|
|
||||||
if event["event"]{"requestsToJoinCommunity"} != nil:
|
if event["event"]{"requestsToJoinCommunity"} != nil:
|
||||||
debug "requests", event = event["event"]["requestsToJoinCommunity"]
|
|
||||||
for jsonCommunity in event["event"]["requestsToJoinCommunity"]:
|
for jsonCommunity in event["event"]["requestsToJoinCommunity"]:
|
||||||
signal.membershipRequests.add(jsonCommunity.toCommunityMembershipRequest)
|
signal.membershipRequests.add(jsonCommunity.toCommunityMembershipRequest)
|
||||||
|
|
||||||
|
if event["event"]{"pinMessages"} != nil:
|
||||||
|
for jsonPinnedMessage in event["event"]["pinMessages"]:
|
||||||
|
var contentType: ContentType
|
||||||
|
try:
|
||||||
|
contentType = ContentType(jsonPinnedMessage{"contentType"}.getInt)
|
||||||
|
except:
|
||||||
|
warn "Unknown content type received", type = jsonPinnedMessage{"contentType"}.getInt
|
||||||
|
contentType = ContentType.Message
|
||||||
|
signal.pinnedMessages.add(Message(
|
||||||
|
id: jsonPinnedMessage{"message_id"}.getStr,
|
||||||
|
chatId: jsonPinnedMessage{"chat_id"}.getStr,
|
||||||
|
localChatId: jsonPinnedMessage{"localChatId"}.getStr,
|
||||||
|
fromAuthor: jsonPinnedMessage{"from"}.getStr,
|
||||||
|
identicon: jsonPinnedMessage{"identicon"}.getStr,
|
||||||
|
alias: jsonPinnedMessage{"alias"}.getStr,
|
||||||
|
clock: jsonPinnedMessage{"clock"}.getInt,
|
||||||
|
isPinned: jsonPinnedMessage{"pinned"}.getBool,
|
||||||
|
contentType: contentType
|
||||||
|
))
|
||||||
|
|
||||||
result = signal
|
result = signal
|
||||||
|
|
||||||
proc toChatMember*(jsonMember: JsonNode): ChatMember =
|
proc toChatMember*(jsonMember: JsonNode): ChatMember =
|
||||||
|
@ -206,8 +225,6 @@ proc toCommunity*(jsonCommunity: JsonNode): Community =
|
||||||
name: chat{"name"}.getStr,
|
name: chat{"name"}.getStr,
|
||||||
canPost: chat{"canPost"}.getBool,
|
canPost: chat{"canPost"}.getBool,
|
||||||
chatType: ChatType.CommunityChat
|
chatType: ChatType.CommunityChat
|
||||||
# TODO get this from access
|
|
||||||
#chat{"permissions"}{"access"}.getInt,
|
|
||||||
))
|
))
|
||||||
|
|
||||||
if jsonCommunity.hasKey("categories") and jsonCommunity["categories"].kind != JNull:
|
if jsonCommunity.hasKey("categories") and jsonCommunity["categories"].kind != JNull:
|
||||||
|
|
|
@ -29,6 +29,7 @@ type EnvelopeExpiredSignal* = ref object of Signal
|
||||||
|
|
||||||
type MessageSignal* = ref object of Signal
|
type MessageSignal* = ref object of Signal
|
||||||
messages*: seq[Message]
|
messages*: seq[Message]
|
||||||
|
pinnedMessages*: seq[Message]
|
||||||
chats*: seq[Chat]
|
chats*: seq[Chat]
|
||||||
contacts*: seq[Profile]
|
contacts*: seq[Profile]
|
||||||
installations*: seq[Installation]
|
installations*: seq[Installation]
|
||||||
|
|
|
@ -335,6 +335,7 @@ ScrollView {
|
||||||
communityId: model.communityId
|
communityId: model.communityId
|
||||||
hasMention: model.hasMention
|
hasMention: model.hasMention
|
||||||
stickerPackId: model.stickerPackId
|
stickerPackId: model.stickerPackId
|
||||||
|
pinnedMessage: model.isPinned
|
||||||
gapFrom: model.gapFrom
|
gapFrom: model.gapFrom
|
||||||
gapTo: model.gapTo
|
gapTo: model.gapTo
|
||||||
prevMessageIndex: {
|
prevMessageIndex: {
|
||||||
|
|
|
@ -29,6 +29,8 @@ Item {
|
||||||
property bool hasMention: false
|
property bool hasMention: false
|
||||||
property string linkUrls: ""
|
property string linkUrls: ""
|
||||||
property bool placeholderMessage: false
|
property bool placeholderMessage: false
|
||||||
|
property bool pinnedMessage: false
|
||||||
|
property bool forceHoverHandler: false // Used to force the HoverHandler to be active (useful for messages in popups)
|
||||||
property string communityId: ""
|
property string communityId: ""
|
||||||
property int stickerPackId: -1
|
property int stickerPackId: -1
|
||||||
property int gapFrom: 0
|
property int gapFrom: 0
|
||||||
|
@ -164,7 +166,7 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function clickMessage(isProfileClick, isSticker = false, isImage = false, image = null, emojiOnly = false) {
|
function clickMessage(isProfileClick, isSticker = false, isImage = false, image = null, emojiOnly = false, hideEmojiPicker = false) {
|
||||||
if (isImage) {
|
if (isImage) {
|
||||||
imageClick(image);
|
imageClick(image);
|
||||||
return;
|
return;
|
||||||
|
@ -176,13 +178,18 @@ Item {
|
||||||
|
|
||||||
// Get contact nickname
|
// Get contact nickname
|
||||||
let nickname = appMain.getUserNickname(fromAuthor)
|
let nickname = appMain.getUserNickname(fromAuthor)
|
||||||
|
messageContextMenu.messageId = root.messageId
|
||||||
messageContextMenu.linkUrls = root.linkUrls
|
messageContextMenu.linkUrls = root.linkUrls
|
||||||
messageContextMenu.isProfile = !!isProfileClick
|
messageContextMenu.isProfile = !!isProfileClick
|
||||||
messageContextMenu.isSticker = isSticker
|
messageContextMenu.isSticker = isSticker
|
||||||
messageContextMenu.emojiOnly = emojiOnly
|
messageContextMenu.emojiOnly = emojiOnly
|
||||||
messageContextMenu.show(userName, fromAuthor, root.profileImageSource || identicon, "", nickname, emojiReactionsModel)
|
messageContextMenu.hideEmojiPicker = hideEmojiPicker
|
||||||
|
messageContextMenu.pinnedMessage = pinnedMessage
|
||||||
|
messageContextMenu.show(userName, fromAuthor, root.profileImageSource || identicon, plainText, nickname, emojiReactionsModel)
|
||||||
// Position the center of the menu where the mouse is
|
// Position the center of the menu where the mouse is
|
||||||
messageContextMenu.x = messageContextMenu.x - messageContextMenu.width / 2
|
if (messageContextMenu.x + messageContextMenu.width + Style.current.padding < root.width) {
|
||||||
|
messageContextMenu.x = messageContextMenu.x - messageContextMenu.width / 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
|
|
|
@ -96,5 +96,26 @@ Rectangle {
|
||||||
text: qsTrId("message-reply")
|
text: qsTrId("message-reply")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StatusIconButton {
|
||||||
|
id: otherBtn
|
||||||
|
icon.name: "dots-icon"
|
||||||
|
width: 32
|
||||||
|
height: 32
|
||||||
|
onClicked: {
|
||||||
|
if (typeof isMessageActive !== "undefined") {
|
||||||
|
isMessageActive = true
|
||||||
|
}
|
||||||
|
clickMessage(false, isSticker, false, null, false, true)
|
||||||
|
}
|
||||||
|
onHoveredChanged: {
|
||||||
|
buttonsContainer.hoverChanged(this.hovered)
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusToolTip {
|
||||||
|
visible: otherBtn.hovered
|
||||||
|
text: qsTr("More")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,13 +67,61 @@ Item {
|
||||||
+ (!chatName.visible && chatImageContent.active ? 6 : 0)
|
+ (!chatName.visible && chatImageContent.active ? 6 : 0)
|
||||||
+ (emojiReactionLoader.active ? emojiReactionLoader.height: 0)
|
+ (emojiReactionLoader.active ? emojiReactionLoader.height: 0)
|
||||||
+ (retry.visible && !chatTime.visible ? Style.current.smallPadding : 0)
|
+ (retry.visible && !chatTime.visible ? Style.current.smallPadding : 0)
|
||||||
|
+ (pinnedRectangleLoader.active ? Style.current.smallPadding : 0)
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
||||||
color: root.isHovered || isMessageActive ? (hasMention ? Style.current.mentionMessageHoverColor : Style.current.backgroundHoverLight) :
|
color: {
|
||||||
|
if (pinnedMessage) {
|
||||||
|
return root.isHovered || isMessageActive ? Style.current.pinnedMessageBackgroundHovered : Style.current.pinnedMessageBackground
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.isHovered || isMessageActive ? (hasMention ? Style.current.mentionMessageHoverColor : Style.current.backgroundHoverLight) :
|
||||||
(hasMention ? Style.current.mentionMessageColor : Style.current.transparent)
|
(hasMention ? Style.current.mentionMessageColor : Style.current.transparent)
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: pinnedRectangleLoader
|
||||||
|
active: pinnedMessage
|
||||||
|
anchors.left: chatName.left
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: active ? Style.current.halfPadding : 0
|
||||||
|
|
||||||
|
sourceComponent: Component {
|
||||||
|
Rectangle {
|
||||||
|
id: pinnedRectangle
|
||||||
|
height: 24
|
||||||
|
width: childrenRect.width + Style.current.smallPadding
|
||||||
|
color: Style.current.pinnedRectangleBackground
|
||||||
|
radius: 12
|
||||||
|
|
||||||
|
SVGImage {
|
||||||
|
id: pinImage
|
||||||
|
source: "../../../../img/pin.svg"
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 3
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
ColorOverlay {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: parent
|
||||||
|
color: Style.current.pinnedMessageBorder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: qsTr("Pinned")
|
||||||
|
anchors.left: pinImage.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
font.pixelSize: 13
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ChatReply {
|
ChatReply {
|
||||||
id: chatReply
|
id: chatReply
|
||||||
|
anchors.top: pinnedRectangleLoader.active ? pinnedRectangleLoader.bottom : parent.top
|
||||||
|
anchors.topMargin: active ? 4 : 0
|
||||||
anchors.left: chatImage.left
|
anchors.left: chatImage.left
|
||||||
longReply: active && textFieldImplicitWidth > width
|
longReply: active && textFieldImplicitWidth > width
|
||||||
container: root.container
|
container: root.container
|
||||||
|
@ -87,8 +135,9 @@ Item {
|
||||||
active: isMessage && headerRepeatCondition
|
active: isMessage && headerRepeatCondition
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: Style.current.padding
|
anchors.leftMargin: Style.current.padding
|
||||||
anchors.top: chatReply.active ? chatReply.bottom : parent.top
|
anchors.top: chatReply.active ? chatReply.bottom :
|
||||||
anchors.topMargin: Style.current.smallPadding
|
pinnedRectangleLoader.active ? pinnedRectangleLoader.bottom : parent.top
|
||||||
|
anchors.topMargin: chatReply.active || pinnedRectangleLoader.active ? 4 : Style.current.smallPadding
|
||||||
}
|
}
|
||||||
|
|
||||||
UsernameLabel {
|
UsernameLabel {
|
||||||
|
@ -245,14 +294,15 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
active: hasMention
|
active: hasMention || pinnedMessage
|
||||||
height: messageContainer.height
|
height: messageContainer.height
|
||||||
anchors.left: messageContainer.left
|
anchors.left: messageContainer.left
|
||||||
|
anchors.top: messageContainer.top
|
||||||
|
|
||||||
sourceComponent: Component {
|
sourceComponent: Component {
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: mentionBorder
|
id: mentionBorder
|
||||||
color: Style.current.mentionColor
|
color: pinnedMessage ? Style.current.pinnedMessageBorder : Style.current.mentionColor
|
||||||
width: 2
|
width: 2
|
||||||
height: parent.height
|
height: parent.height
|
||||||
}
|
}
|
||||||
|
@ -260,7 +310,7 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
enabled: typeof messageContextMenu !== "undefined" && typeof profilePopupOpened !== "undefined" && !messageContextMenu.opened && !profilePopupOpened && !popupOpened
|
enabled: forceHoverHandler || (typeof messageContextMenu !== "undefined" && typeof profilePopupOpened !== "undefined" && !messageContextMenu.opened && !profilePopupOpened && !popupOpened)
|
||||||
onHoveredChanged: setHovered(messageId, hovered)
|
onHoveredChanged: setHovered(messageId, hovered)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,9 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PinnedMessagesPopup {
|
||||||
|
id: pinnedMessagesPopup
|
||||||
|
}
|
||||||
StatusContextMenuButton {
|
StatusContextMenuButton {
|
||||||
id: moreActionsBtn
|
id: moreActionsBtn
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
|
@ -8,9 +8,12 @@ import "../../../../shared/status"
|
||||||
import "./"
|
import "./"
|
||||||
|
|
||||||
PopupMenu {
|
PopupMenu {
|
||||||
|
property string messageId
|
||||||
property bool isProfile: false
|
property bool isProfile: false
|
||||||
property bool isSticker: false
|
property bool isSticker: false
|
||||||
property bool emojiOnly: false
|
property bool emojiOnly: false
|
||||||
|
property bool hideEmojiPicker: false
|
||||||
|
property bool pinnedMessage: false
|
||||||
property string linkUrls: ""
|
property string linkUrls: ""
|
||||||
property alias emojiContainer: emojiContainer
|
property alias emojiContainer: emojiContainer
|
||||||
|
|
||||||
|
@ -53,7 +56,7 @@ PopupMenu {
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: emojiContainer
|
id: emojiContainer
|
||||||
visible: messageContextMenu.emojiOnly || !messageContextMenu.isProfile
|
visible: !hideEmojiPicker && (messageContextMenu.emojiOnly || !messageContextMenu.isProfile)
|
||||||
width: emojiRow.width
|
width: emojiRow.width
|
||||||
height: visible ? emojiRow.height : 0
|
height: visible ? emojiRow.height : 0
|
||||||
|
|
||||||
|
@ -134,6 +137,37 @@ PopupMenu {
|
||||||
visible: !messageContextMenu.emojiOnly
|
visible: !messageContextMenu.emojiOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Action {
|
||||||
|
id: pinAction
|
||||||
|
text: pinnedMessage ? qsTr("Unpin") :
|
||||||
|
qsTr("Pin")
|
||||||
|
onTriggered: {
|
||||||
|
if (pinnedMessage) {
|
||||||
|
chatsModel.unPinMessage(messageId, chatsModel.activeChannel.id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
chatsModel.pinMessage(messageId, chatsModel.activeChannel.id)
|
||||||
|
messageContextMenu.close()
|
||||||
|
}
|
||||||
|
icon.source: "../../../img/pin"
|
||||||
|
icon.width: 16
|
||||||
|
icon.height: 16
|
||||||
|
enabled: chatsModel.activeChannel.chatType !== Constants.chatTypePublic
|
||||||
|
}
|
||||||
|
|
||||||
|
Action {
|
||||||
|
id: copyAction
|
||||||
|
text: qsTr("Copy")
|
||||||
|
onTriggered: {
|
||||||
|
chatsModel.copyToClipboard(messageContextMenu.text)
|
||||||
|
messageContextMenu.close()
|
||||||
|
}
|
||||||
|
icon.source: "../../../../shared/img/copy-to-clipboard-icon"
|
||||||
|
icon.width: 16
|
||||||
|
icon.height: 16
|
||||||
|
}
|
||||||
|
|
||||||
Action {
|
Action {
|
||||||
id: copyLinkAction
|
id: copyLinkAction
|
||||||
text: qsTr("Copy link")
|
text: qsTr("Copy link")
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import "../../../../imports"
|
||||||
|
import "../../../../shared"
|
||||||
|
import "../../../../shared/status"
|
||||||
|
import "../ChatColumn"
|
||||||
|
|
||||||
|
ModalPopup {
|
||||||
|
id: popup
|
||||||
|
|
||||||
|
header: Item {
|
||||||
|
height: childrenRect.height
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: title
|
||||||
|
text: qsTr("Pinned messages")
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
font.bold: true
|
||||||
|
font.pixelSize: 17
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: nbPinnedMessages
|
||||||
|
text: qsTr("%1 message").arg(pinnedMessageListView.count)
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.top: title.bottom
|
||||||
|
anchors.topMargin: 2
|
||||||
|
font.pixelSize: 15
|
||||||
|
color: Style.current.secondaryText
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
anchors.top: nbPinnedMessages.bottom
|
||||||
|
anchors.topMargin: Style.current.padding
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: -Style.current.padding
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: -Style.current.padding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: pinnedMessageListView
|
||||||
|
model: chatsModel.pinnedMessagesList
|
||||||
|
height: parent.height
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: -Style.current.padding
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: -Style.current.padding
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
delegate: Message {
|
||||||
|
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)
|
||||||
|
messageId: model.messageId
|
||||||
|
emojiReactions: model.emojiReactions
|
||||||
|
linkUrls: model.linkUrls
|
||||||
|
communityId: model.communityId
|
||||||
|
hasMention: model.hasMention
|
||||||
|
stickerPackId: model.stickerPackId
|
||||||
|
timeout: model.timeout
|
||||||
|
pinnedMessage: true
|
||||||
|
forceHoverHandler: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: StatusRoundButton {
|
||||||
|
id: btnBack
|
||||||
|
anchors.left: parent.left
|
||||||
|
icon.name: "arrow-right"
|
||||||
|
icon.width: 20
|
||||||
|
icon.height: 16
|
||||||
|
rotation: 180
|
||||||
|
onClicked: popup.close()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M8.6891 4.24964C8.6891 3.7664 8.31098 3.27219 7.84455 3.14582C7.37812 3.01944 7 3.30874 7 3.79199C7 4.27524 7.37812 4.76944 7.84455 4.89582C8.31098 5.02219 8.6891 4.73289 8.6891 4.24964Z" fill="#939BA1"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 1.16699C5.067 1.16699 3.5 2.734 3.5 4.66699C3.5 6.36315 4.70653 7.7775 6.30817 8.09863C6.45243 8.12755 6.5625 8.25034 6.5625 8.39747L6.5625 12.2503C6.5625 12.492 6.75837 12.6878 7 12.6878C7.24162 12.6878 7.4375 12.492 7.4375 12.2503L7.4375 8.39747C7.4375 8.25034 7.54757 8.12755 7.69183 8.09863C9.29347 7.7775 10.5 6.36315 10.5 4.66699C10.5 2.734 8.933 1.16699 7 1.16699ZM4.375 4.66699C4.375 6.11674 5.55025 7.29199 7 7.29199C8.44975 7.29199 9.625 6.11674 9.625 4.66699C9.625 3.21724 8.44975 2.04199 7 2.04199C5.55025 2.04199 4.375 3.21724 4.375 4.66699Z" fill="#939BA1"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 942 B |
|
@ -90,6 +90,11 @@ Theme {
|
||||||
property color contextMenuButtonForegroundColor: midGrey
|
property color contextMenuButtonForegroundColor: midGrey
|
||||||
property color contextMenuButtonBackgroundHoverColor: Qt.hsla(white.hslHue, white.hslSaturation, white.hslLightness, 0.05)
|
property color contextMenuButtonBackgroundHoverColor: Qt.hsla(white.hslHue, white.hslSaturation, white.hslLightness, 0.05)
|
||||||
|
|
||||||
|
property color pinnedMessageBorder: "#FFA67B"
|
||||||
|
property color pinnedMessageBackground: "#1afe8f59"
|
||||||
|
property color pinnedMessageBackgroundHovered: "#33fe8f59"
|
||||||
|
property color pinnedRectangleBackground: "#1afe8f59"
|
||||||
|
|
||||||
property color roundedButtonForegroundColor: white
|
property color roundedButtonForegroundColor: white
|
||||||
property color roundedButtonBackgroundColor: buttonBackgroundColor
|
property color roundedButtonBackgroundColor: buttonBackgroundColor
|
||||||
property color roundedButtonSecondaryForegroundColor: black
|
property color roundedButtonSecondaryForegroundColor: black
|
||||||
|
|
|
@ -90,6 +90,11 @@ Theme {
|
||||||
property color contextMenuButtonForegroundColor: black
|
property color contextMenuButtonForegroundColor: black
|
||||||
property color contextMenuButtonBackgroundHoverColor: Qt.hsla(black.hslHue, black.hslSaturation, black.hslLightness, 0.1)
|
property color contextMenuButtonBackgroundHoverColor: Qt.hsla(black.hslHue, black.hslSaturation, black.hslLightness, 0.1)
|
||||||
|
|
||||||
|
property color pinnedMessageBorder: "#FE8F59"
|
||||||
|
property color pinnedMessageBackground: "#1aFF9F0F"
|
||||||
|
property color pinnedMessageBackgroundHovered: "#33FF9F0F"
|
||||||
|
property color pinnedRectangleBackground: "#1affffff"
|
||||||
|
|
||||||
property color roundedButtonForegroundColor: buttonForegroundColor
|
property color roundedButtonForegroundColor: buttonForegroundColor
|
||||||
property color roundedButtonBackgroundColor: secondaryBackground
|
property color roundedButtonBackgroundColor: secondaryBackground
|
||||||
property color roundedButtonSecondaryForegroundColor: grey2
|
property color roundedButtonSecondaryForegroundColor: grey2
|
||||||
|
|
|
@ -72,6 +72,11 @@ QtObject {
|
||||||
property color tooltipBackgroundColor
|
property color tooltipBackgroundColor
|
||||||
property color tooltipForegroundColor
|
property color tooltipForegroundColor
|
||||||
|
|
||||||
|
property color pinnedMessageBorder
|
||||||
|
property color pinnedMessageBackground
|
||||||
|
property color pinnedMessageBackgroundHovered
|
||||||
|
property color pinnedRectangleBackground
|
||||||
|
|
||||||
property int xlPadding: 32
|
property int xlPadding: 32
|
||||||
property int bigPadding: 24
|
property int bigPadding: 24
|
||||||
property int padding: 16
|
property int padding: 16
|
||||||
|
|
|
@ -162,6 +162,7 @@ DISTFILES += \
|
||||||
app/AppLayouts/Chat/components/EmojiReaction.qml \
|
app/AppLayouts/Chat/components/EmojiReaction.qml \
|
||||||
app/AppLayouts/Chat/components/LeftTabBottomButtons.qml \
|
app/AppLayouts/Chat/components/LeftTabBottomButtons.qml \
|
||||||
app/AppLayouts/Chat/components/NoFriendsRectangle.qml \
|
app/AppLayouts/Chat/components/NoFriendsRectangle.qml \
|
||||||
|
app/AppLayouts/Chat/components/PinnedMessagesPopup.qml \
|
||||||
app/AppLayouts/Chat/components/ProfilePopup.qml \
|
app/AppLayouts/Chat/components/ProfilePopup.qml \
|
||||||
app/AppLayouts/Chat/components/EmojiSection.qml \
|
app/AppLayouts/Chat/components/EmojiSection.qml \
|
||||||
app/AppLayouts/Chat/components/InviteFriendsPopup.qml \
|
app/AppLayouts/Chat/components/InviteFriendsPopup.qml \
|
||||||
|
|
|
@ -137,6 +137,59 @@ Item {
|
||||||
font.pixelSize: 12
|
font.pixelSize: 12
|
||||||
anchors.top: chatName.bottom
|
anchors.top: chatName.bottom
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
property bool hovered: false
|
||||||
|
|
||||||
|
id: pinnedMessagesGroup
|
||||||
|
visible: chatType !== Constants.chatTypePublic && chatsModel.pinnedMessagesList.count > 0
|
||||||
|
width: childrenRect.width
|
||||||
|
height: vertiSep.height
|
||||||
|
anchors.left: chatInfo.right
|
||||||
|
anchors.leftMargin: 4
|
||||||
|
anchors.verticalCenter: chatInfo.verticalCenter
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: vertiSep
|
||||||
|
height: 12
|
||||||
|
width: 1
|
||||||
|
color: Style.current.border
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGImage {
|
||||||
|
id: pinImg
|
||||||
|
source: "../../app/img/pin.svg"
|
||||||
|
height: 14
|
||||||
|
width: 14
|
||||||
|
anchors.left: vertiSep.right
|
||||||
|
anchors.leftMargin: 4
|
||||||
|
anchors.verticalCenter: vertiSep.verticalCenter
|
||||||
|
|
||||||
|
ColorOverlay {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: parent
|
||||||
|
color: pinnedMessagesGroup.hovered ? Style.current.textColor : Style.current.secondaryText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: nbPinnedMessagesText
|
||||||
|
color: pinnedMessagesGroup.hovered ? Style.current.textColor : Style.current.secondaryText
|
||||||
|
text: chatsModel.pinnedMessagesList.count
|
||||||
|
font.pixelSize: 12
|
||||||
|
font.underline: pinnedMessagesGroup.hovered
|
||||||
|
anchors.left: pinImg.right
|
||||||
|
anchors.verticalCenter: vertiSep.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onEntered: pinnedMessagesGroup.hovered = true
|
||||||
|
onExited: pinnedMessagesGroup.hovered = false
|
||||||
|
onClicked: pinnedMessagesPopup.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 71f66f68064e9897cd17b6bcecba426a91405034
|
Subproject commit e9a42bfa2be93d9ee09a82e0893d8019c4bcdd3d
|
Loading…
Reference in New Issue