fix(communities): re-joining of left communities

Fixes: #2649.

Upon receipt of status-go signals which included communities that have been left (`joined: false`), those communities were being rejoined automatically when they should not have been.

fix(communities): Invitation bubble button state updates
The community state inside of the invitation bubble was not reactive to any community actions (such as joining, leaving, updating). In addition, requesting to join a community changed the button’s text to “Pending”, but upon approval, the button’s state was not updating.

The component was setting an observed community in the Component.onCompleted event, which was occurring for all invitation bubbles, but because the community wasn’t bound correctly to the bubble, once a bubble  with a different community was encountered, the community in context of the bubble wasn’t updated and instead used a local copy. Once the community was bound correctly (to be reactive), the states started working correctly.

The invitation bubble has been simplied so that it has states instead of using lots of if/else statements inside of the property bindings. This simplified the component’s logic for things like onClick action and made it a lot easier to read and modify.
This commit is contained in:
Eric Mastro 2021-06-24 17:21:45 +10:00 committed by Iuri Matias
parent 433698b252
commit e281ec3871
4 changed files with 154 additions and 71 deletions

View File

@ -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"

View File

@ -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
self.communities[index] = community

View File

@ -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())

View File

@ -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) {