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:
Jonathan Rainville 2024-10-10 13:01:47 -04:00 committed by GitHub
parent 70fccb3835
commit 0bb2bc0e03
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 149 additions and 72 deletions

View File

@ -309,6 +309,10 @@ proc init*(self: Controller) =
var args = CommunityRequestArgs(e) var args = CommunityRequestArgs(e)
self.delegate.newCommunityMembershipRequestReceived(args.communityRequest) 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.events.on(SIGNAL_NETWORK_CONNECTED) do(e: Args):
self.delegate.onNetworkConnected() self.delegate.onNetworkConnected()
@ -595,6 +599,9 @@ proc getColorId*(self: Controller, pubkey: string): int =
proc asyncGetRevealedAccountsForAllMembers*(self: Controller, communityId: string) = proc asyncGetRevealedAccountsForAllMembers*(self: Controller, communityId: string) =
self.communityService.asyncGetRevealedAccountsForAllMembers(communityId) self.communityService.asyncGetRevealedAccountsForAllMembers(communityId)
proc asyncGetRevealedAccountsForMember*(self: Controller, communityId, memberPubkey: string) =
self.communityService.asyncGetRevealedAccountsForMember(communityId, memberPubkey)
proc asyncReevaluateCommunityMembersPermissions*(self: Controller, communityId: string) = proc asyncReevaluateCommunityMembersPermissions*(self: Controller, communityId: string) =
self.communityService.asyncReevaluateCommunityMembersPermissions(communityId) self.communityService.asyncReevaluateCommunityMembersPermissions(communityId)

View File

@ -391,6 +391,9 @@ method windowDeactivated*(self: AccessInterface) {.base.} =
method communityMembersRevealedAccountsLoaded*(self: AccessInterface, communityId: string, membersRevealedAccounts: MembersRevealedAccounts) {.base.} = method communityMembersRevealedAccountsLoaded*(self: AccessInterface, communityId: string, membersRevealedAccounts: MembersRevealedAccounts) {.base.} =
raise newException(ValueError, "No implementation available") 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 ## Used in test env only, for testing keycard flows
method registerMockedKeycard*(self: AccessInterface, cardIndex: int, readerState: int, keycardState: int, method registerMockedKeycard*(self: AccessInterface, cardIndex: int, readerState: int, keycardState: int,
mockedKeycard: string, mockedKeycardHelper: string) {.base.} = mockedKeycard: string, mockedKeycardHelper: string) {.base.} =

View File

@ -315,21 +315,24 @@ method onCommunityTokensDetailsLoaded[T](self: Module[T], communityId: string,
) )
self.view.model().setTokenItems(communityId, communityTokensItems) 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 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: 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 # Get community members' revealed accounts
# We will update the model later when we finish loading the accounts # We will update the model later when we finish loading the accounts
self.controller.asyncGetRevealedAccountsForAllMembers(communityDetails.id) self.controller.asyncGetRevealedAccountsForAllMembers(communityDetails.id)
# If there are tokens already in the model, we should keep the existing community tokens, until # If there are tokens already in the model, we should keep the existing community tokens, until
# getCommunityTokensDetailsAsync will trigger onCommunityTokensDetailsLoaded # getCommunityTokensDetailsAsync will trigger onCommunityTokensDetailsLoaded
let existingCommunity = self.view.model().getItemById(communityDetails.id)
if not existingCommunity.isEmpty() and not existingCommunity.communityTokens.isNil: if not existingCommunity.isEmpty() and not existingCommunity.communityTokens.isNil:
communityTokensItems = existingCommunity.communityTokens.items communityTokensItems = existingCommunity.communityTokens.items
let (unviewedCount, notificationsCount) = self.controller.sectionUnreadMessagesAndMentionsCount( let (unviewedCount, notificationsCount) = self.controller.sectionUnreadMessagesAndMentionsCount(
communityDetails.id, communityDetails.id,
@ -394,8 +397,16 @@ proc createCommunitySectionItem[T](self: Module[T], communityDetails: CommunityD
state = memberState state = memberState
elif not member.joined: elif not member.joined:
state = MembershipRequestState.AwaitingAddress state = MembershipRequestState.AwaitingAddress
var airdropAddress = ""
result = self.createMemberItem(member.id, "", state, member.role) 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 # pendingRequestsToJoin
communityDetails.pendingRequestsToJoin.map(x => pending_request_item.initItem( communityDetails.pendingRequestsToJoin.map(x => pending_request_item.initItem(
@ -1093,7 +1104,7 @@ method communityEdited*[T](
community: CommunityDto) = community: CommunityDto) =
if(not self.chatSectionModules.contains(community.id)): if(not self.chatSectionModules.contains(community.id)):
return 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 # We need to calculate the unread counts because the community update doesn't come with it
let (unviewedMessagesCount, unviewedMentionsCount) = self.controller.sectionUnreadMessagesAndMentionsCount( let (unviewedMessagesCount, unviewedMentionsCount) = self.controller.sectionUnreadMessagesAndMentionsCount(
communitySectionItem.id, communitySectionItem.id,
@ -1667,6 +1678,20 @@ method communityMembersRevealedAccountsLoaded*[T](self: Module[T], communityId:
self.view.model.setMembersAirdropAddress(communityId, communityMembersAirdropAddress) 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 ## Used in test env only, for testing keycard flows
method registerMockedKeycard*[T](self: Module[T], cardIndex: int, readerState: int, keycardState: int, method registerMockedKeycard*[T](self: Module[T], cardIndex: int, readerState: int, keycardState: int,
mockedKeycard: string, mockedKeycardHelper: string) = mockedKeycard: string, mockedKeycardHelper: string) =
@ -1700,7 +1725,14 @@ method updateRequestToJoinState*[T](self: Module[T], sectionId: string, requestT
if sectionId in self.chatSectionModules: if sectionId in self.chatSectionModules:
self.chatSectionModules[sectionId].updateRequestToJoinState(requestToJoinState) 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 contactDetails = self.controller.getContactDetails(memberId)
let status = self.controller.getStatusForContactWithId(memberId) let status = self.controller.getStatusForContactWithId(memberId)
return initMemberItem( return initMemberItem(
@ -1718,7 +1750,8 @@ proc createMemberItem*[T](self: Module[T], memberId: string, requestId: string,
isVerified = contactDetails.dto.isContactVerified(), isVerified = contactDetails.dto.isContactVerified(),
memberRole = role, memberRole = role,
membershipRequestState = state, membershipRequestState = state,
requestToJoinId = requestId requestToJoinId = requestId,
airdropAddress = airdropAddress,
) )
{.pop.} {.pop.}

View File

@ -396,6 +396,13 @@ QtObject:
ModelRole.AirdropAddress.int 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 # TODO: rename me to removeItemByPubkey
proc removeItemById*(self: Model, pubKey: string) = proc removeItemById*(self: Model, pubKey: string) =
let ind = self.findIndexForMember(pubKey) let ind = self.findIndexForMember(pubKey)
@ -437,3 +444,13 @@ QtObject:
self.dataChanged(index, index, @[ self.dataChanged(index, index, @[
ModelRole.MembershipRequestState.int 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)

View File

@ -473,8 +473,8 @@ QtObject:
if (index == -1): if (index == -1):
return return
for pubkey, revealedAccounts in communityMembersAirdropAddress.pairs: for pubkey, airdropAddress in communityMembersAirdropAddress.pairs:
self.items[index].members.setAirdropAddress(pubkey, revealedAccounts) self.items[index].members.setAirdropAddress(pubkey, airdropAddress)
proc setTokenItems*(self: SectionModel, id: string, communityTokensItems: seq[TokenItem]) = proc setTokenItems*(self: SectionModel, id: string, communityTokensItems: seq[TokenItem]) =
let index = self.getItemIndex(id) let index = self.getItemIndex(id)

View File

@ -48,6 +48,13 @@ type
proc isBanned*(state: CommunityMemberPendingBanOrKick): bool = proc isBanned*(state: CommunityMemberPendingBanOrKick): bool =
return state == CommunityMemberPendingBanOrKick.Banned or state == CommunityMemberPendingBanOrKick.BannedWithAllMessagesDelete 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 type CommunityMembershipRequestDto* = object
id*: string id*: string
publicKey*: string publicKey*: string
@ -55,6 +62,7 @@ type CommunityMembershipRequestDto* = object
communityId*: string communityId*: string
state*: int state*: int
our*: string #FIXME: should be bool our*: string #FIXME: should be bool
revealedAccounts*: seq[RevealedAccount]
type CommunitySettingsDto* = object type CommunitySettingsDto* = object
id*: string id*: string
@ -126,13 +134,6 @@ type CommunityMetricsDto* = object
metricsType*: CommunityMetricsType metricsType*: CommunityMetricsType
intervals*: seq[MetricsIntervalDto] intervals*: seq[MetricsIntervalDto]
type RevealedAccount* = object
address*: string
chainIds*: seq[int]
isAirdropAddress*: bool
type MembersRevealedAccounts* = Table[string, seq[RevealedAccount]]
type CommunityDto* = object type CommunityDto* = object
id*: string id*: string
memberRole*: MemberRole memberRole*: MemberRole
@ -388,6 +389,24 @@ proc toCommunityMetricsDto*(jsonObj: JsonNode): CommunityMetricsDto =
for interval in intervalsObj: for interval in intervalsObj:
result.intervals.add(interval.toMetricsIntervalDto) 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 = proc toCommunityMembershipRequestDto*(jsonObj: JsonNode): CommunityMembershipRequestDto =
result = CommunityMembershipRequestDto() result = CommunityMembershipRequestDto()
discard jsonObj.getProp("id", result.id) discard jsonObj.getProp("id", result.id)
@ -397,6 +416,10 @@ proc toCommunityMembershipRequestDto*(jsonObj: JsonNode): CommunityMembershipReq
discard jsonObj.getProp("communityId", result.communityId) discard jsonObj.getProp("communityId", result.communityId)
discard jsonObj.getProp("our", result.our) 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 = proc toCollapsedCategoryDto*(jsonObj: JsonNode, isCollapsed: bool = false): Category =
result = Category() result = Category()
discard jsonObj.getProp("categoryId", result.id) discard jsonObj.getProp("categoryId", result.id)
@ -579,24 +602,6 @@ proc parseDiscordChannels*(response: JsonNode): seq[DiscordChannelDto] =
for channel in response["discordChannels"].items(): for channel in response["discordChannels"].items():
result.add(channel.toDiscordChannelDto()) 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 = proc toMembersRevealedAccounts*(membersRevealedAccountsObj: JsonNode): MembersRevealedAccounts =
result = initTable[string, seq[RevealedAccount]]() result = initTable[string, seq[RevealedAccount]]()
for (pubkey, revealedAccountsObj) in membersRevealedAccountsObj.pairs: for (pubkey, revealedAccountsObj) in membersRevealedAccountsObj.pairs:

View File

@ -205,6 +205,7 @@ const SIGNAL_COMMUNITY_MEMBER_STATUS_CHANGED* = "communityMemberStatusChanged"
const SIGNAL_COMMUNITY_MEMBERS_CHANGED* = "communityMembersChanged" const SIGNAL_COMMUNITY_MEMBERS_CHANGED* = "communityMembersChanged"
const SIGNAL_COMMUNITY_KICKED* = "communityKicked" const SIGNAL_COMMUNITY_KICKED* = "communityKicked"
const SIGNAL_NEW_REQUEST_TO_JOIN_COMMUNITY* = "newRequestToJoinCommunity" 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_REQUEST_TO_JOIN_COMMUNITY_CANCELED* = "requestToJoinCommunityCanceled"
const SIGNAL_WAITING_ON_NEW_COMMUNITY_OWNER_TO_CONFIRM_REQUEST_TO_REJOIN* = "waitingOnNewCommunityOwnerToConfirmRequestToRejoin" const SIGNAL_WAITING_ON_NEW_COMMUNITY_OWNER_TO_CONFIRM_REQUEST_TO_REJOIN* = "waitingOnNewCommunityOwnerToConfirmRequestToRejoin"
const SIGNAL_CURATED_COMMUNITY_FOUND* = "curatedCommunityFound" const SIGNAL_CURATED_COMMUNITY_FOUND* = "curatedCommunityFound"
@ -769,29 +770,31 @@ QtObject:
proc handleCommunitiesRequestsToJoin(self: Service, membershipRequests: seq[CommunityMembershipRequestDto]) = proc handleCommunitiesRequestsToJoin(self: Service, membershipRequests: seq[CommunityMembershipRequestDto]) =
for membershipRequest in membershipRequests: for membershipRequest in membershipRequests:
if (not self.communities.contains(membershipRequest.communityId)): if (not self.communities.contains(membershipRequest.communityId)):
error "Received a membership request for an unknown community", communityId=membershipRequest.communityId error "Received a membership request for an unknown community", communityId=membershipRequest.communityId
continue continue
let requestToJoinState = RequestToJoinType(membershipRequest.state) let requestToJoinState = RequestToJoinType(membershipRequest.state)
let noAwaitingIndex = self.getWaitingForSharedAddressesRequestIndex(membershipRequest.communityId, membershipRequest.id) == -1 let noAwaitingIndex = self.getWaitingForSharedAddressesRequestIndex(membershipRequest.communityId, membershipRequest.id) == -1
if requestToJoinState == RequestToJoinType.AwaitingAddress and noAwaitingIndex: if requestToJoinState == RequestToJoinType.AwaitingAddress and noAwaitingIndex:
self.communities[membershipRequest.communityId].waitingForSharedAddressesRequestsToJoin.add(membershipRequest) self.communities[membershipRequest.communityId].waitingForSharedAddressesRequestsToJoin.add(membershipRequest)
let myPublicKey = singletonInstance.userProfile.getPubKey() let myPublicKey = singletonInstance.userProfile.getPubKey()
if myPublicKey == membershipRequest.publicKey: if myPublicKey == membershipRequest.publicKey:
self.events.emit(SIGNAL_WAITING_ON_NEW_COMMUNITY_OWNER_TO_CONFIRM_REQUEST_TO_REJOIN, CommunityIdArgs(communityId: membershipRequest.communityId)) 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: elif requestToJoinState == RequestToJoinType.Pending and self.getPendingRequestIndex(membershipRequest.communityId, membershipRequest.id) == -1:
self.communities[membershipRequest.communityId].pendingRequestsToJoin.add(membershipRequest) self.communities[membershipRequest.communityId].pendingRequestsToJoin.add(membershipRequest)
self.events.emit(SIGNAL_NEW_REQUEST_TO_JOIN_COMMUNITY, self.events.emit(SIGNAL_NEW_REQUEST_TO_JOIN_COMMUNITY,
CommunityRequestArgs(communityRequest: membershipRequest)) CommunityRequestArgs(communityRequest: membershipRequest))
else: elif requestToJoinState == RequestToJoinType.Accepted:
try: # Request was accepted, update the member's airdrop address
self.updateMembershipRequestToNewState(membershipRequest.communityId, membershipRequest.id, self.communities[membershipRequest.communityId], self.events.emit(SIGNAL_NEW_REQUEST_TO_JOIN_COMMUNITY_ACCEPTED,
requestToJoinState) CommunityRequestArgs(communityRequest: membershipRequest))
except Exception as e: else:
error "Unknown request", msg = e.msg try:
self.updateMembershipRequestToNewState(membershipRequest.communityId, membershipRequest.id, self.communities[membershipRequest.communityId],
self.events.emit(SIGNAL_COMMUNITY_EDITED, CommunityArgs(community: self.communities[membershipRequest.communityId])) requestToJoinState)
except Exception as e:
error "Unknown request", msg = e.msg
proc init*(self: Service) = proc init*(self: Service) =
self.doConnect() self.doConnect()

View File

@ -53,8 +53,11 @@ StatusDropdown {
listView.positionViewAtBeginning() listView.positionViewAtBeginning()
} }
onAboutToShow: listView.Layout.preferredHeight = Math.min( onAboutToShow: {
searcher.input.edit.forceActiveFocus()
listView.Layout.preferredHeight = Math.min(
listView.implicitHeight, 420) listView.implicitHeight, 420)
}
// only channels (no entries representing categories), sorted according to // only channels (no entries representing categories), sorted according to
// category position and position // category position and position

View File

@ -67,6 +67,7 @@ StatusDropdown {
onOpened: { onOpened: {
listView.positionViewAtBeginning() listView.positionViewAtBeginning()
filterInput.text = "" filterInput.text = ""
filterInput.input.edit.forceActiveFocus()
} }
QtObject { QtObject {

View File

@ -2,6 +2,7 @@ import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import StatusQ 0.1
import StatusQ.Components 0.1 import StatusQ.Components 0.1
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
import StatusQ.Core 0.1 import StatusQ.Core 0.1
@ -512,27 +513,31 @@ StatusScrollView {
model: SortFilterProxyModel { model: SortFilterProxyModel {
sourceModel: membersModel sourceModel: membersModel
sorters : [
StringSorter {
roleName: "preferredDisplayName"
caseSensitivity: Qt.CaseInsensitive
}
]
filters: [ filters: [
ExpressionFilter { FastExpressionFilter {
enabled: membersDropdown.searchText !== "" enabled: membersDropdown.searchText !== ""
function matchesAlias(name, filter) {
return name.split(" ").some(p => p.startsWith(filter))
}
expression: { expression: {
membersDropdown.searchText
const filter = membersDropdown.searchText.toLowerCase() const filter = membersDropdown.searchText.toLowerCase()
return matchesAlias(model.alias.toLowerCase(), filter) return model.alias.toLowerCase().includes(filter)
|| model.displayName.toLowerCase().includes(filter) || model.displayName.toLowerCase().includes(filter)
|| model.ensName.toLowerCase().includes(filter) || model.ensName.toLowerCase().includes(filter)
|| model.localNickname.toLowerCase().includes(filter) || model.localNickname.toLowerCase().includes(filter)
|| model.pubKey.toLowerCase().includes(filter) || model.pubKey.toLowerCase().includes(filter)
} }
expectedRoles: ["alias", "displayName", "ensName", "localNickname", "pubKey"]
}, },
ExpressionFilter { ValueFilter {
expression: !!model.airdropAddress roleName: "airdropAddress"
value: ""
inverted: true
} }
] ]
} }