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.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)
result = initMemberItem(
pubKey = memberId,
@ -137,6 +137,7 @@ proc createMemberItem(self: Module, memberId, requestId: string): MemberItem =
isContact = contactDetails.dto.isContact,
isVerified = contactDetails.dto.isContactVerified(),
requestToJoinId = requestId,
membershipRequestState = status,
)
method getCommunityItem(self: Module, c: CommunityDto): SectionItem =
@ -168,14 +169,14 @@ method getCommunityItem(self: Module, c: CommunityDto): SectionItem =
c.permissions.ensOnly,
c.muted,
c.members.map(proc(member: ChatMember): MemberItem =
result = self.createMemberItem(member.id, "")),
result = self.createMemberItem(member.id, "", MembershipRequestState.Accepted)),
historyArchiveSupportEnabled = c.settings.historyArchiveSupportEnabled,
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 =
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 =
result = self.createMemberItem(requestDto.publicKey, requestDto.id)),
result = self.createMemberItem(requestDto.publicKey, requestDto.id, MembershipRequestState(requestDto.state))),
encrypted = c.encrypted,
communityTokens = @[]
)

View File

@ -313,6 +313,7 @@ proc createChannelGroupItem[T](self: Module[T], channelGroup: ChannelGroupDto):
isVerified = contactDetails.dto.isContactVerified(),
memberRole = member.role,
airdropAddress = member.airdropAccount.address,
membershipRequestState = MembershipRequestState.Accepted
)),
# pendingRequestsToJoin
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),
isContact = contactDetails.dto.isContact,
isVerified = contactDetails.dto.isContactVerified(),
membershipRequestState = MembershipRequestState.Banned,
)
),
# pendingMemberRequests
@ -360,6 +362,7 @@ proc createChannelGroupItem[T](self: Module[T], channelGroup: ChannelGroupDto):
isContact = contactDetails.dto.isContact,
isVerified = contactDetails.dto.isContactVerified(),
requestToJoinId = requestDto.id,
membershipRequestState = MembershipRequestState(requestDto.state),
)
) else: @[],
# declinedMemberRequests
@ -379,6 +382,7 @@ proc createChannelGroupItem[T](self: Module[T], channelGroup: ChannelGroupDto):
isContact = contactDetails.dto.isContact,
isVerified = contactDetails.dto.isContactVerified(),
requestToJoinId = requestDto.id,
membershipRequestState = MembershipRequestState(requestDto.state),
)
) else: @[],
channelGroup.encrypted,

View File

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

View File

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

View File

@ -54,6 +54,18 @@ type MemberRole* {.pure} = enum
ModerateContent
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
ContractTransactionStatus* {.pure.} = enum
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=22642-497015"
],
"MembersTabPanel": [
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba⎜Desktop?type=design&node-id=35909-605774&mode=design&t=KfrAekLfW5mTy68x-0"
],
"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=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 shared.views.chat 1.0
import shared.controls.chat 1.0
import shared.controls 1.0
import AppLayouts.Communities.layouts 1.0
@ -102,28 +103,46 @@ Item {
onClicked: root.unbanUserClicked(model.pubKey)
},
StatusButton {
visible: (root.panelType === MembersTabPanel.TabType.PendingRequests) && isHovered
text: qsTr("Reject")
type: StatusBaseButton.Type.Danger
icon.name: "close-circle"
icon.color: Style.current.danger
onClicked: root.declineRequestToJoin(model.requestToJoinId)
},
DisabledTooltipButton {
id: acceptButton
visible: ((root.panelType === MembersTabPanel.TabType.PendingRequests ||
root.panelType === MembersTabPanel.TabType.DeclinedRequests) && isHovered) ||
model.membershipRequestState === Constants.CommunityMembershipRequestState.AcceptedPending
//TODO: Only the current user can reject a pending request, so we should check that here
StatusButton {
visible: (root.panelType === MembersTabPanel.TabType.PendingRequests ||
root.panelType === MembersTabPanel.TabType.DeclinedRequests) && isHovered
text: qsTr("Accept")
tooltipText: qsTr("Waiting for owner node to come online")
interactive: model.membershipRequestState !== Constants.CommunityMembershipRequestState.AcceptedPending
buttonComponent: StatusButton {
text: model.membershipRequestState == Constants.CommunityMembershipRequestState.AcceptedPending ? qsTr("Accept pending") : qsTr("Accept")
icon.name: "checkmark-circle"
icon.color: Theme.palette.successColor1
icon.color: enabled ? Theme.palette.successColor1 : disabledTextColor
normalColor: Theme.palette.successColor2
hoverColor: Theme.palette.successColor3
textColor: Theme.palette.successColor1
loading: model.requestToJoinLoading
enabled: acceptButton.interactive
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

View File

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

View File

@ -9,16 +9,20 @@ import StatusQ.Components 0.1
import utils 1.0
import shared.panels 1.0
import "../stores"
Item {
id: root
property bool pending: false
property bool accepted: false
property bool declined: false
property bool acceptedPending: false
property bool declinedPending: false
property int membershipStatus: ActivityCenterStore.ActivityCenterMembershipStatus.None
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 declineRequestToJoinCommunity()

View File

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

View File

@ -43,11 +43,7 @@ ActivityNotificationMessage {
}
ctaComponent: MembershipCta {
pending: notification && notification.membershipStatus === ActivityCenterStore.ActivityCenterMembershipStatus.Pending
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
membershipStatus: notification && notification.membershipStatus ? notification.membershipStatus : ActivityNotification.MembershipStatus.None
onAcceptRequestToJoinCommunity: root.store.acceptRequestToJoinCommunity(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

View File

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

View File

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