feat: add mentions to activity center and interactions

Fixes #2610
This commit is contained in:
Jonathan Rainville 2021-06-11 13:41:59 -04:00
parent c0013a0956
commit 414b39d7e0
23 changed files with 767 additions and 189 deletions

View File

@ -41,6 +41,8 @@ proc init*(self: ChatController) =
self.status.chat.init(pubKey, messagesFromContactsOnly)
self.status.stickers.init()
self.view.reactions.init()
self.view.asyncActivityNotificationLoad()
let recentStickers = self.status.stickers.getRecentStickers()
for sticker in recentStickers:

View File

@ -22,6 +22,9 @@ proc handleChatEvents(self: ChatController) =
self.status.events.on("pinnedMessagesLoaded") do(e:Args):
self.view.pushPinnedMessages(MsgsLoadedArgs(e).messages)
self.status.events.on("activityCenterNotificationsLoaded") do(e:Args):
self.view.pushActivityCenterNotifications(ActivityCenterNotificationsArgs(e).activityCenterNotifications)
self.status.events.on("contactUpdate") do(e: Args):
var evArgs = ContactUpdateArgs(e)
self.view.updateUsernames(evArgs.contacts)
@ -56,6 +59,8 @@ proc handleChatEvents(self: ChatController) =
self.view.communities.addMembershipRequests(evArgs.communityMembershipRequests)
if (evArgs.pinnedMessages.len > 0):
self.view.addPinnedMessages(evArgs.pinnedMessages)
if (evArgs.activityCenterNotifications.len > 0):
self.view.addActivityCenterNotification(evArgs.activityCenterNotifications)
self.status.events.on("channelUpdate") do(e: Args):
var evArgs = ChatUpdateArgs(e)

View File

@ -4,7 +4,7 @@ import
proc handleSignals(self: ChatController) =
self.status.events.on(SignalType.Message.event) do(e:Args):
var data = MessageSignal(e)
self.status.chat.update(data.chats, data.messages, data.emojiReactions, data.communities, data.membershipRequests, data.pinnedMessages)
self.status.chat.update(data.chats, data.messages, data.emojiReactions, data.communities, data.membershipRequests, data.pinnedMessages, data.activityCenterNotification)
self.status.events.on(SignalType.DiscoverySummary.event) do(e:Args):
## Handle mailserver peers being added and removed

View File

@ -13,7 +13,7 @@ import ../../status/ens as status_ens
import ../../status/chat/[chat, message]
import ../../status/profile/profile
import web3/[conversions, ethtypes]
import views/[channels_list, message_list, chat_item, suggestions_list, reactions, stickers, groups, transactions, communities, community_list, community_item]
import views/[channels_list, message_list, chat_item, suggestions_list, reactions, stickers, groups, transactions, communities, community_list, community_item, activity_notification_list]
import ../utils/image_utils
import ../../status/tasks/[qt, task_runner_impl]
import ../../status/tasks/marathon/mailserver/worker
@ -29,6 +29,7 @@ type
GetLinkPreviewDataTaskArg = ref object of QObjectTaskArg
link: string
uuid: string
AsyncActivityNotificationLoadTaskArg = ref object of QObjectTaskArg
AsyncMessageLoadTaskArg = ref object of QObjectTaskArg
chatId: string
ResolveEnsTaskArg = ref object of QObjectTaskArg
@ -81,6 +82,19 @@ const asyncMessageLoadTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.}
}
arg.finish(responseJson)
const asyncActivityNotificationLoadTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[AsyncActivityNotificationLoadTaskArg](argEncoded)
var activityNotifications: JsonNode
var activityNotificationsCallSuccess: bool
let activityNotificationsCallResult = rpcActivityCenterNotifications(newJString(""), 20, activityNotificationsCallSuccess)
if(activityNotificationsCallSuccess):
activityNotifications = activityNotificationsCallResult.parseJson()["result"]
let responseJson = %*{
"activityNotifications": activityNotifications
}
arg.finish(responseJson)
proc asyncMessageLoad[T](self: T, slot: string, chatId: string) =
let arg = AsyncMessageLoadTaskArg(
tptr: cast[ByteAddress](asyncMessageLoadTask),
@ -90,6 +104,14 @@ proc asyncMessageLoad[T](self: T, slot: string, chatId: string) =
)
self.status.tasks.threadpool.start(arg)
proc asyncActivityNotificationLoad[T](self: T, slot: string) =
let arg = AsyncActivityNotificationLoadTaskArg(
tptr: cast[ByteAddress](asyncActivityNotificationLoadTask),
vptr: cast[ByteAddress](self.vptr),
slot: slot
)
self.status.tasks.threadpool.start(arg)
const resolveEnsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let
arg = decode[ResolveEnsTaskArg](argEncoded)
@ -111,6 +133,7 @@ QtObject:
status: Status
chats*: ChannelsList
currentSuggestions*: SuggestionsList
activityNotificationList*: ActivityNotificationList
callResult: string
messageList*: OrderedTable[string, ChatMessageList]
pinnedMessagesList*: OrderedTable[string, ChatMessageList]
@ -137,6 +160,7 @@ QtObject:
self.activeChannel.delete
self.contextChannel.delete
self.currentSuggestions.delete
self.activityNotificationList.delete
for msg in self.messageList.values:
msg.delete
for msg in self.pinnedMessagesList.values:
@ -159,6 +183,7 @@ QtObject:
result.activeChannel = newChatItemView(status)
result.contextChannel = newChatItemView(status)
result.currentSuggestions = newSuggestionsList()
result.activityNotificationList = newActivityNotificationList(status)
result.messageList = initOrderedTable[string, ChatMessageList]()
result.pinnedMessagesList = initOrderedTable[string, ChatMessageList]()
result.reactions = newReactionView(status, result.messageList.addr, result.activeChannel)
@ -438,6 +463,15 @@ QtObject:
QtProperty[QVariant] suggestionList:
read = getCurrentSuggestions
proc activityNotificationsChanged*(self: ChatsView) {.signal.}
proc getActivityNotificationList(self: ChatsView): QVariant {.slot.} =
return newQVariant(self.activityNotificationList)
QtProperty[QVariant] activityNotificationList:
read = getActivityNotificationList
notify = activityNotificationsChanged
proc upsertChannel(self: ChatsView, channel: string) =
var chat: Chat = nil
if self.status.chat.channels.hasKey(channel):
@ -481,6 +515,15 @@ QtObject:
# put the message as pinned in the message list
self.messageList[msg.chatId].changeMessagePinned(msg.id, true, msg.pinnedBy)
proc pushActivityCenterNotifications*(self:ChatsView, activityCenterNotifications: seq[ActivityCenterNotification]) =
self.activityNotificationList.setNewData(activityCenterNotifications)
self.activityNotificationsChanged()
proc addActivityCenterNotification*(self:ChatsView, activityCenterNotifications: seq[ActivityCenterNotification]) =
for activityCenterNotification in activityCenterNotifications:
self.activityNotificationList.addActivityNotificationItemToList(activityCenterNotification)
self.activityNotificationsChanged()
proc pushMessages*(self:ChatsView, messages: var seq[Message]) =
for msg in messages.mitems:
self.upsertChannel(msg.chatId)
@ -626,6 +669,9 @@ QtObject:
proc asyncMessageLoad*(self: ChatsView, chatId: string) {.slot.} =
self.asyncMessageLoad("asyncMessageLoaded", chatId)
proc asyncActivityNotificationLoad*(self: ChatsView) {.slot.} =
self.asyncActivityNotificationLoad("asyncActivityNotificationLoaded")
proc asyncMessageLoaded*(self: ChatsView, rpcResponse: string) {.slot.} =
let
rpcResponseObj = rpcResponse.parseJson
@ -636,7 +682,7 @@ QtObject:
let messages = rpcResponseObj{"messages"}
if(messages != nil and messages.kind != JNull):
let chatMessages = parseChatMessagesResponse(chatId, messages)
let chatMessages = parseChatMessagesResponse(messages)
self.status.chat.chatMessages(chatId, true, chatMessages[0], chatMessages[1])
let rxns = rpcResponseObj{"reactions"}
@ -646,9 +692,16 @@ QtObject:
let pinnedMsgs = rpcResponseObj{"pinnedMessages"}
if(pinnedMsgs != nil and pinnedMsgs.kind != JNull):
let pinnedMessages = parseChatPinnedMessagesResponse(chatId, pinnedMsgs)
let pinnedMessages = parseChatPinnedMessagesResponse(pinnedMsgs)
self.status.chat.pinnedMessagesByChatID(chatId, pinnedMessages[0], pinnedMessages[1])
proc asyncActivityNotificationLoaded*(self: ChatsView, rpcResponse: string) {.slot.} =
let rpcResponseObj = rpcResponse.parseJson
if(rpcResponseObj["activityNotifications"].kind != JNull):
let activityNotifications = parseActivityCenterNotifications(rpcResponseObj["activityNotifications"])
self.status.chat.activityCenterNotifications(activityNotifications[0], activityNotifications[1])
proc hideLoadingIndicator*(self: ChatsView) {.slot.} =
self.loadingMessages = false
self.loadingMessagesChanged(false)

View File

@ -0,0 +1,149 @@
import NimQml, Tables, chronicles
import ../../../status/chat/chat
import ../../../status/status
import ../../../status/accounts
import strutils
import message_item
type ActivityCenterNotificationViewItem* = ref object of ActivityCenterNotification
messageItem*: MessageItem
type
NotifRoles {.pure.} = enum
Id = UserRole + 1
ChatId = UserRole + 2
Name = UserRole + 3
NotificationType = UserRole + 4
Message = UserRole + 5
Timestamp = UserRole + 6
Read = UserRole + 7
Dismissed = UserRole + 8
Accepted = UserRole + 9
QtObject:
type
ActivityNotificationList* = ref object of QAbstractListModel
activityCenterNotifications*: seq[ActivityCenterNotificationViewItem]
status: Status
nbUnreadNotifications*: int
proc setup(self: ActivityNotificationList) = self.QAbstractListModel.setup
proc delete(self: ActivityNotificationList) =
self.activityCenterNotifications = @[]
self.QAbstractListModel.delete
proc newActivityNotificationList*(status: Status): ActivityNotificationList =
new(result, delete)
result.activityCenterNotifications = @[]
result.status = status
result.setup()
proc unreadCountChanged*(self: ActivityNotificationList) {.signal.}
proc unreadCount*(self: ActivityNotificationList): int {.slot.} =
self.nbUnreadNotifications
QtProperty[int] unreadCount:
read = unreadCount
notify = unreadCountChanged
method rowCount*(self: ActivityNotificationList, index: QModelIndex = nil): int = self.activityCenterNotifications.len
method data(self: ActivityNotificationList, index: QModelIndex, role: int): QVariant =
if not index.isValid:
return
if index.row < 0 or index.row >= self.activityCenterNotifications.len:
return
let acitivityNotificationItem = self.activityCenterNotifications[index.row]
let communityItemRole = role.NotifRoles
case communityItemRole:
of NotifRoles.Id: result = newQVariant(acitivityNotificationItem.id)
of NotifRoles.ChatId: result = newQVariant(acitivityNotificationItem.chatId)
of NotifRoles.Name: result = newQVariant(acitivityNotificationItem.name)
of NotifRoles.NotificationType: result = newQVariant(acitivityNotificationItem.notificationType.int)
of NotifRoles.Message: result = newQVariant(acitivityNotificationItem.messageItem)
of NotifRoles.Timestamp: result = newQVariant(acitivityNotificationItem.timestamp)
of NotifRoles.Read: result = newQVariant(acitivityNotificationItem.read.bool)
of NotifRoles.Dismissed: result = newQVariant(acitivityNotificationItem.dismissed.bool)
of NotifRoles.Accepted: result = newQVariant(acitivityNotificationItem.accepted.bool)
proc getNotificationData(self: ActivityNotificationList, index: int, data: string): string {.slot.} =
if index < 0 or index >= self.activityCenterNotifications.len: return ("")
let notif = self.activityCenterNotifications[index]
case data:
of "id": result = notif.id
of "chatId": result = notif.chatId
of "name": result = notif.name
of "notificationType": result = $(notif.notificationType.int)
of "timestamp": result = $(notif.timestamp)
of "read": result = $(notif.read)
of "dismissed": result = $(notif.dismissed)
of "accepted": result = $(notif.accepted)
else: result = ("")
method roleNames(self: ActivityNotificationList): Table[int, string] =
{
NotifRoles.Id.int:"id",
NotifRoles.ChatId.int:"chatId",
NotifRoles.Name.int: "name",
NotifRoles.NotificationType.int: "notificationType",
NotifRoles.Message.int: "message",
NotifRoles.Timestamp.int: "timestamp",
NotifRoles.Read.int: "read",
NotifRoles.Dismissed.int: "dismissed",
NotifRoles.Accepted.int: "accepted"
}.toTable
proc markAllActivityCenterNotificationsRead(self: ActivityNotificationList): string {.slot.} =
let error = self.status.chat.markAllActivityCenterNotificationsRead()
if (error != ""):
return error
self.nbUnreadNotifications = 0
self.unreadCountChanged()
for activityCenterNotification in self.activityCenterNotifications:
activityCenterNotification.read = true
let topLeft = self.createIndex(0, 0, nil)
let bottomRight = self.createIndex(self.activityCenterNotifications.len - 1, 0, nil)
self.dataChanged(topLeft, bottomRight, @[NotifRoles.Read.int])
proc toActivityCenterNotificationViewItem*(self: ActivityNotificationList, activityCenterNotification: ActivityCenterNotification): ActivityCenterNotificationViewItem =
ActivityCenterNotificationViewItem(
id: activityCenterNotification.id,
chatId: activityCenterNotification.chatId,
name: activityCenterNotification.name,
notificationType: activityCenterNotification.notificationType,
timestamp: activityCenterNotification.timestamp,
read: activityCenterNotification.read,
dismissed: activityCenterNotification.dismissed,
accepted: activityCenterNotification.accepted,
messageItem: newMessageItem(self.status, activityCenterNotification.message)
)
proc setNewData*(self: ActivityNotificationList, activityCenterNotifications: seq[ActivityCenterNotification]) =
self.beginResetModel()
self.activityCenterNotifications = @[]
for activityCenterNotification in activityCenterNotifications:
self.activityCenterNotifications.add(self.toActivityCenterNotificationViewItem(activityCenterNotification))
self.endResetModel()
self.nbUnreadNotifications = self.status.chat.unreadActivityCenterNotificationsCount()
self.unreadCountChanged()
proc addActivityNotificationItemToList*(self: ActivityNotificationList, activityCenterNotification: ActivityCenterNotification) =
self.beginInsertRows(newQModelIndex(), self.activityCenterNotifications.len, self.activityCenterNotifications.len)
self.activityCenterNotifications.add(self.toActivityCenterNotificationViewItem(activityCenterNotification))
self.endInsertRows()
if (not activityCenterNotification.read):
self.nbUnreadNotifications = self.nbUnreadNotifications + 1

View File

@ -146,6 +146,11 @@ QtObject:
if (channel == nil): return
return channel.color
proc getChannelType*(self: ChannelsList, id: string): int {.slot.} =
let channel = self.getChannelById(id)
if (channel == nil): return ChatType.Unknown.int
return channel.chatType.int
proc updateChat*(self: ChannelsList, channel: Chat) =
let idx = self.upsertChannel(channel)
if idx == -1: return

View File

@ -1,22 +1,29 @@
import sequtils, re, strutils
import NimQml, Tables, sets, json, sugar, re
import ../../../status/status
import ../../../status/accounts
import ../../../status/chat
import ../../../status/chat/[message,stickers]
import ../../../status/profile/profile
import ../../../status/ens
import strformat, strutils, sequtils
let NEW_LINE = re"\n|\r"
proc sectionIdentifier(message: Message): string =
proc sectionIdentifier*(message: Message): string =
result = message.fromAuthor
# Force section change, because group status messages are sent with the
# same fromAuthor, and ends up causing the header to not be shown
if message.contentType == ContentType.Group:
result = "GroupChatMessage"
proc mention(self: ChatMessageList, pubKey: string): string =
if self.status.chat.contacts.hasKey(pubKey):
return ens.userNameOrAlias(self.status.chat.contacts[pubKey], true)
proc mention*(pubKey: string, contacts: Table[string, Profile]): string =
if contacts.hasKey(pubKey):
return ens.userNameOrAlias(contacts[pubKey], true)
generateAlias(pubKey)
# See render-inline in status-react/src/status_im/ui/screens/chat/message/message.cljs
proc renderInline(self: ChatMessageList, elem: TextItem): string =
proc renderInline*(elem: TextItem, contacts: Table[string, Profile]): string =
let value = escape_html(elem.literal).multiReplace(("\r\n", "<br/>")).multiReplace(("\n", "<br/>")).multiReplace((" ", "&nbsp;&nbsp;"))
case elem.textType:
of "": result = value
@ -25,19 +32,19 @@ proc renderInline(self: ChatMessageList, elem: TextItem): string =
of "strong": result = fmt("<strong>{value}</strong>")
of "strong-emph": result = fmt(" <strong><em>{value}</em></strong> ")
of "link": result = fmt("{elem.destination}")
of "mention": result = fmt("<a href=\"//{value}\" class=\"mention\">{self.mention(value)}</a>")
of "mention": result = fmt("<a href=\"//{value}\" class=\"mention\">{mention(value, contacts)}</a>")
of "status-tag": result = fmt("<a href=\"#{value}\" class=\"status-tag\">#{value}</a>")
of "del": result = fmt("<del>{value}</del>")
else: result = fmt(" {value} ")
# See render-block in status-react/src/status_im/ui/screens/chat/message/message.cljs
proc renderBlock(self: ChatMessageList, message: Message): string =
proc renderBlock*(message: Message, contacts: Table[string, Profile]): string =
for pMsg in message.parsedText:
case pMsg.textType:
of "paragraph":
result = result & "<p>"
for children in pMsg.children:
result = result & self.renderInline(children)
result = result & renderInline(children, contacts)
result = result & "</p>"
of "blockquote":
var

View File

@ -0,0 +1,174 @@
import NimQml, std/wrapnils, chronicles
import ../../../status/status
import ../../../status/chat/message
import ../../../status/chat/stickers
import message_format
QtObject:
type MessageItem* = ref object of QObject
messageItem*: Message
status*: Status
proc setup(self: MessageItem) =
self.QObject.setup
proc delete*(self: MessageItem) =
self.QObject.delete
proc newMessageItem*(status: Status, message: Message): MessageItem =
new(result, delete)
result.messageItem = message
result.status = status
result.setup
proc setMessageItem*(self: MessageItem, messageItem: Message) =
self.messageItem = messageItem
proc alias*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.alias
QtProperty[string] alias:
read = alias
proc userName*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.userName
QtProperty[string] userName:
read = userName
proc message*(self: MessageItem): string {.slot.} = result = renderBlock(self.messageItem, self.status.chat.contacts)
QtProperty[string] message:
read = message
proc localName*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.localName
QtProperty[string] localName:
read = localName
proc chatId*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.chatId
QtProperty[string] chatId:
read = chatId
proc clock*(self: MessageItem): int {.slot.} = result = ?.self.messageItem.clock
QtProperty[int] clock:
read = clock
proc gapFrom*(self: MessageItem): int {.slot.} = result = ?.self.messageItem.gapFrom
QtProperty[int] gapFrom:
read = gapFrom
proc gapTo*(self: MessageItem): int {.slot.} = result = ?.self.messageItem.gapTo
QtProperty[int] gapTo:
read = gapTo
proc contentType*(self: MessageItem): int {.slot.} = result = self.messageItem.contentType.int
QtProperty[int] contentType:
read = contentType
proc ensName*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.ensName
QtProperty[string] ensName:
read = ensName
proc fromAuthor*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.fromAuthor
QtProperty[string] fromAuthor:
read = fromAuthor
proc messageId*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.id
QtProperty[string] messageId:
read = messageId
proc identicon*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.identicon
QtProperty[string] identicon:
read = identicon
proc lineCount*(self: MessageItem): int {.slot.} = result = ?.self.messageItem.lineCount
QtProperty[int] lineCount:
read = lineCount
proc localChatId*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.localChatId
QtProperty[string] localChatId:
read = localChatId
proc messageType*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.messageType
QtProperty[string] messageType:
read = messageType
proc replace*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.replace
QtProperty[string] replace:
read = replace
proc responseTo*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.responseTo
QtProperty[string] responseTo:
read = responseTo
proc rtl*(self: MessageItem): bool {.slot.} = result = ?.self.messageItem.rtl
QtProperty[bool] rtl:
read = rtl
proc seen*(self: MessageItem): bool {.slot.} = result = ?.self.messageItem.seen
QtProperty[bool] seen:
read = seen
proc sticker*(self: MessageItem): string {.slot.} = result = self.messageItem.stickerHash.decodeContentHash()
QtProperty[string] sticker:
read = sticker
proc sectionIdentifier*(self: MessageItem): string {.slot.} = result = sectionIdentifier(self.messageItem)
QtProperty[string] sectionIdentifier:
read = sectionIdentifier
proc stickerPackId*(self: MessageItem): int {.slot.} = result = ?.self.messageItem.stickerPackId
QtProperty[int] stickerPackId:
read = stickerPackId
proc plainText*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.text
QtProperty[string] plainText:
read = plainText
proc timestamp*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.timestamp
QtProperty[string] timestamp:
read = timestamp
proc whisperTimestamp*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.whisperTimestamp
QtProperty[string] whisperTimestamp:
read = whisperTimestamp
proc isCurrentUser*(self: MessageItem): bool {.slot.} = result = ?.self.messageItem.isCurrentUser
QtProperty[bool] isCurrentUser:
read = isCurrentUser
proc stickerHash*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.stickerHash
QtProperty[string] stickerHash:
read = stickerHash
proc outgoingStatus*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.outgoingStatus
QtProperty[string] outgoingStatus:
read = outgoingStatus
proc linkUrls*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.linkUrls
QtProperty[string] linkUrls:
read = linkUrls
proc image*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.image
QtProperty[string] image:
read = image
proc audio*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.audio
QtProperty[string] audio:
read = audio
proc communityId*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.communityId
QtProperty[string] communityId:
read = communityId
proc audioDurationMs*(self: MessageItem): int {.slot.} = result = ?.self.messageItem.audioDurationMs
QtProperty[int] audioDurationMs:
read = audioDurationMs
proc hasMention*(self: MessageItem): bool {.slot.} = result = ?.self.messageItem.hasMention
QtProperty[bool] hasMention:
read = hasMention
proc isPinned*(self: MessageItem): bool {.slot.} = result = ?.self.messageItem.isPinned
QtProperty[bool] isPinned:
read = isPinned
proc pinnedBy*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.pinnedBy
QtProperty[string] pinnedBy:
read = pinnedBy

View File

@ -1,4 +1,4 @@
import NimQml, Tables, sets, json, sugar, chronicles
import NimQml, Tables, sets, json, sugar, chronicles, sequtils
import ../../../status/status
import ../../../status/accounts
import ../../../status/chat
@ -6,6 +6,7 @@ import ../../../status/chat/[message,stickers]
import ../../../status/profile/profile
import ../../../status/ens
import strformat, strutils
import message_format
type
ChatMessageRoles {.pure.} = enum
@ -61,8 +62,6 @@ QtObject:
proc setup(self: ChatMessageList) =
self.QAbstractListModel.setup
include message_format
proc fetchMoreMessagesButton(self: ChatMessageList): Message =
result = Message()
result.contentType = ContentType.FetchMoreMessagesButton;
@ -148,7 +147,7 @@ QtObject:
let chatMessageRole = role.ChatMessageRoles
case chatMessageRole:
of ChatMessageRoles.UserName: result = newQVariant(message.userName)
of ChatMessageRoles.Message: result = newQVariant(self.renderBlock(message))
of ChatMessageRoles.Message: result = newQVariant(renderBlock(message, self.status.chat.contacts))
of ChatMessageRoles.PlainText: result = newQVariant(message.text)
of ChatMessageRoles.Timestamp: result = newQVariant(message.timestamp)
of ChatMessageRoles.Clock: result = newQVariant($message.clock)
@ -236,13 +235,13 @@ QtObject:
let message = self.messages[index]
case data:
of "userName": result = (message.userName)
of "publicKey": result = (message.fromAuthor)
of "alias": result = (message.alias)
of "localName": result = (message.localName)
of "ensName": result = (message.ensName)
of "message": result = (self.renderBlock(message))
of "identicon": result = (message.identicon)
of "userName": result = message.userName
of "publicKey": result = message.fromAuthor
of "alias": result = message.alias
of "localName": result = message.localName
of "ensName": result = message.ensName
of "message": result = (renderBlock(message, self.status.chat.contacts))
of "identicon": result = message.identicon
of "timestamp": result = $(message.timestamp)
of "image": result = $(message.image)
of "contentType": result = $(message.contentType.int)

View File

@ -29,6 +29,7 @@ type
emojiReactions*: seq[Reaction]
communities*: seq[Community]
communityMembershipRequests*: seq[CommunityMembershipRequest]
activityCenterNotifications*: seq[ActivityCenterNotification]
ChatIdArg* = ref object of Args
chatId*: string
@ -42,10 +43,12 @@ type
CommunityActiveChangedArgs* = ref object of Args
active*: bool
MsgsLoadedArgs* = ref object of Args
messages*: seq[Message]
ActivityCenterNotificationsArgs* = ref object of Args
activityCenterNotifications*: seq[ActivityCenterNotification]
ReactionsLoadedArgs* = ref object of Args
reactions*: seq[Reaction]
@ -105,7 +108,7 @@ proc cleanSpamChatGroups(self: ChatModel, chats: seq[Chat], contacts: seq[Profil
else:
result.add(chat)
proc update*(self: ChatModel, chats: seq[Chat], messages: seq[Message], emojiReactions: seq[Reaction], communities: seq[Community], communityMembershipRequests: seq[CommunityMembershipRequest], pinnedMessages: seq[Message]) =
proc update*(self: ChatModel, chats: seq[Chat], messages: seq[Message], emojiReactions: seq[Reaction], communities: seq[Community], communityMembershipRequests: seq[CommunityMembershipRequest], pinnedMessages: seq[Message], activityCenterNotifications: seq[ActivityCenterNotification]) =
var contacts = getAddedContacts()
var chatList = chats
@ -126,7 +129,7 @@ proc update*(self: ChatModel, chats: seq[Chat], messages: seq[Message], emojiRea
if self.lastMessageTimestamps[chatId] > ts:
self.lastMessageTimestamps[chatId] = ts
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chatList, contacts: @[], emojiReactions: emojiReactions, communities: communities, communityMembershipRequests: communityMembershipRequests, pinnedMessages: pinnedMessages))
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages,chats: chatList, contacts: @[], emojiReactions: emojiReactions, communities: communities, communityMembershipRequests: communityMembershipRequests, pinnedMessages: pinnedMessages, activityCenterNotifications: activityCenterNotifications))
proc hasChannel*(self: ChatModel, chatId: string): bool =
self.channels.hasKey(chatId)
@ -177,21 +180,6 @@ proc requestMissingCommunityInfos*(self: ChatModel) =
for communityId in self.communitiesToFetch:
status_chat.requestCommunityInfo(communityId)
proc activityCenterNotification*(self: ChatModel, initialLoad:bool = true) =
# Notifications were already loaded, since cursor will
# be nil/empty if there are no more notifs
if(not initialLoad and self.activityCenterCursor == ""): return
status_chat.activityCenterNotification(self.activityCenterCursor)
# self.activityCenterCursor[chatId] = messageTuple[0];
# if messageTuple[1].len > 0:
# let lastMsgIndex = messageTuple[1].len - 1
# let ts = times.convert(Milliseconds, Seconds, messageTuple[1][lastMsgIndex].whisperTimestamp.parseInt())
# self.lastMessageTimestamps[chatId] = ts
# self.events.emit("messagesLoaded", MsgsLoadedArgs(messages: messageTuple[1]))
proc init*(self: ChatModel, pubKey: string, messagesFromContactsOnly: bool) =
self.publicKey = pubKey
self.messagesFromContactsOnly = messagesFromContactsOnly
@ -202,8 +190,6 @@ proc init*(self: ChatModel, pubKey: string, messagesFromContactsOnly: bool) =
if (messagesFromContactsOnly):
chatList = self.cleanSpamChatGroups(chatList, contacts)
# self.activityCenterNotification()
let profileUpdatesChatIds = chatList.filter(c => c.chatType == ChatType.Profile).map(c => c.id)
if chatList.filter(c => c.chatType == ChatType.Timeline).len == 0:
@ -555,3 +541,31 @@ proc pinnedMessagesByChatID*(self: ChatModel, chatId: string, cursor: string = "
self.msgCursor[chatId] = cursor
self.events.emit("pinnedMessagesLoaded", MsgsLoadedArgs(messages: pinnedMessages))
proc activityCenterNotifications*(self: ChatModel, initialLoad: bool = true) =
# Notifications were already loaded, since cursor will
# be nil/empty if there are no more notifs
if(not initialLoad and self.activityCenterCursor == ""): return
let activityCenterNotificationsTuple = status_chat.activityCenterNotification(self.activityCenterCursor)
self.activityCenterCursor = activityCenterNotificationsTuple[0];
self.events.emit("activityCenterNotificationsLoaded", ActivityCenterNotificationsArgs(activityCenterNotifications: activityCenterNotificationsTuple[1]))
proc activityCenterNotifications*(self: ChatModel, cursor: string = "", activityCenterNotifications: seq[ActivityCenterNotification]) =
self.activityCenterCursor = cursor
self.events.emit("activityCenterNotificationsLoaded", ActivityCenterNotificationsArgs(activityCenterNotifications: activityCenterNotifications))
proc markAllActivityCenterNotificationsRead*(self: ChatModel): string =
try:
status_chat.markAllActivityCenterNotificationsRead()
except Exception as e:
error "Error marking all as read", msg = e.msg
result = e.msg
proc unreadActivityCenterNotificationsCount*(self: ChatModel): int =
status_chat.unreadActivityCenterNotificationsCount()

View File

@ -11,6 +11,12 @@ type ChatType* {.pure.}= enum
Timeline = 5
CommunityChat = 6
type ActivityCenterNotificationType* {.pure.}= enum
Unknown = 0,
NewOneToOne = 1,
NewPrivateGroupChat = 2,
Mention = 3
proc isOneToOne*(self: ChatType): bool = self == ChatType.OneToOne
proc isTimeline*(self: ChatType): bool = self == ChatType.Timeline
@ -122,6 +128,17 @@ type Community* = object
membershipRequests*: seq[CommunityMembershipRequest]
communityColor*: string
type ActivityCenterNotification* = ref object of RootObj
id*: string # ID is the id of the chat, for public chats it is the name e.g. status, for one-to-one is the hex encoded public key and for group chats is a random uuid appended with the hex encoded pk of the creator of the chat
chatId*: string
name*: string
notificationType*: ActivityCenterNotificationType
message*: Message
timestamp*: int64
read*: bool
dismissed*: bool
accepted*: bool
proc `$`*(self: Chat): string =
result = fmt"Chat(id:{self.id}, name:{self.name}, active:{self.isActive}, type:{self.chatType})"

View File

@ -73,7 +73,7 @@ proc loadChats*(): seq[Chat] =
result.add(chat)
result.sort(sortChats)
proc parseChatMessagesResponse*(chatId: string, rpcResult: JsonNode): (string, seq[Message]) =
proc parseChatMessagesResponse*(rpcResult: JsonNode): (string, seq[Message]) =
var messages: seq[Message] = @[]
let messagesObj = rpcResult{"messages"}
if(messagesObj != nil and messagesObj.kind != JNull):
@ -82,6 +82,15 @@ proc parseChatMessagesResponse*(chatId: string, rpcResult: JsonNode): (string, s
messages.add(jsonMsg.toMessage(pk))
return (rpcResult{"cursor"}.getStr, messages)
proc parseActivityCenterNotifications*(rpcResult: JsonNode): (string, seq[ActivityCenterNotification]) =
let pk = status_settings.getSetting[string](Setting.PublicKey, "0x0")
var notifs: seq[ActivityCenterNotification] = @[]
var msg: Message
if rpcResult{"notifications"}.kind != JNull:
for jsonMsg in rpcResult["notifications"]:
notifs.add(jsonMsg.toActivityCenterNotification(pk))
return (rpcResult{"cursor"}.getStr, notifs)
proc rpcChatMessages*(chatId: string, cursorVal: JsonNode, limit: int, success: var bool): string =
success = true
try:
@ -101,7 +110,7 @@ proc chatMessages*(chatId: string, cursor: string = ""): (string, seq[Message])
var success: bool
let callResult = rpcChatMessages(chatId, cursorVal, 20, success)
if success:
result = parseChatMessagesResponse(chatId, callResult.parseJson()["result"])
result = parseChatMessagesResponse(callResult.parseJson()["result"])
proc parseReactionsResponse*(chatId: string, rpcResult: JsonNode): (string, seq[Reaction]) =
var reactions: seq[Reaction] = @[]
@ -517,7 +526,7 @@ proc banUserFromCommunity*(pubKey: string, communityId: string): string =
}])
proc parseChatPinnedMessagesResponse*(chatId: string, rpcResult: JsonNode): (string, seq[Message]) =
proc parseChatPinnedMessagesResponse*(rpcResult: JsonNode): (string, seq[Message]) =
var messages: seq[Message] = @[]
let messagesObj = rpcResult{"pinnedMessages"}
if(messagesObj != nil and messagesObj.kind != JNull):
@ -549,7 +558,7 @@ proc pinnedMessagesByChatID*(chatId: string, cursor: string): (string, seq[Messa
var success: bool
let callResult = rpcPinnedChatMessages(chatId, cursorVal, 20, success)
if success:
result = parseChatPinnedMessagesResponse(chatId, callResult.parseJson()["result"])
result = parseChatPinnedMessagesResponse(callResult.parseJson()["result"])
proc setPinMessage*(messageId: string, chatId: string, pinned: bool) =
discard callPrivateRPC("sendPinMessage".prefix, %*[{
@ -566,7 +575,7 @@ proc rpcActivityCenterNotifications*(cursorVal: JsonNode, limit: int, success: v
success = false
result = e.msg
proc activityCenterNotification*(cursor: string = "") =
proc activityCenterNotification*(cursor: string = ""): (string, seq[ActivityCenterNotification]) =
var cursorVal: JsonNode
if cursor == "":
@ -576,6 +585,14 @@ proc activityCenterNotification*(cursor: string = "") =
var success: bool
let callResult = rpcActivityCenterNotifications(cursorVal, 20, success)
debug "Activity center", callResult
# if success:
# result = parseChatMessagesResponse(chatId, callResult.parseJson()["result"])
if success:
result = parseActivityCenterNotifications(callResult.parseJson()["result"])
proc markAllActivityCenterNotificationsRead*() =
discard callPrivateRPC("markAllActivityCenterNotificationsRead".prefix, %*[])
proc unreadActivityCenterNotificationsCount*(): int =
let rpcResult = callPrivateRPC("unreadActivityCenterNotificationsCount".prefix, %*[]).parseJson
if rpcResult{"result"}.kind != JNull:
return rpcResult["result"].getInt

View File

@ -23,6 +23,8 @@ proc toCommunity*(jsonCommunity: JsonNode): Community
proc toCommunityMembershipRequest*(jsonCommunityMembershipRequest: JsonNode): CommunityMembershipRequest
proc toActivityCenterNotification*(jsonNotification: JsonNode, pk: string): ActivityCenterNotification
proc fromEvent*(event: JsonNode): Signal =
var signal:MessageSignal = MessageSignal()
signal.messages = @[]
@ -66,6 +68,10 @@ proc fromEvent*(event: JsonNode): Signal =
for jsonCommunity in event["event"]["requestsToJoinCommunity"]:
signal.membershipRequests.add(jsonCommunity.toCommunityMembershipRequest)
if event["event"]{"activityCenterNotifications"} != nil:
for jsonNotification in event["event"]["activityCenterNotifications"]:
signal.activityCenterNotification.add(jsonNotification.toActivityCenterNotification(pk))
if event["event"]{"pinMessages"} != nil:
for jsonPinnedMessage in event["event"]["pinMessages"]:
var contentType: ContentType
@ -365,3 +371,24 @@ proc toReaction*(jsonReaction: JsonNode): Reaction =
emojiId: jsonReaction{"emojiId"}.getInt,
retracted: jsonReaction{"retracted"}.getBool
)
proc toActivityCenterNotification*(jsonNotification: JsonNode, pk: string): ActivityCenterNotification =
var activityCenterNotificationType: ActivityCenterNotificationType
try:
activityCenterNotificationType = ActivityCenterNotificationType(jsonNotification{"type"}.getInt)
except:
warn "Unknown notification type received", type = jsonNotification{"type"}.getInt
activityCenterNotificationType = ActivityCenterNotificationType.Unknown
result = ActivityCenterNotification(
id: jsonNotification{"id"}.getStr,
chatId: jsonNotification{"chatId"}.getStr,
name: jsonNotification{"name"}.getStr,
notificationType: activityCenterNotificationType,
timestamp: jsonNotification{"timestamp"}.getInt,
read: jsonNotification{"read"}.getBool,
dismissed: jsonNotification{"dismissed"}.getBool,
accepted: jsonNotification{"accepted"}.getBool
)
if jsonNotification.contains("message") and jsonNotification{"message"}.kind != JNull:
result.message = jsonNotification{"message"}.toMessage(pk)

View File

@ -36,6 +36,7 @@ type MessageSignal* = ref object of Signal
emojiReactions*: seq[Reaction]
communities*: seq[Community]
membershipRequests*: seq[CommunityMembershipRequest]
activityCenterNotification*: seq[ActivityCenterNotification]
type CommunitySignal* = ref object of Signal
community*: Community

View File

@ -1,10 +1,12 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQml.Models 2.13
import "../../../../shared"
import "../../../../shared/status"
import "../../../../imports"
import "./ChatComponents"
import "../components"
import "./MessageComponents"
Popup {
enum Filter {
@ -43,104 +45,182 @@ Popup {
id: activityCenterTopBar
}
Column {
id: notificationsContainer
ScrollView {
id: scrollView
anchors.top: activityCenterTopBar.bottom
anchors.topMargin: 13
anchors.bottom: parent.bottom
anchors.bottomMargin: Style.current.smallPadding
width: parent.width
clip: true
property Component profilePopupComponent: ProfilePopup {
id: profilePopup
onClosed: destroy()
}
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
// TODO remove this once it is handled by the activity center
Repeater {
id: contactList
model: profileModel.contacts.contactRequests
delegate: ContactRequest {
visible: activityCenter.currentFilter === ActivityCenter.Filter.All || activityCenter.currentFilter === ActivityCenter.Filter.ContactRequests
name: Utils.removeStatusEns(model.name)
address: model.address
localNickname: model.localNickname
identicon: model.thumbnailImage || model.identicon
// TODO set to transparent bg if the notif is read
color: Utils.setColorAlpha(Style.current.blue, 0.1)
radius: 0
profileClick: function (showFooter, userName, fromAuthor, identicon, textParam, nickName) {
var popup = profilePopupComponent.createObject(contactList);
popup.openPopup(showFooter, userName, fromAuthor, identicon, textParam, nickName);
}
onBlockContactActionTriggered: {
blockContactConfirmationDialog.contactName = name
blockContactConfirmationDialog.contactAddress = address
blockContactConfirmationDialog.open()
}
}
}
StyledText {
text: "Today"
anchors.left: parent.left
anchors.leftMargin: Style.current.padding
font.pixelSize: 15
bottomPadding: 4
topPadding: Style.current.halfPadding
color: Style.current.secondaryText
}
Rectangle {
visible: activityCenter.currentFilter === ActivityCenter.Filter.All || activityCenter.currentFilter === ActivityCenter.Filter.Mentions
Column {
id: notificationsContainer
width: parent.width
height: visible ? childrenRect.height + Style.current.smallPadding : 0
color: Utils.setColorAlpha(Style.current.blue, 0.1)
spacing: 0
Message {
id: placeholderMessage
anchors.right: undefined
messageId: "placeholderMessage"
userName: "@vitalik"
identicon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAb0lEQVR4Ae3UQQqAIBRF0Wj9ba9Bq6l5JBQqfn/ngDMH3YS3AAB/tO3H+XRG3b9bR/+gVoREI2RapVXpfd5+X5oXERKNkHS+rk3tOpWkeREh0QiZVu91ql2zNC8iJBoh0yqtSqt1slpCghICANDPBc0ESPh0bHkHAAAAAElFTkSuQmCC"
message: "@roger great question my dude"
contentType: Constants.messageType
placeholderMessage: true
property Component profilePopupComponent: ProfilePopup {
id: profilePopup
onClosed: destroy()
}
StatusIconButton {
id: markReadBtn
icon.name: "double-check"
iconColor: Style.current.primary
icon.width: 24
icon.height: 24
width: 32
height: 32
onClicked: console.log('TODO mark read')
anchors.right: parent.right
anchors.rightMargin: 12
anchors.verticalCenter: placeholderMessage.verticalCenter
z: 52
// TODO remove this once it is handled by the activity center
Repeater {
id: contactList
model: profileModel.contacts.contactRequests
StatusToolTip {
visible: markReadBtn.hovered
text: qsTr("Mark as Read")
orientation: "left"
x: - width - Style.current.padding
y: markReadBtn.height / 2 - height / 2 + 4
delegate: ContactRequest {
visible: activityCenter.currentFilter === ActivityCenter.Filter.All || activityCenter.currentFilter === ActivityCenter.Filter.ContactRequests
name: Utils.removeStatusEns(model.name)
address: model.address
localNickname: model.localNickname
identicon: model.thumbnailImage || model.identicon
// TODO set to transparent bg if the notif is read
color: Utils.setColorAlpha(Style.current.blue, 0.1)
radius: 0
profileClick: function (showFooter, userName, fromAuthor, identicon, textParam, nickName) {
var popup = profilePopupComponent.createObject(contactList);
popup.openPopup(showFooter, userName, fromAuthor, identicon, textParam, nickName);
}
onBlockContactActionTriggered: {
blockContactConfirmationDialog.contactName = name
blockContactConfirmationDialog.contactAddress = address
blockContactConfirmationDialog.open()
}
}
}
ActivityChannelBadge {
name: "status-desktop-ui"
chatType: Constants.chatTypePublic
chatId: "status-desktop-ui"
anchors.top: markReadBtn.bottom
anchors.topMargin: Style.current.halfPadding
anchors.left: parent.left
anchors.leftMargin: 61 // TODO find a way to align with the text of the message
Repeater {
model: notifDelegateList
}
}
// TODO add reply placeholder and chaeck if we can do the bubble under
DelegateModelGeneralized {
id: notifDelegateList
lessThan: [
function(left, right) { return left.timestamp > right.timestamp }
]
model: chatsModel.activityNotificationList
delegate: Item {
id: notificationDelegate
width: parent.width
height: notifLoader.active ? childrenRect.height : 0
property int idx: DelegateModel.itemsIndex
Loader {
id: notifLoader
anchors.top: parent.top
active: !!sourceComponent
width: parent.width
sourceComponent: {
switch (model.notificationType) {
// TODO add to constants (mention)
case 3: return messageNotificationComponent
default: return null
}
}
}
Component {
id: messageNotificationComponent
Rectangle {
visible: activityCenter.currentFilter === ActivityCenter.Filter.All || activityCenter.currentFilter === ActivityCenter.Filter.Mentions
width: parent.width
height: childrenRect.height + Style.current.smallPadding
color: model.read ? Style.current.transparent : Utils.setColorAlpha(Style.current.blue, 0.1)
Message {
id: notificationMessage
anchors.right: undefined
fromAuthor: model.message.fromAuthor
chatId: model.message.chatId
userName: model.message.userName
alias: model.message.alias
localName: model.message.localName
message: model.message.message
plainText: model.message.plainText
identicon: model.message.identicon
isCurrentUser: model.message.isCurrentUser
timestamp: model.message.timestamp
sticker: model.message.sticker
contentType: model.message.contentType
outgoingStatus: model.message.outgoingStatus
responseTo: model.message.responseTo
imageClick: imagePopup.openPopup.bind(imagePopup)
messageId: model.message.messageId
linkUrls: model.message.linkUrls
communityId: model.message.communityId
hasMention: model.message.hasMention
stickerPackId: model.message.stickerPackId
pinnedBy: model.message.pinnedBy
pinnedMessage: model.message.isPinned
activityCenterMessage: true
prevMessageIndex: {
if (notificationDelegate.idx === 0) {
return 0
}
// This is used in order to have access to the previous message and determine the timestamp
// we can't rely on the index because the sequence of messages is not ordered on the nim side
if (notificationDelegate.idx < notifDelegateList.items.count - 1) {
return notifDelegateList.items.get(notificationDelegate.idx - 1).model.index
}
return -1;
}
prevMsgTimestamp: notificationDelegate.idx === 0 ? "" : chatsModel.activityNotificationList.getNotificationData(prevMessageIndex, "timestamp")
}
// TODO add this back when single MarkAsRead is available
// StatusIconButton {
// id: markReadBtn
// icon.name: "double-check"
// iconColor: Style.current.primary
// icon.width: 24
// icon.height: 24
// width: 32
// height: 32
// onClicked: console.log('TODO mark read')
// anchors.right: parent.right
// anchors.rightMargin: 12
// anchors.verticalCenter: notificationMessage.verticalCenter
// z: 52
// StatusToolTip {
// visible: markReadBtn.hovered
// text: qsTr("Mark as Read")
// orientation: "left"
// x: - width - Style.current.padding
// y: markReadBtn.height / 2 - height / 2 + 4
// }
// }
ActivityChannelBadge {
name: model.name
chatId: model.chatId
anchors.top: notificationMessage.bottom
anchors.left: parent.left
anchors.leftMargin: 61 // TODO find a way to align with the text of the message
}
}
}
}
}
// StyledText {
// text: "Today"
// anchors.left: parent.left
// anchors.leftMargin: Style.current.padding
// font.pixelSize: 15
// bottomPadding: 4
// topPadding: Style.current.halfPadding
// color: Style.current.secondaryText
// }
}
}
}

View File

@ -71,7 +71,9 @@ Item {
icon.height: 24
width: 32
height: 32
onClicked: console.log('TODO mark all as read')
onClicked: {
errorText.text = chatsModel.activityNotificationList.markAllActivityCenterNotificationsRead()
}
StatusToolTip {
visible: markAllReadBtn.hovered
@ -109,4 +111,12 @@ Item {
}
}
}
StyledText {
id: errorText
visible: !!text
anchors.top: filterButtons.bottom
anchors.topMargin: Style.current.smallPadding
color: Style.current.danger
}
}

View File

@ -8,7 +8,7 @@ Rectangle {
property string chatId: ""
property string name: "channelName"
property string identicon
property int chatType: Constants.chatTypePublic
property int chatType: chatsModel.chats.getChannelType(chatId)
property int realChatType: {
if (chatType === Constants.chatTypeCommunity) {
// TODO add a check for private community chats once it is created

View File

@ -29,6 +29,7 @@ Item {
property bool hasMention: false
property string linkUrls: ""
property bool placeholderMessage: false
property bool activityCenterMessage: false
property bool pinnedMessage: false
property string pinnedBy
property bool forceHoverHandler: false // Used to force the HoverHandler to be active (useful for messages in popups)
@ -176,7 +177,7 @@ Item {
}
function clickMessage(isProfileClick, isSticker = false, isImage = false, image = null, emojiOnly = false, hideEmojiPicker = false) {
if (placeholderMessage) {
if (placeholderMessage || activityCenterMessage) {
return
}
@ -300,7 +301,7 @@ Item {
height: 12
}
}
StyledText {
id: fetchMoreButton
font.weight: Font.Medium

View File

@ -23,7 +23,7 @@ Item {
+ (dateGroupLbl.visible ? dateGroupLbl.height + dateGroupLbl.anchors.topMargin : 0)
MouseArea {
enabled: !placeholderMessage
enabled: !placeholderMessage && !activityCenterMessage
anchors.fill: messageContainer
acceptedButtons: Qt.RightButton
onClicked: messageMouseArea.clicked(mouse)
@ -53,6 +53,9 @@ Item {
DateGroup {
id: dateGroupLbl
previousMessageIndex: prevMessageIndex
previousMessageTimestamp: prevMsgTimestamp
messageTimestamp: timestamp
}
Rectangle {
@ -60,7 +63,7 @@ Item {
id: messageContainer
anchors.top: dateGroupLbl.visible ? dateGroupLbl.bottom : parent.top
anchors.topMargin: dateGroupLbl.visible ? Style.current.padding : 0
anchors.topMargin: dateGroupLbl.visible ? (activityCenterMessage ? 4 : Style.current.padding) : 0
height: childrenRect.height
+ (chatName.visible || emojiReactionLoader.active ? Style.current.halfPadding : 0)
+ (chatName.visible && emojiReactionLoader.active ? Style.current.padding : 0)
@ -71,6 +74,10 @@ Item {
width: parent.width
color: {
if (placeholderMessage || activityCenterMessage) {
return Style.current.transparent
}
if (pinnedMessage) {
return root.isHovered || isMessageActive ? Style.current.pinnedMessageBackgroundHovered : Style.current.pinnedMessageBackground
}
@ -297,7 +304,7 @@ Item {
}
Loader {
active: hasMention || pinnedMessage
active: !activityCenterMessage && (hasMention || pinnedMessage)
height: messageContainer.height
anchors.left: messageContainer.left
anchors.top: messageContainer.top

View File

@ -3,20 +3,29 @@ import "../../../../../shared"
import "../../../../../imports"
StyledText {
property int previousMessageIndex: -1
property string previousMessageTimestamp
property string messageTimestamp
id: dateGroupLbl
font.pixelSize: 13
color: Style.current.secondaryText
horizontalAlignment: Text.AlignHCenter
anchors.horizontalCenter: parent.horizontalCenter
anchors.horizontalCenter: activityCenterMessage ? undefined : parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: visible ? (activityCenterMessage ? Style.current.halfPadding : 20) : 0
anchors.left: parent.left
anchors.leftMargin: activityCenterMessage ? Style.current.padding : 0
text: {
if (prevMessageIndex == -1) return ""; // identifier
if (previousMessageIndex === -1) return ""; // identifier
let now = new Date()
let yesterday = new Date()
yesterday.setDate(now.getDate()-1)
var currentMsgDate = new Date(parseInt(timestamp, 10));
var prevMsgDate = prevMsgTimestamp === "" ? new Date(0) : new Date(parseInt(prevMsgTimestamp, 10));
var currentMsgDate = new Date(parseInt(messageTimestamp, 10));
var prevMsgDate = previousMessageTimestamp === "" ? new Date(0) : new Date(parseInt(previousMessageTimestamp, 10));
if (currentMsgDate.getDay() === prevMsgDate.getDay()) {
return ""
@ -59,6 +68,4 @@ StyledText {
}
}
visible: text !== ""
anchors.top: parent.top
anchors.topMargin: this.visible ? 20 : 0
}

View File

@ -3,7 +3,7 @@ import "../../../../../shared"
import "../../../../../imports"
MouseArea {
enabled: !placeholderMessage
enabled: !placeholderMessage && !activityCenterMessage
cursorShape: chatText.hoveredLink ? Qt.PointingHandCursor : undefined
acceptedButtons: Qt.RightButton | Qt.LeftButton
z: 50

View File

@ -18,6 +18,9 @@ Item {
DateGroup {
id: dateGroupLbl
previousMessageIndex: prevMessageIndex
previousMessageTimestamp: prevMsgTimestamp
messageTimestamp: timestamp
}
UserImage {

View File

@ -135,50 +135,50 @@ Item {
}
}
// Rectangle {
// id: separator
// width: 1
// height: 24
// color: Style.current.separator
// anchors.verticalCenter: parent.verticalCenter
// }
Rectangle {
id: separator
width: 1
height: 24
color: Style.current.separator
anchors.verticalCenter: parent.verticalCenter
}
// StatusIconButton {
// id: activityCenterBtn
// icon.name: "bell"
// iconColor: Style.current.contextMenuButtonForegroundColor
// hoveredIconColor: Style.current.contextMenuButtonForegroundColor
// highlightedBackgroundColor: Style.current.contextMenuButtonBackgroundHoverColor
// anchors.verticalCenter: parent.verticalCenter
StatusIconButton {
id: activityCenterBtn
icon.name: "bell"
iconColor: Style.current.contextMenuButtonForegroundColor
hoveredIconColor: Style.current.contextMenuButtonForegroundColor
highlightedBackgroundColor: Style.current.contextMenuButtonBackgroundHoverColor
anchors.verticalCenter: parent.verticalCenter
// onClicked: activityCenter.open()
onClicked: activityCenter.open()
// Rectangle {
// // TODO unhardcode this
// property int nbUnseenNotifs: 3
Rectangle {
property int nbUnseenNotifs: chatsModel.activityNotificationList.unreadCount
// id: badge
// visible: nbUnseenNotifs > 0
// anchors.top: parent.top
// anchors.topMargin: -2
// anchors.left: parent.right
// anchors.leftMargin: -17
// radius: height / 2
// color: Style.current.blue
// border.color: activityCenterBtn.hovered ? Style.current.secondaryBackground : Style.current.background
// border.width: 2
// width: badge.nbUnseenNotifs < 10 ? 18 : badge.width + 14
// height: 18
id: badge
visible: nbUnseenNotifs > 0
anchors.top: parent.top
anchors.topMargin: -2
anchors.left: parent.right
anchors.leftMargin: -17
radius: height / 2
color: Style.current.blue
border.color: activityCenterBtn.hovered ? Style.current.secondaryBackground : Style.current.background
border.width: 2
width: badge.nbUnseenNotifs < 10 ? 18 : badgeText.width + 14
height: 18
// Text {
// font.pixelSize: 12
// color: Style.current.white
// anchors.centerIn: parent
// text: badge.nbUnseenNotifs
// }
// }
// }
// }
Text {
id: badgeText
font.pixelSize: 12
color: Style.current.white
anchors.centerIn: parent
text: badge.nbUnseenNotifs
}
}
}
}
ActivityCenter {
id: activityCenter