refactor(community_tokens): only fetch holders when going to the page (#16308)

* refactor(community_tokens): only fetch holders when going to the page

Fixes #16307

Instead of fetching community token holders each time members change, we fetch when the page for the token is opened.
It shows a small loading text then the resulting holders.
If the list is already available (fetched previously, we show it directly).
There is still the timer to refresh the list if you stay on the page.

* add loading property to storybook
This commit is contained in:
Jonathan Rainville 2024-09-19 16:32:38 -04:00 committed by GitHub
parent ea8827ec97
commit 3f8dfee3cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 199 additions and 119 deletions

View File

@ -178,9 +178,3 @@ proc declineOwnership*(self: Controller, communityId: string) =
proc asyncGetOwnerTokenOwnerAddress*(self: Controller, chainId: int, contractAddress: string) = proc asyncGetOwnerTokenOwnerAddress*(self: Controller, chainId: int, contractAddress: string) =
self.communityTokensService.asyncGetOwnerTokenOwnerAddress(chainId, contractAddress) self.communityTokensService.asyncGetOwnerTokenOwnerAddress(chainId, contractAddress)
proc startTokenHoldersManagement*(self: Controller, chainId: int, contractAddress: string) =
self.communityTokensService.startTokenHoldersManagement(chainId, contractAddress)
proc stopTokenHoldersManagement*(self: Controller) =
self.communityTokensService.stopTokenHoldersManagement()

View File

@ -119,9 +119,3 @@ method onOwnerTokenOwnerAddress*(self: AccessInterface, chainId: int, contractAd
method asyncGetOwnerTokenDetails*(self: AccessInterface, communityId: string) {.base.} = method asyncGetOwnerTokenDetails*(self: AccessInterface, communityId: string) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method startTokenHoldersManagement*(self: AccessInterface, chainId: int, contractAddress: string) {.base.} =
raise newException(ValueError, "No implementation available")
method stopTokenHoldersManagement*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -20,6 +20,7 @@ type
burnState*: ContractTransactionStatus burnState*: ContractTransactionStatus
remoteDestructedAddresses*: seq[string] remoteDestructedAddresses*: seq[string]
tokenOwnersModel*: token_owners_model.TokenOwnersModel tokenOwnersModel*: token_owners_model.TokenOwnersModel
tokenHoldersLoading*: bool
proc initTokenItem*( proc initTokenItem*(
tokenDto: CommunityTokenDto, tokenDto: CommunityTokenDto,
@ -29,7 +30,7 @@ proc initTokenItem*(
burnState: ContractTransactionStatus, burnState: ContractTransactionStatus,
remoteDestructedAddresses: seq[string], remoteDestructedAddresses: seq[string],
remainingSupply: Uint256, remainingSupply: Uint256,
destructedAmount: Uint256 destructedAmount: Uint256,
): TokenItem = ): TokenItem =
result.tokenDto = tokenDto result.tokenDto = tokenDto
if network != nil: if network != nil:
@ -45,6 +46,7 @@ proc initTokenItem*(
# TODO: provide number of messages here # TODO: provide number of messages here
result = initTokenOwnersItem(owner.contactId, owner.name, owner.imageSource, 0, owner.collectibleOwner, remoteDestructedAddresses) result = initTokenOwnersItem(owner.contactId, owner.name, owner.imageSource, 0, owner.collectibleOwner, remoteDestructedAddresses)
)) ))
result.tokenHoldersLoading = false
proc `$`*(self: TokenItem): string = proc `$`*(self: TokenItem): string =
result = fmt"""TokenItem( result = fmt"""TokenItem(
@ -55,6 +57,7 @@ proc `$`*(self: TokenItem): string =
destructedAmount: {self.destructedAmount}, destructedAmount: {self.destructedAmount},
burnState: {self.burnState}, burnState: {self.burnState},
tokenOwnersModel: {self.tokenOwnersModel}, tokenOwnersModel: {self.tokenOwnersModel},
tokenHoldersLoading: {self.tokenHoldersLoading},
remoteDestructedAddresses: {self.remoteDestructedAddresses} remoteDestructedAddresses: {self.remoteDestructedAddresses}
]""" ]"""

View File

@ -26,6 +26,7 @@ type
ChainName ChainName
ChainIcon ChainIcon
TokenOwnersModel TokenOwnersModel
TokenHoldersLoading
AccountName AccountName
AccountAddress AccountAddress
RemainingSupply RemainingSupply
@ -50,77 +51,106 @@ QtObject:
new(result, delete) new(result, delete)
result.setup result.setup
proc updateDeployState*(self: TokenModel, chainId: int, contractAddress: string, deployState: DeployState) = proc getItemIndex(self: TokenModel, chainId: int, contractAddress: string): int =
for i in 0 ..< self.items.len: for i in 0 ..< self.items.len:
if((self.items[i].tokenDto.address == contractAddress) and (self.items[i].tokenDto.chainId == chainId)): if self.items[i].tokenDto.address == contractAddress and self.items[i].tokenDto.chainId == chainId:
self.items[i].tokenDto.deployState = deployState return i
let index = self.createIndex(i, 0, nil) return -1
proc updateDeployState*(self: TokenModel, chainId: int, contractAddress: string, deployState: DeployState) =
let itemIdx = self.getItemIndex(chainId, contractAddress)
if itemIdx == -1 and self.items[itemIdx].tokenDto.deployState == deployState:
return
self.items[itemIdx].tokenDto.deployState = deployState
let index = self.createIndex(itemIdx, 0, nil)
defer: index.delete defer: index.delete
self.dataChanged(index, index, @[ModelRole.DeployState.int]) self.dataChanged(index, index, @[ModelRole.DeployState.int])
return
proc updateAddress*(self: TokenModel, chainId: int, oldContractAddress: string, newContractAddress: string) = proc updateAddress*(self: TokenModel, chainId: int, oldContractAddress: string, newContractAddress: string) =
for i in 0 ..< self.items.len: let itemIdx = self.getItemIndex(chainId, oldContractAddress)
if((self.items[i].tokenDto.address == oldContractAddress) and (self.items[i].tokenDto.chainId == chainId)): if itemIdx == -1 and self.items[itemIdx].tokenDto.address == newContractAddress:
self.items[i].tokenDto.address = newContractAddress return
let index = self.createIndex(i, 0, nil)
self.items[itemIdx].tokenDto.address = newContractAddress
let index = self.createIndex(itemIdx, 0, nil)
defer: index.delete defer: index.delete
self.dataChanged(index, index, @[ModelRole.TokenAddress.int]) self.dataChanged(index, index, @[ModelRole.TokenAddress.int])
return
proc updateBurnState*(self: TokenModel, chainId: int, contractAddress: string, burnState: ContractTransactionStatus) = proc updateBurnState*(self: TokenModel, chainId: int, contractAddress: string, burnState: ContractTransactionStatus) =
for i in 0 ..< self.items.len: let itemIdx = self.getItemIndex(chainId, contractAddress)
if((self.items[i].tokenDto.address == contractAddress) and (self.items[i].tokenDto.chainId == chainId)): if itemIdx == -1 and self.items[itemIdx].burnState == burnState:
self.items[i].burnState = burnState return
let index = self.createIndex(i, 0, nil)
self.items[itemIdx].burnState = burnState
let index = self.createIndex(itemIdx, 0, nil)
defer: index.delete defer: index.delete
self.dataChanged(index, index, @[ModelRole.BurnState.int]) self.dataChanged(index, index, @[ModelRole.BurnState.int])
return
proc updateRemoteDestructedAddresses*(self: TokenModel, chainId: int, contractAddress: string, remoteDestructedAddresses: seq[string]) = proc updateRemoteDestructedAddresses*(self: TokenModel, chainId: int, contractAddress: string, remoteDestructedAddresses: seq[string]) =
for i in 0 ..< self.items.len: let itemIdx = self.getItemIndex(chainId, contractAddress)
if((self.items[i].tokenDto.address == contractAddress) and (self.items[i].tokenDto.chainId == chainId)): if itemIdx == -1 and self.items[itemIdx].remoteDestructedAddresses == remoteDestructedAddresses:
self.items[i].remoteDestructedAddresses = remoteDestructedAddresses return
let index = self.createIndex(i, 0, nil)
self.items[itemIdx].remoteDestructedAddresses = remoteDestructedAddresses
let index = self.createIndex(itemIdx, 0, nil)
defer: index.delete defer: index.delete
self.dataChanged(index, index, @[ModelRole.RemotelyDestructState.int]) self.dataChanged(index, index, @[ModelRole.RemotelyDestructState.int])
self.items[i].tokenOwnersModel.updateRemoteDestructState(remoteDestructedAddresses) self.items[itemIdx].tokenOwnersModel.updateRemoteDestructState(remoteDestructedAddresses)
self.dataChanged(index, index, @[ModelRole.TokenOwnersModel.int]) self.dataChanged(index, index, @[ModelRole.TokenOwnersModel.int])
return
proc updateSupply*(self: TokenModel, chainId: int, contractAddress: string, supply: Uint256, destructedAmount: Uint256) = proc updateSupply*(self: TokenModel, chainId: int, contractAddress: string, supply: Uint256, destructedAmount: Uint256) =
for i in 0 ..< self.items.len: let itemIdx = self.getItemIndex(chainId, contractAddress)
if((self.items[i].tokenDto.address == contractAddress) and (self.items[i].tokenDto.chainId == chainId)): if itemIdx == -1:
if self.items[i].tokenDto.supply != supply or self.items[i].destructedAmount != destructedAmount: return
self.items[i].tokenDto.supply = supply
self.items[i].destructedAmount = destructedAmount if self.items[itemIdx].tokenDto.supply != supply or self.items[itemIdx].destructedAmount != destructedAmount:
let index = self.createIndex(i, 0, nil) self.items[itemIdx].tokenDto.supply = supply
self.items[itemIdx].destructedAmount = destructedAmount
let index = self.createIndex(itemIdx, 0, nil)
defer: index.delete defer: index.delete
self.dataChanged(index, index, @[ModelRole.Supply.int]) self.dataChanged(index, index, @[ModelRole.Supply.int])
return
proc updateRemainingSupply*(self: TokenModel, chainId: int, contractAddress: string, remainingSupply: Uint256) = proc updateRemainingSupply*(self: TokenModel, chainId: int, contractAddress: string, remainingSupply: Uint256) =
for i in 0 ..< self.items.len: let itemIdx = self.getItemIndex(chainId, contractAddress)
if((self.items[i].tokenDto.address == contractAddress) and (self.items[i].tokenDto.chainId == chainId)): if itemIdx == -1 and self.items[itemIdx].remainingSupply == remainingSupply:
if self.items[i].remainingSupply != remainingSupply:
self.items[i].remainingSupply = remainingSupply
let index = self.createIndex(i, 0, nil)
defer: index.delete
self.dataChanged(index, index, @[ModelRole.RemainingSupply.int])
return return
proc setCommunityTokenOwners*(self: TokenModel, chainId: int, contractAddress: string, owners: seq[CommunityCollectibleOwner]) = self.items[itemIdx].remainingSupply = remainingSupply
for i in 0 ..< self.items.len: let index = self.createIndex(itemIdx, 0, nil)
if((self.items[i].tokenDto.address == contractAddress) and (self.items[i].tokenDto.chainId == chainId)):
self.items[i].tokenOwnersModel.setItems(owners.map(proc(owner: CommunityCollectibleOwner): TokenOwnersItem =
# TODO: provide number of messages here
result = initTokenOwnersItem(owner.contactId, owner.name, owner.imageSource, 0, owner.collectibleOwner, self.items[i].remoteDestructedAddresses)
))
let index = self.createIndex(i, 0, nil)
defer: index.delete defer: index.delete
self.dataChanged(index, index, @[ModelRole.TokenOwnersModel.int]) self.dataChanged(index, index, @[ModelRole.RemainingSupply.int])
proc hasTokenHolders*(self: TokenModel, chainId: int, contractAddress: string): bool =
let itemIdx = self.getItemIndex(chainId, contractAddress)
if itemIdx == -1:
return false
return self.items[itemIdx].tokenOwnersModel.count > 0
proc setCommunityTokenOwners*(self: TokenModel, chainId: int, contractAddress: string, owners: seq[CommunityCollectibleOwner]) =
let itemIdx = self.getItemIndex(chainId, contractAddress)
if itemIdx == -1:
return return
self.items[itemIdx].tokenHoldersLoading = false
self.items[itemIdx].tokenOwnersModel.setItems(owners.map(proc(owner: CommunityCollectibleOwner): TokenOwnersItem =
# TODO: provide number of messages here
result = initTokenOwnersItem(owner.contactId, owner.name, owner.imageSource, 0, owner.collectibleOwner, self.items[itemIdx].remoteDestructedAddresses)
))
let index = self.createIndex(itemIdx, 0, nil)
defer: index.delete
self.dataChanged(index, index, @[ModelRole.TokenOwnersModel.int, ModelRole.TokenHoldersLoading.int])
proc setCommunityTokenHoldersLoading*(self: TokenModel, chainId: int, contractAddress: string, value: bool) =
let itemIdx = self.getItemIndex(chainId, contractAddress)
if itemIdx == -1 and self.items[itemIdx].tokenHoldersLoading == value:
return
self.items[itemIdx].tokenHoldersLoading = value
let index = self.createIndex(itemIdx, 0, nil)
defer: index.delete
self.dataChanged(index, index, @[ModelRole.TokenHoldersLoading.int])
proc countChanged(self: TokenModel) {.signal.} proc countChanged(self: TokenModel) {.signal.}
proc setItems*(self: TokenModel, items: seq[TokenItem]) = proc setItems*(self: TokenModel, items: seq[TokenItem]) =
@ -129,7 +159,6 @@ QtObject:
self.endResetModel() self.endResetModel()
self.countChanged() self.countChanged()
proc getOwnerToken*(self: TokenModel): TokenItem = proc getOwnerToken*(self: TokenModel): TokenItem =
for i in 0 ..< self.items.len: for i in 0 ..< self.items.len:
if(self.items[i].tokenDto.privilegesLevel == PrivilegesLevel.Owner): if(self.items[i].tokenDto.privilegesLevel == PrivilegesLevel.Owner):
@ -144,17 +173,18 @@ QtObject:
self.endInsertRows() self.endInsertRows()
self.countChanged() self.countChanged()
proc removeItemByChainIdAndAddress*(self: TokenModel, chainId: int, address: string) = proc removeItemByChainIdAndAddress*(self: TokenModel, chainId: int, contractAddress: string) =
for i in 0 ..< self.items.len: let itemIdx = self.getItemIndex(chainId, contractAddress)
if((self.items[i].tokenDto.address == address) and (self.items[i].tokenDto.chainId == chainId)): if itemIdx == -1:
return
let parentModelIndex = newQModelIndex() let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete defer: parentModelIndex.delete
self.beginRemoveRows(parentModelIndex, i, i) self.beginRemoveRows(parentModelIndex, itemIdx, itemIdx)
self.items.delete(i) self.items.delete(itemIdx)
self.endRemoveRows() self.endRemoveRows()
self.countChanged() self.countChanged()
return
proc getCount*(self: TokenModel): int {.slot.} = proc getCount*(self: TokenModel): int {.slot.} =
self.items.len self.items.len
@ -184,6 +214,7 @@ QtObject:
ModelRole.ChainName.int:"chainName", ModelRole.ChainName.int:"chainName",
ModelRole.ChainIcon.int:"chainIcon", ModelRole.ChainIcon.int:"chainIcon",
ModelRole.TokenOwnersModel.int:"tokenOwnersModel", ModelRole.TokenOwnersModel.int:"tokenOwnersModel",
ModelRole.TokenHoldersLoading.int:"tokenHoldersLoading",
ModelRole.AccountName.int:"accountName", ModelRole.AccountName.int:"accountName",
ModelRole.AccountAddress.int:"accountAddress", ModelRole.AccountAddress.int:"accountAddress",
ModelRole.RemainingSupply.int:"remainingSupply", ModelRole.RemainingSupply.int:"remainingSupply",
@ -235,6 +266,8 @@ QtObject:
result = newQVariant(item.chainIcon) result = newQVariant(item.chainIcon)
of ModelRole.TokenOwnersModel: of ModelRole.TokenOwnersModel:
result = newQVariant(item.tokenOwnersModel) result = newQVariant(item.tokenOwnersModel)
of ModelRole.TokenHoldersLoading:
result = newQVariant(item.tokenHoldersLoading)
of ModelRole.AccountName: of ModelRole.AccountName:
result = newQVariant(item.accountName) result = newQVariant(item.accountName)
of ModelRole.AccountAddress: of ModelRole.AccountAddress:

View File

@ -385,9 +385,3 @@ method onOwnerTokenOwnerAddress*(self: Module, chainId: int, contractAddress: st
"contractAddress": contractAddress "contractAddress": contractAddress
} }
self.view.setOwnerTokenDetails($jsonObj) self.view.setOwnerTokenDetails($jsonObj)
method startTokenHoldersManagement*(self: Module, chainId: int, contractAddress: string) =
self.controller.startTokenHoldersManagement(chainId, contractAddress)
method stopTokenHoldersManagement*(self: Module) =
self.controller.stopTokenHoldersManagement()

View File

@ -145,9 +145,3 @@ QtObject:
QtProperty[string] ownerTokenDetails: QtProperty[string] ownerTokenDetails:
read = getOwnerTokenDetails read = getOwnerTokenDetails
notify = ownerTokenDetailsChanged notify = ownerTokenDetailsChanged
proc startTokenHoldersManagement*(self: View, chainId: int, contractAddress: string) {.slot.} =
self.communityTokensModule.startTokenHoldersManagement(chainId, contractAddress)
proc stopTokenHoldersManagement*(self: View) {.slot.} =
self.communityTokensModule.stopTokenHoldersManagement()

View File

@ -392,6 +392,10 @@ proc init*(self: Controller) =
let args = CommunityTokenOwnersArgs(e) let args = CommunityTokenOwnersArgs(e)
self.delegate.onCommunityTokenOwnersFetched(args.communityId, args.chainId, args.contractAddress, args.owners) self.delegate.onCommunityTokenOwnersFetched(args.communityId, args.chainId, args.contractAddress, args.owners)
self.events.on(SIGNAL_COMMUNITY_TOKEN_OWNERS_LOADING_FAILED) do(e: Args):
let args = CommunityTokenOwnersArgs(e)
self.delegate.errorLoadingTokenHolders(args.communityId, args.chainId, args.contractAddress, args.error)
self.events.on(SIGNAL_ACCEPT_REQUEST_TO_JOIN_LOADING) do(e: Args): self.events.on(SIGNAL_ACCEPT_REQUEST_TO_JOIN_LOADING) do(e: Args):
var args = CommunityMemberArgs(e) var args = CommunityMemberArgs(e)
self.delegate.onAcceptRequestToJoinLoading(args.communityId, args.pubKey) self.delegate.onAcceptRequestToJoinLoading(args.communityId, args.pubKey)
@ -412,10 +416,6 @@ proc init*(self: Controller) =
let args = CommunityMemberStatusUpdatedArgs(e) let args = CommunityMemberStatusUpdatedArgs(e)
self.delegate.onMembershipStateUpdated(args.communityId, args.memberPubkey, args.state) self.delegate.onMembershipStateUpdated(args.communityId, args.memberPubkey, args.state)
self.events.on(SIGNAL_COMMUNITY_MEMBERS_CHANGED) do(e: Args):
let args = CommunityMembersArgs(e)
self.communityTokensService.fetchCommunityTokenOwners(args.communityId)
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_FLOW_TERMINATED) do(e: Args): self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_FLOW_TERMINATED) do(e: Args):
let args = SharedKeycarModuleFlowTerminatedArgs(e) let args = SharedKeycarModuleFlowTerminatedArgs(e)
if args.uniqueIdentifier == UNIQUE_MAIN_MODULE_KEYCARD_SYNC_IDENTIFIER: if args.uniqueIdentifier == UNIQUE_MAIN_MODULE_KEYCARD_SYNC_IDENTIFIER:
@ -597,3 +597,9 @@ proc asyncGetRevealedAccountsForAllMembers*(self: Controller, communityId: strin
proc asyncReevaluateCommunityMembersPermissions*(self: Controller, communityId: string) = proc asyncReevaluateCommunityMembersPermissions*(self: Controller, communityId: string) =
self.communityService.asyncReevaluateCommunityMembersPermissions(communityId) self.communityService.asyncReevaluateCommunityMembersPermissions(communityId)
proc startTokenHoldersManagement*(self: Controller, chainId: int, contractAddress: string) =
self.communityTokensService.startTokenHoldersManagement(chainId, contractAddress)
proc stopTokenHoldersManagement*(self: Controller) =
self.communityTokensService.stopTokenHoldersManagement()

View File

@ -337,6 +337,9 @@ method onOwnerTokensDeploymentStarted*(self: AccessInterface, ownerToken: Commun
method onCommunityTokenOwnersFetched*(self: AccessInterface, communityId: string, chainId: int, contractAddress: string, owners: seq[CommunityCollectibleOwner]) {.base.} = method onCommunityTokenOwnersFetched*(self: AccessInterface, communityId: string, chainId: int, contractAddress: string, owners: seq[CommunityCollectibleOwner]) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method errorLoadingTokenHolders*(self: AccessInterface, communityId: string, chainId: int, contractAddress: string, error: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onCommunityTokenDeployStateChanged*(self: AccessInterface, communityId: string, chainId: int, contractAddress: string, deployState: DeployState) {.base.} = method onCommunityTokenDeployStateChanged*(self: AccessInterface, communityId: string, chainId: int, contractAddress: string, deployState: DeployState) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
@ -421,6 +424,12 @@ method openSectionChatAndMessage*(self: AccessInterface, sectionId: string, chat
method updateRequestToJoinState*(self: AccessInterface, sectionId: string, requestToJoinState: RequestToJoinState) {.base.} = method updateRequestToJoinState*(self: AccessInterface, sectionId: string, requestToJoinState: RequestToJoinState) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method startTokenHoldersManagement*(self: AccessInterface, communityId: string, chainId: int, contractAddress: string) {.base.} =
raise newException(ValueError, "No implementation available")
method stopTokenHoldersManagement*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
# This way (using concepts) is used only for the modules managed by AppController # This way (using concepts) is used only for the modules managed by AppController
type type
DelegateInterface* = concept c DelegateInterface* = concept c

View File

@ -1213,6 +1213,20 @@ method onCommunityTokenRemoved*[T](self: Module[T], communityId: string, chainId
if item.id != "": if item.id != "":
item.removeCommunityToken(chainId, address) item.removeCommunityToken(chainId, address)
method startTokenHoldersManagement*[T](self: Module[T], communityId: string, chainId: int, contractAddress: string) =
self.controller.startTokenHoldersManagement(chainId, contractAddress)
let item = self.view.model().getItemById(communityId)
if item.id != "" and not item.communityTokens.hasTokenHolders(chainId, contractAddress):
item.setCommunityTokenHoldersLoading(chainId, contractAddress, value = true)
method stopTokenHoldersManagement*[T](self: Module[T]) =
self.controller.stopTokenHoldersManagement()
method errorLoadingTokenHolders*[T](self: Module[T], communityId: string, chainId: int, contractAddress: string, error: string) =
let item = self.view.model().getItemById(communityId)
if item.id != "":
item.setCommunityTokenHoldersLoading(chainId, contractAddress, value = false)
method onCommunityTokenOwnersFetched*[T](self: Module[T], communityId: string, chainId: int, contractAddress: string, owners: seq[CommunityCollectibleOwner]) = method onCommunityTokenOwnersFetched*[T](self: Module[T], communityId: string, chainId: int, contractAddress: string, owners: seq[CommunityCollectibleOwner]) =
let item = self.view.model().getItemById(communityId) let item = self.view.model().getItemById(communityId)
if item.id != "": if item.id != "":

View File

@ -375,3 +375,9 @@ QtObject:
proc emitCommunityMemberStatusEphemeralNotification*(self:View, communityName: string, memberName: string, proc emitCommunityMemberStatusEphemeralNotification*(self:View, communityName: string, memberName: string,
membershipState: int) = membershipState: int) =
self.communityMemberStatusEphemeralNotification(communityName, memberName, membershipState) self.communityMemberStatusEphemeralNotification(communityName, memberName, membershipState)
proc startTokenHoldersManagement*(self: View, communityId: string, chainId: int, contractAddress: string) {.slot.} =
self.delegate.startTokenHoldersManagement(communityId, chainId, contractAddress)
proc stopTokenHoldersManagement*(self: View) {.slot.} =
self.delegate.stopTokenHoldersManagement()

View File

@ -373,6 +373,9 @@ proc updateRemoteDestructedAddresses*(self: SectionItem, chainId: int, contractA
proc setCommunityTokenOwners*(self: SectionItem, chainId: int, contractAddress: string, owners: seq[CommunityCollectibleOwner]) {.inline.} = proc setCommunityTokenOwners*(self: SectionItem, chainId: int, contractAddress: string, owners: seq[CommunityCollectibleOwner]) {.inline.} =
self.communityTokensModel.setCommunityTokenOwners(chainId, contractAddress, owners) self.communityTokensModel.setCommunityTokenOwners(chainId, contractAddress, owners)
proc setCommunityTokenHoldersLoading*(self: SectionItem, chainId: int, contractAddress: string, value: bool) {.inline.} =
self.communityTokensModel.setCommunityTokenHoldersLoading(chainId, contractAddress, value)
proc communityTokens*(self: SectionItem): community_tokens_model.TokenModel {.inline.} = proc communityTokens*(self: SectionItem): community_tokens_model.TokenModel {.inline.} =
self.communityTokensModel self.communityTokensModel

View File

@ -156,6 +156,7 @@ type
contractAddress*: string contractAddress*: string
chainId*: int chainId*: int
owners*: seq[CommunityCollectibleOwner] owners*: seq[CommunityCollectibleOwner]
error*: string
type type
CommunityTokensDetailsArgs* = ref object of Args CommunityTokensDetailsArgs* = ref object of Args
@ -259,6 +260,7 @@ const SIGNAL_COMPUTE_SELF_DESTRUCT_FEE* = "communityTokens-computeSelfDestructFe
const SIGNAL_COMPUTE_BURN_FEE* = "communityTokens-computeBurnFee" const SIGNAL_COMPUTE_BURN_FEE* = "communityTokens-computeBurnFee"
const SIGNAL_COMPUTE_AIRDROP_FEE* = "communityTokens-computeAirdropFee" const SIGNAL_COMPUTE_AIRDROP_FEE* = "communityTokens-computeAirdropFee"
const SIGNAL_COMMUNITY_TOKEN_OWNERS_FETCHED* = "communityTokens-communityTokenOwnersFetched" const SIGNAL_COMMUNITY_TOKEN_OWNERS_FETCHED* = "communityTokens-communityTokenOwnersFetched"
const SIGNAL_COMMUNITY_TOKEN_OWNERS_LOADING_FAILED* = "communityTokens-communityTokenOwnersLoadingFailed"
const SIGNAL_REMOTE_DESTRUCT_STATUS* = "communityTokens-communityTokenRemoteDestructStatus" const SIGNAL_REMOTE_DESTRUCT_STATUS* = "communityTokens-communityTokenRemoteDestructStatus"
const SIGNAL_BURN_STATUS* = "communityTokens-communityTokenBurnStatus" const SIGNAL_BURN_STATUS* = "communityTokens-communityTokenBurnStatus"
const SIGNAL_BURN_ACTION_RECEIVED* = "communityTokens-communityTokenBurnActionReceived" const SIGNAL_BURN_ACTION_RECEIVED* = "communityTokens-communityTokenBurnActionReceived"
@ -1290,17 +1292,28 @@ QtObject:
proc onCommunityTokenOwnersFetched*(self:Service, response: string) {.slot.} = proc onCommunityTokenOwnersFetched*(self:Service, response: string) {.slot.} =
let responseJson = response.parseJson() let responseJson = response.parseJson()
if responseJson{"error"}.kind != JNull and responseJson{"error"}.getStr != "":
let errorMessage = responseJson["error"].getStr
error "Can't fetch community token owners", chainId=responseJson{"chainId"}, contractAddress=responseJson{"contractAddress"}, errorMsg=errorMessage
return
let chainId = responseJson{"chainId"}.getInt let chainId = responseJson{"chainId"}.getInt
let contractAddress = responseJson{"contractAddress"}.getStr let contractAddress = responseJson{"contractAddress"}.getStr
let communityId = responseJson{"communityId"}.getStr let communityId = responseJson{"communityId"}.getStr
try:
if responseJson{"error"}.kind != JNull and responseJson{"error"}.getStr != "":
raise newException(ValueError, responseJson["error"].getStr)
let communityTokenOwners = toCommunityCollectibleOwners(responseJson{"result"}) let communityTokenOwners = toCommunityCollectibleOwners(responseJson{"result"})
self.tokenOwnersCache[(chainId, contractAddress)] = communityTokenOwners self.tokenOwnersCache[(chainId, contractAddress)] = communityTokenOwners
let data = CommunityTokenOwnersArgs(chainId: chainId, contractAddress: contractAddress, communityId: communityId, owners: communityTokenOwners) let data = CommunityTokenOwnersArgs(chainId: chainId, contractAddress: contractAddress, communityId: communityId, owners: communityTokenOwners)
self.events.emit(SIGNAL_COMMUNITY_TOKEN_OWNERS_FETCHED, data) self.events.emit(SIGNAL_COMMUNITY_TOKEN_OWNERS_FETCHED, data)
except Exception as e:
error "Can't fetch community token owners", chainId=responseJson{"chainId"}, contractAddress=responseJson{"contractAddress"}, errorMsg=e.msg
let data = CommunityTokenOwnersArgs(
chainId: chainId,
contractAddress: contractAddress,
communityId: communityId,
error: e.msg,
)
self.events.emit(SIGNAL_COMMUNITY_TOKEN_OWNERS_LOADING_FAILED, data)
# restart token holders timer # restart token holders timer
self.restartTokenHoldersTimer(chainId, contractAddress) self.restartTokenHoldersTimer(chainId, contractAddress)
@ -1313,14 +1326,6 @@ QtObject:
let community = self.communityService.getCommunityById(communityId) let community = self.communityService.getCommunityById(communityId)
return community.isPrivilegedUser() return community.isPrivilegedUser()
# used when community members changed
proc fetchCommunityTokenOwners*(self: Service, communityId: string) =
if not self.iAmCommunityPrivilegedUser(communityId):
return
let tokens = self.getCommunityTokens(communityId)
for token in tokens:
self.fetchCommunityOwners(token)
proc getOwnerToken*(self: Service, communityId: string): CommunityTokenDto = proc getOwnerToken*(self: Service, communityId: string): CommunityTokenDto =
let communityTokens = self.getCommunityTokens(communityId) let communityTokens = self.getCommunityTokens(communityId)
for token in communityTokens: for token in communityTokens:

View File

@ -41,6 +41,7 @@ SplitView {
chainName: networksGroup.checkedButton.text chainName: networksGroup.checkedButton.text
chainIcon: networksGroup.checkedButton.chainIcon chainIcon: networksGroup.checkedButton.chainIcon
accountName: "helloworld" accountName: "helloworld"
tokenHoldersLoading: loadingTokenHolders.checked
// collectible-specific properties // collectible-specific properties
remotelyDestructState: remotelyDestructStateBox.checked remotelyDestructState: remotelyDestructStateBox.checked
@ -141,6 +142,16 @@ SplitView {
} }
} }
GroupBox {
Layout.fillWidth: true
CheckBox {
id: loadingTokenHolders
text: "Loading Token Holders"
checked: false
}
}
GroupBox { GroupBox {
Layout.fillWidth: true Layout.fillWidth: true

View File

@ -51,4 +51,7 @@ QtObject {
// Asset-specific properties: // Asset-specific properties:
property int decimals: 2 property int decimals: 2
// Loading indicators
property bool tokenHoldersLoading: false
} }

View File

@ -1005,6 +1005,7 @@ StackView {
token.accountAddress: model.accountAddress token.accountAddress: model.accountAddress
token.multiplierIndex: model.multiplierIndex token.multiplierIndex: model.multiplierIndex
token.tokenAddress: model.tokenAddress token.tokenAddress: model.tokenAddress
token.tokenHoldersLoading: model.tokenHoldersLoading
} }
onCountChanged: { onCountChanged: {

View File

@ -375,7 +375,7 @@ StatusSectionLayout {
onRegisterBurnTokenFeesSubscriber: d.feesBroker.registerBurnFeesSubscriber(feeSubscriber) onRegisterBurnTokenFeesSubscriber: d.feesBroker.registerBurnFeesSubscriber(feeSubscriber)
onStartTokenHoldersManagement: communityTokensStore.startTokenHoldersManagement(chainId, address) onStartTokenHoldersManagement: communityTokensStore.startTokenHoldersManagement(root.community.id, chainId, address)
onStopTokenHoldersManagement: communityTokensStore.stopTokenHoldersManagement() onStopTokenHoldersManagement: communityTokensStore.stopTokenHoldersManagement()

View File

@ -96,6 +96,7 @@ StatusScrollView {
id: d id: d
readonly property int iconSize: 20 readonly property int iconSize: 20
property bool loadingTokenHolders: false
readonly property var renamedTokenOwnersModel: RolesRenamingModel { readonly property var renamedTokenOwnersModel: RolesRenamingModel {
sourceModel: root.tokenOwnersModel sourceModel: root.tokenOwnersModel
@ -172,7 +173,7 @@ StatusScrollView {
wrapMode: Text.Wrap wrapMode: Text.Wrap
font.pixelSize: Style.current.primaryTextFontSize font.pixelSize: Style.current.primaryTextFontSize
color: Theme.palette.baseColor1 color: Theme.palette.baseColor1
text: qsTr("Review token details before minting it as they cant be edited later") text: qsTr("Review token details before minting it as they can't be edited later")
} }
} }
@ -228,7 +229,15 @@ StatusScrollView {
id: tokenHolderLoader id: tokenHolderLoader
visible: !root.preview && root.deploymentCompleted visible: !root.preview && root.deploymentCompleted
sourceComponent: isOwnerTokenItem ? tokenHolderContact : sortableTokenHolderPanel sourceComponent: isOwnerTokenItem ? tokenHolderContact : (token.tokenHoldersLoading ? tokenHoldersLoadingComponent : sortableTokenHolderPanel)
}
Component {
id: tokenHoldersLoadingComponent
StatusBaseText {
text: qsTr("Loading token holders...")
}
} }
Component { Component {

View File

@ -7,6 +7,7 @@ QtObject {
id: root id: root
property var communityTokensModuleInst: communityTokensModule ?? null property var communityTokensModuleInst: communityTokensModule ?? null
property var mainModuleInst: mainModule ?? null
// Network selection properties: // Network selection properties:
property var flatNetworks: networksModule.flatNetworks property var flatNetworks: networksModule.flatNetworks
@ -231,11 +232,11 @@ QtObject {
communityTokensModuleInst.asyncGetOwnerTokenDetails(communityId) communityTokensModuleInst.asyncGetOwnerTokenDetails(communityId)
} }
function startTokenHoldersManagement(chainId, contractAddress) { function startTokenHoldersManagement(communityId, chainId, contractAddress) {
communityTokensModuleInst.startTokenHoldersManagement(chainId, contractAddress) mainModuleInst.startTokenHoldersManagement(communityId, chainId, contractAddress)
} }
function stopTokenHoldersManagement() { function stopTokenHoldersManagement() {
communityTokensModuleInst.stopTokenHoldersManagement() mainModuleInst.stopTokenHoldersManagement()
} }
} }