fix(ActivityCenter): Refactor components for viewing notifications

This commit is contained in:
MishkaRogachev 2022-09-16 16:06:52 +03:00 committed by Mikhail Rogachev
parent edbd9adbb6
commit 2d02a3cb2e
12 changed files with 134 additions and 176 deletions

View File

@ -27,7 +27,7 @@ method unreadActivityCenterNotificationsCount*(self: AccessInterface): int {.bas
method convertToItems*(self: AccessInterface, activityCenterNotifications: seq[ActivityCenterNotificationDto]): seq[Item] {.base.} = method convertToItems*(self: AccessInterface, activityCenterNotifications: seq[ActivityCenterNotificationDto]): seq[Item] {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method getActivityCenterNotifications*(self: AccessInterface): seq[Item] {.base.} = method fetchActivityCenterNotifications*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method markAllActivityCenterNotificationsRead*(self: AccessInterface): string {.base.} = method markAllActivityCenterNotificationsRead*(self: AccessInterface): string {.base.} =

View File

@ -54,6 +54,7 @@ method load*(self: Module) =
singletonInstance.engine.setRootContextProperty("activityCenterModule", self.viewVariant) singletonInstance.engine.setRootContextProperty("activityCenterModule", self.viewVariant)
self.controller.init() self.controller.init()
self.view.load() self.view.load()
self.fetchActivityCenterNotifications()
method isLoaded*(self: Module): bool = method isLoaded*(self: Module): bool =
return self.moduleLoaded return self.moduleLoaded
@ -138,7 +139,7 @@ method convertToItems*(
) )
) )
method getActivityCenterNotifications*(self: Module): seq[notification_item.Item] = method fetchActivityCenterNotifications*(self: Module) =
let activityCenterNotifications = self.controller.getActivityCenterNotifications() let activityCenterNotifications = self.controller.getActivityCenterNotifications()
self.view.pushActivityCenterNotifications(self.convertToItems(activityCenterNotifications)) self.view.pushActivityCenterNotifications(self.convertToItems(activityCenterNotifications))

View File

@ -50,7 +50,7 @@ QtObject:
self.model.updateUnreadCount(count) self.model.updateUnreadCount(count)
proc loadMoreNotifications(self: View) {.slot.} = proc loadMoreNotifications(self: View) {.slot.} =
discard self.delegate.getActivityCenterNotifications() self.delegate.fetchActivityCenterNotifications()
proc markAllActivityCenterNotificationsRead(self: View): string {.slot.} = proc markAllActivityCenterNotificationsRead(self: View): string {.slot.} =
result = self.delegate.markAllActivityCenterNotificationsRead() result = self.delegate.markAllActivityCenterNotificationsRead()

View File

@ -36,7 +36,7 @@ proc `$`*(self: ActivityCenterNotificationDto): string =
read: {$self.read}, read: {$self.read},
dismissed: {$self.dismissed}, dismissed: {$self.dismissed},
accepted: {$self.accepted}, accepted: {$self.accepted},
message:{self.message} message: {self.message}
)""" )"""
proc toActivityCenterNotificationDto*(jsonObj: JsonNode): ActivityCenterNotificationDto = proc toActivityCenterNotificationDto*(jsonObj: JsonNode): ActivityCenterNotificationDto =

View File

@ -103,8 +103,6 @@ QtObject:
self.threadpool.start(arg) self.threadpool.start(arg)
proc getActivityCenterNotifications*(self: Service): seq[ActivityCenterNotificationDto] = proc getActivityCenterNotifications*(self: Service): seq[ActivityCenterNotificationDto] =
if(self.cursor == ""): return
var cursorVal: JsonNode var cursorVal: JsonNode
if self.cursor == "": if self.cursor == "":
@ -116,7 +114,6 @@ QtObject:
let activityCenterNotificationsTuple = parseActivityCenterNotifications(callResult.result) let activityCenterNotificationsTuple = parseActivityCenterNotifications(callResult.result)
self.cursor = activityCenterNotificationsTuple[0]; self.cursor = activityCenterNotificationsTuple[0];
result = activityCenterNotificationsTuple[1] result = activityCenterNotificationsTuple[1]
proc markActivityCenterNotificationRead*( proc markActivityCenterNotificationRead*(
@ -155,12 +152,11 @@ QtObject:
proc markAllActivityCenterNotificationsRead*(self: Service, initialLoad: bool = true):string = proc markAllActivityCenterNotificationsRead*(self: Service, initialLoad: bool = true):string =
try: try:
discard backend.markAllActivityCenterNotificationsRead() discard backend.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 # Accroding specs: Clicking the "Mark all as read" MUST mark mentions and replies items as read in the selected category
# means that we need to clear all notifications for all types.
var types : seq[ActivityCenterNotificationType] var types : seq[ActivityCenterNotificationType]
for t in ActivityCenterNotificationType: types.add(ActivityCenterNotificationType.Mention)
types.add(t) types.add(ActivityCenterNotificationType.Reply)
self.events.emit(SIGNAL_MARK_NOTIFICATIONS_AS_READ, self.events.emit(SIGNAL_MARK_NOTIFICATIONS_AS_READ,
MarkAsReadNotificationProperties(notificationTypes: types, isAll: true)) MarkAsReadNotificationProperties(notificationTypes: types, isAll: true))

View File

@ -16,7 +16,10 @@ Item {
property bool hasMentions: false property bool hasMentions: false
property bool hasReplies: false property bool hasReplies: false
property bool hasContactRequests: false
property bool hideReadNotifications: false property bool hideReadNotifications: false
property int unreadNotificationsCount: 0
property int currentActivityCategory: ActivityCenterPopup.ActivityCategory.All property int currentActivityCategory: ActivityCenterPopup.ActivityCategory.All
@ -24,6 +27,7 @@ Item {
signal categoryTriggered(int category) signal categoryTriggered(int category)
signal markAllReadClicked() signal markAllReadClicked()
signal showHideReadNotifications(bool hideReadNotifications)
height: 64 height: 64
@ -37,15 +41,15 @@ Item {
StatusListView { StatusListView {
id: listView id: listView
// NOTE: some entries are hidden until implimentation // NOTE: some entries are hidden until implimentation
model: [ { text: qsTr("All"), category: ActivityCenterPopup.ActivityCategory.All, visible: true }, model: [ { text: qsTr("All"), category: ActivityCenterPopup.ActivityCategory.All, visible: true, enabled: true },
{ text: qsTr("Admin"), category: ActivityCenterPopup.ActivityCategory.Admin, visible: false }, { text: qsTr("Admin"), category: ActivityCenterPopup.ActivityCategory.Admin, visible: false, enabled: true },
{ text: qsTr("Mentions"), category: ActivityCenterPopup.ActivityCategory.Mentions, visible: true }, { text: qsTr("Mentions"), category: ActivityCenterPopup.ActivityCategory.Mentions, visible: true, enabled: root.hasMentions },
{ text: qsTr("Replies"), category: ActivityCenterPopup.ActivityCategory.Replies, visible: true }, { text: qsTr("Replies"), category: ActivityCenterPopup.ActivityCategory.Replies, visible: true, enabled: root.hasReplies },
{ text: qsTr("Contact requests"), category: ActivityCenterPopup.ActivityCategory.ContactRequests, visible: true }, { text: qsTr("Contact requests"), category: ActivityCenterPopup.ActivityCategory.ContactRequests, visible: true, enabled: root.hasContactRequests },
{ text: qsTr("Identity verification"), category: ActivityCenterPopup.ActivityCategory.IdentityVerification, visible: false }, { text: qsTr("Identity verification"), category: ActivityCenterPopup.ActivityCategory.IdentityVerification, visible: false, enabled: true },
{ text: qsTr("Transactions"), category: ActivityCenterPopup.ActivityCategory.Transactions, visible: false }, { text: qsTr("Transactions"), category: ActivityCenterPopup.ActivityCategory.Transactions, visible: false, enabled: true },
{ text: qsTr("Membership"), category: ActivityCenterPopup.ActivityCategory.Membership, visible: false }, { text: qsTr("Membership"), category: ActivityCenterPopup.ActivityCategory.Membership, visible: false, enabled: true },
{ text: qsTr("System"), category: ActivityCenterPopup.ActivityCategory.System, visible: false } ] { text: qsTr("System"), category: ActivityCenterPopup.ActivityCategory.System, visible: false, enabled: true } ]
orientation: StatusListView.Horizontal orientation: StatusListView.Horizontal
spacing: 0 spacing: 0
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
@ -53,6 +57,7 @@ Item {
Layout.fillHeight: true Layout.fillHeight: true
delegate: StatusFlatButton { delegate: StatusFlatButton {
enabled: modelData.enabled
visible: modelData.visible visible: modelData.visible
width: visible ? implicitWidth : 0 width: visible ? implicitWidth : 0
text: modelData.text text: modelData.text
@ -61,13 +66,15 @@ Item {
size: StatusBaseButton.Size.Small size: StatusBaseButton.Size.Small
highlighted: modelData.category == root.currentActivityCategory highlighted: modelData.category == root.currentActivityCategory
onClicked: root.categoryTriggered(modelData.category) onClicked: root.categoryTriggered(modelData.category)
onEnabledChanged: if (!enabled && highlighted) root.categoryTriggered(ActivityCenterPopup.ActivityCategory.All)
} }
} }
StatusFlatRoundButton { StatusFlatRoundButton {
id: markAllReadBtn id: markAllReadBtn
enabled: root.unreadNotificationsCount > 0
icon.name: "double-checkmark" icon.name: "double-checkmark"
onClicked: markAllReadClicked() onClicked: root.markAllReadClicked()
StatusToolTip { StatusToolTip {
visible: markAllReadBtn.hovered visible: markAllReadBtn.hovered
@ -77,12 +84,12 @@ Item {
StatusFlatRoundButton { StatusFlatRoundButton {
id: hideReadNotificationsBtn id: hideReadNotificationsBtn
icon.name: "hide" icon.name: root.hideReadNotifications ? "hide" : "show"
onClicked: hideReadNotifications = !hideReadNotifications onClicked: root.showHideReadNotifications(!root.hideReadNotifications)
StatusToolTip { StatusToolTip {
visible: markAllReadBtn.hovered visible: hideReadNotificationsBtn.hovered
text: hideReadNotifications ? qsTr("Show read notifications") : qsTr("Hide read notifications") text: root.hideReadNotifications ? qsTr("Show read notifications") : qsTr("Hide read notifications")
} }
} }
} }

View File

@ -20,6 +20,7 @@ import "../panels"
Popup { Popup {
id: root id: root
// NOTE: temporary enum until we have different categories on UI and status-go sides
enum ActivityCategory { enum ActivityCategory {
All, All,
Admin, Admin,
@ -32,11 +33,11 @@ Popup {
System System
} }
property int currentActivityCategory: ActivityCenterPopup.ActivityCategory.All property int currentActivityCategory: ActivityCenterPopup.ActivityCategory.All
property bool hasMentions: false property int mentionsCount: 0
property bool hasReplies: false property int repliesCount: 0
// property bool hasContactRequests: false property int contactRequestsCount: 0
property bool hideReadNotifications: false property bool hideReadNotifications: false
property var store property var store
property var chatSectionModule property var chatSectionModule
property var messageContextMenu: MessageContextMenuView { property var messageContextMenu: MessageContextMenuView {
@ -62,7 +63,6 @@ Popup {
} }
onOpened: { onOpened: {
root.store.loadMoreNotifications()
Global.popupOpened = true Global.popupOpened = true
} }
onClosed: { onClosed: {
@ -99,16 +99,15 @@ Popup {
ActivityCenterPopupTopBarPanel { ActivityCenterPopupTopBarPanel {
id: activityCenterTopBar id: activityCenterTopBar
width: parent.width width: parent.width
hasReplies: root.hasReplies unreadNotificationsCount: root.unreadNotificationsCount
hasMentions: root.hasMentions hasReplies: root.repliesCount > 0
hasMentions: root.mentionsCount > 0
hasContactRequests: root.contactRequestsCount > 0
hideReadNotifications: root.hideReadNotifications hideReadNotifications: root.hideReadNotifications
currentActivityCategory: root.currentActivityCategory currentActivityCategory: root.currentActivityCategory
onCategoryTriggered: { onCategoryTriggered: root.currentActivityCategory = category
root.currentActivityCategory = category; onMarkAllReadClicked: errorText = root.store.activityCenterModuleInst.markAllActivityCenterNotificationsRead()
} onShowHideReadNotifications: root.hideReadNotifications = hideReadNotifications
onMarkAllReadClicked: {
errorText = root.store.activityCenterModuleInst.markAllActivityCenterNotificationsRead()
}
} }
StatusListView { StatusListView {
@ -122,7 +121,7 @@ Popup {
model: SortFilterProxyModel { model: SortFilterProxyModel {
sourceModel: root.store.activityCenterList sourceModel: root.store.activityCenterList
filters: ExpressionFilter { expression: filterActivityCategories(model.notificationType) } filters: ExpressionFilter { expression: filterActivityCategories(model.notificationType) && !(root.hideReadNotifications && model.read) }
} }
delegate: DelegateChooser { delegate: DelegateChooser {
@ -130,93 +129,38 @@ Popup {
DelegateChoice { DelegateChoice {
roleValue: Constants.activityCenterNotificationTypeMention roleValue: Constants.activityCenterNotificationTypeMention
ActivityNotificationMention { store: root.store; notification: model }
ActivityNotificationMention {
store: root.store
notification: model
Component.onCompleted: root.mentionsCount++
Component.onDestruction: root.mentionsCount--
}
} }
DelegateChoice { DelegateChoice {
roleValue: Constants.activityCenterNotificationTypeReply roleValue: Constants.activityCenterNotificationTypeReply
ActivityNotificationReply { store: root.store; notification: model }
ActivityNotificationReply {
store: root.store
notification: model
Component.onCompleted: root.repliesCount++
Component.onDestruction: root.repliesCount--
}
} }
DelegateChoice { DelegateChoice {
roleValue: Constants.activityCenterNotificationTypeContactRequest roleValue: Constants.activityCenterNotificationTypeContactRequest
ActivityNotificationContactRequest { store: root.store; notification: model }
ActivityNotificationContactRequest {
store: root.store
notification: model
Component.onCompleted: root.contactRequestsCount++
Component.onDestruction: root.contactRequestsCount--
}
} }
} }
} }
} }
// // TODO: replace with StatusListView
// StatusScrollView {
// id: scrollView
// anchors.left: parent.left
// anchors.right: parent.right
// anchors.top: activityCenterTopBar.bottom
// anchors.topMargin: 13
// anchors.bottom: parent.bottom
// anchors.bottomMargin: Style.current.smallPadding
// width: parent.width
// ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
// Column {
// id: notificationsContainer
// width: scrollView.availableWidth
// spacing: 0
// Repeater {
// model: notifDelegateList
// }
// DelegateModelGeneralized {
// id: notifDelegateList
// lessThan: [
// function(left, right) { return left.timestamp > right.timestamp }
// ]
// model: root.store.activityCenterList
// delegate: Item {
// id: notificationDelegate
// width: parent.availableWidth
// height: notifLoader.active ? childrenRect.height : 0
// property int idx: DelegateModel.itemsIndex
// Component.onCompleted: {
// switch (model.notificationType) {
// case Constants.activityCenterNotificationTypeMention:
// if (!hasMentions) {
// hasMentions = true
// }
// break
// case Constants.activityCenterNotificationTypeReply:
// if (!hasReplies) {
// hasReplies = true
// }
// break
// }
// }
// Loader {
// property int previousNotificationIndex: {
// 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;
// }
// readonly property string previousNotificationTimestamp: notificationDelegate.idx === 0 ? "" :
// root.store.activityCenterList.getNotificationData(previousNotificationIndex, "timestamp")
// onPreviousNotificationTimestampChanged: {
// root.store.messageStore.prevMsgTimestamp = previousNotificationTimestamp;
// }
// id: notifLoader // id: notifLoader
// anchors.top: parent.top // anchors.top: parent.top
// active: !!sourceComponent // active: !!sourceComponent
@ -274,19 +218,3 @@ Popup {
// } // }
// } // }
// Item {
// visible: root.store.activityCenterModuleInst.hasMoreToShow
// width: parent.width
// height: visible ? showMoreBtn.height + showMoreBtn.anchors.topMargin : 0
// StatusButton {
// id: showMoreBtn
// text: qsTr("Show more")
// anchors.horizontalCenter: parent.horizontalCenter
// anchors.top: parent.top
// anchors.topMargin: Style.current.smallPadding
// onClicked: root.store.activityCenterModuleInst.loadMoreNotifications()
// }
// }
// }
// }
// }

View File

@ -21,10 +21,6 @@ Item {
StatusFlatRoundButton { StatusFlatRoundButton {
id: markReadBtn id: markReadBtn
width: 32
height: 32
icon.width: 24
icon.height: 24
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: Style.current.padding anchors.rightMargin: Style.current.padding

View File

@ -7,43 +7,10 @@ import StatusQ.Components 0.1
import shared 1.0 import shared 1.0
import utils 1.0 import utils 1.0
import shared.panels.chat 1.0
ActivityNotificationBase { ActivityNotificationMessage {
id: root id: root
markReadBtnVisible: false // TODO: mark as read ignores notification type
height: 60 // markReadBtnVisible: false
StatusSmartIdenticon {
id: identicon
anchors.left: parent.left
anchors.leftMargin: Style.current.padding
anchors.verticalCenter: parent.verticalCenter
name: notification.author
asset.color: Theme.palette.miscColor5
}
RowLayout {
anchors.top: parent.top
anchors.left: identicon.right
StatusBaseText {
text: notification.name
font.pixelSize: 15
color: Style.current.primary
}
StatusBaseText {
text: Utils.getElidedPk(notification.author) + " \u2022"
font.pixelSize: 12
color: Style.current.secondaryText
}
ChatTimePanel {
font.pixelSize: 12
color: Style.current.secondaryText
timestamp: notification.timestamp
}
}
} }

View File

@ -9,7 +9,7 @@ import shared 1.0
import utils 1.0 import utils 1.0
import shared.panels.chat 1.0 import shared.panels.chat 1.0
ActivityNotificationBase { ActivityNotificationMessage {
id: root id: root
} }

View File

@ -0,0 +1,63 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import shared 1.0
import utils 1.0
import shared.views.chat 1.0
ActivityNotificationBase {
id: root
signal activityCenterClose()
height: Math.max(60, notificationMessage.height)
MessageView {
id: notificationMessage
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
z: -1
rootStore: root.store
messageStore: root.store.messageStore
messageId: notification.id
senderDisplayName: notification.message.senderDisplayName
messageText: notification.message.messageText
responseToMessageWithId: notification.message.responseToMessageWithId
senderId: notification.message.senderId
senderLocalName: notification.message.senderLocalName
senderIcon: notification.message.senderIcon
amISender: notification.message.amISender
messageImage: notification.message.messageImage
messageTimestamp: notification.timestamp
messageOutgoingStatus: notification.message.outgoingStatus
messageContentType: notification.message.contentType
senderTrustStatus: notification.message.senderTrustStatus
activityCenterMessage: true
activityCenterMessageRead: false
onImageClicked: Global.openImagePopup(image, root.messageContextMenu)
scrollToBottom: null
messageClickHandler: (sender,
point,
isProfileClick,
isSticker = false,
isImage = false,
image = null,
isEmoji = false,
ideEmojiPicker = false,
isReply = false,
isRightClickOnImage = false,
imageSource = "") => {
if (isProfileClick) {
return Global.openProfilePopup(notification.message.senderId)
}
activityCenterClose()
root.store.activityCenterModuleInst.switchTo(notification.sectionId, notification.chatId, notification.id)
}
}
}

View File

@ -9,7 +9,7 @@ import shared 1.0
import utils 1.0 import utils 1.0
import shared.panels.chat 1.0 import shared.panels.chat 1.0
ActivityNotificationBase { ActivityNotificationMessage {
id: root id: root
} }