refactor(act-center): refactor activity center to the new arch

This commit is contained in:
Jonathan Rainville 2021-12-13 14:52:55 -05:00 committed by Sale Djenic
parent 27f8c33dbd
commit 45fddc55ba
26 changed files with 1421 additions and 203 deletions

View File

@ -27,6 +27,7 @@ import ../../app_service/service/stickers/service as stickers_service
import ../../app_service/service/about/service as about_service
import ../../app_service/service/node_configuration/service as node_configuration_service
import ../../app_service/service/network/service as network_service
import ../../app_service/service/activity_center/service as activity_center_service
import ../modules/startup/module as startup_module
import ../modules/main/module as main_module
@ -89,6 +90,7 @@ type
stickersService: stickers_service.Service
aboutService: about_service.Service
networkService: network_service.Service
activityCenterService: activity_center_service.Service
languageService: language_service.Service
mnemonicService: mnemonic_service.Service
privacyService: privacy_service.Service
@ -140,6 +142,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.chatService = chat_service.newService(statusFoundation.status.events, result.contactsService)
result.communityService = community_service.newService(result.chatService)
result.messageService = message_service.newService(statusFoundation.status.events, statusFoundation.threadpool)
result.activityCenterService = activity_center_service.newService(statusFoundation.status.events, statusFoundation.threadpool, result.chatService)
result.tokenService = token_service.newService(statusFoundation.status.events, statusFoundation.threadpool,
result.settingsService)
result.collectibleService = collectible_service.newService(result.settingsService)
@ -196,7 +199,8 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.mnemonicService,
result.privacyService,
result.providerService,
result.stickersService
result.stickersService,
result.activityCenterService
)
# Do connections
@ -226,6 +230,7 @@ proc delete*(self: AppController) =
self.walletAccountService.delete
self.aboutService.delete
self.networkService.delete
self.activityCenterService.delete
self.dappPermissionsService.delete
self.providerService.delete
self.ensService.delete
@ -270,6 +275,7 @@ proc load(self: AppController) =
self.languageService.init()
self.stickersService.init()
self.networkService.init()
self.activityCenterService.init()
let pubKey = self.settingsService.getPublicKey()
singletonInstance.localAccountSensitiveSettings.setFileName(pubKey)

View File

@ -9,6 +9,7 @@ import status/types/community as old_community
import ../../../../app_service/service/message/dto/[message, pinned_message, reaction]
import ../../../../app_service/service/chat/dto/[chat]
import ../../../../app_service/service/community/dto/[community]
import ../../../../app_service/service/activity_center/dto/[notification]
import ../../../../app_service/service/contacts/dto/[contacts, status_update]
type MessageSignal* = ref object of Signal
@ -20,7 +21,7 @@ type MessageSignal* = ref object of Signal
emojiReactions*: seq[ReactionDto]
communities*: seq[CommunityDto]
membershipRequests*: seq[old_community.CommunityMembershipRequest]
activityCenterNotification*: seq[ActivityCenterNotification]
activityCenterNotifications*: seq[ActivityCenterNotificationDto]
statusUpdates*: seq[StatusUpdateDto]
deletedMessages*: seq[RemovedMessage]
@ -70,7 +71,7 @@ proc fromEvent*(T: type MessageSignal, event: JsonNode): MessageSignal =
if event["event"]{"activityCenterNotifications"} != nil:
for jsonNotification in event["event"]["activityCenterNotifications"]:
signal.activityCenterNotification.add(jsonNotification.toActivityCenterNotification())
signal.activityCenterNotifications.add(jsonNotification.toActivityCenterNotificationDto())
if event["event"]{"pinMessages"} != nil:
discard

View File

@ -0,0 +1,105 @@
import Tables, stint
import eventemitter
import ./controller_interface
import ./io_interface
import ../../../../app/core/signals/types
import ../../../../app_service/service/activity_center/service as activity_center_service
import ../../../../app_service/service/contacts/service as contacts_service
import ../../../../app_service/service/chat/service as chat_service
export controller_interface
type
Controller*[T: controller_interface.DelegateInterface] = ref object of controller_interface.AccessInterface
delegate: io_interface.AccessInterface
events: EventEmitter
activityCenterService: activity_center_service.Service
contactsService: contacts_service.Service
proc newController*[T](
delegate: io_interface.AccessInterface,
events: EventEmitter,
activityCenterService: activity_center_service.Service,
contactsService: contacts_service.Service
): Controller[T] =
result = Controller[T]()
result.delegate = delegate
result.events = events
result.activityCenterService = activityCenterService
result.contactsService = contactsService
method delete*[T](self: Controller[T]) =
discard
method init*[T](self: Controller[T]) =
self.events.on(SIGNAL_ACTIVITY_CENTER_NOTIFICATIONS_LOADED) do(e: Args):
let args = ActivityCenterNotificationsArgs(e)
self.delegate.pushActivityCenterNotifications(args.activityCenterNotifications)
self.events.on(chat_service.SIGNAL_CHAT_UPDATE) do(e: Args):
var evArgs = ChatUpdateArgsNew(e)
if (evArgs.activityCenterNotifications.len > 0):
self.delegate.addActivityCenterNotification(evArgs.activityCenterNotifications)
self.events.on(activity_center_service.SIGNAL_MARK_NOTIFICATIONS_AS_ACCEPTED) do(e: Args):
var evArgs = MarkAsAcceptedNotificationProperties(e)
self.delegate.acceptActivityCenterNotificationsDone(evArgs.notificationIds)
self.events.on(activity_center_service.SIGNAL_MARK_NOTIFICATIONS_AS_DISMISSED) do(e: Args):
var evArgs = MarkAsDismissedNotificationProperties(e)
self.delegate.dismissActivityCenterNotificationsDone(evArgs.notificationIds)
self.events.on(activity_center_service.SIGNAL_MARK_NOTIFICATIONS_AS_READ) do(e: Args):
var evArgs = MarkAsReadNotificationProperties(e)
if (evArgs.isAll):
self.delegate.markAllActivityCenterNotificationsReadDone()
return
if (evArgs.notificationIds.len > 0):
self.delegate.markActivityCenterNotificationReadDone(evArgs.notificationIds)
self.events.on(activity_center_service.SIGNAL_MARK_NOTIFICATIONS_AS_UNREAD) do(e: Args):
var evArgs = MarkAsUnreadNotificationProperties(e)
if (evArgs.notificationIds.len > 0):
self.delegate.markActivityCenterNotificationUnreadDone(evArgs.notificationIds)
self.events.on(SignalType.Message.event) do(e: Args):
var evArgs = MessageSignal(e)
if (evArgs.activityCenterNotifications.len > 0):
self.delegate.addActivityCenterNotification(evArgs.activityCenterNotifications)
method hasMoreToShow*[T](self: Controller[T]): bool =
return self.activityCenterService.hasMoreToShow()
method unreadActivityCenterNotificationsCount*[T](self: Controller[T]): int =
return self.activityCenterService.unreadActivityCenterNotificationsCount()
method getContactDetails*[T](self: Controller[T], contactId: string): ContactDetails =
return self.contactsService.getContactDetails(contactId)
method getActivityCenterNotifications*[T](self: Controller[T]): seq[ActivityCenterNotificationDto] =
return self.activityCenterService.getActivityCenterNotifications()
method markAllActivityCenterNotificationsRead*[T](self: Controller[T]): string =
return self.activityCenterService.markAllActivityCenterNotificationsRead()
method markActivityCenterNotificationRead*[T](
self: Controller[T],
notificationId: string,
markAsReadProps: MarkAsReadNotificationProperties
): string =
return self.activityCenterService.markActivityCenterNotificationRead(notificationId, markAsReadProps)
method markActivityCenterNotificationUnread*[T](
self: Controller[T],
notificationId: string,
markAsUnreadProps: MarkAsUnreadNotificationProperties
): string =
return self.activityCenterService.markActivityCenterNotificationUnread(notificationId, markAsUnreadProps)
method acceptActivityCenterNotifications*[T](self: Controller[T], notificationIds: seq[string]): string =
return self.activityCenterService.acceptActivityCenterNotifications(notificationIds)
method dismissActivityCenterNotifications*[T](self: Controller[T], notificationIds: seq[string]): string =
return self.activityCenterService.dismissActivityCenterNotifications(notificationIds)

View File

@ -0,0 +1,44 @@
import ../../../../app_service/service/contacts/service as contacts_service
import ../../../../app_service/service/activity_center/service as activity_center_service
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
## Abstract class for any input/interaction with this module.
method delete*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method init*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method hasMoreToShow*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
method unreadActivityCenterNotificationsCount*(self: AccessInterface): int {.base.} =
raise newException(ValueError, "No implementation available")
method getContactDetails*(self: AccessInterface, contactId: string): ContactDetails {.base.} =
raise newException(ValueError, "No implementation available")
method getActivityCenterNotifications*(self: AccessInterface): seq[ActivityCenterNotificationDto] {.base.} =
raise newException(ValueError, "No implementation available")
method markAllActivityCenterNotificationsRead*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
method markActivityCenterNotificationRead*(self: AccessInterface, notificationId: string, markAsReadProps: MarkAsReadNotificationProperties): string {.base.} =
raise newException(ValueError, "No implementation available")
method markActivityCenterNotificationUnread*(self: AccessInterface, notificationId: string, markAsUnreadProps: MarkAsUnreadNotificationProperties): string {.base.} =
raise newException(ValueError, "No implementation available")
method acceptActivityCenterNotifications*(self: AccessInterface, notificationIds: seq[string]): string {.base.} =
raise newException(ValueError, "No implementation available")
method dismissActivityCenterNotifications*(self: AccessInterface, notificationIds: seq[string]): string {.base.} =
raise newException(ValueError, "No implementation available")
type
## Abstract class (concept) which must be implemented by object/s used in this
## module.
DelegateInterface* = concept c

View File

@ -0,0 +1,73 @@
import Tables, stint
import ./item
import ../../../../app_service/service/activity_center/service as activity_center_service
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
## Abstract class for any input/interaction with this module.
method delete*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method load*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method isLoaded*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
method viewDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method hasMoreToShow*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
method unreadActivityCenterNotificationsCount*(self: AccessInterface): int {.base.} =
raise newException(ValueError, "No implementation available")
method convertToItems*(self: AccessInterface, activityCenterNotifications: seq[ActivityCenterNotificationDto]): seq[Item] {.base.} =
raise newException(ValueError, "No implementation available")
method getActivityCenterNotifications*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method markAllActivityCenterNotificationsRead*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
method markAllActivityCenterNotificationsReadDone*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method dismissActivityCenterNotificationsDone*(self: AccessInterface, notificationIds: seq[string]) {.base.} =
raise newException(ValueError, "No implementation available")
method markActivityCenterNotificationReadDone*(self: AccessInterface, notificationIds: seq[string]) {.base.} =
raise newException(ValueError, "No implementation available")
method markActivityCenterNotificationUnreadDone*(self: AccessInterface, notificationIds: seq[string]) {.base.} =
raise newException(ValueError, "No implementation available")
method acceptActivityCenterNotificationsDone*(self: AccessInterface, notificationIds: seq[string]) {.base.} =
raise newException(ValueError, "No implementation available")
method markActivityCenterNotificationRead*(self: AccessInterface, notificationId: string, communityId: string, channelId: string, nType: int): string {.base.} =
raise newException(ValueError, "No implementation available")
method markActivityCenterNotificationUnread*(self: AccessInterface, notificationId: string, communityId: string, channelId: string, nType: int): string {.base.} =
raise newException(ValueError, "No implementation available")
method pushActivityCenterNotifications*(self: AccessInterface, activityCenterNotifications: seq[ActivityCenterNotificationDto]) {.base.} =
raise newException(ValueError, "No implementation available")
method addActivityCenterNotification*(self: AccessInterface, activityCenterNotifications: seq[ActivityCenterNotificationDto]) {.base.} =
raise newException(ValueError, "No implementation available")
method acceptActivityCenterNotifications*(self: AccessInterface, notificationIds: seq[string]): string {.base.} =
raise newException(ValueError, "No implementation available")
method dismissActivityCenterNotifications*(self: AccessInterface, notificationIds: seq[string]): string {.base.} =
raise newException(ValueError, "No implementation available")
type
## Abstract class (concept) which must be implemented by object/s used in this
## module.
DelegateInterface* = concept c
c.activityCenterDidLoad()

View File

@ -0,0 +1,85 @@
import strformat, stint
import ../../shared_models/message_item_qobject
type Item* = ref object
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
author: string
notificationType: int
timestamp: int64
read: bool
dismissed: bool
accepted: bool
messageItem: MessageItem
proc initItem*(
id: string,
chatId: string,
name: string,
author: string,
notificationType: int,
timestamp: int64,
read: bool,
dismissed: bool,
accepted: bool,
messageItem: MessageItem
): Item =
result = Item()
result.id = id
result.chatId = chatId
result.name = name
result.author = author
result.notificationType = notificationType
result.timestamp = timestamp
result.read = read
result.dismissed = dismissed
result.accepted = accepted
result.messageItem = messageItem
proc `$`*(self: Item): string =
result = fmt"""StickerItem(
id: {self.id},
name: {$self.name},
chatId: {$self.chatId},
author: {$self.author},
notificationType: {$self.notificationType},
timestamp: {$self.timestamp},
read: {$self.read},
dismissed: {$self.dismissed},
accepted: {$self.accepted},
# messageItem: {$self.messageItem},
]"""
proc id*(self: Item): string =
return self.id
proc name*(self: Item): string =
return self.name
proc author*(self: Item): string =
return self.author
proc chatId*(self: Item): string =
return self.chatId
proc notificationType*(self: Item): int =
return self.notificationType
proc timestamp*(self: Item): int64 =
return self.timestamp
proc read*(self: Item): bool =
return self.read
proc `read=`*(self: Item, value: bool) =
self.read = value
proc dismissed*(self: Item): bool =
return self.dismissed
proc accepted*(self: Item): bool =
return self.accepted
proc messageItem*(self: Item): MessageItem =
return self.messageItem

View File

@ -0,0 +1,196 @@
import NimQml, Tables, chronicles, json, sequtils, strformat, strutils
import ./item
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
Author = UserRole + 10
QtObject:
type
Model* = ref object of QAbstractListModel
activityCenterNotifications*: seq[Item]
nbUnreadNotifications*: int
proc setup(self: Model) = self.QAbstractListModel.setup
proc delete(self: Model) =
self.activityCenterNotifications = @[]
self.QAbstractListModel.delete
proc newModel*(): Model =
new(result, delete)
result.activityCenterNotifications = @[]
result.setup()
proc getUnreadNotificationsForChat*(self: Model, chatId: string): seq[string] =
result = @[]
for notification in self.activityCenterNotifications:
if (notification.chatId == chatId and not notification.read):
result.add(notification.id)
proc unreadCountChanged*(self: Model) {.signal.}
proc unreadCount*(self: Model): int {.slot.} =
self.nbUnreadNotifications
QtProperty[int] unreadCount:
read = unreadCount
notify = unreadCountChanged
proc markAllAsRead*(self: Model) =
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])
method rowCount*(self: Model, index: QModelIndex = nil): int = self.activityCenterNotifications.len
method data(self: Model, 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.Author: result = newQVariant(acitivityNotificationItem.author)
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: Model, 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 "author": result = notif.author
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: Model): Table[int, string] =
{
NotifRoles.Id.int:"id",
NotifRoles.ChatId.int:"chatId",
NotifRoles.Name.int: "name",
NotifRoles.Author.int: "author",
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 reduceUnreadCount(self: Model, numberNotifs: int) =
self.nbUnreadNotifications = self.nbUnreadNotifications - numberNotifs
if (self.nbUnreadNotifications < 0):
self.nbUnreadNotifications = 0
self.unreadCountChanged()
proc markActivityCenterNotificationUnread*(self: Model, notificationId: string) =
self.nbUnreadNotifications = self.nbUnreadNotifications + 1
self.unreadCountChanged()
var i = 0
for acnViewItem in self.activityCenterNotifications:
if (acnViewItem.id == notificationId):
acnViewItem.read = false
let index = self.createIndex(i, 0, nil)
self.dataChanged(index, index, @[NotifRoles.Read.int])
break
i.inc
proc markActivityCenterNotificationRead*(self: Model, notificationId: string) =
self.nbUnreadNotifications = self.nbUnreadNotifications - 1
if (self.nbUnreadNotifications < 0):
self.nbUnreadNotifications = 0
self.unreadCountChanged()
var i = 0
for acnViewItem in self.activityCenterNotifications:
if (acnViewItem.id == notificationId):
acnViewItem.read = true
let index = self.createIndex(i, 0, nil)
self.dataChanged(index, index, @[NotifRoles.Read.int])
break
i.inc
proc removeNotifications*(self: Model, ids: seq[string]) =
var i = 0
var indexesToDelete: seq[int] = @[]
for activityCenterNotification in self.activityCenterNotifications:
for id in ids:
if (activityCenterNotification.id == id):
indexesToDelete.add(i)
break
i = i + 1
i = 0
for index in indexesToDelete:
let indexUpdated = index - i
self.beginRemoveRows(newQModelIndex(), indexUpdated, indexUpdated)
self.activityCenterNotifications.delete(indexUpdated)
self.endRemoveRows()
i = i + 1
self.reduceUnreadCount(ids.len)
proc setNewData*(self: Model, activityCenterNotifications: seq[Item]) =
self.beginResetModel()
self.activityCenterNotifications = activityCenterNotifications
self.endResetModel()
proc addActivityNotificationItemToList*(self: Model, activityCenterNotification: Item, addToCount: bool = true) =
self.beginInsertRows(newQModelIndex(), self.activityCenterNotifications.len, self.activityCenterNotifications.len)
self.activityCenterNotifications.add(activityCenterNotification)
self.endInsertRows()
if (addToCount and not activityCenterNotification.read):
self.nbUnreadNotifications = self.nbUnreadNotifications + 1
proc updateUnreadCount*(self: Model, count: int) =
self.nbUnreadNotifications = count
self.unreadCountChanged()
proc addActivityNotificationItemsToList*(self: Model, activityCenterNotifications: seq[Item]) =
if (self.activityCenterNotifications.len == 0):
self.setNewData(activityCenterNotifications)
else:
for activityCenterNotification in activityCenterNotifications:
var found = false
for notif in self.activityCenterNotifications:
if activityCenterNotification.id == notif.id:
found = true
break
if found: continue
self.addActivityNotificationItemToList(activityCenterNotification, false)

View File

@ -0,0 +1,176 @@
import NimQml, Tables, stint, sugar, sequtils
import eventemitter
import ./io_interface, ./view, ./controller
import ./item as notification_item
import ../../shared_models/message_item as message_item
import ../../shared_models/message_item_qobject as message_item_qobject
import ../../../global/global_singleton
import ../../../../app_service/service/activity_center/service as activity_center_service
import ../../../../app_service/service/contacts/service as contacts_service
export io_interface
type
Module* [T: io_interface.DelegateInterface] = ref object of io_interface.AccessInterface
delegate: T
controller: controller.AccessInterface
view: View
viewVariant: QVariant
moduleLoaded: bool
proc newModule*[T](
delegate: T,
events: EventEmitter,
activityCenterService: activity_center_service.Service,
contactsService: contacts_service.Service
): Module[T] =
result = Module[T]()
result.delegate = delegate
result.view = newView(result)
result.viewVariant = newQVariant(result.view)
result.controller = controller.newController[Module[T]](
result,
events,
activityCenterService,
contactsService
)
result.moduleLoaded = false
method delete*[T](self: Module[T]) =
self.view.delete
method load*[T](self: Module[T]) =
singletonInstance.engine.setRootContextProperty("activityCenterModule", self.viewVariant)
self.controller.init()
self.view.load()
method isLoaded*[T](self: Module[T]): bool =
return self.moduleLoaded
method viewDidLoad*[T](self: Module[T]) =
self.moduleLoaded = true
self.delegate.activityCenterDidLoad()
method hasMoreToShow*[T](self: Module[T]): bool =
self.controller.hasMoreToShow()
method unreadActivityCenterNotificationsCount*[T](self: Module[T]): int =
self.controller.unreadActivityCenterNotificationsCount()
method convertToItems*[T](
self: Module[T],
activityCenterNotifications: seq[ActivityCenterNotificationDto]
): seq[notification_item.Item] =
result = activityCenterNotifications.map(
proc(n: ActivityCenterNotificationDto): Item =
var messageItem = MessageItem()
if (n.message.id == ""):
# If there is a message in the Notification, transfer it to a MessageItem (QObject)
let contactDetails = self.controller.getContactDetails(n.message.`from`)
messageItem = message_item_qobject.newMessageItem(initItem(
n.message.id,
n.message.responseTo,
n.message.`from`,
contactDetails.displayName,
contactDetails.localNickname,
contactDetails.icon,
contactDetails.isIconIdenticon,
contactDetails.isCurrentUser,
n.message.outgoingStatus,
n.message.text,
n.message.image,
n.message.seen,
n.message.timestamp,
ContentType(n.message.contentType),
n.message.messageType
))
return notification_item.initItem(
n.id,
n.chatId,
n.name,
n.author,
n.notificationType.int,
n.timestamp,
n.read,
n.dismissed,
n.accepted,
messageItem
)
)
method getActivityCenterNotifications*[T](self: Module[T]): seq[notification_item.Item] =
let activityCenterNotifications = self.controller.getActivityCenterNotifications()
self.view.pushActivityCenterNotifications(self.convertToItems(activityCenterNotifications))
method markAllActivityCenterNotificationsRead*[T](self: Module[T]): string =
self.controller.markAllActivityCenterNotificationsRead()
method markAllActivityCenterNotificationsReadDone*[T](self: Module[T]): string =
self.view.markAllActivityCenterNotificationsReadDone()
method markActivityCenterNotificationRead*[T](
self: Module[T],
notificationId: string,
communityId: string,
channelId: string,
nType: int
): string =
let notificationType = ActivityCenterNotificationType(nType)
let markAsReadProps = MarkAsReadNotificationProperties(
notificationIds: @[notificationId],
communityId: communityId,
channelId: channelId,
notificationTypes: @[notificationType]
)
result = self.controller.markActivityCenterNotificationRead(notificationId, markAsReadProps)
method markActivityCenterNotificationReadDone*[T](self: Module[T], notificationIds: seq[string]) =
for notificationId in notificationIds:
self.view.markActivityCenterNotificationReadDone(notificationId)
method pushActivityCenterNotifications*[T](
self: Module[T],
activityCenterNotifications: seq[ActivityCenterNotificationDto]
) =
self.view.pushActivityCenterNotifications(self.convertToItems(activityCenterNotifications))
method addActivityCenterNotification*[T](
self: Module[T],
activityCenterNotifications: seq[ActivityCenterNotificationDto]
) =
self.view.addActivityCenterNotification(self.convertToItems(activityCenterNotifications))
method markActivityCenterNotificationUnread*[T](
self: Module[T],
notificationId: string,
communityId: string,
channelId: string,
nType: int
): string =
let notificationType = ActivityCenterNotificationType(nType)
let markAsUnreadProps = MarkAsUnreadNotificationProperties(
notificationIds: @[notificationId],
communityId: communityId,
channelId: channelId,
notificationTypes: @[notificationType]
)
result = self.controller.markActivityCenterNotificationUnread(notificationId, markAsUnreadProps)
method markActivityCenterNotificationUnreadDone*[T](self: Module[T], notificationIds: seq[string]) =
for notificationId in notificationIds:
self.view.markActivityCenterNotificationUnreadDone(notificationId)
method acceptActivityCenterNotificationsDone*[T](self: Module[T], notificationIds: seq[string]) =
self.view.acceptActivityCenterNotificationsDone(notificationIds)
method acceptActivityCenterNotifications*[T](self: Module[T], notificationIds: seq[string]): string =
self.controller.acceptActivityCenterNotifications(notificationIds)
method dismissActivityCenterNotificationsDone*[T](self: Module[T], notificationIds: seq[string]) =
self.view.dismissActivityCenterNotificationsDone(notificationIds)
method dismissActivityCenterNotifications*[T](self: Module[T], notificationIds: seq[string]): string =
self.controller.dismissActivityCenterNotifications(notificationIds)

View File

@ -0,0 +1,128 @@
import NimQml, json, strutils, json_serialization, sequtils, strformat
import ../../../../app_service/service/activity_center/service as activity_center_service
import ./model
import ./io_interface, ./item
QtObject:
type
View* = ref object of QObject
delegate: io_interface.AccessInterface
model: Model
modelVariant: QVariant
proc delete*(self: View) =
self.QObject.delete
proc newView*(delegate: io_interface.AccessInterface): View =
new(result, delete)
result.QObject.setup
result.delegate = delegate
result.model = newModel()
result.modelVariant = newQVariant(result.model)
proc load*(self: View) =
self.delegate.viewDidLoad()
proc activityNotificationsChanged*(self: View) {.signal.}
proc getModel(self: View): QVariant {.slot.} =
return newQVariant(self.modelVariant)
QtProperty[QVariant] model:
read = getModel
notify = activityNotificationsChanged
proc hasMoreToShowChanged*(self: View) {.signal.}
proc hasMoreToShow*(self: View): bool {.slot.} =
self.delegate.hasMoreToShow()
QtProperty[bool] hasMoreToShow:
read = hasMoreToShow
notify = hasMoreToShowChanged
proc pushActivityCenterNotifications*(self:View, activityCenterNotifications: seq[Item]) =
self.model.addActivityNotificationItemsToList(activityCenterNotifications)
self.activityNotificationsChanged()
self.hasMoreToShowChanged()
let count = self.delegate.unreadActivityCenterNotificationsCount()
self.model.updateUnreadCount(count)
proc loadMoreNotifications(self: View) {.slot.} =
self.delegate.getActivityCenterNotifications()
proc markAllActivityCenterNotificationsRead(self: View): string {.slot.} =
result = self.delegate.markAllActivityCenterNotificationsRead()
proc markAllActivityCenterNotificationsReadDone*(self: View) {.slot.} =
self.model.markAllAsRead()
proc markActivityCenterNotificationRead(
self: View,
notificationId: string,
communityId: string,
channelId: string,
nType: int
): void {.slot.} =
discard self.delegate.markActivityCenterNotificationRead(notificationId, communityId, channelId, nType)
proc markActivityCenterNotificationReadDone*(self: View, notificationId: string) =
self.model.markActivityCenterNotificationRead(notificationId)
proc markActivityCenterNotificationUnreadDone*(self: View, notificationId: string) =
self.model.markActivityCenterNotificationUnread(notificationId)
proc markAllChatMentionsAsRead*(self: View, communityId: string, chatId: string) =
let notifsIds = self.model.getUnreadNotificationsForChat(chatId)
for notifId in notifsIds:
# TODO change the 3 to the real type
self.markActivityCenterNotificationRead(notifId, communityId, chatId, ActivityCenterNotificationType.Mention.int)
proc markActivityCenterNotificationUnread(
self: View,
notificationId: string,
communityId: string,
channelId: string,
nType: int
): void {.slot.} =
discard self.delegate.markActivityCenterNotificationUnread(
notificationId,
communityId,
channelId,
nType
)
proc acceptActivityCenterNotifications(self: View, idsJson: string): string {.slot.} =
let ids = map(parseJson(idsJson).getElems(), proc(x:JsonNode):string = x.getStr())
result = self.delegate.acceptActivityCenterNotifications(ids)
proc acceptActivityCenterNotificationsDone*(self: View, notificationIds: seq[string]) =
self.model.removeNotifications(notificationIds)
proc acceptActivityCenterNotification(self: View, id: string): string {.slot.} =
self.acceptActivityCenterNotifications(fmt"[""{id}""]")
proc dismissActivityCenterNotifications(self: View, idsJson: string): string {.slot.} =
let ids = map(parseJson(idsJson).getElems(), proc(x:JsonNode):string = x.getStr())
result = self.delegate.dismissActivityCenterNotifications(ids)
proc dismissActivityCenterNotification(self: View, id: string): string {.slot.} =
self.dismissActivityCenterNotifications(fmt"[""{id}""]")
proc dismissActivityCenterNotificationsDone*(self: View, notificationIds: seq[string]) =
self.model.removeNotifications(notificationIds)
proc addActivityCenterNotification*(self: View, activityCenterNotifications: seq[Item]) =
for activityCenterNotification in activityCenterNotifications:
# TODO this should be handled by the chat or community module
# if self.channelView.activeChannel.id == activityCenterNotification.chatId:
# activityCenterNotification.read = true
# let communityId = self.status.chat.getCommunityIdForChat(activityCenterNotification.chatId)
# if communityId != "":
# self.communities.joinedCommunityList.decrementMentions(communityId, activityCenterNotification.chatId)
self.model.addActivityNotificationItemToList(activityCenterNotification)
self.activityNotificationsChanged()

View File

@ -103,4 +103,7 @@ method unmuteChat*(self: Controller) =
self.chatService.unmuteChat(self.chatId)
method getContactById*(self: Controller, contactId: string): ContactsDto =
return self.contactService.getContactById(contactId)
return self.contactService.getContactById(contactId)
method getContactDetails*(self: Controller, contactId: string): ContactDetails =
return self.contactService.getContactDetails(contactId)

View File

@ -1,6 +1,7 @@
import ../../../../../app_service/service/contacts/dto/[contacts]
import ../../../../../app_service/service/message/dto/[message, reaction]
import ../../../../../app_service/service/chat/dto/[chat]
import ../../../../../app_service/service/contacts/service
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
@ -42,4 +43,7 @@ method unmuteChat*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method getContactById*(self: AccessInterface, contactId: string): ContactsDto {.base.} =
raise newException(ValueError, "No implementation available")
method getContactDetails*(self: AccessInterface, contactId: string): ContactDetails {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -128,18 +128,25 @@ proc buildPinnedMessageItem(self: Module, messageId: string, item: var pinned_ms
if(err.len > 0):
return false
let sender = self.controller.getContactById(m.`from`)
let senderDisplayName = sender.userNameOrAlias()
let amISender = m.`from` == singletonInstance.userProfile.getPubKey()
var senderIcon = sender.identicon
var isSenderIconIdenticon = sender.identicon.len > 0
if(sender.image.thumbnail.len > 0):
senderIcon = sender.image.thumbnail
isSenderIconIdenticon = false
let contactDetails = self.controller.getContactDetails(m.`from`)
var item = initItem(m.id, m.responseTo, m.`from`, senderDisplayName, sender.localNickname, senderIcon,
isSenderIconIdenticon, amISender, m.outgoingStatus, m.text, m.image, m.seen, m.timestamp, m.contentType.ContentType,
m.messageType)
var item = initItem(
m.id,
m.responseTo,
m.`from`,
contactDetails.displayName,
contactDetails.localNickname,
contactDetails.icon,
contactDetails.isIconIdenticon,
contactDetails.isCurrentUser,
m.outgoingStatus,
m.text,
m.image,
m.seen,
m.timestamp,
m.contentType.ContentType,
m.messageType
)
item.pinned = true
for r in reactions:

View File

@ -10,6 +10,7 @@ import browser_section/module as browser_section_module
import profile_section/module as profile_section_module
import app_search/module as app_search_module
import stickers/module as stickers_module
import activity_center/module as activity_center_module
import ../../../app_service/service/keychain/service as keychain_service
import ../../../app_service/service/chat/service as chat_service
@ -31,6 +32,7 @@ import ../../../app_service/service/language/service as language_service
import ../../../app_service/service/mnemonic/service as mnemonic_service
import ../../../app_service/service/privacy/service as privacy_service
import ../../../app_service/service/stickers/service as stickers_service
import ../../../app_service/service/activity_center/service as activity_center_service
import eventemitter
@ -48,6 +50,7 @@ type
browserSectionModule: browser_section_module.AccessInterface
profileSectionModule: profile_section_module.AccessInterface
stickersModule: stickers_module.AccessInterface
activityCenterModule: activity_center_module.AccessInterface
appSearchModule: app_search_module.AccessInterface
moduleLoaded: bool
@ -73,7 +76,8 @@ proc newModule*[T](
mnemonicService: mnemonic_service.ServiceInterface,
privacyService: privacy_service.ServiceInterface,
providerService: provider_service.ServiceInterface,
stickersService: stickers_service.Service
stickersService: stickers_service.Service,
activityCenterService: activity_center_service.Service
): Module[T] =
result = Module[T]()
result.delegate = delegate
@ -94,6 +98,7 @@ proc newModule*[T](
result.profileSectionModule = profile_section_module.newModule(result, events, accountsService, settingsService,
profileService, contactsService, aboutService, languageService, mnemonicService, privacyService)
result.stickersModule = stickers_module.newModule(result, events, stickersService)
result.activityCenterModule = activity_center_module.newModule(result, events, activityCenterService, contactsService)
result.appSearchModule = app_search_module.newModule(result, events, contactsService, chatService, communityService,
messageService)
@ -101,6 +106,7 @@ method delete*[T](self: Module[T]) =
self.chatSectionModule.delete
self.profileSectionModule.delete
self.stickersModule.delete
self.activityCenterModule.delete
for cModule in self.communitySectionsModule.values:
cModule.delete
self.communitySectionsModule.clear
@ -213,6 +219,7 @@ method load*[T](
# self.nodeManagementSectionModule.load()
self.profileSectionModule.load()
self.stickersModule.load()
self.activityCenterModule.load()
self.appSearchModule.load()
# Set active section on app start
@ -241,6 +248,9 @@ proc checkIfModuleDidLoad [T](self: Module[T]) =
if(not self.stickersModule.isLoaded()):
return
if(not self.activityCenterModule.isLoaded()):
return
if(not self.appSearchModule.isLoaded()):
return
@ -259,6 +269,9 @@ method appSearchDidLoad*[T](self: Module[T]) =
proc stickersDidLoad*[T](self: Module[T]) =
self.checkIfModuleDidLoad()
proc activityCenterDidLoad*[T](self: Module[T]) =
self.checkIfModuleDidLoad()
proc walletSectionDidLoad*[T](self: Module[T]) =
self.checkIfModuleDidLoad()

View File

@ -0,0 +1,102 @@
import Tables, json
type
ImageItem* = object
thumbnail*: string
large*: string
proc initImageItem*(
thumbnail: string,
large: string
): ImageItem =
result = ImageItem()
result.thumbnail = thumbnail
result.large = large
proc thumbnail*(self: ImageItem): string {.inline.} =
self.thumbnail
proc large*(self: ImageItem): string {.inline.} =
self.large
type
Item* = ref object
id: string
name: string
ensVerified: bool
alias: string
identicon: string
lastUpdated: int64
lastUpdatedLocally: int64
localNickname: string
image: ImageItem
added: bool
blocked: bool
isSyncing: bool
hasAddedUs: bool
removed: bool
proc initItem*(
id: string,
name: string,
ensVerified: bool,
alias: string,
identicon: string,
lastUpdated: int64,
lastUpdatedLocally: int64,
image: ImageItem,
added: bool,
blocked: bool,
isSyncing: bool,
hasAddedUs: bool,
removed: bool
): Item =
result = Item()
result.id = id
result.name = name
result.ensVerified = ensVerified
result.alias = alias
result.lastUpdated = lastUpdated
result.lastUpdatedLocally = lastUpdatedLocally
result.image = image
result.added = added
result.blocked = blocked
result.isSyncing = isSyncing
result.hasAddedUs = hasAddedUs
result.removed = removed
proc id*(self: Item): string {.inline.} =
self.id
proc name*(self: Item): string {.inline.} =
self.name
proc ensVerified*(self: Item): bool {.inline.} =
self.ensVerified
proc alias*(self: Item): string {.inline.} =
self.alias
proc lastUpdated*(self: Item): int64 {.inline.} =
self.lastUpdated
proc lastUpdatedLocally*(self: Item): int64 {.inline.} =
self.lastUpdatedLocally
proc image*(self: Item): ImageItem {.inline.} =
self.image
proc added*(self: Item): bool {.inline.} =
self.added
proc blocked*(self: Item): bool {.inline.} =
self.blocked
proc isSyncing*(self: Item): bool {.inline.} =
self.isSyncing
proc hasAddedUs*(self: Item): bool {.inline.} =
self.hasAddedUs
proc removed*(self: Item): bool {.inline.} =
self.removed

View File

@ -66,6 +66,27 @@ proc initItem*(id, responseToMessageWithId, senderId, senderDisplayName, senderL
result.messageType = messageType
result.pinned = false
proc `$`*(self: Item): string =
result = fmt"""Item(
id: {$self.id},
responseToMessageWithId: {self.responseToMessageWithId},
senderId: {self.senderId},
senderDisplayName: {$self.senderDisplayName},
senderLocalName: {self.senderLocalName},
amISender: {$self.amISender},
isSenderIconIdenticon: {$self.isSenderIconIdenticon},
seen: {$self.seen},
outgoingStatus:{$self.outgoingStatus},
messageText:{self.messageText},
messageImage:{self.messageImage},
timestamp:{$self.timestamp},
contentType:{$self.contentType.int},
messageType:{$self.messageType},
chatTypeThisMessageBelongsTo:{self.chatTypeThisMessageBelongsTo},
chatColorThisMessageBelongsTo:{self.chatColorThisMessageBelongsTo},
pinned:{$self.pinned}
)"""
proc id*(self: Item): string {.inline.} =
self.id
@ -99,6 +120,12 @@ proc messageText*(self: Item): string {.inline.} =
proc messageImage*(self: Item): string {.inline.} =
self.messageImage
proc stickerPack*(self: Item): int {.inline.} =
self.stickerPack
proc stickerHash*(self: Item): string {.inline.} =
self.stickerHash
proc seen*(self: Item): bool {.inline.} =
self.seen
@ -192,21 +219,6 @@ proc getCountsForReactions*(self: Item): seq[JsonNode] =
result.add(%* {"emojiId": k, "counts": v.len})
proc `$`*(self: Item): string =
result = fmt"""MessageItem(
id: {self.id},
responseToMessageWithId: {self.responseToMessageWithId},
senderId: {self.senderId},
senderDisplayName: {self.senderDisplayName},
senderLocalName: {self.senderLocalName},
timestamp: {self.timestamp},
contentType: {self.contentType.int},
messageType:{self.messageType},
chatTypeThisMessageBelongsTo:{self.chatTypeThisMessageBelongsTo},
chatColorThisMessageBelongsTo:{self.chatColorThisMessageBelongsTo},
pinned:{self.pinned}
]"""
proc toJsonNode*(self: Item): JsonNode =
result = %* {
"id": self.id,

View File

@ -0,0 +1,111 @@
import NimQml, std/wrapnils
import ./message_item
QtObject:
type MessageItem* = ref object of QObject
messageItem*: message_item.Item
proc setup(self: MessageItem) =
self.QObject.setup
proc delete*(self: MessageItem) =
self.QObject.delete
proc newMessageItem*(message: message_item.Item): MessageItem =
new(result, delete)
result.messageItem = message
result.setup
proc `$`*(self: MessageItem): string =
result = $self.messageItem
proc setMessageItem*(self: MessageItem, messageItem: message_item.Item) =
self.messageItem = messageItem
proc responseToMessageWithId*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.responseToMessageWithId
QtProperty[string] responseToMessageWithId:
read = responseToMessageWithId
proc senderId*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.senderId
QtProperty[string] senderId:
read = senderId
proc senderDisplayName*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.senderDisplayName
QtProperty[string] senderDisplayName:
read = senderDisplayName
proc senderLocalName*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.senderLocalName
QtProperty[string] senderLocalName:
read = senderLocalName
proc amISender*(self: MessageItem): bool {.slot.} = result = ?.self.messageItem.amISender
QtProperty[bool] amISender:
read = amISender
proc senderIcon*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.senderIcon
QtProperty[string] senderIcon:
read = senderIcon
proc isSenderIconIdenticon*(self: MessageItem): bool {.slot.} = result = ?.self.messageItem.isSenderIconIdenticon
QtProperty[bool] isSenderIconIdenticon:
read = isSenderIconIdenticon
proc seen*(self: MessageItem): bool {.slot.} = result = ?.self.messageItem.seen
QtProperty[bool] seen:
read = seen
proc outgoingStatus*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.outgoingStatus
QtProperty[string] outgoingStatus:
read = outgoingStatus
proc messageText*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.messageText
QtProperty[string] messageText:
read = messageText
proc messageImage*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.messageImage
QtProperty[string] messageImage:
read = messageImage
proc stickerHash*(self: MessageItem): string {.slot.} = result = ?.self.messageItem.stickerHash
QtProperty[string] stickerHash:
read = stickerHash
proc stickerPack*(self: MessageItem): int {.slot.} = result = ?.self.messageItem.stickerPack
QtProperty[int] stickerPack:
read = stickerPack
# Convert to int
# proc gapFrom*(self: MessageItem): int64 {.slot.} = result = ?.self.messageItem.gapFrom
# QtProperty[int64] gapFrom:
# read = gapFrom
# proc gapTo*(self: MessageItem): int64 {.slot.} = result = ?.self.messageItem.gapTo
# QtProperty[int64] gapTo:
# read = gapTo
# proc timestamp*(self: MessageItem): int64 {.slot.} = result = ?.self.messageItem.timestamp
# QtProperty[int64] timestamp:
# read = timestamp
proc contentType*(self: MessageItem): int {.slot.} =
if (self.messageItem.isNil): return 0
result = self.messageItem.contentType.int
QtProperty[int] contentType:
read = contentType
proc messageType*(self: MessageItem): int {.slot.} = result = ?.self.messageItem.messageType
QtProperty[int] messageType:
read = messageType
# TODO find a way to pass reactions since they are not basic types (might need to be a Model)
# proc reactions*(self: MessageItem): int {.slot.} = result = ?.self.messageItem.reactions
# QtProperty[int] reactions:
# read = reactions
# proc reactionIds*(self: MessageItem): int {.slot.} = result = ?.self.messageItem.reactionIds
# QtProperty[int] reactionIds:
# read = reactionIds
proc pinned*(self: MessageItem): bool {.slot.} = result = ?.self.messageItem.pinned
QtProperty[int] bool:
read = bool

View File

@ -0,0 +1,16 @@
include ../../common/json_utils
include ../../../app/core/tasks/common
type
AsyncActivityNotificationLoadTaskArg = ref object of QObjectTaskArg
cursor: string
limit: int
const asyncActivityNotificationLoadTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[AsyncActivityNotificationLoadTaskArg](argEncoded)
let activityNotificationsCallResult = status_activity_center.rpcActivityCenterNotifications(newJString(arg.cursor), arg.limit)
let responseJson = %*{
"activityNotifications": activityNotificationsCallResult.result
}
arg.finish(responseJson)

View File

@ -0,0 +1,72 @@
{.used.}
import json, strformat, strutils, stint, json_serialization
import ../../message/dto/message
include ../../../common/json_utils
include ../../../common/utils
type ActivityCenterNotificationType* {.pure.}= enum
Unknown = 0,
NewOneToOne = 1,
NewPrivateGroupChat = 2,
Mention = 3
Reply = 4
type ActivityCenterNotificationDto* = 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
author*: string
notificationType*: ActivityCenterNotificationType
message*: MessageDto
timestamp*: int64
read*: bool
dismissed*: bool
accepted*: bool
proc `$`*(self: ActivityCenterNotificationDto): string =
result = fmt"""ActivityCenterNotificationDto(
id: {$self.id},
chatId: {self.chatId},
author: {self.author},
notificationType: {$self.notificationType.int},
timestamp: {self.timestamp},
read: {$self.read},
dismissed: {$self.dismissed},
accepted: {$self.accepted},
message:{self.message}
)"""
proc toActivityCenterNotificationDto*(jsonObj: JsonNode): ActivityCenterNotificationDto =
result = ActivityCenterNotificationDto()
discard jsonObj.getProp("id", result.id)
discard jsonObj.getProp("chatId", result.chatId)
discard jsonObj.getProp("author", result.author)
result.notificationType = ActivityCenterNotificationType.Unknown
var notificationTypeInt: int
if (jsonObj.getProp("notificationType", notificationTypeInt) and
(notificationTypeInt >= ord(low(ActivityCenterNotificationType)) or
notificationTypeInt <= ord(high(ActivityCenterNotificationType)))):
result.notificationType = ActivityCenterNotificationType(notificationTypeInt)
discard jsonObj.getProp("timestamp", result.timestamp)
discard jsonObj.getProp("read", result.read)
discard jsonObj.getProp("dismissed", result.dismissed)
discard jsonObj.getProp("accepted", result.accepted)
if jsonObj.contains("message") and jsonObj{"message"}.kind != JNull:
result.message = jsonObj{"message"}.toMessageDto()
elif result.notificationType == ActivityCenterNotificationType.NewOneToOne and
jsonObj.contains("lastMessage") and jsonObj{"lastMessage"}.kind != JNull:
result.message = jsonObj{"lastMessage"}.toMessageDto()
proc parseActivityCenterNotifications*(rpcResult: JsonNode): (string, seq[ActivityCenterNotificationDto]) =
var notifs: seq[ActivityCenterNotificationDto] = @[]
if rpcResult{"notifications"}.kind != JNull:
for jsonMsg in rpcResult["notifications"]:
notifs.add(jsonMsg.toActivityCenterNotificationDto())
return (rpcResult{"cursor"}.getStr, notifs)

View File

@ -0,0 +1,195 @@
import NimQml, json, sequtils, chronicles, strutils, strutils, stint
import eventemitter
import ../../../app/core/[main]
import ../../../app/core/tasks/[qt, threadpool]
import web3/ethtypes, web3/conversions, stew/byteutils, nimcrypto, json_serialization, chronicles
import json, tables, json_serialization
import ../chat/service as chat_service
import status/statusgo_backend_new/activity_center as status_activity_center
import status/statusgo_backend_new/response_type
import ./dto/notification
export notification
include async_tasks
logScope:
topics = "activity-center-service"
type
ActivityCenterNotificationsArgs* = ref object of Args
activityCenterNotifications*: seq[ActivityCenterNotificationDto]
MarkAsAcceptedNotificationProperties* = ref object of Args
notificationIds*: seq[string]
MarkAsDismissedNotificationProperties* = ref object of Args
notificationIds*: seq[string]
MarkAsReadNotificationProperties* = ref object of Args
isAll*: bool
notificationIds*: seq[string]
communityId*: string
channelId*: string
notificationTypes*: seq[ActivityCenterNotificationType]
MarkAsUnreadNotificationProperties* = ref object of Args
notificationIds*: seq[string]
communityId*: string
channelId*: string
notificationTypes*: seq[ActivityCenterNotificationType]
# Signals which may be emitted by this service:
# TODO remove new when refactor is done
const SIGNAL_ACTIVITY_CENTER_NOTIFICATIONS_LOADED* = "activityCenterNotificationsLoaded-new"
const SIGNAL_MARK_NOTIFICATIONS_AS_READ* = "markNotificationsAsRead-new"
const SIGNAL_MARK_NOTIFICATIONS_AS_UNREAD* = "markNotificationsAsUnread-new"
const SIGNAL_MARK_NOTIFICATIONS_AS_ACCEPTED* = "markNotificationsAsAccepted"
const SIGNAL_MARK_NOTIFICATIONS_AS_DISMISSED* = "markNotificationsAsDismissed"
const DEFAULT_LIMIT = 20
QtObject:
type Service* = ref object of QObject
threadpool: ThreadPool
events: EventEmitter
chatService: chat_service.Service
cursor*: string
# Forward declaration
proc asyncActivityNotificationLoad*(self: Service)
proc delete*(self: Service) =
self.QObject.delete
proc newService*(
events: EventEmitter,
threadpool: ThreadPool,
chatService: chat_service.Service
): Service =
new(result, delete)
result.QObject.setup
result.events = events
result.threadpool = threadpool
result.chatService = chatService
proc init*(self: Service) =
self.asyncActivityNotificationLoad()
proc hasMoreToShow*(self: Service): bool =
return self.cursor != ""
proc asyncActivityNotificationLoad*(self: Service) =
let arg = AsyncActivityNotificationLoadTaskArg(
tptr: cast[ByteAddress](asyncActivityNotificationLoadTask),
vptr: cast[ByteAddress](self.vptr),
slot: "asyncActivityNotificationLoaded",
cursor: "",
limit: DEFAULT_LIMIT
)
self.threadpool.start(arg)
proc getActivityCenterNotifications*(self: Service): seq[ActivityCenterNotificationDto] =
if(self.cursor == ""): return
var cursorVal: JsonNode
if self.cursor == "":
cursorVal = newJNull()
else:
cursorVal = newJString(self.cursor)
let callResult = status_activity_center.rpcActivityCenterNotifications(cursorVal, DEFAULT_LIMIT)
let activityCenterNotificationsTuple = parseActivityCenterNotifications(callResult.result)
self.cursor = activityCenterNotificationsTuple[0];
result = activityCenterNotificationsTuple[1]
proc markActivityCenterNotificationRead*(
self: Service,
notificationId: string,
markAsReadProps: MarkAsReadNotificationProperties
): string =
try:
discard status_activity_center.markActivityCenterNotificationsRead(@[notificationId])
self.events.emit(SIGNAL_MARK_NOTIFICATIONS_AS_READ, markAsReadProps)
except Exception as e:
error "Error marking as read", msg = e.msg
result = e.msg
proc unreadActivityCenterNotificationsCount*(self: Service): int =
try:
let response = status_activity_center.unreadActivityCenterNotificationsCount()
if response.result.kind != JNull:
return response.result.getInt
except Exception as e:
error "Error getting unread acitvity center unread count", msg = e.msg
proc markActivityCenterNotificationUnread*(
self: Service,
notificationId: string,
markAsUnreadProps: MarkAsUnreadNotificationProperties
): string =
try:
discard status_activity_center.markActivityCenterNotificationsUnread(@[notificationId])
self.events.emit(SIGNAL_MARK_NOTIFICATIONS_AS_UNREAD, markAsUnreadProps)
except Exception as e:
error "Error marking as unread", msg = e.msg
result = e.msg
proc markAllActivityCenterNotificationsRead*(self: Service, initialLoad: bool = true):string =
try:
discard status_activity_center.markAllActivityCenterNotificationsRead()
# This proc should accept ActivityCenterNotificationType in order to clear all notifications
# per type, that's why we have this part here. If we add all types to notificationsType that
# means that we need to clear all notifications for all types.
var types : seq[ActivityCenterNotificationType]
for t in ActivityCenterNotificationType:
types.add(t)
self.events.emit(SIGNAL_MARK_NOTIFICATIONS_AS_READ,
MarkAsReadNotificationProperties(notificationTypes: types, isAll: true))
except Exception as e:
error "Error marking all as read", msg = e.msg
result = e.msg
proc asyncActivityNotificationLoaded*(self: Service, rpcResponse: string) {.slot.} =
let rpcResponseObj = rpcResponse.parseJson
if(rpcResponseObj["activityNotifications"].kind != JNull):
let activityCenterNotificationsTuple = parseActivityCenterNotifications(rpcResponseObj["activityNotifications"])
self.cursor = activityCenterNotificationsTuple[0]
self.events.emit(SIGNAL_ACTIVITY_CENTER_NOTIFICATIONS_LOADED,
ActivityCenterNotificationsArgs(activityCenterNotifications: activityCenterNotificationsTuple[1]))
proc acceptActivityCenterNotifications*(self: Service, notificationIds: seq[string]): string =
try:
let response = status_activity_center.acceptActivityCenterNotifications(notificationIds)
let (chats, messages) = self.chatService.parseChatResponse2(response)
self.events.emit(chat_service.SIGNAL_CHAT_UPDATE,
ChatUpdateArgsNew(messages: messages, chats: chats))
except Exception as e:
error "Error marking as accepted", msg = e.msg
result = e.msg
proc dismissActivityCenterNotifications*(self: Service, notificationIds: seq[string]): string =
try:
discard status_activity_center.dismissActivityCenterNotifications(notificationIds)
self.events.emit(SIGNAL_MARK_NOTIFICATIONS_AS_DISMISSED,
MarkAsDismissedNotificationProperties(notificationIds: notificationIds))
except Exception as e:
error "Error marking as dismissed", msg = e.msg
result = e.msg

View File

@ -2,6 +2,7 @@ import NimQml, Tables, json, sequtils, strformat, chronicles, os
import ./dto/chat as chat_dto
import ../message/dto/message as message_dto
import ../activity_center/dto/notification as notification_dto
import ../contacts/service as contact_service
import status/statusgo_backend_new/chat as status_chat
import status/statusgo_backend_new/chatCommands as status_chat_commands
@ -34,7 +35,7 @@ type
# emojiReactions*: seq[Reaction]
# communities*: seq[Community]
# communityMembershipRequests*: seq[CommunityMembershipRequest]
# activityCenterNotifications*: seq[ActivityCenterNotification]
activityCenterNotifications*: seq[ActivityCenterNotificationDto]
# statusUpdates*: seq[StatusUpdate]
# deletedMessages*: seq[RemovedMessage]

View File

@ -36,6 +36,13 @@ type
ContactsStatusUpdatedArgs* = ref object of Args
statusUpdates*: seq[StatusUpdateDto]
ContactDetails* = ref object of RootObj
displayName*: string
localNickname*: string
icon*: string
isIconIdenticon*: bool
isCurrentUser*: bool
# Local Constants:
const CheckStatusIntervalInMilliseconds = 5000 # 5 seconds, this is timeout how often do we check for user status.
const OnlineLimitInSeconds = int(5.5 * 60) # 5.5 minutes
@ -341,3 +348,15 @@ QtObject:
timeoutInMilliseconds: CheckStatusIntervalInMilliseconds
)
self.threadpool.start(arg)
proc getContactDetails*(self: Service, pubKey: string): ContactDetails =
result = ContactDetails()
let contact = self.getContactById(pubKey)
result.displayName = contact.userNameOrAlias()
result.isCurrentUser = pubKey == singletonInstance.userProfile.getPubKey()
result.icon = contact.identicon
result.isIconIdenticon = contact.identicon.len > 0
if(contact.image.thumbnail.len > 0):
result.icon = contact.image.thumbnail
result.isIconIdenticon = false

View File

@ -1,141 +0,0 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import utils 1.0
import "../../../../../shared"
import "../../../../../shared/popups"
import "../../../../../shared/panels"
import "../../../../../shared/status"
import ".."
Item {
width: parent.width
height: 64
Row {
id: filterButtons
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: Style.current.padding
height: allBtn.height
spacing: Style.current.padding
StatusButton {
id: allBtn
//% "All"
text: qsTrId("all")
type: "secondary"
size: "small"
highlighted: activityCenter.currentFilter === ActivityCenter.Filter.All
onClicked: activityCenter.currentFilter = ActivityCenter.Filter.All
}
StatusButton {
id: mentionsBtn
//% "Mentions"
text: qsTrId("mentions")
type: "secondary"
enabled: hasMentions
size: "small"
highlighted: activityCenter.currentFilter === ActivityCenter.Filter.Mentions
onClicked: activityCenter.currentFilter = ActivityCenter.Filter.Mentions
}
StatusButton {
id: repliesbtn
//% "Replies"
text: qsTrId("replies")
enabled: hasReplies
type: "secondary"
size: "small"
highlighted: activityCenter.currentFilter === ActivityCenter.Filter.Replies
onClicked: activityCenter.currentFilter = ActivityCenter.Filter.Replies
}
// StatusButton {
// id: contactRequestsBtn
// //% "Contact requests"
// text: qsTrId("contact-requests")
// enabled: hasContactRequests
// type: "secondary"
// size: "small"
// highlighted: activityCenter.currentFilter === ActivityCenter.Filter.ContactRequests
// onClicked: activityCenter.currentFilter = ActivityCenter.Filter.ContactRequests
// }
}
Row {
id: otherButtons
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: Style.current.padding
height: markAllReadBtn.height
spacing: Style.current.padding
StatusIconButton {
id: markAllReadBtn
icon.name: "double-check"
iconColor: Style.current.primary
icon.width: 24
icon.height: 24
width: 32
height: 32
onClicked: {
// Not Refactored Yet
// errorText.text = chatsModel.activityNotificationList.markAllActivityCenterNotificationsRead()
}
StatusToolTip {
visible: markAllReadBtn.hovered
//% "Mark all as Read"
text: qsTrId("mark-all-as-read")
}
}
StatusContextMenuButton {
id: moreActionsBtn
onClicked: moreActionsMenu.open()
// TODO: replace with StatusPopupMenu
PopupMenu {
id: moreActionsMenu
x: moreActionsBtn.width - moreActionsMenu.width
y: moreActionsBtn.height + 4
Action {
icon.source: hideReadNotifications ? Style.svg("eye") : Style.svg("eye-barred")
icon.width: 16
icon.height: 16
text: hideReadNotifications ?
//% "Show read notifications"
qsTrId("show-read-notifications") :
//% "Hide read notifications"
qsTrId("hide-read-notifications")
onTriggered: hideReadNotifications = !hideReadNotifications
}
Action {
icon.source: Style.svg("bell")
icon.width: 16
icon.height: 16
//% "Notification settings"
text: qsTrId("chat-notification-preferences")
onTriggered: {
activityCenter.close()
Global.changeAppSectionBySectionType(Constants.appSection.profile)
// TODO: replace with shared store constant
// Profile/RootStore.notifications_id
profileLayoutContainer.changeProfileSection(7)
}
}
}
}
}
StyledText {
id: errorText
visible: !!text
anchors.top: filterButtons.bottom
anchors.topMargin: Style.current.smallPadding
color: Style.current.danger
}
}

View File

@ -81,8 +81,7 @@ Popup {
profileLayoutContainer.changeProfileSection(7)
}
onMarkAllReadClicked: {
// Not Refactored Yet
// errorText = activityCenter.store.chatsModelInst.activityNotificationList.markAllActivityCenterNotificationsRead()
errorText = activityCenter.store.activityCenterModuleInst.markAllActivityCenterNotificationsRead()
}
}
@ -146,9 +145,7 @@ Popup {
function(left, right) { return left.timestamp > right.timestamp }
]
// Not Refactored Yet
model: []
// model: activityCenter.store.chatsModelInst.activityNotificationList
model: activityCenter.store.activityCenterList
delegate: Item {
id: notificationDelegate
@ -186,9 +183,7 @@ Popup {
}
return -1;
}
// Not Refactored Yet
property string previousNotificationTimestamp: ""
// property string previousNotificationTimestamp: notificationDelegate.idx === 0 ? "" : chatsModel.activityNotificationList.getNotificationData(previousNotificationIndex, "timestamp")
property string previousNotificationTimestamp: notificationDelegate.idx === 0 ? "" : activityCenter.store.activityCenterList.getNotificationData(previousNotificationIndex, "timestamp")
onPreviousNotificationTimestampChanged: {
activityCenter.store.messageStore.prevMsgTimestamp = previousNotificationTimestamp;
}
@ -227,8 +222,7 @@ Popup {
}
Item {
// Not Refactored Yet
// visible: activityCenter.store.chatsModelInst.activityNotificationList.hasMoreToShow
visible: activityCenter.store.activityCenterModuleInst.hasMoreToShow
width: parent.width
height: visible ? showMoreBtn.height + showMoreBtn.anchors.topMargin : 0
StatusButton {
@ -238,8 +232,7 @@ Popup {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: Style.current.smallPadding
// Not Refactored Yet
// onClicked: activityCenter.store.chatsModelInst.activityNotificationList.loadMoreNotifications()
onClicked: activityCenter.store.activityCenterModuleInst.loadMoreNotifications()
}
}
}

View File

@ -17,6 +17,8 @@ QtObject {
// Not Refactored Yet
// property var profileModelInst: profileModel
property var profileModuleInst: profileModule
property var activityCenterModuleInst: activityCenterModule
property var activityCenterList: activityCenterModuleInst.model
property var userProfileInst: userProfile

View File

@ -155,10 +155,8 @@ Item {
anchors.right: parent.right
anchors.rightMargin: Style.current.halfPadding
anchors.verticalCenter: parent.verticalCenter
// Not Refactored Yet
// onAcceptClicked: root.store.chatsModelInst.activityNotificationList.acceptActivityCenterNotification(model.id)
// Not Refactored Yet
// onDeclineClicked: root.store.chatsModelInst.activityNotificationList.dismissActivityCenterNotification(model.id)
onAcceptClicked: root.store.activityCenterModuleInst.acceptActivityCenterNotification(model.id)
onDeclineClicked: root.store.activityCenterModuleInst.dismissActivityCenterNotification(model.id)
onProfileClicked: groupRequestContent.openProfile()
onBlockClicked: {
// Not Refactored Yet
@ -171,10 +169,9 @@ Item {
BlockContactConfirmationDialog {
id: blockContactConfirmationDialog
onBlockButtonClicked: {
// Not Refactored Yet
// root.store.profileModuleInst.blockContact(blockContactConfirmationDialog.contactAddress)
// root.store.chatsModelInst.activityNotificationList.dismissActivityCenterNotification(model.id)
// blockContactConfirmationDialog.close()
root.store.profileModuleInst.blockContact(blockContactConfirmationDialog.contactAddress)
root.store.activityCenterModuleInst.dismissActivityCenterNotification(model.id)
blockContactConfirmationDialog.close()
}
}
}

View File

@ -58,11 +58,10 @@ Item {
tooltip.x: -tooltip.width - Style.current.padding
tooltip.y: markReadBtn.height / 2 - height / 2 + 4
onClicked: {
// Not Refactored Yet
// if (!model.read) {
// return root.store.chatsModelInst.activityNotificationList.markActivityCenterNotificationRead(model.id, model.message.communityId, model.message.chatId, model.notificationType)
// }
// return root.store.chatsModelInst.activityNotificationList.markActivityCenterNotificationUnread(model.id, model.message.communityId, model.message.chatId, model.notificationType)
if (!model.read) {
return root.store.activityCenterModuleInst.markActivityCenterNotificationRead(model.id, model.message.communityId, model.message.chatId, model.notificationType)
}
return root.store.activityCenterModuleInst.markActivityCenterNotificationUnread(model.id, model.message.communityId, model.message.chatId, model.notificationType)
}
}
}
@ -77,13 +76,12 @@ Item {
// const setActiveChannel = root.store.chatsModelInst.channelView.setActiveChannel
// const chatId = model.message.chatId
// const messageId = model.message.messageId
// root.store.profileModuleInst.addContact(model.author)
root.store.activityCenterModuleInst.acceptActivityCenterNotification(model.id)
// root.store.chatsModelInst.activityNotificationList.acceptActivityCenterNotification(model.id)
// setActiveChannel(chatId)
// positionAtMessage(messageId)
}
// Not Refactored Yet
// onDeclineClicked: root.store.chatsModelInst.activityNotificationList.dismissActivityCenterNotification(model.id)
onDeclineClicked: root.store.activityCenterModuleInst.dismissActivityCenterNotification(model.id)
onProfileClicked: root.openProfile()
onBlockClicked: {
// Not Refactored Yet
@ -98,8 +96,8 @@ Item {
onBlockButtonClicked: {
// Not Refactored Yet
// root.store.profileModuleInst.blockContact(blockContactConfirmationDialog.contactAddress)
// root.store.chatsModelInst.activityNotificationList.dismissActivityCenterNotification(model.id)
// blockContactConfirmationDialog.close()
root.store.activityCenterModuleInst.dismissActivityCenterNotification(model.id)
blockContactConfirmationDialog.close()
}
}
}