Skip to content

Commit 1df43cc

Browse files
authored
Merge pull request #878 from subquery/feature-explorer-card-network-val
feat: enhance ProjectCard to display network type and update useProjectList for manifest retrieval
2 parents c02edf0 + af8f939 commit 1df43cc

File tree

7 files changed

+21339
-15021
lines changed

7 files changed

+21339
-15021
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,7 @@ src/globalTypes.ts
3232
.env
3333
dist
3434

35-
stats.html
35+
stats.html
36+
37+
.yarn
38+
.data

.yarnrc.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
nodeLinker: node-modules

src/components/ProjectCard/ProjectCard.tsx

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
import * as React from 'react';
5-
import { IndexerName } from '@components/IndexerDetails/IndexerName';
65
import { Manifest } from '@hooks/useGetDeploymentManifest';
76
import { Address, Typography } from '@subql/components';
87
import { ProjectFieldsFragment, ProjectType } from '@subql/network-query';
@@ -12,6 +11,7 @@ import BigNumber from 'bignumber.js';
1211
import dayjs from 'dayjs';
1312
import { toSvg } from 'jdenticon';
1413

14+
import { getNetworkNameByChainId } from 'src/const/const';
1515
import { ProjectMetadata } from 'src/models';
1616

1717
import IPFSImage from '../IPFSImage';
@@ -26,6 +26,8 @@ type Props = {
2626
};
2727

2828
const { PROJECT_NAV } = ROUTES;
29+
const MAX_NETWORK_NAME_LENGTH = 28;
30+
const MAX_CHAIN_ID_LENGTH = 18;
2931

3032
const ProjectCard: React.FC<Props> = ({ project, href, onClick }) => {
3133
const ipfsImage = React.useMemo(() => {
@@ -49,6 +51,41 @@ const ProjectCard: React.FC<Props> = ({ project, href, onClick }) => {
4951
);
5052
}, [project?.deployments?.nodes]);
5153

54+
const networkVal = React.useMemo(() => {
55+
if (project.type === ProjectType.RPC && project.manifest?.rpcFamily) {
56+
return project.manifest.rpcFamily[0];
57+
}
58+
59+
const chainId =
60+
project.type === ProjectType.SUBQUERY
61+
? project.manifest?.network?.chainId
62+
: project.manifest?.dataSources?.[0]?.network;
63+
if (!chainId) return '-';
64+
65+
return (
66+
getNetworkNameByChainId(chainId, {
67+
projectName: project.metadata?.name,
68+
projectId: project.id,
69+
source: 'ProjectCard',
70+
}) || `Chain ID ${chainId}`
71+
);
72+
}, [project.type, project.manifest, project.id, project.metadata?.name]);
73+
74+
const isChainIdFallback = React.useMemo(() => {
75+
return `${networkVal || '-'}`.startsWith('Chain ID ');
76+
}, [networkVal]);
77+
78+
const chainIdDisplayVal = React.useMemo(() => {
79+
if (!isChainIdFallback) return '';
80+
const val = `${networkVal || '-'}`.replace('Chain ID ', '');
81+
return val.length > MAX_CHAIN_ID_LENGTH ? `${val.slice(0, MAX_CHAIN_ID_LENGTH)}...` : val;
82+
}, [isChainIdFallback, networkVal]);
83+
84+
const networkDisplayVal = React.useMemo(() => {
85+
const val = `${networkVal || '-'}`;
86+
return val.length > MAX_NETWORK_NAME_LENGTH ? `${val.slice(0, MAX_NETWORK_NAME_LENGTH)}...` : val;
87+
}, [networkVal]);
88+
5289
return (
5390
<a
5491
href={href ? href : `${PROJECT_NAV}/${project.id}`}
@@ -76,7 +113,20 @@ const ProjectCard: React.FC<Props> = ({ project, href, onClick }) => {
76113
</div>
77114

78115
{project.type === ProjectType.SUBQUERY || project.type === ProjectType.SUBGRAPH ? (
79-
<IndexerName address={project.owner} size="tiny" />
116+
<div className="flex">
117+
{isChainIdFallback ? (
118+
<>
119+
<Typography variant="small" type="secondary">
120+
Chain ID
121+
</Typography>
122+
<Typography variant="small" style={{ marginLeft: 6 }}>
123+
{chainIdDisplayVal}
124+
</Typography>
125+
</>
126+
) : (
127+
<Typography variant="small">{networkDisplayVal}</Typography>
128+
)}
129+
</div>
80130
) : (
81131
<Typography variant="small" style={{ textTransform: 'uppercase' }}>
82132
{project.manifest?.rpcFamily?.[0]}

src/components/ProjectHeader/ProjectHeader.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import clsx from 'clsx';
2222
import dayjs from 'dayjs';
2323
import { toSvg } from 'jdenticon';
2424

25-
import { ETH_TYPE_DICTION, NETWORK_TYPE_DICTION } from 'src/const/const';
25+
import { getNetworkNameByChainId } from 'src/const/const';
2626
import { useProjectStore } from 'src/stores/project';
2727

2828
import Detail from '../Detail';
@@ -104,11 +104,14 @@ const ProjectHeader: React.FC<Props> = ({
104104
project.type === ProjectType.SUBQUERY ? manifest?.network?.chainId : manifest?.dataSources?.[0]?.network;
105105
if (!chainId) return '-';
106106

107-
const polkadotName = NETWORK_TYPE_DICTION[chainId];
108-
const ethName = ETH_TYPE_DICTION[chainId];
109-
110-
return polkadotName || ethName || chainId;
111-
}, [project.type, manifest]);
107+
return (
108+
getNetworkNameByChainId(chainId, {
109+
projectName: project.metadata?.name,
110+
projectId: project.id,
111+
source: 'ProjectHeader',
112+
}) || chainId
113+
);
114+
}, [project.type, manifest, project.id, project.metadata?.name]);
112115

113116
const isOnwer = React.useMemo(() => account === project.owner, [project.owner, account]);
114117

src/const/const.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,22 @@ export const NETWORK_TYPE_DICTION: { [key in string]: string } = {
3131
'0xa85cfb9b9fd4d622a5b28289a02347af987d8f73fa3108450e2b4a11c1ce5755': 'Basilisk',
3232
'0xaa3876c1dc8a1afcc2e9a685a49ff7704cfd36ad8c90bf2702b9d1b00cc40011': 'Altair',
3333
'0x1bb969d85965e4bb5a651abbedf21a54b6b31a21f66b5401cc3f1e286268d736': 'Phala',
34+
'0x4a12be580bb959937a1c7a61d5cf24428ed67fa571974b4007645d1886e7c89f': 'Subsocial',
35+
'4221332d34e1694168c2a0c0b3fd0f273809612cb13d000d5c2e00e85f50f796': 'Concordium Testnet',
36+
'9dd9ca4d19e9393877d2c44b70f89acbfc0883c2243e5eeaecc0d1cd0503f478': 'Concordium Mainnet',
37+
'5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d': 'Solana Mainnet',
38+
EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG: 'Solana Devnet',
39+
'4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY': 'Solana Testnet',
40+
'fetchhub-4': 'Fetch.ai Mainnet',
41+
'juno-1': 'Juno Mainnet',
42+
'near-testnet': 'NEAR Testnet',
43+
mainnet: 'NEAR Mainnet',
44+
'axelar-dojo-1': 'Axelar Dojo',
45+
'telos-testnet': 'Telos Testnet',
46+
'coti-mainnet': 'COTI Mainnet',
47+
flare: 'Flare',
48+
'Public Global Stellar Network ; September 2015': 'Stellar Mainnet',
49+
'Test SDF Network ; September 2015': 'Stellar Testnet',
3450
};
3551

3652
export const ETH_TYPE_DICTION: { [key in string]: string } = {
@@ -971,4 +987,57 @@ export const ETH_TYPE_DICTION: { [key in string]: string } = {
971987
'666301171999': 'PDC Mainnet',
972988
'6022140761023': 'Molereum Network',
973989
'2716446429837000': 'DCHAIN',
990+
'97': 'BNB Smart Chain Testnet',
991+
'232': 'Lens',
992+
'999': 'HyperEVM',
993+
'2741': 'Abstract',
994+
'4158': 'CrossFi Mainnet',
995+
'6900': 'Nibiru cataclysm-1',
996+
'48900': 'Zircuit Mainnet',
997+
'102031': 'Creditcoin Testnet',
998+
'421614': 'Arbitrum Sepolia',
999+
'84532': 'Base Sepolia',
1000+
'11155111': 'Ethereum Sepolia',
1001+
'65000000': 'Autonity Mainnet',
1002+
'10200': 'Gnosis Chiado Testnet',
1003+
gnosis: 'Gnosis',
1004+
};
1005+
1006+
const missingNetworkChainIdSet = new Set<string>();
1007+
const missingNetworkChainIdWithProjectSet = new Set<string>();
1008+
1009+
type NetworkNameContext = {
1010+
projectName?: string;
1011+
projectId?: string;
1012+
source?: string;
1013+
};
1014+
1015+
export const getNetworkNameByChainId = (chainId?: string, context?: NetworkNameContext): string | undefined => {
1016+
if (!chainId) return undefined;
1017+
1018+
const networkName = NETWORK_TYPE_DICTION[chainId] || ETH_TYPE_DICTION[chainId];
1019+
1020+
// Temporary debug: collect unmapped chain IDs while browsing pages locally.
1021+
if (!networkName && import.meta.env.DEV) {
1022+
const projectName = context?.projectName || 'unknown';
1023+
const projectId = context?.projectId || 'unknown';
1024+
const source = context?.source || 'unknown';
1025+
const missingKey = `${chainId}::${projectId}::${source}`;
1026+
1027+
if (missingNetworkChainIdWithProjectSet.has(missingKey)) {
1028+
return networkName;
1029+
}
1030+
1031+
missingNetworkChainIdWithProjectSet.add(missingKey);
1032+
missingNetworkChainIdSet.add(chainId);
1033+
1034+
(globalThis as { __missingNetworkChainIds?: string[] }).__missingNetworkChainIds =
1035+
Array.from(missingNetworkChainIdSet);
1036+
1037+
console.warn(
1038+
`[NetworkMap] Missing chain id mapping: ${chainId} | project: ${projectName} (${projectId}) | source: ${source}`,
1039+
);
1040+
}
1041+
1042+
return networkName;
9741043
};

src/hooks/useProjectList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const ProjectItem: React.FC<{
2626
onClick?: () => void;
2727
}> = ({ project, makeRedirectHref, onClick }) => {
2828
const { getMetadataFromCid } = useProjectMetadata();
29-
const { manifest } = useGetDeploymentManifest(project.type === ProjectType.RPC ? project.deploymentId : '');
29+
const { manifest } = useGetDeploymentManifest(project.deploymentId);
3030

3131
const { data: metadata } = useAsyncMemo(() => getMetadataFromCid(project.metadata), [project]);
3232

0 commit comments

Comments
 (0)