feat(ActivityCenter): Community membership notifications

Close #7277
This commit is contained in:
MishkaRogachev 2022-10-05 18:51:42 +04:00 committed by Mikhail Rogachev
parent f122607ada
commit d4e4e66fc6
14 changed files with 313 additions and 8 deletions

View File

@ -4,6 +4,7 @@ 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
communityId: string
sectionId: string
name: string
author: string
@ -18,6 +19,7 @@ type Item* = ref object
proc initItem*(
id: string,
chatId: string,
communityId: string,
sectionId: string,
name: string,
author: string,
@ -32,6 +34,7 @@ proc initItem*(
result = Item()
result.id = id
result.chatId = chatId
result.communityId = communityId
result.sectionId = sectionId
result.name = name
result.author = author
@ -48,6 +51,7 @@ proc `$`*(self: Item): string =
id: {self.id},
name: {$self.name},
chatId: {$self.chatId},
communityId: {$self.communityId},
sectionId: {$self.sectionId},
author: {$self.author},
notificationType: {$self.notificationType},
@ -71,6 +75,9 @@ proc author*(self: Item): string =
proc chatId*(self: Item): string =
return self.chatId
proc communityId*(self: Item): string =
return self.communityId
proc sectionId*(self: Item): string =
return self.sectionId

View File

@ -5,6 +5,7 @@ type
NotifRoles {.pure.} = enum
Id = UserRole + 1
ChatId
CommunityId
SectionId
Name
NotificationType
@ -72,6 +73,7 @@ QtObject:
case communityItemRole:
of NotifRoles.Id: result = newQVariant(acitivityNotificationItem.id)
of NotifRoles.ChatId: result = newQVariant(acitivityNotificationItem.chatId)
of NotifRoles.CommunityId: result = newQVariant(acitivityNotificationItem.communityId)
of NotifRoles.SectionId: result = newQVariant(acitivityNotificationItem.sectionId)
of NotifRoles.Name: result = newQVariant(acitivityNotificationItem.name)
of NotifRoles.Author: result = newQVariant(acitivityNotificationItem.author)
@ -90,6 +92,7 @@ QtObject:
case data:
of "id": result = notif.id
of "chatId": result = notif.chatId
of "communityId": result = notif.communityId
of "sectionId": result = notif.sectionId
of "name": result = notif.name
of "author": result = notif.author
@ -104,6 +107,7 @@ QtObject:
{
NotifRoles.Id.int:"id",
NotifRoles.ChatId.int:"chatId",
NotifRoles.CommunityId.int:"communityId",
NotifRoles.SectionId.int: "sectionId",
NotifRoles.Name.int: "name",
NotifRoles.Author.int: "author",

View File

@ -127,6 +127,7 @@ method convertToItems*(
return notification_item.initItem(
n.id,
n.chatId,
n.communityId,
sectionId,
n.name,
n.author,

View File

@ -13,10 +13,15 @@ type ActivityCenterNotificationType* {.pure.}= enum
Mention = 3,
Reply = 4,
ContactRequest = 5
CommunityInvitation = 6
CommunityRequest = 7
CommunityMembershipRequest = 8
CommunityKicked = 9
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
communityId*: string
name*: string
author*: string
notificationType*: ActivityCenterNotificationType
@ -30,6 +35,7 @@ proc `$`*(self: ActivityCenterNotificationDto): string =
result = fmt"""ActivityCenterNotificationDto(
id: {$self.id},
chatId: {self.chatId},
communityId: {self.communityId},
author: {self.author},
notificationType: {$self.notificationType.int},
timestamp: {self.timestamp},
@ -43,6 +49,7 @@ proc toActivityCenterNotificationDto*(jsonObj: JsonNode): ActivityCenterNotifica
result = ActivityCenterNotificationDto()
discard jsonObj.getProp("id", result.id)
discard jsonObj.getProp("chatId", result.chatId)
discard jsonObj.getProp("communityId", result.communityId)
discard jsonObj.getProp("author", result.author)
result.notificationType = ActivityCenterNotificationType.Unknown

View File

@ -142,7 +142,7 @@ QtObject:
if response.result.kind != JNull:
return response.result.getInt
except Exception as e:
error "Error getting unread acitvity center unread count", msg = e.msg
error "Error getting unread activity center unread count", msg = e.msg
proc markActivityCenterNotificationUnread*(
self: Service,

View File

@ -14,9 +14,11 @@ import "../popups"
Item {
id: root
property bool hasAdmin: false
property bool hasMentions: false
property bool hasReplies: false
property bool hasContactRequests: false
property bool hasMembership: false
property bool hideReadNotifications: false
property int unreadNotificationsCount: 0
@ -42,13 +44,13 @@ Item {
id: listView
// NOTE: some entries are hidden until implimentation
model: [ { text: qsTr("All"), category: ActivityCenterPopup.ActivityCategory.All, visible: true, enabled: true },
{ text: qsTr("Admin"), category: ActivityCenterPopup.ActivityCategory.Admin, visible: false, enabled: true },
{ text: qsTr("Admin"), category: ActivityCenterPopup.ActivityCategory.Admin, visible: true, enabled: root.hasAdmin },
{ text: qsTr("Mentions"), category: ActivityCenterPopup.ActivityCategory.Mentions, visible: true, enabled: root.hasMentions },
{ text: qsTr("Replies"), category: ActivityCenterPopup.ActivityCategory.Replies, visible: true, enabled: root.hasReplies },
{ text: qsTr("Contact requests"), category: ActivityCenterPopup.ActivityCategory.ContactRequests, visible: true, enabled: root.hasContactRequests },
{ text: qsTr("Identity verification"), category: ActivityCenterPopup.ActivityCategory.IdentityVerification, visible: false, enabled: true },
{ text: qsTr("Transactions"), category: ActivityCenterPopup.ActivityCategory.Transactions, visible: false, enabled: true },
{ text: qsTr("Membership"), category: ActivityCenterPopup.ActivityCategory.Membership, visible: false, enabled: true },
{ text: qsTr("Membership"), category: ActivityCenterPopup.ActivityCategory.Membership, visible: true, enabled: root.hasMembership },
{ text: qsTr("System"), category: ActivityCenterPopup.ActivityCategory.System, visible: false, enabled: true } ]
orientation: StatusListView.Horizontal
spacing: 0

View File

@ -0,0 +1,81 @@
import QtQuick 2.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import utils 1.0
import shared.panels 1.0
Item {
id: root
property bool pending: false
property bool accepted: false
property bool declined: false
signal acceptRequestToJoinCommunity()
signal declineRequestToJoinCommunity()
width: Math.max(textItem.width, buttons.width)
height: Math.max(textItem.height, buttons.height)
StatusBaseText {
id: textItem
anchors.centerIn: parent
visible: !pending
text: {
if (root.accepted) {
return qsTr("Accepted")
} else if (root.declined) {
return qsTr("Declined")
}
return ""
}
color: {
if (root.accepted) {
return Theme.palette.successColor1
} else if (root.dismissed) {
return Theme.palette.dangerColor1
}
return Theme.palette.directColor1
}
}
Row {
id: buttons
anchors.centerIn: parent
visible: pending
spacing: Style.current.padding
StatusRoundIcon {
asset.name: "thumbs-up"
asset.color: Theme.palette.white
asset.bgWidth: 28
asset.bgHeight: 28
asset.bgColor: Theme.palette.successColor1
MouseArea {
id: thumbsUpSensor
hoverEnabled: true
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: root.acceptRequestToJoinCommunity()
}
}
StatusRoundIcon {
asset.name: "thumbs-down"
asset.color: Theme.palette.white
asset.bgWidth: 28
asset.bgHeight: 28
asset.bgColor: Theme.palette.dangerColor1
MouseArea {
id: thumbsDownSensor
hoverEnabled: true
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: root.declineRequestToJoinCommunity()
}
}
}
}

View File

@ -33,9 +33,11 @@ Popup {
System
}
property int currentActivityCategory: ActivityCenterPopup.ActivityCategory.All
property int adminCount: 0
property int mentionsCount: 0
property int repliesCount: 0
property int contactRequestsCount: 0
property int membershipCount: 0
property var store
property var acStore
@ -51,12 +53,18 @@ Popup {
switch (root.currentActivityCategory) {
case ActivityCenterPopup.ActivityCategory.All:
return true
case ActivityCenterPopup.ActivityCategory.Admin:
return notificationType === Constants.activityCenterNotificationTypeCommunityMembershipRequest
case ActivityCenterPopup.ActivityCategory.Mentions:
return notificationType === Constants.activityCenterNotificationTypeMention
case ActivityCenterPopup.ActivityCategory.Replies:
return notificationType === Constants.activityCenterNotificationTypeReply
case ActivityCenterPopup.ActivityCategory.ContactRequests:
return notificationType === Constants.activityCenterNotificationTypeContactRequest
case ActivityCenterPopup.ActivityCategory.Membership:
return notificationType === Constants.activityCenterNotificationTypeCommunityInvitation ||
notificationType === Constants.activityCenterNotificationTypeCommunityMembershipRequest ||
notificationType === Constants.activityCenterNotificationTypeCommunityRequest
default:
return false
}
@ -65,13 +73,24 @@ Popup {
function calcNotificationType(notificationType, cnt) {
switch (notificationType) {
case Constants.activityCenterNotificationTypeMention:
root.mentionsCount += cnt
root.mentionsCount += cnt;
break;
case Constants.activityCenterNotificationTypeReply:
root.repliesCount += cnt
root.repliesCount += cnt;
break;
case Constants.activityCenterNotificationTypeContactRequest:
root.contactRequestsCount += cnt
root.contactRequestsCount += cnt;
break;
case Constants.activityCenterNotificationTypeCommunityInvitation:
root.membershipCount += cnt;
break;
case Constants.activityCenterNotificationTypeCommunityMembershipRequest:
// NOTE: not a typo, membership requests are shown in both categories
root.membershipCount += cnt;
root.adminCount += cnt;
break;
case Constants.activityCenterNotificationTypeCommunityRequest:
root.membershipCount += cnt;
break;
default:
break;
@ -125,9 +144,11 @@ Popup {
id: activityCenterTopBar
width: parent.width
unreadNotificationsCount: root.unreadNotificationsCount
hasAdmin: root.adminCount > 0
hasReplies: root.repliesCount > 0
hasMentions: root.mentionsCount > 0
hasContactRequests: root.contactRequestsCount > 0
hasMembership: root.membershipCount > 0
hideReadNotifications: acStore.hideReadNotifications
currentActivityCategory: root.currentActivityCategory
onCategoryTriggered: root.currentActivityCategory = category
@ -196,6 +217,42 @@ Popup {
onActivityCenterClose: root.close()
}
}
DelegateChoice {
roleValue: Constants.activityCenterNotificationTypeCommunityInvitation
ActivityNotificationCommunityInvitation {
width: listView.availableWidth
store: root.store
notification: model
messageContextMenu: root.messageContextMenu
previousNotificationIndex: Math.min(listView.count - 1, index + 1)
onActivityCenterClose: root.close()
}
}
DelegateChoice {
roleValue: Constants.activityCenterNotificationTypeCommunityMembershipRequest
ActivityNotificationCommunityMembershipRequest {
width: listView.availableWidth
store: root.store
notification: model
messageContextMenu: root.messageContextMenu
previousNotificationIndex: Math.min(listView.count - 1, index + 1)
onActivityCenterClose: root.close()
}
}
DelegateChoice {
roleValue: Constants.activityCenterNotificationTypeCommunityRequest
ActivityNotificationCommunityRequest {
width: listView.availableWidth
store: root.store
notification: model
messageContextMenu: root.messageContextMenu
previousNotificationIndex: Math.min(listView.count - 1, index + 1)
onActivityCenterClose: root.close()
}
}
}
}
}

View File

@ -36,7 +36,7 @@ Item {
Loader {
id: ctaLoader
anchors.bottom: bodyLoader.bottom
anchors.verticalCenter: bodyLoader.verticalCenter
anchors.right: parent.right
anchors.rightMargin: Style.current.padding

View File

@ -0,0 +1,34 @@
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 shared.panels 1.0
import utils 1.0
import "../controls"
ActivityNotificationMessage {
id: root
badgeComponent: CommunityBadge {
id: communityBadge
property var community: root.store.getCommunityDetailsAsJson(notification.message.communityId)
communityName: community.name
communityImage: community.image
communityColor: community.color
onCommunityNameClicked: {
root.store.setActiveCommunity(notification.message.communityId)
}
onChannelNameClicked: {
root.activityCenterClose()
root.store.activityCenterModuleInst.switchTo(notification.sectionId, notification.chatId, notification.id)
}
}
}

View File

@ -0,0 +1,75 @@
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 shared.panels 1.0
import utils 1.0
import shared.views.chat 1.0
import "../controls"
import "../panels"
ActivityNotificationBase {
id: root
property var messageContextMenu
property int previousNotificationIndex
readonly property string previousNotificationTimestamp: previousNotificationIndex == 0 ?
"" : root.store.activityCenterList.getNotificationData(
previousNotificationIndex, "timestamp")
signal activityCenterClose()
bodyComponent: MessageView {
readonly property var contactDetails: Utils.getContactDetailsAsJson(senderId)
rootStore: root.store
messageStore: root.store.messageStore
messageId: notification.id
messageText: qsTr("Wants to join")
messageTimestamp: notification.timestamp
senderId: notification.author
senderIcon: contactDetails.displayIcon
senderDisplayName: contactDetails.name
activityCenterMessage: true
activityCenterMessageRead: false
scrollToBottom: null
prevMessageIndex: root.previousNotificationIndex
prevMsgTimestamp: root.previousNotificationTimestamp
onImageClicked: Global.openImagePopup(image, root.messageContextMenu)
CommunityBadge {
readonly property var community: root.store.getCommunityDetailsAsJson(notification.communityId)
anchors.bottom: parent.bottom
anchors.bottomMargin: 6
anchors.left: parent.left
anchors.leftMargin: 160 // TODO: get right text margin
communityName: community.name
communityImage: community.image
communityColor: community.color
onCommunityNameClicked: {
root.store.setActiveCommunity(notification.message.communityId)
}
onChannelNameClicked: {
root.activityCenterClose()
root.store.activityCenterModuleInst.switchTo(notification.sectionId, notification.chatId, notification.id)
}
}
}
ctaComponent: MembershipCta {
pending: true
accepted: false
declined: false
// onAcceptRequestToJoinCommunity: communitySectionModule.acceptRequestToJoinCommunity(id)
// onDeclineRequestToJoinCommunity: communitySectionModule.declineRequestToJoinCommunity(id)
}
}

View File

@ -0,0 +1,34 @@
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 shared.panels 1.0
import utils 1.0
import "../controls"
ActivityNotificationMessage {
id: root
badgeComponent: CommunityBadge {
id: communityBadge
property var community: root.store.getCommunityDetailsAsJson(notification.message.communityId)
communityName: community.name
communityImage: community.image
communityColor: community.color
onCommunityNameClicked: {
root.store.setActiveCommunity(notification.message.communityId)
}
onChannelNameClicked: {
root.activityCenterClose()
root.store.activityCenterModuleInst.switchTo(notification.sectionId, notification.chatId, notification.id)
}
}
}

View File

@ -519,6 +519,9 @@ QtObject {
readonly property int activityCenterNotificationTypeMention: 3
readonly property int activityCenterNotificationTypeReply: 4
readonly property int activityCenterNotificationTypeContactRequest: 5
readonly property int activityCenterNotificationTypeCommunityInvitation: 6
readonly property int activityCenterNotificationTypeCommunityRequest: 7
readonly property int activityCenterNotificationTypeCommunityMembershipRequest: 8
readonly property int contactRequestStateNone: 0
readonly property int contactRequestStatePending: 1

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit 993c236c04517deb636e6551dfd553f427ffcce7
Subproject commit de61ed1213db6f05052bd60e18a2ecc46a27496d