From acda30d9ec8636b0976385c569cbde2366fd37be Mon Sep 17 00:00:00 2001 From: Bram Borggreve Date: Wed, 13 Nov 2024 14:37:04 -0500 Subject: [PATCH 1/6] fix: properly handle vote accounts for multiple clusters --- .../src/lib/api-role-resolver.service.ts | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/libs/api/role/data-access/src/lib/api-role-resolver.service.ts b/libs/api/role/data-access/src/lib/api-role-resolver.service.ts index f365400a..d9e1080d 100644 --- a/libs/api/role/data-access/src/lib/api-role-resolver.service.ts +++ b/libs/api/role/data-access/src/lib/api-role-resolver.service.ts @@ -55,9 +55,8 @@ export class ApiRoleResolverService { const startedAt = Date.now() const conditions = await this.getRoleConditions({ community }) - const voteIdentities = await this.getVoteIdentities({ conditions }) - - await this.syncCommunityMembers({ communityId, voteIdentities }) + const clusterVoteIdentities = await this.getVoteIdentities({ conditions }) + await this.syncCommunityMembers({ communityId, voteIdentities: Object.values(clusterVoteIdentities).flat() }) const [roleMap, users] = await Promise.all([ this.getRoleMap({ community }), @@ -83,6 +82,7 @@ export class ApiRoleResolverService { // Now we want to loop over each condition and check the assets for (const condition of conditions) { + const voteIdentities = clusterVoteIdentities[condition.token.cluster] if (condition.token?.type === NetworkTokenType.Validator && voteIdentities.length) { if (voteIdentities.find((identity) => resolved.solanaIds.includes(identity))) { resolved.conditions.push(condition) @@ -128,29 +128,33 @@ export class ApiRoleResolverService { return Promise.resolve(result) } - async getVoteIdentities({ conditions }: { conditions: RoleCondition[] }) { + async getVoteIdentities({ conditions }: { conditions: RoleCondition[] }): Promise> { + const result: Record = { + [NetworkCluster.SolanaCustom]: [], + [NetworkCluster.SolanaDevnet]: [], + [NetworkCluster.SolanaMainnet]: [], + [NetworkCluster.SolanaTestnet]: [], + } const hasValidatorCondition: RoleCondition | undefined = conditions.find( (c) => c.type === NetworkTokenType.Validator, ) if (!hasValidatorCondition) { this.logger.debug(`getVoteIdentities: No validator conditions found.`) - return [] + return result } const clusters: NetworkCluster[] = conditions.map((c) => c.token.cluster) - const accounts: string[] = [] - for (const cluster of clusters) { try { const accountsForCluster = await this.network.cluster.getVoteIdentities(cluster) this.logger.debug(`[${cluster}] getVoteIdentities: Found ${accountsForCluster.length} identities.`) - accounts.push(...accountsForCluster) + result[cluster].push(...accountsForCluster) } catch (e) { this.logger.error(`[${cluster}] getVoteIdentities: Error getting vote identities for cluster.: ${e}`) } } - return accounts + return result } async syncCommunityMembers({ communityId, voteIdentities }: { communityId: string; voteIdentities: string[] }) { From 3be7a1d1efd2ee715237511bb5235acf82547784 Mon Sep 17 00:00:00 2001 From: Bram Borggreve Date: Wed, 13 Nov 2024 14:51:52 -0500 Subject: [PATCH 2/6] fix: throw error if admin tries to delete network token with role conditions --- .../data-access/src/lib/api-network-token-data.service.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/api/network-token/data-access/src/lib/api-network-token-data.service.ts b/libs/api/network-token/data-access/src/lib/api-network-token-data.service.ts index de862c1f..299b468a 100644 --- a/libs/api/network-token/data-access/src/lib/api-network-token-data.service.ts +++ b/libs/api/network-token/data-access/src/lib/api-network-token-data.service.ts @@ -89,6 +89,12 @@ export class ApiNetworkTokenDataService { async delete(networkTokenId: string) { await this.findOne(networkTokenId) + const conditions = await this.core.data.roleCondition.findMany({ + where: { token: { id: networkTokenId } }, + }) + if (conditions.length) { + throw new Error(`Network token ${networkTokenId} is used by ${conditions.length} role conditions`) + } const deleted = await this.core.data.networkToken.delete({ where: { id: networkTokenId } }) return !!deleted } From 5eb18e0125b135fcdcfdbbabaada8d901a203320 Mon Sep 17 00:00:00 2001 From: Bram Borggreve Date: Wed, 13 Nov 2024 14:52:21 -0500 Subject: [PATCH 3/6] fix: improve admin ui error handling --- .../src/lib/use-admin-find-many-bot.ts | 14 ++++++++++---- .../src/lib/use-admin-find-many-community.ts | 14 ++++++++++---- .../src/lib/use-admin-find-many-network-asset.ts | 16 +++++++++++----- .../src/lib/use-admin-find-many-network-token.ts | 14 ++++++++++---- .../src/lib/use-admin-find-many-network.ts | 14 ++++++++++---- .../src/lib/use-admin-find-many-role.ts | 14 ++++++++++---- .../src/lib/use-admin-find-many-snapshot.ts | 14 ++++++++++---- .../src/lib/use-admin-find-many-user.ts | 16 +++++++++++----- 8 files changed, 82 insertions(+), 34 deletions(-) diff --git a/libs/web/bot/data-access/src/lib/use-admin-find-many-bot.ts b/libs/web/bot/data-access/src/lib/use-admin-find-many-bot.ts index 13723019..59e1199a 100644 --- a/libs/web/bot/data-access/src/lib/use-admin-find-many-bot.ts +++ b/libs/web/bot/data-access/src/lib/use-admin-find-many-bot.ts @@ -46,9 +46,15 @@ export function useAdminFindManyBot(props: Partial & { co return undefined }), deleteBot: (botId: string) => - sdk.adminDeleteBot({ botId }).then(() => { - toastSuccess('Bot deleted') - return query.refetch() - }), + sdk + .adminDeleteBot({ botId }) + .then(() => { + toastSuccess('Bot deleted') + return query.refetch() + }) + .catch((err) => { + toastError(err.message) + return undefined + }), } } diff --git a/libs/web/community/data-access/src/lib/use-admin-find-many-community.ts b/libs/web/community/data-access/src/lib/use-admin-find-many-community.ts index d4dc97e9..8e802411 100644 --- a/libs/web/community/data-access/src/lib/use-admin-find-many-community.ts +++ b/libs/web/community/data-access/src/lib/use-admin-find-many-community.ts @@ -46,9 +46,15 @@ export function useAdminFindManyCommunity(props?: Partial - sdk.adminDeleteCommunity({ communityId }).then(() => { - toastSuccess('Community deleted') - return query.refetch() - }), + sdk + .adminDeleteCommunity({ communityId }) + .then(() => { + toastSuccess('Community deleted') + return query.refetch() + }) + .catch((err) => { + toastError(err.message) + return undefined + }), } } diff --git a/libs/web/network-asset/data-access/src/lib/use-admin-find-many-network-asset.ts b/libs/web/network-asset/data-access/src/lib/use-admin-find-many-network-asset.ts index 13d0983d..f0e23aad 100644 --- a/libs/web/network-asset/data-access/src/lib/use-admin-find-many-network-asset.ts +++ b/libs/web/network-asset/data-access/src/lib/use-admin-find-many-network-asset.ts @@ -1,6 +1,6 @@ import { AdminFindManyNetworkAssetInput, NetworkCluster, NetworkTokenType } from '@pubkey-link/sdk' import { useSdk } from '@pubkey-link/web-core-data-access' -import { toastSuccess } from '@pubkey-ui/core' +import { toastError, toastSuccess } from '@pubkey-ui/core' import { useQuery } from '@tanstack/react-query' import { useState } from 'react' @@ -35,9 +35,15 @@ export function useAdminFindManyNetworkAsset( }, setSearch, deleteNetworkAsset: (networkAssetId: string) => - sdk.adminDeleteNetworkAsset({ networkAssetId }).then(() => { - toastSuccess('NetworkAsset deleted') - return query.refetch() - }), + sdk + .adminDeleteNetworkAsset({ networkAssetId }) + .then(() => { + toastSuccess('NetworkAsset deleted') + return query.refetch() + }) + .catch((err) => { + toastError(err.message) + return undefined + }), } } diff --git a/libs/web/network-token/data-access/src/lib/use-admin-find-many-network-token.ts b/libs/web/network-token/data-access/src/lib/use-admin-find-many-network-token.ts index c7849acb..1c1c1db7 100644 --- a/libs/web/network-token/data-access/src/lib/use-admin-find-many-network-token.ts +++ b/libs/web/network-token/data-access/src/lib/use-admin-find-many-network-token.ts @@ -53,9 +53,15 @@ export function useAdminFindManyNetworkToken( return undefined }), deleteNetworkToken: (networkTokenId: string) => - sdk.adminDeleteNetworkToken({ networkTokenId }).then(() => { - toastSuccess('NetworkToken deleted') - return query.refetch() - }), + sdk + .adminDeleteNetworkToken({ networkTokenId }) + .then(() => { + toastSuccess('NetworkToken deleted') + return query.refetch() + }) + .catch((err) => { + toastError(err.message) + return undefined + }), } } diff --git a/libs/web/network/data-access/src/lib/use-admin-find-many-network.ts b/libs/web/network/data-access/src/lib/use-admin-find-many-network.ts index bd9db334..cf70cfa6 100644 --- a/libs/web/network/data-access/src/lib/use-admin-find-many-network.ts +++ b/libs/web/network/data-access/src/lib/use-admin-find-many-network.ts @@ -46,9 +46,15 @@ export function useAdminFindManyNetwork(props?: Partial - sdk.adminDeleteNetwork({ networkId }).then(() => { - toastSuccess('Network deleted') - return query.refetch() - }), + sdk + .adminDeleteNetwork({ networkId }) + .then(() => { + toastSuccess('Network deleted') + return query.refetch() + }) + .catch((err) => { + toastError(err.message) + return undefined + }), } } diff --git a/libs/web/role/data-access/src/lib/use-admin-find-many-role.ts b/libs/web/role/data-access/src/lib/use-admin-find-many-role.ts index a28782a4..a9e27939 100644 --- a/libs/web/role/data-access/src/lib/use-admin-find-many-role.ts +++ b/libs/web/role/data-access/src/lib/use-admin-find-many-role.ts @@ -46,9 +46,15 @@ export function useAdminFindManyRole(props: Partial & { return undefined }), deleteRole: (roleId: string) => - sdk.adminDeleteRole({ roleId }).then(() => { - toastSuccess('Role deleted') - return query.refetch() - }), + sdk + .adminDeleteRole({ roleId }) + .then(() => { + toastSuccess('Role deleted') + return query.refetch() + }) + .catch((err) => { + toastError(err.message) + return undefined + }), } } diff --git a/libs/web/snapshot/data-access/src/lib/use-admin-find-many-snapshot.ts b/libs/web/snapshot/data-access/src/lib/use-admin-find-many-snapshot.ts index 8ffb629d..e30e4f2d 100644 --- a/libs/web/snapshot/data-access/src/lib/use-admin-find-many-snapshot.ts +++ b/libs/web/snapshot/data-access/src/lib/use-admin-find-many-snapshot.ts @@ -46,9 +46,15 @@ export function useAdminFindManySnapshot(props: Partial - sdk.adminDeleteSnapshot({ snapshotId }).then(() => { - toastSuccess('Snapshot deleted') - return query.refetch() - }), + sdk + .adminDeleteSnapshot({ snapshotId }) + .then(() => { + toastSuccess('Snapshot deleted') + return query.refetch() + }) + .catch((err) => { + toastError(err.message) + return undefined + }), } } diff --git a/libs/web/user/data-access/src/lib/use-admin-find-many-user.ts b/libs/web/user/data-access/src/lib/use-admin-find-many-user.ts index 19164632..1545a0ef 100644 --- a/libs/web/user/data-access/src/lib/use-admin-find-many-user.ts +++ b/libs/web/user/data-access/src/lib/use-admin-find-many-user.ts @@ -1,6 +1,6 @@ import { AdminFindManyUserInput, UserRole, UserStatus } from '@pubkey-link/sdk' import { useSdk } from '@pubkey-link/web-core-data-access' -import { toastSuccess } from '@pubkey-ui/core' +import { toastError, toastSuccess } from '@pubkey-ui/core' import { useQuery } from '@tanstack/react-query' import { useState } from 'react' @@ -45,9 +45,15 @@ export function useAdminFindManyUser(props?: AdminFindManyUserInput) { setStatus(status) }, deleteUser: (userId: string) => - sdk.adminDeleteUser({ userId }).then(() => { - toastSuccess('User deleted') - return query.refetch() - }), + sdk + .adminDeleteUser({ userId }) + .then(() => { + toastSuccess('User deleted') + return query.refetch() + }) + .catch((err) => { + toastError(err.message) + return undefined + }), } } From 3e481f0ab389bfcbda4f8e0a39893f2b31723dc7 Mon Sep 17 00:00:00 2001 From: Bram Borggreve Date: Wed, 13 Nov 2024 14:54:37 -0500 Subject: [PATCH 4/6] fix: no communities message --- libs/web/user/feature/src/lib/user-profile-tab-communities.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/web/user/feature/src/lib/user-profile-tab-communities.tsx b/libs/web/user/feature/src/lib/user-profile-tab-communities.tsx index f22a2c48..071dba98 100644 --- a/libs/web/user/feature/src/lib/user-profile-tab-communities.tsx +++ b/libs/web/user/feature/src/lib/user-profile-tab-communities.tsx @@ -11,7 +11,7 @@ export default function UserProfileTabCommunities({ isAuthUser, username }: { is ) : ( ) } From 63fbbd02e367fc8aa477c7135755ae18ce0a3a13 Mon Sep 17 00:00:00 2001 From: Bram Borggreve Date: Wed, 13 Nov 2024 14:54:58 -0500 Subject: [PATCH 5/6] refactor: change genesis hash network token name --- .../data-access/src/lib/api-network-token-data.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/api/network-token/data-access/src/lib/api-network-token-data.service.ts b/libs/api/network-token/data-access/src/lib/api-network-token-data.service.ts index 299b468a..0ec1fca9 100644 --- a/libs/api/network-token/data-access/src/lib/api-network-token-data.service.ts +++ b/libs/api/network-token/data-access/src/lib/api-network-token-data.service.ts @@ -54,10 +54,10 @@ export class ApiNetworkTokenDataService { network: { connect: { cluster: network.cluster } }, type: NetworkTokenType.Validator, account: hash, - name: `${network.cluster.replace('Solana', 'Solana ')} Genesis`, + name: `${network.cluster.replace('Solana', 'Solana ')}`, + description: `Genesis Hash for ${network.cluster.replace('Solana', 'Solana ')}`, program: SystemProgram.programId.toBase58(), } - await this.core.data.networkToken.create({ data }) this.logger.log( `ensureGenesisHash: Created Genesis Block for cluster ${network.cluster} with genesis hash ${hash}`, From 29a9f284abdd78e6776f9ca850ef46d42564d3fa Mon Sep 17 00:00:00 2001 From: Bram Borggreve Date: Wed, 13 Nov 2024 15:00:29 -0500 Subject: [PATCH 6/6] fix: properly handle empty collectibles/tokens tabs --- .../src/lib/user-network-asset-list.feature.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/libs/web/network-asset/feature/src/lib/user-network-asset-list.feature.tsx b/libs/web/network-asset/feature/src/lib/user-network-asset-list.feature.tsx index dc870e75..0a182cf1 100644 --- a/libs/web/network-asset/feature/src/lib/user-network-asset-list.feature.tsx +++ b/libs/web/network-asset/feature/src/lib/user-network-asset-list.feature.tsx @@ -65,7 +65,7 @@ export default function UserNetworkAssetListFeature({ {query.isLoading ? ( - ) : items?.length ? ( + ) : groups?.length ? ( {groups.map((group) => ( @@ -86,8 +86,19 @@ export default function UserNetworkAssetListFeature({ ))} ) : ( - + )} ) } + +function typeName(type: NetworkTokenType) { + switch (type) { + case NetworkTokenType.Fungible: + return 'tokens' + case NetworkTokenType.NonFungible: + return 'collectibles' + default: + return type + } +}