diff --git a/src/app/chat/views/communities.nim b/src/app/chat/views/communities.nim index cac0314e4f..d6fbe17fd2 100644 --- a/src/app/chat/views/communities.nim +++ b/src/app/chat/views/communities.nim @@ -174,28 +174,35 @@ QtObject: proc communityAdded*(self: CommunitiesView, communityId: string) {.signal.} + proc observedCommunityChanged*(self: CommunitiesView) {.signal.} + proc communityChanged*(self: CommunitiesView, communityId: string) {.signal.} + proc addCommunityToList*(self: CommunitiesView, community: Community) = let communityCheck = self.communityList.getCommunityById(community.id) if (communityCheck.id == ""): self.communityList.addCommunityItemToList(community) self.communityAdded(community.id) + self.communityChanged(community.id) else: self.communityList.replaceCommunity(community) + self.communityChanged(community.id) if (self.activeCommunity.active and self.activeCommunity.communityItem.id == community.id): self.activeCommunity.setCommunityItem(community) if (self.observedCommunity.communityItem.id == community.id): self.observedCommunity.setCommunityItem(community) + self.observedCommunityChanged() - if (community.joined == true): + if (community.joined == true and community.isMember == true): let joinedCommunityCheck = self.joinedCommunityList.getCommunityById(community.id) if (joinedCommunityCheck.id == ""): self.joinedCommunityList.addCommunityItemToList(community) else: self.joinedCommunityList.replaceCommunity(community) - elif (community.isMember == true): - discard self.joinCommunity(community.id, false) + self.joinedCommunitiesChanged() + + if (community.isMember == true): var i = 0 for communityRequest in self.myCommunityRequests: if (communityRequest.communityId == community.id): @@ -203,6 +210,9 @@ QtObject: self.myCommunityRequests.delete(i, i) break i = i + 1 + # TODO: handle membership request rejection + # @cammellos mentioned this would likely changed in Communities Phase 3, so + # no need to polish now. proc isCommunityRequestPending*(self: CommunitiesView, communityId: string): bool {.slot.} = for communityRequest in self.myCommunityRequests: @@ -277,8 +287,6 @@ QtObject: error "Error creating the category", msg = e.msg result = fmt"Error creating the category: {e.msg}" - proc observedCommunityChanged*(self: CommunitiesView) {.signal.} - proc setObservedCommunity*(self: CommunitiesView, communityId: string) {.slot.} = if(communityId == ""): return var community = self.communityList.getCommunityById(communityId) @@ -303,9 +311,12 @@ QtObject: if (communityId == self.activeCommunity.communityItem.id): self.activeCommunity.setActive(false) self.joinedCommunityList.removeCommunityItemFromList(communityId) + self.joinedCommunitiesChanged() var updatedCommunity = self.communityList.getCommunityById(communityId) updatedCommunity.joined = false self.communityList.replaceCommunity(updatedCommunity) + self.communitiesChanged() + self.communityChanged(communityId) except Exception as e: error "Error leaving the community", msg = e.msg result = fmt"Error leaving the community: {e.msg}" @@ -367,10 +378,21 @@ QtObject: except Exception as e: error "Error requesting to join the community", msg = e.msg + proc removeMembershipRequest(self: CommunitiesView, requestId: string, accepted: bool) = + var i = 0 + for request in self.myCommunityRequests: + if (request.id == requestId): + self.myCommunityRequests.delete(i, i) + let name = self.getCommunityNameById(request.communityId) + self.membershipRequestChanged(request.communityId, name, accepted) + break + i = i + 1 + self.activeCommunity.communityMembershipRequestList.removeCommunityMembershipRequestItemFromList(requestId) + proc acceptRequestToJoinCommunity*(self: CommunitiesView, requestId: string): string {.slot.} = try: self.status.chat.acceptRequestToJoinCommunity(requestId) - self.activeCommunity.communityMembershipRequestList.removeCommunityMembershipRequestItemFromList(requestId) + self.removeMembershipRequest(requestId, true) except Exception as e: error "Error accepting request to join the community", msg = e.msg return "Error accepting request to join the community" @@ -379,7 +401,7 @@ QtObject: proc declineRequestToJoinCommunity*(self: CommunitiesView, requestId: string): string {.slot.} = try: self.status.chat.declineRequestToJoinCommunity(requestId) - self.activeCommunity.communityMembershipRequestList.removeCommunityMembershipRequestItemFromList(requestId) + self.removeMembershipRequest(requestId, false) except Exception as e: error "Error declining request to join the community", msg = e.msg return "Error declining request to join the community" diff --git a/src/app/chat/views/community_list.nim b/src/app/chat/views/community_list.nim index 2f52533d67..18aa938b17 100644 --- a/src/app/chat/views/community_list.nim +++ b/src/app/chat/views/community_list.nim @@ -1,8 +1,11 @@ -import NimQml, Tables, chronicles -import ../../../status/chat/chat -import ../../../status/status -import ../../../status/accounts -import strutils +import # std libs + NimQml, Tables, json, strutils + +import # vendor libs + chronicles, json_serialization + +import # status-desktop libs + ../../../status/chat/chat, ../../../status/status, ../../../status/accounts type CommunityRoles {.pure.} = enum @@ -117,6 +120,11 @@ QtObject: else: result = newQVariant("") of CommunityRoles.CommunityColor: result = newQVariant(communityItem.communityColor) + + proc getCommunityByIdJson(self: CommunityList, communityId: string): string {.slot.} = + for community in self.communities: + if (community.id == communityId): + result = Json.encode(community) method roleNames(self: CommunityList): Table[int, string] = { @@ -196,4 +204,4 @@ QtObject: if idx == -1: return community.categories.delete(idx) let index = self.communities.findIndexById(communityId) - self.communities[index] = community \ No newline at end of file + self.communities[index] = community diff --git a/src/status/libstatus/chat.nim b/src/status/libstatus/chat.nim index 1539647b7c..0466ed7978 100644 --- a/src/status/libstatus/chat.nim +++ b/src/status/libstatus/chat.nim @@ -315,7 +315,7 @@ proc createCommunity*(name: string, description: string, access: int, ensOnly: b if rpcResult{"error"} != nil: let error = Json.decode($rpcResult{"error"}, RpcError) - raise newException(RpcException, "Error editing community channel: " & error.message) + raise newException(RpcException, "Error creating community: " & error.message) if rpcResult{"result"} != nil and rpcResult{"result"}.kind != JNull: result = rpcResult["result"]["communities"][0].toCommunity() @@ -338,7 +338,7 @@ proc editCommunity*(communityId: string, name: string, description: string, acce if rpcResult{"error"} != nil: let error = Json.decode($rpcResult{"error"}, RpcError) - raise newException(RpcException, "Error editing community channel: " & error.message) + raise newException(RpcException, "Error editing community: " & error.message) if rpcResult{"result"} != nil and rpcResult{"result"}.kind != JNull: result = rpcResult["result"]["communities"][0].toCommunity() @@ -481,8 +481,7 @@ proc requestToJoinCommunity*(communityId: string, ensName: string): seq[Communit }]).parseJSON() var communityRequests: seq[CommunityMembershipRequest] = @[] - - if rpcResult{"result"}{"requestsToJoinCommunity"}.kind != JNull: + if rpcResult{"result"}{"requestsToJoinCommunity"} != nil and rpcResult{"result"}{"requestsToJoinCommunity"}.kind != JNull: for jsonCommunityReqest in rpcResult["result"]["requestsToJoinCommunity"]: communityRequests.add(jsonCommunityReqest.toCommunityMembershipRequest()) diff --git a/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/InvitationBubble.qml b/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/InvitationBubble.qml index b475b4785d..e7ba3f6458 100644 --- a/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/InvitationBubble.qml +++ b/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/InvitationBubble.qml @@ -17,12 +17,28 @@ Item { height: childrenRect.height width: rectangleBubbleLoader.width - Component.onCompleted: { - chatsModel.communities.setObservedCommunity(root.communityId) - - root.invitedCommunity = chatsModel.communities.observedCommunity + function getCommunity() { + let community = JSON.parse(chatsModel.communities.list.getCommunityByIdJson(communityId)); + if (community) { + community.nbMembers = community.members.length; + } + return community } + Component.onCompleted: { + root.invitedCommunity = getCommunity() + } + + Connections { + target: chatsModel.communities + onCommunityChanged: function (communityId) { + if (communityId === root.communityId) { + root.invitedCommunity = getCommunity() + } + } + } + + Loader { id: rectangleBubbleLoader active: !!invitedCommunity @@ -32,6 +48,8 @@ Item { sourceComponent: Component { Rectangle { id: rectangleBubble + property alias button: joinBtn + property bool isPendingRequest: chatsModel.communities.isCommunityRequestPending(communityId) width: 270 height: childrenRect.height + Style.current.halfPadding radius: 16 @@ -39,6 +57,74 @@ Item { border.color: Style.current.border border.width: 1 + states: [ + State { + name: "requiresEns" + when: invitedCommunity.ensOnly && !profileModel.profile.ensVerified + PropertyChanges { + target: joinBtn + text: qsTr("Membership requires an ENS username") + enabled: false + } + }, + State { + name: "inviteOnly" + when: invitedCommunity.access === Constants.communityChatInvitationOnlyAccess + PropertyChanges { + target: joinBtn + text: qsTr("You need to be invited") + enabled: false + } + }, + State { + name: "pending" + when: invitedCommunity.access === Constants.communityChatOnRequestAccess && + rectangleBubble.isPendingRequest + PropertyChanges { + target: joinBtn + text: qsTr("Pending") + enabled: false + } + }, + State { + name: "joined" + when: invitedCommunity.joined && invitedCommunity.isMember + PropertyChanges { + target: joinBtn + text: qsTr("View") + } + }, + State { + name: "requestToJoin" + when: invitedCommunity.access === Constants.communityChatOnRequestAccess && + // !invitedCommunity.joined && !invitedCommunity.isMember + invitedCommunity.canRequestAccess + PropertyChanges { + target: joinBtn + text: qsTr("Request to join") + + } + }, + State { + name: "unjoined" + when: invitedCommunity.access === Constants.communityChatOnRequestAccess && + invitedCommunity.isMember + PropertyChanges { + target: joinBtn + text: qsTr("Join") + } + } + ] + + Connections { + target: chatsModel.communities + onMembershipRequestChanged: function(communityId, communityName, requestAccepted) { + if (communityId === root.communityId) { + rectangleBubble.isPendingRequest = false + } + } + } + // TODO add check if verified StyledText { id: title @@ -58,10 +144,18 @@ Item { StyledText { id: invitedYou - text: isCurrentUser ? + text: { + if (chatsModel.channelView.activeChannel.chatType === Constants.chatTypeOneToOne) { + return isCurrentUser ? qsTr("You invited %1 to join a community").arg(chatsModel.userNameOrAlias(chatsModel.channelView.activeChannel.id)) - //% "%1 invited you to join a community" - : qsTrId("-1-invited-you-to-join-a-community").arg(displayUserName) + //% "%1 invited you to join a community" + : qsTrId("-1-invited-you-to-join-a-community").arg(displayUserName) + } else { + return isCurrentUser ? + qsTr("You shared a community") + : qsTr("A community has been shared") + } + } anchors.top: title.bottom anchors.topMargin: 4 anchors.left: parent.left @@ -127,69 +221,29 @@ Item { } StatusButton { - property int access: invitedCommunity.access - property bool isPendingRequest: { - if (invitedCommunity.access !== Constants.communityChatOnRequestAccess) { - return false - } - return chatsModel.communities.isCommunityRequestPending(communityId) - } id: joinBtn type: "secondary" anchors.top: sep2.bottom width: parent.width height: 44 - enabled: { - if (invitedCommunity.ensOnly && !profileModel.profile.ensVerified) { - return false - } - if (joinBtn.access === Constants.communityChatInvitationOnlyAccess || isPendingRequest) { - return false - } - - return true - } - text: { - if (invitedCommunity.ensOnly && !profileModel.profile.ensVerified) { - return qsTr("Membership requires an ENS username") - } - if (invitedCommunity.canJoin) { - return qsTr("Join") - } - if (invitedCommunity.joined || invitedCommunity.isMember) { - return qsTr("View") - } - if (isPendingRequest) { - return qsTr("Pending") - } - - switch(joinBtn.access) { - case Constants.communityChatPublicAccess: return qsTr("Join") - case Constants.communityChatInvitationOnlyAccess: return qsTr("You need to be invited"); - case Constants.communityChatOnRequestAccess: return qsTr("Request to join") - default: return qsTr("Unknown community"); - } - } + enabled: true + text: qsTr("Unsupported state") onClicked: { let error - if (invitedCommunity.joined || invitedCommunity.isMember) { + if (rectangleBubble.state === "joined") { chatsModel.communities.setActiveCommunity(communityId); return + } else if (rectangleBubble.state === "unjoined") { + error = chatsModel.communities.joinCommunity(communityId, true) } - - if (joinBtn.access === Constants.communityChatOnRequestAccess) { + else if (rectangleBubble.state === "requestToJoin") { error = chatsModel.communities.requestToJoinCommunity(communityId, profileModel.profile.ensVerified ? profileModel.profile.username : "") if (!error) { - enabled = false - text = qsTr("Pending") + rectangleBubble.isPendingRequest = chatsModel.communities.isCommunityRequestPending(communityId) } - } else { - error = chatsModel.communities.joinCommunity(communityId, true) - enabled = false - text = qsTr("Joined") } if (error) {