mirror of
https://github.com/status-im/status-desktop.git
synced 2025-02-23 12:08:53 +00:00
Fix some of the freezes experienced by the admins when the community updates (#16384)
* fix: force focus on search inputs in permissions and sort members * fix(admin): fix freezes when community gets updated * fix(airdrop): use FastExpressionFilter to speed up Iterates #16043 When the community gets updated by any means, we reconstruct the section item, which is already bad, but we also re-fetch all tokens and all shared addresses, which in turn re-updates models for no reason. Instead, I make sure to only fetch those on first section build, then, I get the new shared addresses when members join using the request to join response that comes from status-go
This commit is contained in:
parent
70fccb3835
commit
0bb2bc0e03
@ -309,6 +309,10 @@ proc init*(self: Controller) =
|
||||
var args = CommunityRequestArgs(e)
|
||||
self.delegate.newCommunityMembershipRequestReceived(args.communityRequest)
|
||||
|
||||
self.events.on(SIGNAL_NEW_REQUEST_TO_JOIN_COMMUNITY_ACCEPTED) do(e: Args):
|
||||
var args = CommunityRequestArgs(e)
|
||||
self.delegate.communityMemberRevealedAccountsAdded(args.communityRequest)
|
||||
|
||||
self.events.on(SIGNAL_NETWORK_CONNECTED) do(e: Args):
|
||||
self.delegate.onNetworkConnected()
|
||||
|
||||
@ -595,6 +599,9 @@ proc getColorId*(self: Controller, pubkey: string): int =
|
||||
proc asyncGetRevealedAccountsForAllMembers*(self: Controller, communityId: string) =
|
||||
self.communityService.asyncGetRevealedAccountsForAllMembers(communityId)
|
||||
|
||||
proc asyncGetRevealedAccountsForMember*(self: Controller, communityId, memberPubkey: string) =
|
||||
self.communityService.asyncGetRevealedAccountsForMember(communityId, memberPubkey)
|
||||
|
||||
proc asyncReevaluateCommunityMembersPermissions*(self: Controller, communityId: string) =
|
||||
self.communityService.asyncReevaluateCommunityMembersPermissions(communityId)
|
||||
|
||||
|
@ -391,6 +391,9 @@ method windowDeactivated*(self: AccessInterface) {.base.} =
|
||||
method communityMembersRevealedAccountsLoaded*(self: AccessInterface, communityId: string, membersRevealedAccounts: MembersRevealedAccounts) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method communityMemberRevealedAccountsAdded*(self: AccessInterface, request: CommunityMembershipRequestDto) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
## Used in test env only, for testing keycard flows
|
||||
method registerMockedKeycard*(self: AccessInterface, cardIndex: int, readerState: int, keycardState: int,
|
||||
mockedKeycard: string, mockedKeycardHelper: string) {.base.} =
|
||||
|
@ -315,21 +315,24 @@ method onCommunityTokensDetailsLoaded[T](self: Module[T], communityId: string,
|
||||
)
|
||||
self.view.model().setTokenItems(communityId, communityTokensItems)
|
||||
|
||||
proc createCommunitySectionItem[T](self: Module[T], communityDetails: CommunityDto): SectionItem =
|
||||
proc createCommunitySectionItem[T](self: Module[T], communityDetails: CommunityDto, isEdit: bool = false): SectionItem =
|
||||
var communityTokensItems: seq[TokenItem]
|
||||
var communityMembersAirdropAddress: Table[string, string]
|
||||
|
||||
let existingCommunity = self.view.model().getItemById(communityDetails.id)
|
||||
if communityDetails.memberRole == MemberRole.Owner or communityDetails.memberRole == MemberRole.TokenMaster:
|
||||
self.controller.getCommunityTokensDetailsAsync(communityDetails.id)
|
||||
if not isEdit:
|
||||
# When first creating the section, we load the community tokens and the members revealed accounts
|
||||
self.controller.getCommunityTokensDetailsAsync(communityDetails.id)
|
||||
|
||||
# Get community members' revealed accounts
|
||||
# We will update the model later when we finish loading the accounts
|
||||
self.controller.asyncGetRevealedAccountsForAllMembers(communityDetails.id)
|
||||
# Get community members' revealed accounts
|
||||
# We will update the model later when we finish loading the accounts
|
||||
self.controller.asyncGetRevealedAccountsForAllMembers(communityDetails.id)
|
||||
|
||||
# If there are tokens already in the model, we should keep the existing community tokens, until
|
||||
# getCommunityTokensDetailsAsync will trigger onCommunityTokensDetailsLoaded
|
||||
let existingCommunity = self.view.model().getItemById(communityDetails.id)
|
||||
if not existingCommunity.isEmpty() and not existingCommunity.communityTokens.isNil:
|
||||
communityTokensItems = existingCommunity.communityTokens.items
|
||||
communityTokensItems = existingCommunity.communityTokens.items
|
||||
|
||||
let (unviewedCount, notificationsCount) = self.controller.sectionUnreadMessagesAndMentionsCount(
|
||||
communityDetails.id,
|
||||
@ -394,8 +397,16 @@ proc createCommunitySectionItem[T](self: Module[T], communityDetails: CommunityD
|
||||
state = memberState
|
||||
elif not member.joined:
|
||||
state = MembershipRequestState.AwaitingAddress
|
||||
|
||||
result = self.createMemberItem(member.id, "", state, member.role)
|
||||
var airdropAddress = ""
|
||||
if not existingCommunity.isEmpty() and not existingCommunity.communityTokens.isNil:
|
||||
airdropAddress = existingCommunity.members.getAirdropAddressForMember(member.id)
|
||||
result = self.createMemberItem(
|
||||
member.id,
|
||||
requestId = "",
|
||||
state,
|
||||
member.role,
|
||||
airdropAddress,
|
||||
)
|
||||
),
|
||||
# pendingRequestsToJoin
|
||||
communityDetails.pendingRequestsToJoin.map(x => pending_request_item.initItem(
|
||||
@ -1093,7 +1104,7 @@ method communityEdited*[T](
|
||||
community: CommunityDto) =
|
||||
if(not self.chatSectionModules.contains(community.id)):
|
||||
return
|
||||
var communitySectionItem = self.createCommunitySectionItem(community)
|
||||
var communitySectionItem = self.createCommunitySectionItem(community, isEdit = true)
|
||||
# We need to calculate the unread counts because the community update doesn't come with it
|
||||
let (unviewedMessagesCount, unviewedMentionsCount) = self.controller.sectionUnreadMessagesAndMentionsCount(
|
||||
communitySectionItem.id,
|
||||
@ -1667,6 +1678,20 @@ method communityMembersRevealedAccountsLoaded*[T](self: Module[T], communityId:
|
||||
|
||||
self.view.model.setMembersAirdropAddress(communityId, communityMembersAirdropAddress)
|
||||
|
||||
method communityMemberRevealedAccountsAdded*[T](self: Module[T], request: CommunityMembershipRequestDto) =
|
||||
var airdropAddress = ""
|
||||
for revealedAccount in request.revealedAccounts:
|
||||
if revealedAccount.isAirdropAddress:
|
||||
airdropAddress = revealedAccount.address
|
||||
discard
|
||||
|
||||
if airdropAddress == "":
|
||||
warn "Request to join doesn't have an airdrop address", requestId = request.id, communityId = request.communityId
|
||||
return
|
||||
|
||||
let communityMembersAirdropAddress = {request.publicKey: airdropAddress}.toTable
|
||||
self.view.model.setMembersAirdropAddress(request.communityId, communityMembersAirdropAddress)
|
||||
|
||||
## Used in test env only, for testing keycard flows
|
||||
method registerMockedKeycard*[T](self: Module[T], cardIndex: int, readerState: int, keycardState: int,
|
||||
mockedKeycard: string, mockedKeycardHelper: string) =
|
||||
@ -1700,7 +1725,14 @@ method updateRequestToJoinState*[T](self: Module[T], sectionId: string, requestT
|
||||
if sectionId in self.chatSectionModules:
|
||||
self.chatSectionModules[sectionId].updateRequestToJoinState(requestToJoinState)
|
||||
|
||||
proc createMemberItem*[T](self: Module[T], memberId: string, requestId: string, state: MembershipRequestState, role: MemberRole): MemberItem =
|
||||
proc createMemberItem*[T](
|
||||
self: Module[T],
|
||||
memberId: string,
|
||||
requestId: string,
|
||||
state: MembershipRequestState,
|
||||
role: MemberRole,
|
||||
airdropAddress: string = "",
|
||||
): MemberItem =
|
||||
let contactDetails = self.controller.getContactDetails(memberId)
|
||||
let status = self.controller.getStatusForContactWithId(memberId)
|
||||
return initMemberItem(
|
||||
@ -1718,7 +1750,8 @@ proc createMemberItem*[T](self: Module[T], memberId: string, requestId: string,
|
||||
isVerified = contactDetails.dto.isContactVerified(),
|
||||
memberRole = role,
|
||||
membershipRequestState = state,
|
||||
requestToJoinId = requestId
|
||||
requestToJoinId = requestId,
|
||||
airdropAddress = airdropAddress,
|
||||
)
|
||||
|
||||
{.pop.}
|
||||
|
@ -396,6 +396,13 @@ QtObject:
|
||||
ModelRole.AirdropAddress.int
|
||||
])
|
||||
|
||||
proc getAirdropAddressForMember*(self: Model, pubKey: string): string =
|
||||
let idx = self.findIndexForMember(pubKey)
|
||||
if idx == -1:
|
||||
return ""
|
||||
|
||||
return self.items[idx].airdropAddress
|
||||
|
||||
# TODO: rename me to removeItemByPubkey
|
||||
proc removeItemById*(self: Model, pubKey: string) =
|
||||
let ind = self.findIndexForMember(pubKey)
|
||||
@ -437,3 +444,13 @@ QtObject:
|
||||
self.dataChanged(index, index, @[
|
||||
ModelRole.MembershipRequestState.int
|
||||
])
|
||||
|
||||
proc getNewMembers*(self: Model, pubkeys: seq[string]): seq[string] =
|
||||
for pubkey in pubkeys:
|
||||
var found = false
|
||||
for item in self.items:
|
||||
if item.pubKey == pubkey:
|
||||
found = true
|
||||
break
|
||||
if not found:
|
||||
result.add(pubkey)
|
||||
|
@ -473,8 +473,8 @@ QtObject:
|
||||
if (index == -1):
|
||||
return
|
||||
|
||||
for pubkey, revealedAccounts in communityMembersAirdropAddress.pairs:
|
||||
self.items[index].members.setAirdropAddress(pubkey, revealedAccounts)
|
||||
for pubkey, airdropAddress in communityMembersAirdropAddress.pairs:
|
||||
self.items[index].members.setAirdropAddress(pubkey, airdropAddress)
|
||||
|
||||
proc setTokenItems*(self: SectionModel, id: string, communityTokensItems: seq[TokenItem]) =
|
||||
let index = self.getItemIndex(id)
|
||||
|
@ -48,6 +48,13 @@ type
|
||||
proc isBanned*(state: CommunityMemberPendingBanOrKick): bool =
|
||||
return state == CommunityMemberPendingBanOrKick.Banned or state == CommunityMemberPendingBanOrKick.BannedWithAllMessagesDelete
|
||||
|
||||
type RevealedAccount* = object
|
||||
address*: string
|
||||
chainIds*: seq[int]
|
||||
isAirdropAddress*: bool
|
||||
|
||||
type MembersRevealedAccounts* = Table[string, seq[RevealedAccount]]
|
||||
|
||||
type CommunityMembershipRequestDto* = object
|
||||
id*: string
|
||||
publicKey*: string
|
||||
@ -55,6 +62,7 @@ type CommunityMembershipRequestDto* = object
|
||||
communityId*: string
|
||||
state*: int
|
||||
our*: string #FIXME: should be bool
|
||||
revealedAccounts*: seq[RevealedAccount]
|
||||
|
||||
type CommunitySettingsDto* = object
|
||||
id*: string
|
||||
@ -126,13 +134,6 @@ 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
|
||||
@ -388,6 +389,24 @@ proc toCommunityMetricsDto*(jsonObj: JsonNode): CommunityMetricsDto =
|
||||
for interval in intervalsObj:
|
||||
result.intervals.add(interval.toMetricsIntervalDto)
|
||||
|
||||
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 toCommunityMembershipRequestDto*(jsonObj: JsonNode): CommunityMembershipRequestDto =
|
||||
result = CommunityMembershipRequestDto()
|
||||
discard jsonObj.getProp("id", result.id)
|
||||
@ -397,6 +416,10 @@ proc toCommunityMembershipRequestDto*(jsonObj: JsonNode): CommunityMembershipReq
|
||||
discard jsonObj.getProp("communityId", result.communityId)
|
||||
discard jsonObj.getProp("our", result.our)
|
||||
|
||||
var revealedAccountObj: JsonNode
|
||||
if(jsonObj.getProp("revealedAccounts", revealedAccountObj)):
|
||||
result.revealedAccounts = toRevealedAccounts(revealedAccountObj)
|
||||
|
||||
proc toCollapsedCategoryDto*(jsonObj: JsonNode, isCollapsed: bool = false): Category =
|
||||
result = Category()
|
||||
discard jsonObj.getProp("categoryId", result.id)
|
||||
@ -579,24 +602,6 @@ proc parseDiscordChannels*(response: JsonNode): seq[DiscordChannelDto] =
|
||||
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:
|
||||
|
@ -205,6 +205,7 @@ const SIGNAL_COMMUNITY_MEMBER_STATUS_CHANGED* = "communityMemberStatusChanged"
|
||||
const SIGNAL_COMMUNITY_MEMBERS_CHANGED* = "communityMembersChanged"
|
||||
const SIGNAL_COMMUNITY_KICKED* = "communityKicked"
|
||||
const SIGNAL_NEW_REQUEST_TO_JOIN_COMMUNITY* = "newRequestToJoinCommunity"
|
||||
const SIGNAL_NEW_REQUEST_TO_JOIN_COMMUNITY_ACCEPTED* = "requestToJoinCommunityAccepted"
|
||||
const SIGNAL_REQUEST_TO_JOIN_COMMUNITY_CANCELED* = "requestToJoinCommunityCanceled"
|
||||
const SIGNAL_WAITING_ON_NEW_COMMUNITY_OWNER_TO_CONFIRM_REQUEST_TO_REJOIN* = "waitingOnNewCommunityOwnerToConfirmRequestToRejoin"
|
||||
const SIGNAL_CURATED_COMMUNITY_FOUND* = "curatedCommunityFound"
|
||||
@ -769,29 +770,31 @@ QtObject:
|
||||
|
||||
proc handleCommunitiesRequestsToJoin(self: Service, membershipRequests: seq[CommunityMembershipRequestDto]) =
|
||||
for membershipRequest in membershipRequests:
|
||||
if (not self.communities.contains(membershipRequest.communityId)):
|
||||
error "Received a membership request for an unknown community", communityId=membershipRequest.communityId
|
||||
continue
|
||||
if (not self.communities.contains(membershipRequest.communityId)):
|
||||
error "Received a membership request for an unknown community", communityId=membershipRequest.communityId
|
||||
continue
|
||||
|
||||
let requestToJoinState = RequestToJoinType(membershipRequest.state)
|
||||
let noAwaitingIndex = self.getWaitingForSharedAddressesRequestIndex(membershipRequest.communityId, membershipRequest.id) == -1
|
||||
if requestToJoinState == RequestToJoinType.AwaitingAddress and noAwaitingIndex:
|
||||
self.communities[membershipRequest.communityId].waitingForSharedAddressesRequestsToJoin.add(membershipRequest)
|
||||
let myPublicKey = singletonInstance.userProfile.getPubKey()
|
||||
if myPublicKey == membershipRequest.publicKey:
|
||||
self.events.emit(SIGNAL_WAITING_ON_NEW_COMMUNITY_OWNER_TO_CONFIRM_REQUEST_TO_REJOIN, CommunityIdArgs(communityId: membershipRequest.communityId))
|
||||
elif RequestToJoinType.Pending == requestToJoinState and self.getPendingRequestIndex(membershipRequest.communityId, membershipRequest.id) == -1:
|
||||
self.communities[membershipRequest.communityId].pendingRequestsToJoin.add(membershipRequest)
|
||||
self.events.emit(SIGNAL_NEW_REQUEST_TO_JOIN_COMMUNITY,
|
||||
CommunityRequestArgs(communityRequest: membershipRequest))
|
||||
else:
|
||||
try:
|
||||
self.updateMembershipRequestToNewState(membershipRequest.communityId, membershipRequest.id, self.communities[membershipRequest.communityId],
|
||||
requestToJoinState)
|
||||
except Exception as e:
|
||||
error "Unknown request", msg = e.msg
|
||||
|
||||
self.events.emit(SIGNAL_COMMUNITY_EDITED, CommunityArgs(community: self.communities[membershipRequest.communityId]))
|
||||
let requestToJoinState = RequestToJoinType(membershipRequest.state)
|
||||
let noAwaitingIndex = self.getWaitingForSharedAddressesRequestIndex(membershipRequest.communityId, membershipRequest.id) == -1
|
||||
if requestToJoinState == RequestToJoinType.AwaitingAddress and noAwaitingIndex:
|
||||
self.communities[membershipRequest.communityId].waitingForSharedAddressesRequestsToJoin.add(membershipRequest)
|
||||
let myPublicKey = singletonInstance.userProfile.getPubKey()
|
||||
if myPublicKey == membershipRequest.publicKey:
|
||||
self.events.emit(SIGNAL_WAITING_ON_NEW_COMMUNITY_OWNER_TO_CONFIRM_REQUEST_TO_REJOIN, CommunityIdArgs(communityId: membershipRequest.communityId))
|
||||
elif requestToJoinState == RequestToJoinType.Pending and self.getPendingRequestIndex(membershipRequest.communityId, membershipRequest.id) == -1:
|
||||
self.communities[membershipRequest.communityId].pendingRequestsToJoin.add(membershipRequest)
|
||||
self.events.emit(SIGNAL_NEW_REQUEST_TO_JOIN_COMMUNITY,
|
||||
CommunityRequestArgs(communityRequest: membershipRequest))
|
||||
elif requestToJoinState == RequestToJoinType.Accepted:
|
||||
# Request was accepted, update the member's airdrop address
|
||||
self.events.emit(SIGNAL_NEW_REQUEST_TO_JOIN_COMMUNITY_ACCEPTED,
|
||||
CommunityRequestArgs(communityRequest: membershipRequest))
|
||||
else:
|
||||
try:
|
||||
self.updateMembershipRequestToNewState(membershipRequest.communityId, membershipRequest.id, self.communities[membershipRequest.communityId],
|
||||
requestToJoinState)
|
||||
except Exception as e:
|
||||
error "Unknown request", msg = e.msg
|
||||
|
||||
proc init*(self: Service) =
|
||||
self.doConnect()
|
||||
|
@ -53,8 +53,11 @@ StatusDropdown {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
|
||||
onAboutToShow: listView.Layout.preferredHeight = Math.min(
|
||||
onAboutToShow: {
|
||||
searcher.input.edit.forceActiveFocus()
|
||||
listView.Layout.preferredHeight = Math.min(
|
||||
listView.implicitHeight, 420)
|
||||
}
|
||||
|
||||
// only channels (no entries representing categories), sorted according to
|
||||
// category position and position
|
||||
|
@ -67,6 +67,7 @@ StatusDropdown {
|
||||
onOpened: {
|
||||
listView.positionViewAtBeginning()
|
||||
filterInput.text = ""
|
||||
filterInput.input.edit.forceActiveFocus()
|
||||
}
|
||||
|
||||
QtObject {
|
||||
|
@ -2,6 +2,7 @@ import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import StatusQ 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core 0.1
|
||||
@ -512,27 +513,31 @@ StatusScrollView {
|
||||
model: SortFilterProxyModel {
|
||||
sourceModel: membersModel
|
||||
|
||||
sorters : [
|
||||
StringSorter {
|
||||
roleName: "preferredDisplayName"
|
||||
caseSensitivity: Qt.CaseInsensitive
|
||||
}
|
||||
]
|
||||
|
||||
filters: [
|
||||
ExpressionFilter {
|
||||
FastExpressionFilter {
|
||||
enabled: membersDropdown.searchText !== ""
|
||||
|
||||
function matchesAlias(name, filter) {
|
||||
return name.split(" ").some(p => p.startsWith(filter))
|
||||
}
|
||||
|
||||
expression: {
|
||||
membersDropdown.searchText
|
||||
|
||||
const filter = membersDropdown.searchText.toLowerCase()
|
||||
return matchesAlias(model.alias.toLowerCase(), filter)
|
||||
return model.alias.toLowerCase().includes(filter)
|
||||
|| model.displayName.toLowerCase().includes(filter)
|
||||
|| model.ensName.toLowerCase().includes(filter)
|
||||
|| model.localNickname.toLowerCase().includes(filter)
|
||||
|| model.pubKey.toLowerCase().includes(filter)
|
||||
}
|
||||
expectedRoles: ["alias", "displayName", "ensName", "localNickname", "pubKey"]
|
||||
},
|
||||
ExpressionFilter {
|
||||
expression: !!model.airdropAddress
|
||||
ValueFilter {
|
||||
roleName: "airdropAddress"
|
||||
value: ""
|
||||
inverted: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user