parent
c0013a0956
commit
414b39d7e0
|
@ -42,6 +42,8 @@ proc init*(self: ChatController) =
|
|||
self.status.stickers.init()
|
||||
self.view.reactions.init()
|
||||
|
||||
self.view.asyncActivityNotificationLoad()
|
||||
|
||||
let recentStickers = self.status.stickers.getRecentStickers()
|
||||
for sticker in recentStickers:
|
||||
self.view.stickers.addRecentStickerToList(sticker)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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((" ", " "))
|
||||
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
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
@ -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})"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -18,6 +18,9 @@ Item {
|
|||
|
||||
DateGroup {
|
||||
id: dateGroupLbl
|
||||
previousMessageIndex: prevMessageIndex
|
||||
previousMessageTimestamp: prevMsgTimestamp
|
||||
messageTimestamp: timestamp
|
||||
}
|
||||
|
||||
UserImage {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue