From 24d26cc0387f28289505adba7704cb06f9eea6e6 Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Mon, 7 Aug 2023 15:27:56 -0400 Subject: [PATCH] feat(airdrop): get revealed accounts using new API instead of desc Fixes #11817 Instead of getting revealed accounts from the community description (it's no longer available), uses the new `getRevealedAccountsForAllMembers` API. Uses it async so that we do not slow the start process. The model is updated correctly when we finish loading them. --- .../chat_content/users/module.nim | 1 - src/app/modules/main/controller.nim | 9 ++++- src/app/modules/main/io_interface.nim | 3 ++ src/app/modules/main/module.nim | 18 ++++++++-- .../modules/shared_models/member_model.nim | 32 ++++++++++++----- .../modules/shared_models/section_model.nim | 11 +++++- src/app_service/service/chat/dto/chat.nim | 22 ------------ .../service/community/async_tasks.nim | 19 +++++++++++ .../service/community/dto/community.nim | 30 ++++++++++++++++ src/app_service/service/community/service.nim | 34 +++++++++++++++++++ src/backend/communities.nim | 11 ++++++ .../views/CommunitySettingsView.qml | 9 +---- vendor/status-go | 2 +- 13 files changed, 156 insertions(+), 45 deletions(-) diff --git a/src/app/modules/main/chat_section/chat_content/users/module.nim b/src/app/modules/main/chat_section/chat_content/users/module.nim index f604535938..2f46a6d521 100644 --- a/src/app/modules/main/chat_section/chat_content/users/module.nim +++ b/src/app/modules/main/chat_section/chat_content/users/module.nim @@ -133,7 +133,6 @@ proc addChatMember(self: Module, member: ChatMember) = memberRole = member.role, joined = member.joined, isUntrustworthy = contactDetails.dto.trustStatus == TrustStatus.Untrustworthy, - airdropAddress = member.airdropAccount.address, )) method onChatMembersAdded*(self: Module, ids: seq[string]) = diff --git a/src/app/modules/main/controller.nim b/src/app/modules/main/controller.nim index 8736527648..c858f2d052 100644 --- a/src/app/modules/main/controller.nim +++ b/src/app/modules/main/controller.nim @@ -1,4 +1,4 @@ -import chronicles, stint +import chronicles, stint, tables import ../../global/app_sections_config as conf import ../../global/global_singleton import ../../global/app_signals @@ -248,6 +248,10 @@ proc init*(self: Controller) = let args = CommunityArgs(e) self.delegate.communityEdited(args.community) + self.events.on(SIGNAL_COMMUNITY_MEMBERS_REVEALED_ACCOUNTS_LOADED) do(e:Args): + let args = CommunityMembersRevealedAccountsArgs(e) + self.delegate.communityMembersRevealedAccountsLoaded(args.communityId, args.membersRevealedAccounts) + self.events.on(SIGNAL_COMMUNITY_PRIVATE_KEY_REMOVED) do(e:Args): let args = CommunityArgs(e) self.delegate.communityEdited(args.community) @@ -524,3 +528,6 @@ proc getColorHash*(self: Controller, pubkey: string): ColorHashDto = proc getColorId*(self: Controller, pubkey: string): int = procs_from_visual_identity_service.colorIdOf(pubkey) + +proc asyncGetRevealedAccountsForAllMembers*(self: Controller, communityId: string) = + self.communityService.asyncGetRevealedAccountsForAllMembers(communityId) diff --git a/src/app/modules/main/io_interface.nim b/src/app/modules/main/io_interface.nim index 6ca10fb1e0..bffd56ab0c 100644 --- a/src/app/modules/main/io_interface.nim +++ b/src/app/modules/main/io_interface.nim @@ -330,6 +330,9 @@ method windowActivated*(self: AccessInterface) {.base.} = method windowDeactivated*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") +method communityMembersRevealedAccountsLoaded*(self: AccessInterface, communityId: string, membersRevealedAccounts: MembersRevealedAccounts) {.base.} = + raise newException(ValueError, "No implementation available") + # This way (using concepts) is used only for the modules managed by AppController type DelegateInterface* = concept c diff --git a/src/app/modules/main/module.nim b/src/app/modules/main/module.nim index ac132b3faa..593dbed466 100644 --- a/src/app/modules/main/module.nim +++ b/src/app/modules/main/module.nim @@ -262,6 +262,11 @@ proc createChannelGroupItem[T](self: Module[T], channelGroup: ChannelGroupDto): communityTokensItems = communityTokens.map(proc(tokenDto: CommunityTokenDto): TokenItem = result = self.createTokenItem(tokenDto) ) + # Get community members' revealed accounts + # We will update the model later when we finish loading the accounts + # TODO add TokenMaster here too when it's available + if communityDetails.memberRole == MemberRole.Owner: + self.controller.asyncGetRevealedAccountsForAllMembers(channelGroup.id) let unviewedCount = channelGroup.unviewedMessagesCount let notificationsCount = channelGroup.unviewedMentionsCount @@ -312,9 +317,8 @@ proc createChannelGroupItem[T](self: Module[T], channelGroup: ChannelGroupDto): isContact = contactDetails.dto.isContact, 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( x.id, @@ -1305,3 +1309,13 @@ method windowActivated*[T](self: Module[T]) = method windowDeactivated*[T](self: Module[T]) = self.controller.speedupArchivesImport() +method communityMembersRevealedAccountsLoaded*[T](self: Module[T], communityId: string, membersRevealedAccounts: MembersRevealedAccounts) = + var communityMembersAirdropAddress: Table[string, string] + for pubkey, revealedAccounts in membersRevealedAccounts.pairs: + for revealedAccount in revealedAccounts: + if revealedAccount.isAirdropAddress: + communityMembersAirdropAddress[pubkey] = revealedAccount.address + discard + + self.view.model.setMembersAirdropAddress(communityId, communityMembersAirdropAddress) + diff --git a/src/app/modules/shared_models/member_model.nim b/src/app/modules/shared_models/member_model.nim index ecd908805f..4dc7e0cd0a 100644 --- a/src/app/modules/shared_models/member_model.nim +++ b/src/app/modules/shared_models/member_model.nim @@ -298,19 +298,33 @@ QtObject: ModelRole.IsUntrustworthy.int, ]) - proc setOnlineStatus*(self: Model, pubKey: string, - onlineStatus: OnlineStatus) = - let ind = self.findIndexForMember(pubKey) - if(ind == -1): + proc setOnlineStatus*(self: Model, pubKey: string, onlineStatus: OnlineStatus) = + let idx = self.findIndexForMember(pubKey) + if(idx == -1): return - if(self.items[ind].onlineStatus == onlineStatus): + if(self.items[idx].onlineStatus == onlineStatus): return - var item = self.items[ind] - item.onlineStatus = onlineStatus - self.removeItemWithIndex(ind) - self.addItem(item) + self.items[idx].onlineStatus = onlineStatus + let index = self.createIndex(idx, 0, nil) + self.dataChanged(index, index, @[ + ModelRole.OnlineStatus.int + ]) + + proc setAirdropAddress*(self: Model, pubKey: string, airdropAddress: string) = + let idx = self.findIndexForMember(pubKey) + if(idx == -1): + return + + if(self.items[idx].airdropAddress == airdropAddress): + return + + self.items[idx].airdropAddress = airdropAddress + let index = self.createIndex(idx, 0, nil) + self.dataChanged(index, index, @[ + ModelRole.AirdropAddress.int + ]) # TODO: rename me to removeItemByPubkey proc removeItemById*(self: Model, pubKey: string) = diff --git a/src/app/modules/shared_models/section_model.nim b/src/app/modules/shared_models/section_model.nim index c23a4483ba..1a64eb215d 100644 --- a/src/app/modules/shared_models/section_model.nim +++ b/src/app/modules/shared_models/section_model.nim @@ -445,4 +445,13 @@ QtObject: "nbMembers": item.members.getCount(), "encrypted": item.encrypted, } - return $jsonObj \ No newline at end of file + return $jsonObj + + proc setMembersAirdropAddress*(self: SectionModel, id: string, communityMembersAirdropAddress: Table[string, string]) = + let index = self.getItemIndex(id) + if (index == -1): + return + + for pubkey, revealedAccounts in communityMembersAirdropAddress.pairs: + self.items[index].members.setAirdropAddress(pubkey, revealedAccounts) + diff --git a/src/app_service/service/chat/dto/chat.nim b/src/app_service/service/chat/dto/chat.nim index 4d34945496..4629fa1f90 100644 --- a/src/app_service/service/chat/dto/chat.nim +++ b/src/app_service/service/chat/dto/chat.nim @@ -35,16 +35,10 @@ type large*: string banner*: string -type RevealedAccount* = object - address*: string - chainIds*: seq[int] - isAirdropAddress*: bool - type ChatMember* = object id*: string joined*: bool role*: MemberRole - airdropAccount*: RevealedAccount type CheckPermissionsResultDto* = object criteria*: seq[bool] @@ -242,22 +236,6 @@ proc toChannelMember*(jsonObj: JsonNode, memberId: string, joined: bool): ChatMe if(jsonObj.getProp("roles", rolesObj)): for roleObj in rolesObj: roles.add(roleObj.getInt) - - var revealedAccountsObj: JsonNode - if jsonObj.getProp("revealed_accounts", revealedAccountsObj): - for revealedAccountObj in revealedAccountsObj: - if revealedAccountObj{"isAirdropAddress"}.getBool: - var chainIdsObj: JsonNode - var chainIds: seq[int] = @[] - if revealedAccountObj.getProp("chain_ids", chainIdsObj): - for chainIdObj in chainIdsObj: - chainIds.add(chainIdObj.getInt) - - result.airdropAccount = RevealedAccount( - address: revealedAccountObj["address"].getStr, - chainIds: chainIds, - isAirdropAddress: true, - ) result.role = MemberRole.None if roles.contains(MemberRole.Owner.int): diff --git a/src/app_service/service/community/async_tasks.nim b/src/app_service/service/community/async_tasks.nim index 29ea8c75c6..a1939cafc4 100644 --- a/src/app_service/service/community/async_tasks.nim +++ b/src/app_service/service/community/async_tasks.nim @@ -185,3 +185,22 @@ const asyncImportCommunityTask: Task = proc(argEncoded: string) {.gcsafe, nimcal arg.finish(%* { "error": e.msg, }) + +type + AsyncGetRevealedAccountsForAllMembersArg = ref object of QObjectTaskArg + communityId: string + +const asyncGetRevealedAccountsForAllMembersTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let arg = decode[AsyncGetRevealedAccountsForAllMembersArg](argEncoded) + try: + let response = status_go.getRevealedAccountsForAllMembers(arg.communityId) + arg.finish(%* { + "communityId": arg.communityId, + "response": response, + "error": "", + }) + except Exception as e: + arg.finish(%* { + "communityId": arg.communityId, + "error": e.msg, + }) diff --git a/src/app_service/service/community/dto/community.nim b/src/app_service/service/community/dto/community.nim index bdc7db3289..09891aef47 100644 --- a/src/app_service/service/community/dto/community.nim +++ b/src/app_service/service/community/dto/community.nim @@ -106,6 +106,13 @@ type CommunityMetricsDto* = object metricsType*: CommunityMetricsType intervals*: seq[MetricsIntervalDto] +type RevealedAccount* = object + address*: string + chainIds*: seq[int] + isAirdropAddress*: bool + +type MembersRevealedAccounts* = Table[string, seq[RevealedAccount]] + type CommunityDto* = object id*: string memberRole*: MemberRole @@ -524,3 +531,26 @@ proc parseDiscordChannels*(response: JsonNode): seq[DiscordChannelDto] = if (response["discordChannels"].kind == JArray): for channel in response["discordChannels"].items(): result.add(channel.toDiscordChannelDto()) + +proc toRevealedAccount*(revealedAccountObj: JsonNode): RevealedAccount = + var chainIdsObj: JsonNode + var chainIds: seq[int] = @[] + if revealedAccountObj.getProp("chain_ids", chainIdsObj): + for chainIdObj in chainIdsObj: + chainIds.add(chainIdObj.getInt) + + result = RevealedAccount( + address: revealedAccountObj["address"].getStr, + chainIds: chainIds, + isAirdropAddress: revealedAccountObj{"isAirdropAddress"}.getBool, + ) + +proc toRevealedAccounts*(revealedAccountsObj: JsonNode): seq[RevealedAccount] = + result = @[] + for revealedAccountObj in revealedAccountsObj: + result.add(revealedAccountObj.toRevealedAccount()) + +proc toMembersRevealedAccounts*(membersRevealedAccountsObj: JsonNode): MembersRevealedAccounts = + result = initTable[string, seq[RevealedAccount]]() + for (pubkey, revealedAccountsObj) in membersRevealedAccountsObj.pairs: + result[pubkey] = revealedAccountsObj.toRevealedAccounts() \ No newline at end of file diff --git a/src/app_service/service/community/service.nim b/src/app_service/service/community/service.nim index 9285849dd2..5a78f44808 100644 --- a/src/app_service/service/community/service.nim +++ b/src/app_service/service/community/service.nim @@ -125,6 +125,10 @@ type communityId*: string metricsType*: CommunityMetricsType + CommunityMembersRevealedAccountsArgs* = ref object of Args + communityId*: string + membersRevealedAccounts*: MembersRevealedAccounts + # Signals which may be emitted by this service: const SIGNAL_COMMUNITY_DATA_LOADED* = "communityDataLoaded" const SIGNAL_COMMUNITY_JOINED* = "communityJoined" @@ -133,6 +137,7 @@ const SIGNAL_COMMUNITY_MY_REQUEST_ADDED* = "communityMyRequestAdded" const SIGNAL_COMMUNITY_MY_REQUEST_FAILED* = "communityMyRequestFailed" const SIGNAL_COMMUNITY_EDIT_SHARED_ADDRESSES_SUCCEEDED* = "communityEditSharedAddressesSucceded" const SIGNAL_COMMUNITY_EDIT_SHARED_ADDRESSES_FAILED* = "communityEditSharedAddressesFailed" +const SIGNAL_COMMUNITY_MEMBERS_REVEALED_ACCOUNTS_LOADED* = "communityMembersRevealedAccountsLoaded" const SIGNAL_COMMUNITY_LEFT* = "communityLeft" const SIGNAL_COMMUNITY_CREATED* = "communityCreated" const SIGNAL_COMMUNITY_ADDED* = "communityAdded" @@ -2011,3 +2016,32 @@ QtObject: result = response.result.getStr except Exception as e: error "error while getting community public key", msg = e.msg + + proc asyncGetRevealedAccountsForAllMembers*(self: Service, communityId: string) = + let arg = AsyncGetRevealedAccountsForAllMembersArg( + tptr: cast[ByteAddress](asyncGetRevealedAccountsForAllMembersTask), + vptr: cast[ByteAddress](self.vptr), + slot: "onAsyncGetRevealedAccountsForAllMembersCompleted", + communityId: communityId, + ) + self.threadpool.start(arg) + + proc onAsyncGetRevealedAccountsForAllMembersCompleted*(self: Service, response: string) {.slot.} = + try: + let rpcResponseObj = response.parseJson + + if rpcResponseObj{"error"}.kind != JNull and rpcResponseObj{"error"}.getStr != "": + raise newException(RpcException, rpcResponseObj["error"].getStr) + + if rpcResponseObj["response"]{"error"}.kind != JNull: + let error = Json.decode(rpcResponseObj["response"]["error"].getStr, RpcError) + raise newException(RpcException, error.message) + + let revealedAccounts = rpcResponseObj["response"]["result"].toMembersRevealedAccounts + + self.events.emit(SIGNAL_COMMUNITY_MEMBERS_REVEALED_ACCOUNTS_LOADED, CommunityMembersRevealedAccountsArgs( + communityId: rpcResponseObj["communityId"].getStr, + membersRevealedAccounts: revealedAccounts + )) + except Exception as e: + error "error while getting the community members' revealed addressesses", msg = e.msg diff --git a/src/backend/communities.nim b/src/backend/communities.nim index c6bba721c5..8150884185 100644 --- a/src/backend/communities.nim +++ b/src/backend/communities.nim @@ -61,6 +61,17 @@ proc editSharedAddresses*( "airdropAddress": airdropAddress, }]) +proc getRevealedAccountsForMember*( + communityId: string, + memberPubkey: string, + ): RpcResponse[JsonNode] {.raises: [Exception].} = + result = callPrivateRPC("getRevealedAccounts".prefix, %*[communityId, memberPubkey]) + +proc getRevealedAccountsForAllMembers*( + communityId: string, + ): RpcResponse[JsonNode] {.raises: [Exception].} = + result = callPrivateRPC("getRevealedAccountsForAllMembers".prefix, %*[communityId]) + proc checkPermissionsToJoinCommunity*(communityId: string): RpcResponse[JsonNode] {.raises: [Exception].} = result = callPrivateRPC("checkPermissionsToJoinCommunity".prefix, %*[{ "communityId": communityId diff --git a/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml b/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml index 8ff9c5f131..89a7ea3045 100644 --- a/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml +++ b/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml @@ -504,14 +504,7 @@ StatusSectionLayout { assetsModel: assetsModelLoader.item collectiblesModel: collectiblesModelLoader.item - membersModel: { - const chatContentModule = root.rootStore.currentChatContentModule() - if (!chatContentModule || !chatContentModule.usersModule) { - // New communities have no chats, so no chatContentModule - return null - } - return chatContentModule.usersModule.model - } + membersModel: community.members accountsModel: SortFilterProxyModel { sourceModel: root.rootStore.accounts diff --git a/vendor/status-go b/vendor/status-go index a63e417f9f..b4b0d26aa4 160000 --- a/vendor/status-go +++ b/vendor/status-go @@ -1 +1 @@ -Subproject commit a63e417f9f6c779300f35b51998770742f5e363f +Subproject commit b4b0d26aa4c3f11ee66e3aeb404834cd8c5e48c8