feat(community): Add Pending states to community membership request decisions in members tab panel

This commit is contained in:
Alex Jbanca 2023-08-07 16:35:14 +03:00 committed by Alex Jbanca
parent e9a2b183c7
commit 7d0d321b35
14 changed files with 143 additions and 37 deletions

View File

@ -121,7 +121,7 @@ method curatedCommunitiesLoadingFailed*(self: Module) =
self.curatedCommunitiesLoaded = true self.curatedCommunitiesLoaded = true
self.view.setCuratedCommunitiesLoading(false) self.view.setCuratedCommunitiesLoading(false)
proc createMemberItem(self: Module, memberId, requestId: string): MemberItem = proc createMemberItem(self: Module, memberId, requestId: string, status: MembershipRequestState): MemberItem =
let contactDetails = self.controller.getContactDetails(memberId) let contactDetails = self.controller.getContactDetails(memberId)
result = initMemberItem( result = initMemberItem(
pubKey = memberId, pubKey = memberId,
@ -137,6 +137,7 @@ proc createMemberItem(self: Module, memberId, requestId: string): MemberItem =
isContact = contactDetails.dto.isContact, isContact = contactDetails.dto.isContact,
isVerified = contactDetails.dto.isContactVerified(), isVerified = contactDetails.dto.isContactVerified(),
requestToJoinId = requestId, requestToJoinId = requestId,
membershipRequestState = status,
) )
method getCommunityItem(self: Module, c: CommunityDto): SectionItem = method getCommunityItem(self: Module, c: CommunityDto): SectionItem =
@ -168,14 +169,14 @@ method getCommunityItem(self: Module, c: CommunityDto): SectionItem =
c.permissions.ensOnly, c.permissions.ensOnly,
c.muted, c.muted,
c.members.map(proc(member: ChatMember): MemberItem = c.members.map(proc(member: ChatMember): MemberItem =
result = self.createMemberItem(member.id, "")), result = self.createMemberItem(member.id, "", MembershipRequestState.Accepted)),
historyArchiveSupportEnabled = c.settings.historyArchiveSupportEnabled, historyArchiveSupportEnabled = c.settings.historyArchiveSupportEnabled,
bannedMembers = c.bannedMembersIds.map(proc(bannedMemberId: string): MemberItem = bannedMembers = c.bannedMembersIds.map(proc(bannedMemberId: string): MemberItem =
result = self.createMemberItem(bannedMemberId, "")), result = self.createMemberItem(bannedMemberId, "", MembershipRequestState.Banned)),
pendingMemberRequests = c.pendingRequestsToJoin.map(proc(requestDto: CommunityMembershipRequestDto): MemberItem = pendingMemberRequests = c.pendingRequestsToJoin.map(proc(requestDto: CommunityMembershipRequestDto): MemberItem =
result = self.createMemberItem(requestDto.publicKey, requestDto.id)), result = self.createMemberItem(requestDto.publicKey, requestDto.id, MembershipRequestState(requestDto.state))),
declinedMemberRequests = c.declinedRequestsToJoin.map(proc(requestDto: CommunityMembershipRequestDto): MemberItem = declinedMemberRequests = c.declinedRequestsToJoin.map(proc(requestDto: CommunityMembershipRequestDto): MemberItem =
result = self.createMemberItem(requestDto.publicKey, requestDto.id)), result = self.createMemberItem(requestDto.publicKey, requestDto.id, MembershipRequestState(requestDto.state))),
encrypted = c.encrypted, encrypted = c.encrypted,
communityTokens = @[] communityTokens = @[]
) )

View File

@ -313,6 +313,7 @@ proc createChannelGroupItem[T](self: Module[T], channelGroup: ChannelGroupDto):
isVerified = contactDetails.dto.isContactVerified(), isVerified = contactDetails.dto.isContactVerified(),
memberRole = member.role, memberRole = member.role,
airdropAddress = member.airdropAccount.address, airdropAddress = member.airdropAccount.address,
membershipRequestState = MembershipRequestState.Accepted
)), )),
# pendingRequestsToJoin # pendingRequestsToJoin
if (isCommunity): communityDetails.pendingRequestsToJoin.map(x => pending_request_item.initItem( if (isCommunity): communityDetails.pendingRequestsToJoin.map(x => pending_request_item.initItem(
@ -341,6 +342,7 @@ proc createChannelGroupItem[T](self: Module[T], channelGroup: ChannelGroupDto):
onlineStatus = toOnlineStatus(self.controller.getStatusForContactWithId(bannedMemberId).statusType), onlineStatus = toOnlineStatus(self.controller.getStatusForContactWithId(bannedMemberId).statusType),
isContact = contactDetails.dto.isContact, isContact = contactDetails.dto.isContact,
isVerified = contactDetails.dto.isContactVerified(), isVerified = contactDetails.dto.isContactVerified(),
membershipRequestState = MembershipRequestState.Banned,
) )
), ),
# pendingMemberRequests # pendingMemberRequests
@ -360,6 +362,7 @@ proc createChannelGroupItem[T](self: Module[T], channelGroup: ChannelGroupDto):
isContact = contactDetails.dto.isContact, isContact = contactDetails.dto.isContact,
isVerified = contactDetails.dto.isContactVerified(), isVerified = contactDetails.dto.isContactVerified(),
requestToJoinId = requestDto.id, requestToJoinId = requestDto.id,
membershipRequestState = MembershipRequestState(requestDto.state),
) )
) else: @[], ) else: @[],
# declinedMemberRequests # declinedMemberRequests
@ -379,6 +382,7 @@ proc createChannelGroupItem[T](self: Module[T], channelGroup: ChannelGroupDto):
isContact = contactDetails.dto.isContact, isContact = contactDetails.dto.isContact,
isVerified = contactDetails.dto.isContactVerified(), isVerified = contactDetails.dto.isContactVerified(),
requestToJoinId = requestDto.id, requestToJoinId = requestDto.id,
membershipRequestState = MembershipRequestState(requestDto.state),
) )
) else: @[], ) else: @[],
channelGroup.encrypted, channelGroup.encrypted,

View File

@ -12,6 +12,7 @@ type
requestToJoinId: string requestToJoinId: string
requestToJoinLoading*: bool requestToJoinLoading*: bool
airdropAddress*: string airdropAddress*: string
membershipRequestState: MembershipRequestState
# FIXME: remove defaults # FIXME: remove defaults
proc initMemberItem*( proc initMemberItem*(
@ -37,6 +38,7 @@ proc initMemberItem*(
requestToJoinId: string = "", requestToJoinId: string = "",
requestToJoinLoading: bool = false, requestToJoinLoading: bool = false,
airdropAddress: string = "", airdropAddress: string = "",
membershipRequestState: MembershipRequestState = MembershipRequestState.None
): MemberItem = ): MemberItem =
result = MemberItem() result = MemberItem()
result.memberRole = memberRole result.memberRole = memberRole
@ -44,6 +46,7 @@ proc initMemberItem*(
result.requestToJoinId = requestToJoinId result.requestToJoinId = requestToJoinId
result.requestToJoinLoading = requestToJoinLoading result.requestToJoinLoading = requestToJoinLoading
result.airdropAddress = airdropAddress result.airdropAddress = airdropAddress
result.membershipRequestState = membershipRequestState
result.UserItem.setup( result.UserItem.setup(
pubKey = pubKey, pubKey = pubKey,
displayName = displayName, displayName = displayName,
@ -86,7 +89,8 @@ proc `$`*(self: MemberItem): string =
outgoingVerificationStatus: {$self.outgoingVerificationStatus.int}, outgoingVerificationStatus: {$self.outgoingVerificationStatus.int},
memberRole: {self.memberRole}, memberRole: {self.memberRole},
joined: {self.joined}, joined: {self.joined},
requestToJoinId: {self.requestToJoinId} requestToJoinId: {self.requestToJoinId},
membershipRequestState: {$self.membershipRequestState.int}
]""" ]"""
proc memberRole*(self: MemberItem): MemberRole {.inline.} = proc memberRole*(self: MemberItem): MemberRole {.inline.} =
@ -112,3 +116,6 @@ proc requestToJoinLoading*(self: MemberItem): bool {.inline.} =
proc airdropAddress*(self: MemberItem): string {.inline.} = proc airdropAddress*(self: MemberItem): string {.inline.} =
self.airdropAddress self.airdropAddress
proc membershipRequestState*(self: MemberItem): MembershipRequestState {.inline.} =
self.membershipRequestState

View File

@ -30,6 +30,7 @@ type
RequestToJoinId RequestToJoinId
RequestToJoinLoading RequestToJoinLoading
AirdropAddress AirdropAddress
MembershipRequestState
QtObject: QtObject:
type type
@ -98,6 +99,7 @@ QtObject:
ModelRole.RequestToJoinId.int: "requestToJoinId", ModelRole.RequestToJoinId.int: "requestToJoinId",
ModelRole.RequestToJoinLoading.int: "requestToJoinLoading", ModelRole.RequestToJoinLoading.int: "requestToJoinLoading",
ModelRole.AirdropAddress.int: "airdropAddress", ModelRole.AirdropAddress.int: "airdropAddress",
ModelRole.MembershipRequestState.int: "membershipRequestState"
}.toTable }.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant = method data(self: Model, index: QModelIndex, role: int): QVariant =
@ -155,6 +157,8 @@ QtObject:
result = newQVariant(item.requestToJoinLoading) result = newQVariant(item.requestToJoinLoading)
of ModelRole.AirdropAddress: of ModelRole.AirdropAddress:
result = newQVariant(item.airdropAddress) result = newQVariant(item.airdropAddress)
of ModelRole.MembershipRequestState:
result = newQVariant(item.membershipRequestState.int)
proc addItem*(self: Model, item: MemberItem) = proc addItem*(self: Model, item: MemberItem) =
self.beginInsertRows(newQModelIndex(), self.items.len, self.items.len) self.beginInsertRows(newQModelIndex(), self.items.len, self.items.len)

View File

@ -54,6 +54,18 @@ type MemberRole* {.pure} = enum
ModerateContent ModerateContent
Admin Admin
type MembershipRequestState* {.pure} = enum
None = 0,
Pending = 1,
Accepted = 2,
Declined = 3,
AcceptedPending = 4,
DeclinedPending = 5,
Banned = 6,
Kicked = 7,
BannedPending = 8,
KickedPending = 9
type type
ContractTransactionStatus* {.pure.} = enum ContractTransactionStatus* {.pure.} = enum
Failed, Failed,

View File

@ -142,6 +142,9 @@
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22647-498410", "https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22647-498410",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22642-497015" "https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22642-497015"
], ],
"MembersTabPanel": [
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba⎜Desktop?type=design&node-id=35909-605774&mode=design&t=KfrAekLfW5mTy68x-0"
],
"MintTokensSettingsPanel": [ "MintTokensSettingsPanel": [
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22721%3A498587&t=v2Krj5iZQaSTK7Om-1", "https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22721%3A498587&t=v2Krj5iZQaSTK7Om-1",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=2934%3A480877&t=v2Krj5iZQaSTK7Om-1", "https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=2934%3A480877&t=v2Krj5iZQaSTK7Om-1",

View File

@ -0,0 +1,39 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import AppLayouts.Communities.panels 1.0
import Models 1.0
import SortFilterProxyModel 0.2
SplitView {
id: root
MembersTabPanel {
id: membersTabPanelPage
placeholderText: "Placeholder text"
model: usersModelWithMembershipState
panelType: MembersTabPanel.TabType.PendingRequests
}
UsersModel {
id: usersModel
}
SortFilterProxyModel {
id: usersModelWithMembershipState
readonly property var acceptedStates: [0, 3, 4]
sourceModel: usersModel
proxyRoles: [
ExpressionRole {
name: "membershipRequestState"
expression: usersModelWithMembershipState.acceptedStates[model.index % (usersModelWithMembershipState.acceptedStates.length)]
},
ExpressionRole {
name: "requestToJoinLoading"
expression: false
}
]
}
}

View File

@ -11,6 +11,7 @@ import StatusQ.Popups 0.1
import utils 1.0 import utils 1.0
import shared.views.chat 1.0 import shared.views.chat 1.0
import shared.controls.chat 1.0 import shared.controls.chat 1.0
import shared.controls 1.0
import AppLayouts.Communities.layouts 1.0 import AppLayouts.Communities.layouts 1.0
@ -102,28 +103,46 @@ Item {
onClicked: root.unbanUserClicked(model.pubKey) onClicked: root.unbanUserClicked(model.pubKey)
}, },
StatusButton { DisabledTooltipButton {
visible: (root.panelType === MembersTabPanel.TabType.PendingRequests) && isHovered id: acceptButton
text: qsTr("Reject") visible: ((root.panelType === MembersTabPanel.TabType.PendingRequests ||
type: StatusBaseButton.Type.Danger root.panelType === MembersTabPanel.TabType.DeclinedRequests) && isHovered) ||
icon.name: "close-circle" model.membershipRequestState === Constants.CommunityMembershipRequestState.AcceptedPending
icon.color: Style.current.danger //TODO: Only the current user can reject a pending request, so we should check that here
onClicked: root.declineRequestToJoin(model.requestToJoinId)
},
StatusButton { tooltipText: qsTr("Waiting for owner node to come online")
visible: (root.panelType === MembersTabPanel.TabType.PendingRequests || interactive: model.membershipRequestState !== Constants.CommunityMembershipRequestState.AcceptedPending
root.panelType === MembersTabPanel.TabType.DeclinedRequests) && isHovered buttonComponent: StatusButton {
text: qsTr("Accept") text: model.membershipRequestState == Constants.CommunityMembershipRequestState.AcceptedPending ? qsTr("Accept pending") : qsTr("Accept")
icon.name: "checkmark-circle" icon.name: "checkmark-circle"
icon.color: Theme.palette.successColor1 icon.color: enabled ? Theme.palette.successColor1 : disabledTextColor
normalColor: Theme.palette.successColor2 normalColor: Theme.palette.successColor2
hoverColor: Theme.palette.successColor3 hoverColor: Theme.palette.successColor3
textColor: Theme.palette.successColor1 textColor: Theme.palette.successColor1
loading: model.requestToJoinLoading loading: model.requestToJoinLoading
enabled: acceptButton.interactive
onClicked: root.acceptRequestToJoin(model.requestToJoinId) onClicked: root.acceptRequestToJoin(model.requestToJoinId)
} }
},
DisabledTooltipButton {
id: rejectButton
//using opacity instead of visible to avoid the acceptButton jumping around
opacity: ((root.panelType === MembersTabPanel.TabType.PendingRequests) && isHovered) ||
model.membershipRequestState === Constants.CommunityMembershipRequestState.RejectedPending
//TODO: Only the current user can reject a pending request, so we should check that here
tooltipText: qsTr("Waiting for owner node to come online")
interactive: model.membershipRequestState !== Constants.CommunityMembershipRequestState.RejectedPending
buttonComponent: StatusButton {
text: model.membershipRequestState == Constants.CommunityMembershipRequestState.RejectedPending ? qsTr("Reject pending") : qsTr("Reject")
type: StatusBaseButton.Type.Danger
icon.name: "close-circle"
icon.color: enabled ? Style.current.danger : disabledTextColor
enabled: rejectButton.interactive
onClicked: root.declineRequestToJoin(model.requestToJoinId)
}
}
] ]
width: membersList.width width: membersList.width

View File

@ -14,6 +14,7 @@ JoinCommunityCenterPanel 1.0 JoinCommunityCenterPanel.qml
JoinCommunityHeaderPanel 1.0 JoinCommunityHeaderPanel.qml JoinCommunityHeaderPanel 1.0 JoinCommunityHeaderPanel.qml
JoinPermissionsOverlayPanel 1.0 JoinPermissionsOverlayPanel.qml JoinPermissionsOverlayPanel 1.0 JoinPermissionsOverlayPanel.qml
MembersSettingsPanel 1.0 MembersSettingsPanel.qml MembersSettingsPanel 1.0 MembersSettingsPanel.qml
MembersTabPanel 1.0 MembersTabPanel.qml
MintTokensFooterPanel 1.0 MintTokensFooterPanel.qml MintTokensFooterPanel 1.0 MintTokensFooterPanel.qml
MintTokensSettingsPanel 1.0 MintTokensSettingsPanel.qml MintTokensSettingsPanel 1.0 MintTokensSettingsPanel.qml
OverviewSettingsChart 1.0 OverviewSettingsChart.qml OverviewSettingsChart 1.0 OverviewSettingsChart.qml

View File

@ -9,16 +9,20 @@ import StatusQ.Components 0.1
import utils 1.0 import utils 1.0
import shared.panels 1.0 import shared.panels 1.0
import "../stores"
Item { Item {
id: root id: root
property bool pending: false property int membershipStatus: ActivityCenterStore.ActivityCenterMembershipStatus.None
property bool accepted: false
property bool declined: false
property bool acceptedPending: false
property bool declinedPending: false
property bool ctaAllowed: true property bool ctaAllowed: true
readonly property bool pending: membershipStatus === ActivityCenterStore.ActivityCenterMembershipStatus.Pending
readonly property bool accepted: membershipStatus === ActivityCenterStore.ActivityCenterMembershipStatus.Accepted
readonly property bool declined: membershipStatus === ActivityCenterStore.ActivityCenterMembershipStatus.Declined
readonly property bool acceptedPending: membershipStatus === ActivityCenterStore.ActivityCenterMembershipStatus.AcceptedPending
readonly property bool declinedPending: membershipStatus === ActivityCenterStore.ActivityCenterMembershipStatus.DeclinedPending
signal acceptRequestToJoinCommunity() signal acceptRequestToJoinCommunity()
signal declineRequestToJoinCommunity() signal declineRequestToJoinCommunity()

View File

@ -39,6 +39,7 @@ QtObject {
} }
enum ActivityCenterMembershipStatus { enum ActivityCenterMembershipStatus {
None = 0,
Pending = 1, Pending = 1,
Accepted = 2, Accepted = 2,
Declined = 3, Declined = 3,

View File

@ -43,11 +43,7 @@ ActivityNotificationMessage {
} }
ctaComponent: MembershipCta { ctaComponent: MembershipCta {
pending: notification && notification.membershipStatus === ActivityCenterStore.ActivityCenterMembershipStatus.Pending membershipStatus: notification && notification.membershipStatus ? notification.membershipStatus : ActivityNotification.MembershipStatus.None
accepted: notification && notification.membershipStatus === ActivityCenterStore.ActivityCenterMembershipStatus.Accepted
declined: notification && notification.membershipStatus === ActivityCenterStore.ActivityCenterMembershipStatus.Declined
acceptedPending: notification && notification.membershipStatus === ActivityCenterStore.ActivityCenterMembershipStatus.AcceptedPending
declinedPending: notification && notification.membershipStatus === ActivityCenterStore.ActivityCenterMembershipStatus.DeclinedPending
onAcceptRequestToJoinCommunity: root.store.acceptRequestToJoinCommunity(notification.id, notification.communityId) onAcceptRequestToJoinCommunity: root.store.acceptRequestToJoinCommunity(notification.id, notification.communityId)
onDeclineRequestToJoinCommunity: root.store.declineRequestToJoinCommunity(notification.id, notification.communityId) onDeclineRequestToJoinCommunity: root.store.declineRequestToJoinCommunity(notification.id, notification.communityId)
//TODO: Get backend value. If the membersip is in acceptedPending or declinedPending state, another user can't accept or decline the request //TODO: Get backend value. If the membersip is in acceptedPending or declinedPending state, another user can't accept or decline the request

View File

@ -10,6 +10,8 @@ Item {
property alias tooltipText: tooltip.text property alias tooltipText: tooltip.text
property int buttonType: DisabledTooltipButton.Normal property int buttonType: DisabledTooltipButton.Normal
property bool interactive: true property bool interactive: true
property Component buttonComponent: buttonType === DisabledTooltipButton.Normal ? normalButton : flatButton
signal clicked() signal clicked()
enum Type { enum Type {
@ -23,7 +25,7 @@ Item {
Loader { Loader {
id: buttonLoader id: buttonLoader
anchors.centerIn: parent anchors.centerIn: parent
sourceComponent: buttonType === DisabledTooltipButton.Normal ? normalButton : flatButton sourceComponent: buttonComponent
active: root.visible active: root.visible
} }
HoverHandler { HoverHandler {

View File

@ -1115,6 +1115,19 @@ QtObject {
BannedMembers BannedMembers
} }
enum CommunityMembershipRequestState {
None = 0,
Pending,
Accepted,
Rejected,
AcceptedPending,
RejectedPending,
Banned,
Kicked,
BannedPending,
KickedPending
}
readonly property QtObject walletAccountColors: QtObject { readonly property QtObject walletAccountColors: QtObject {
readonly property string primary: "primary" readonly property string primary: "primary"
readonly property string purple: "purple" readonly property string purple: "purple"