Skip to content

Commit

Permalink
feat(@desktop/communities): add new join community workflow
Browse files Browse the repository at this point in the history
- user is able to see community without being a member
- invitation bubble always display "Go to Community"
- join community buttons are displayed in community view

main part of: #7072
  • Loading branch information
osmaczko committed Sep 22, 2022
1 parent 104f525 commit 966f6dc
Show file tree
Hide file tree
Showing 16 changed files with 133 additions and 154 deletions.
4 changes: 2 additions & 2 deletions src/app/modules/main/communities/controller.nim
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ proc getAllCommunities*(self: Controller): seq[CommunityDto] =
proc getCuratedCommunities*(self: Controller): seq[CuratedCommunity] =
result = self.communityService.getCuratedCommunities()

proc joinCommunity*(self: Controller, communityId: string): string =
self.communityService.joinCommunity(communityId)
proc spectateCommunity*(self: Controller, communityId: string): string =
self.communityService.spectateCommunity(communityId)

proc requestToJoinCommunity*(self: Controller, communityId: string, ensName: string) =
self.communityService.requestToJoinCommunity(communityId, ensName)
Expand Down
2 changes: 1 addition & 1 deletion src/app/modules/main/communities/io_interface.nim
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ method setCuratedCommunities*(self: AccessInterface, curatedCommunities: seq[Com
method getCommunityItem*(self: AccessInterface, community: CommunityDto): SectionItem {.base.} =
raise newException(ValueError, "No implementation available")

method joinCommunity*(self: AccessInterface, communityId: string): string {.base.} =
method spectateCommunity*(self: AccessInterface, communityId: string): string {.base.} =
raise newException(ValueError, "No implementation available")

method createCommunity*(self: AccessInterface, name: string, description, introMessage, outroMessage: string, access: int,
Expand Down
5 changes: 3 additions & 2 deletions src/app/modules/main/communities/module.nim
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ method getCommunityItem(self: Module, c: CommunityDto): SectionItem =
enabled = true,
c.joined,
c.canJoin,
c.spectated,
c.canManageUsers,
c.canRequestAccess,
c.isMember,
Expand Down Expand Up @@ -167,8 +168,8 @@ method setAllCommunities*(self: Module, communities: seq[CommunityDto]) =
method communityAdded*(self: Module, community: CommunityDto) =
self.view.addItem(self.getCommunityItem(community))

method joinCommunity*(self: Module, communityId: string): string =
self.controller.joinCommunity(communityId)
method spectateCommunity*(self: Module, communityId: string): string =
self.controller.spectateCommunity(communityId)

method communityEdited*(self: Module, community: CommunityDto) =
self.view.model().editItem(self.getCommunityItem(community))
Expand Down
6 changes: 2 additions & 4 deletions src/app/modules/main/communities/view.nim
Original file line number Diff line number Diff line change
Expand Up @@ -204,10 +204,8 @@ QtObject:
read = getDiscordDataExtractionInProgress
notify = discordDataExtractionInProgressChanged

proc joinCommunity*(self: View, communityId: string, ensName: string) {.slot.} =
# Users always have to request to join a community but might
# get automatically accepted.
self.delegate.requestToJoinCommunity(communityId, ensName)
proc spectateCommunity*(self: View, communityId: string, ensName: string) {.slot.} =
discard self.delegate.spectateCommunity(communityId)

proc createCommunity*(self: View, name: string,
description: string, introMessage: string, outroMessage: string,
Expand Down
15 changes: 15 additions & 0 deletions src/app/modules/main/controller.nim
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,21 @@ proc init*(self: Controller) =
setActive = args.fromUserAction
)

self.events.on(SIGNAL_COMMUNITY_SPECTATED) do(e:Args):
let args = CommunityArgs(e)
self.delegate.communityJoined(
args.community,
self.events,
self.settingsService,
self.contactsService,
self.chatService,
self.communityService,
self.messageService,
self.gifService,
self.mailserversService,
setActive = args.fromUserAction
)

self.events.on(TOGGLE_SECTION) do(e:Args):
let args = ToggleSectionArgs(e)
self.delegate.toggleSection(args.sectionType)
Expand Down
1 change: 1 addition & 0 deletions src/app/modules/main/module.nim
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ proc createChannelGroupItem[T](self: Module[T], c: ChannelGroupDto): SectionItem
enabled = true,
if (isCommunity): communityDetails.joined else: true,
if (isCommunity): communityDetails.canJoin else: true,
if (isCommunity): communityDetails.spectated else: false,
c.canManageUsers,
if (isCommunity): communityDetails.canRequestAccess else: true,
if (isCommunity): communityDetails.isMember else: true,
Expand Down
7 changes: 7 additions & 0 deletions src/app/modules/shared_models/section_item.nim
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type
isMember: bool
joined: bool
canJoin: bool
spectated: bool
canManageUsers: bool
canRequestAccess: bool
access: int
Expand Down Expand Up @@ -67,6 +68,7 @@ proc initItem*(
enabled = true,
joined = false,
canJoin = false,
spectated = false,
canManageUsers = false,
canRequestAccess = false,
isMember = false,
Expand Down Expand Up @@ -99,6 +101,7 @@ proc initItem*(
result.enabled = enabled
result.joined = joined
result.canJoin = canJoin
result.spectated = spectated
result.canManageUsers = canManageUsers
result.canRequestAccess = canRequestAccess
result.isMember = isMember
Expand Down Expand Up @@ -141,6 +144,7 @@ proc `$`*(self: SectionItem): string =
enabled:{self.enabled},
joined:{self.joined},
canJoin:{self.canJoin},
spectated:{self.spectated},
canManageUsers:{self.canManageUsers},
canRequestAccess:{self.canRequestAccess},
isMember:{self.isMember},
Expand Down Expand Up @@ -221,6 +225,9 @@ proc joined*(self: SectionItem): bool {.inline.} =
proc canJoin*(self: SectionItem): bool {.inline.} =
self.canJoin

proc spectated*(self: SectionItem): bool {.inline.} =
self.spectated

proc canRequestAccess*(self: SectionItem): bool {.inline.} =
self.canRequestAccess

Expand Down
6 changes: 6 additions & 0 deletions src/app/modules/shared_models/section_model.nim
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type
Active
Enabled
Joined
Spectated
IsMember
CanJoin
CanManageUsers
Expand Down Expand Up @@ -89,6 +90,7 @@ QtObject:
ModelRole.Active.int:"active",
ModelRole.Enabled.int:"enabled",
ModelRole.Joined.int:"joined",
ModelRole.Spectated.int:"spectated",
ModelRole.IsMember.int:"isMember",
ModelRole.CanJoin.int:"canJoin",
ModelRole.CanManageUsers.int:"canManageUsers",
Expand Down Expand Up @@ -148,6 +150,8 @@ QtObject:
result = newQVariant(item.enabled)
of ModelRole.Joined:
result = newQVariant(item.joined)
of ModelRole.Spectated:
result = newQVariant(item.spectated)
of ModelRole.IsMember:
result = newQVariant(item.isMember)
of ModelRole.CanJoin:
Expand Down Expand Up @@ -272,6 +276,7 @@ QtObject:
ModelRole.IsMember.int,
ModelRole.CanJoin.int,
ModelRole.Joined.int,
ModelRole.Spectated.int,
ModelRole.Muted.int,
ModelRole.MembersModel.int,
ModelRole.PendingRequestsToJoinModel.int,
Expand Down Expand Up @@ -379,6 +384,7 @@ QtObject:
"active": item.active,
"enabled": item.enabled,
"joined": item.joined,
"spectated": item.spectated,
"canJoin": item.canJoin,
"canManageUsers": item.canManageUsers,
"canRequestAccess": item.canRequestAccess,
Expand Down
2 changes: 2 additions & 0 deletions src/app_service/service/community/dto/community.nim
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type CommunityDto* = object
admin*: bool
verified*: bool
joined*: bool
spectated*: bool
requestedAccessAt: int64
name*: string
description*: string
Expand Down Expand Up @@ -124,6 +125,7 @@ proc toCommunityDto*(jsonObj: JsonNode): CommunityDto =
discard jsonObj.getProp("admin", result.admin)
discard jsonObj.getProp("verified", result.verified)
discard jsonObj.getProp("joined", result.joined)
discard jsonObj.getProp("spectated", result.spectated)
discard jsonObj.getProp("requestedAccessAt", result.requestedAccessAt)
discard jsonObj.getProp("name", result.name)
discard jsonObj.getProp("description", result.description)
Expand Down
53 changes: 30 additions & 23 deletions src/app_service/service/community/service.nim
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ type

# Signals which may be emitted by this service:
const SIGNAL_COMMUNITY_JOINED* = "communityJoined"
const SIGNAL_COMMUNITY_SPECTATED* = "communitySpectated"
const SIGNAL_COMMUNITY_MY_REQUEST_ADDED* = "communityMyRequestAdded"
const SIGNAL_COMMUNITY_LEFT* = "communityLeft"
const SIGNAL_COMMUNITY_CREATED* = "communityCreated"
Expand Down Expand Up @@ -254,22 +255,15 @@ QtObject:
var community = communities[0]

if(not self.allCommunities.hasKey(community.id)):
self.allCommunities[community.id] = community
self.events.emit(SIGNAL_COMMUNITY_ADDED, CommunityArgs(community: community))
# add or update community
self.allCommunities[community.id] = community
return

if(self.curatedCommunities.hasKey(community.id)):
self.curatedCommunities[community.id].available = true
self.curatedCommunities[community.id].community = community

if(not self.joinedCommunities.hasKey(community.id)):
if (community.joined and community.isMember):
self.joinedCommunities[community.id] = community
keepIf(self.myCommunityRequests, request => request.communityId != community.id)
self.events.emit(SIGNAL_COMMUNITY_JOINED, CommunityArgs(community: community, fromUserAction: false))
return

let prev_community = self.joinedCommunities[community.id]
let prev_community = self.allCommunities[community.id]

# If there's settings without `id` it means the original
# signal didn't include actual communitySettings, hence we
Expand Down Expand Up @@ -353,9 +347,18 @@ QtObject:
let data = CommunityChatArgs(chat: updatedChat)
self.events.emit(SIGNAL_COMMUNITY_CHANNEL_EDITED, data)

self.saveUpdatedJoinedCommunity(community)
self.allCommunities[community.id] = community
self.events.emit(SIGNAL_COMMUNITIES_UPDATE, CommunitiesArgs(communities: @[community]))

if(not self.joinedCommunities.hasKey(community.id)):
if (community.joined and community.isMember):
self.joinedCommunities[community.id] = community
self.events.emit(SIGNAL_COMMUNITY_JOINED, CommunityArgs(community: community, fromUserAction: false))
# remove my pending requests
keepIf(self.myCommunityRequests, request => request.communityId != community.id)
else:
self.saveUpdatedJoinedCommunity(community)

proc init*(self: Service) =
self.doConnect()
self.communityTags = self.loadCommunityTags();
Expand Down Expand Up @@ -512,6 +515,11 @@ QtObject:
return false
return self.allCommunities[communityId].joined and self.allCommunities[communityId].isMember

proc isUserSpectatingCommunity*(self: Service, communityId: string): bool =
if(not self.allCommunities.contains(communityId)):
return false
return self.allCommunities[communityId].spectated

proc userCanJoin*(self: Service, communityId: string): bool =
if(not self.allCommunities.contains(communityId)):
return false
Expand All @@ -528,39 +536,35 @@ QtObject:

return true

proc joinCommunity*(self: Service, communityId: string): string =
proc spectateCommunity*(self: Service, communityId: string): string =
result = ""
try:
if (not self.userCanJoin(communityId) or self.isUserMemberOfCommunity(communityId)):
if (self.isUserSpectatingCommunity(communityId) or self.isUserMemberOfCommunity(communityId)):
return

let response = status_go.joinCommunity(communityId)
let response = status_go.spectateCommunity(communityId)

if response.error != nil:
let error = Json.decode($response.error, RpcError)
raise newException(RpcException, "Error joining community: " & error.message)

if response.result == nil or response.result.kind == JNull:
error "error: ", procName="joinCommunity", errDesription = "result is nil"
error "error: ", procName="spectateCommunity", errDesription = "result is nil"
return

if not response.result.hasKey("communities") or response.result["communities"].kind != JArray or response.result["communities"].len == 0:
error "error: ", procName="joinCommunity", errDesription = "no 'communities' key in response"
error "error: ", procName="spectateCommunity", errDesription = "no 'communities' key in response"
return

if not response.result.hasKey("communitiesSettings") or response.result["communitiesSettings"].kind != JArray or response.result["communitiesSettings"].len == 0:
error "error: ", procName="joinCommunity", errDesription = "no 'communitiesSettings' key in response"
error "error: ", procName="spectateCommunity", errDesription = "no 'communitiesSettings' key in response"
return

if not self.processRequestsToJoinCommunity(response.result):
error "error: ", procName="joinCommunity", errDesription = "no 'requestsToJoinCommunity' key in response"

var updatedCommunity = response.result["communities"][0].toCommunityDto()
let communitySettings = response.result["communitiesSettings"][0].toCommunitySettingsDto()

updatedCommunity.settings = communitySettings
self.allCommunities[communityId] = updatedCommunity
self.joinedCommunities[communityId] = updatedCommunity

for k, chat in updatedCommunity.chats:
let fullChatId = communityId & chat.id
Expand All @@ -575,7 +579,7 @@ QtObject:
self.chatService.updateOrAddChat(chatDto)

self.events.emit(SIGNAL_COMMUNITIES_UPDATE, CommunitiesArgs(communities: @[updatedCommunity]))
self.events.emit(SIGNAL_COMMUNITY_JOINED, CommunityArgs(community: updatedCommunity, fromUserAction: true))
self.events.emit(SIGNAL_COMMUNITY_SPECTATED, CommunityArgs(community: updatedCommunity, fromUserAction: true))
except Exception as e:
error "Error joining the community", msg = e.msg
result = fmt"Error joining the community: {e.msg}"
Expand Down Expand Up @@ -645,6 +649,9 @@ QtObject:
self.joinedCommunities.del(communityId)
self.events.emit(SIGNAL_COMMUNITY_LEFT, CommunityIdArgs(communityId: communityId))

# remove related community requests
keepIf(self.myCommunityRequests, request => request.communityId != communityId)

except Exception as e:
error "Error leaving community", msg = e.msg, communityId

Expand Down Expand Up @@ -1150,7 +1157,7 @@ QtObject:
var pubKeys: seq[string] = @[]
for pubKey in pubKeysParsed:
pubKeys.add(pubKey.getStr)
# We no longer send invites, but merely share the community so
# We no longer send invites, but merely share the community so
# users can request access (with automatic acception)
let response = status_go.shareCommunityToUsers(communityId, pubKeys, inviteMessage)
discard self.chatService.processMessageUpdateAfterSend(response)
Expand Down
4 changes: 2 additions & 2 deletions src/backend/communities.nim
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ proc getCuratedCommunities*(): RpcResponse[JsonNode] {.raises: [Exception].} =
proc getAllCommunities*(): RpcResponse[JsonNode] {.raises: [Exception].} =
result = callPrivateRPC("communities".prefix)

proc joinCommunity*(communityId: string): RpcResponse[JsonNode] {.raises: [Exception].} =
result = callPrivateRPC("joinCommunity".prefix, %*[communityId])
proc spectateCommunity*(communityId: string): RpcResponse[JsonNode] {.raises: [Exception].} =
result = callPrivateRPC("spectateCommunity".prefix, %*[communityId])

proc requestToJoinCommunity*(communityId: string, ensName: string): RpcResponse[JsonNode] {.raises: [Exception].} =
result = callPrivateRPC("requestToJoinCommunity".prefix, %*[{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ StatusModal {
// text = qsTr("Pending")
// }
} else {
error = root.store.communitiesModuleInst.joinCommunity(root.communityId, root.store.userProfileInst.ensName)
error = root.store.communitiesModuleInst.requestToJoinCommunity(root.communityId, root.store.userProfileInst.ensName)
}

if (error) {
Expand Down
6 changes: 3 additions & 3 deletions ui/app/AppLayouts/Chat/stores/RootStore.qml
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,8 @@ QtObject {
chatCommunitySectionModule.reorderCommunityChat(categoryId, chatId, to)
}

function joinCommunity(id, ensName) {
return communitiesModuleInst.joinCommunity(id, ensName)
function spectateCommunity(id, ensName) {
return communitiesModuleInst.spectateCommunity(id, ensName)
}

function requestToJoinCommunity(id, ensName) {
Expand Down Expand Up @@ -428,7 +428,7 @@ QtObject {
const userCanJoin = userCanJoin(communityId)
// TODO find what to do when you can't join
if (userCanJoin) {
joinCommunity(communityId, userProfileInst.ensName)
requestToJoinCommunity(communityId, userProfileInst.ensName)
}
}
return result
Expand Down
10 changes: 10 additions & 0 deletions ui/app/AppLayouts/Chat/views/ChatContentView.qml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ ColumnLayout {

property bool stickersLoaded: false

// FIXME: this should be section data related only to that view, not the active one
readonly property var activeSectionData: rootStore.mainModuleInst ? rootStore.mainModuleInst.activeSection || {} : {}

// NOTE: Used this property change as it is the current way used for displaying new channel/chat data of content view.
// If in the future content is loaded dynamically, input focus should be activated when loaded / created content view.
onHeightChanged: {
Expand Down Expand Up @@ -187,6 +190,8 @@ ColumnLayout {
anchors.fill: parent
anchors.margins: Style.current.smallPadding

enabled: root.activeSectionData.joined

store: root.rootStore
usersStore: root.usersStore

Expand All @@ -204,6 +209,11 @@ ColumnLayout {
value: qsTr("This user has been blocked.")
}

Binding on chatInputPlaceholder {
when: !root.activeSectionData.joined
value: qsTr("You need to join this community to send messages")
}

onSendTransactionCommandButtonClicked: {
if(!chatContentModule) {
console.debug("error on sending transaction command - chat content module is not set")
Expand Down
Loading

0 comments on commit 966f6dc

Please sign in to comment.