Skip to content

Commit 9142771

Browse files
authored
Merge branch 'main' into yash/infra-deployment
2 parents 44d8d8b + 1a45f21 commit 9142771

File tree

5 files changed

+193
-38
lines changed

5 files changed

+193
-38
lines changed

apps/dashboard/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
"react-day-picker": "^8.10.1",
8686
"react-dom": "18.3.1",
8787
"react-dropzone": "^14.2.9",
88+
"react-error-boundary": "^4.1.2",
8889
"react-hook-form": "7.52.0",
8990
"react-intersection-observer": "^9.10.3",
9091
"react-markdown": "^9.0.1",
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { CopyTextButton } from "@/components/ui/CopyTextButton";
2+
import { ScrollShadow } from "@/components/ui/ScrollShadow/ScrollShadow";
3+
import { useMemo } from "react";
4+
5+
export function UnexpectedValueErrorMessage(props: {
6+
value: unknown;
7+
title: string;
8+
description: string;
9+
className?: string;
10+
}) {
11+
const stringifiedValue = useMemo(() => {
12+
try {
13+
return JSON.stringify(props.value, null, 2);
14+
} catch {
15+
return undefined;
16+
}
17+
}, [props.value]);
18+
19+
return (
20+
<div className={props.className}>
21+
<p className="text-base text-destructive-text"> {props.title} </p>
22+
<p className="text-foreground text-sm">{props.description}</p>
23+
{stringifiedValue && (
24+
<div className="mt-3">
25+
<p className="mb-0.5 text-muted-foreground text-sm">Value Received</p>
26+
<ScrollShadow className="rounded-lg bg-muted/50">
27+
<code className="block whitespace-pre p-4 font-mono text-xs">
28+
{stringifiedValue}
29+
</code>
30+
</ScrollShadow>
31+
32+
<CopyTextButton
33+
copyIconPosition="left"
34+
textToShow="Copy value"
35+
textToCopy={stringifiedValue}
36+
tooltip="Copy value"
37+
className="-translate-x-2 mt-1 text-muted-foreground"
38+
variant="ghost"
39+
/>
40+
</div>
41+
)}
42+
</div>
43+
);
44+
}

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/token-id.tsx

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"use client";
22

3+
import { UnexpectedValueErrorMessage } from "@/components/blocks/error-fallbacks/unexpect-value-error-message";
34
import { WalletAddress } from "@/components/blocks/wallet-address";
45
import { CopyTextButton } from "@/components/ui/CopyTextButton";
56
import { Spinner } from "@/components/ui/Spinner/Spinner";
@@ -53,6 +54,8 @@ interface TokenIdPageProps {
5354
isErc721: boolean;
5455
}
5556

57+
// TODO: verify the entire nft object with zod schema and display an error message
58+
5659
export const TokenIdPage: React.FC<TokenIdPageProps> = ({
5760
contract,
5861
tokenId,
@@ -132,15 +135,15 @@ export const TokenIdPage: React.FC<TokenIdPageProps> = ({
132135
height={isMobile ? "100%" : "300px"}
133136
/>
134137
</Card>
138+
135139
<Flex flexDir="column" gap={6} w="full" px={2}>
136-
<Flex flexDir="column" gap={2}>
137-
<Heading size="title.lg">{nft.metadata.name}</Heading>
140+
<Flex flexDir="column" gap={1.5}>
141+
<NFTName value={nft.metadata.name} />
138142
{nft.metadata?.description && (
139-
<Text size="label.md" noOfLines={50} whiteSpace="pre-wrap">
140-
{nft.metadata.description}
141-
</Text>
143+
<NFTDescription value={nft.metadata.description} />
142144
)}
143145
</Flex>
146+
144147
<Flex flexDir="column" gap={{ base: 0, md: 4 }}>
145148
<Box
146149
w="full"
@@ -338,3 +341,39 @@ export const TokenIdPage: React.FC<TokenIdPageProps> = ({
338341
</Flex>
339342
);
340343
};
344+
345+
function NFTName(props: {
346+
value: unknown;
347+
}) {
348+
if (typeof props.value === "string") {
349+
return (
350+
<h2 className="font-semibold text-lg tracking-tight"> {props.value}</h2>
351+
);
352+
}
353+
354+
return (
355+
<UnexpectedValueErrorMessage
356+
title="Invalid Name"
357+
description="Name is not a string"
358+
value={props.value}
359+
className="mb-3 rounded-lg border border-border p-4"
360+
/>
361+
);
362+
}
363+
364+
function NFTDescription(props: {
365+
value: unknown;
366+
}) {
367+
if (typeof props.value === "string") {
368+
return <p className="text-muted-foreground"> {props.value}</p>;
369+
}
370+
371+
return (
372+
<UnexpectedValueErrorMessage
373+
title="Invalid Description"
374+
description="Description is not a string"
375+
value={props.value}
376+
className="mb-3 rounded-lg border border-border p-4"
377+
/>
378+
);
379+
}

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/table.tsx

Lines changed: 84 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
"use client";
22

3+
import { UnexpectedValueErrorMessage } from "@/components/blocks/error-fallbacks/unexpect-value-error-message";
34
import { WalletAddress } from "@/components/blocks/wallet-address";
5+
import { CopyTextButton } from "@/components/ui/CopyTextButton";
46
import { useDashboardRouter } from "@/lib/DashboardRouter";
7+
import { cn } from "@/lib/utils";
58
import {
69
Flex,
710
IconButton,
@@ -16,6 +19,7 @@ import {
1619
Thead,
1720
Tr,
1821
} from "@chakra-ui/react";
22+
import * as Sentry from "@sentry/nextjs";
1923
import { MediaCell } from "components/contract-pages/table/table-columns/cells/media-cell";
2024
import { useChainSlug } from "hooks/chains/chainSlug";
2125
import {
@@ -26,6 +30,7 @@ import {
2630
ChevronRightIcon,
2731
} from "lucide-react";
2832
import { useEffect, useMemo, useState } from "react";
33+
import { ErrorBoundary, type FallbackProps } from "react-error-boundary";
2934
import {
3035
type CellProps,
3136
type Column,
@@ -72,24 +77,51 @@ export const NFTGetAllTable: React.FC<ContractOverviewNFTGetAllProps> = ({
7277
{
7378
Header: "Name",
7479
accessor: (row) => row.metadata.name,
75-
Cell: (cell: CellProps<NFT, string>) => (
76-
<Text noOfLines={1} size="label.md">
77-
{cell.value}
78-
</Text>
79-
),
80+
Cell: (cell: CellProps<NFT, string>) => {
81+
if (typeof cell.value !== "string") {
82+
return (
83+
<UnexpectedValueErrorMessage
84+
title="Invalid Name"
85+
description="Name is not a string"
86+
value={cell.value}
87+
className="w-[300px] py-3"
88+
/>
89+
);
90+
}
91+
92+
return (
93+
<p className="line-clamp-1 text-muted-foreground text-sm">
94+
{cell.value}
95+
</p>
96+
);
97+
},
8098
},
8199
{
82100
Header: "Description",
83101
accessor: (row) => row.metadata.description,
84-
Cell: (cell: CellProps<NFT, string>) => (
85-
<Text
86-
noOfLines={4}
87-
size="body.md"
88-
fontStyle={!cell.value ? "italic" : "normal"}
89-
>
90-
{cell.value || "No description"}
91-
</Text>
92-
),
102+
Cell: (cell: CellProps<NFT, string>) => {
103+
if (typeof cell.value !== "string") {
104+
return (
105+
<UnexpectedValueErrorMessage
106+
title="Invalid description"
107+
description="Description is not a string"
108+
value={cell.value}
109+
className="w-[300px] py-3"
110+
/>
111+
);
112+
}
113+
114+
return (
115+
<p
116+
className={cn(
117+
"line-clamp-4 max-w-[200px] whitespace-normal text-muted-foreground text-sm",
118+
{ italic: !cell.value },
119+
)}
120+
>
121+
{cell.value || "No description"}
122+
</p>
123+
);
124+
},
93125
},
94126
];
95127
if (isErc721) {
@@ -280,18 +312,22 @@ export const NFTGetAllTable: React.FC<ContractOverviewNFTGetAllProps> = ({
280312
// biome-ignore lint/suspicious/noArrayIndexKey: FIXME
281313
key={rowIndex}
282314
>
283-
{row.cells.map((cell, cellIndex) => (
284-
<Td
285-
{...cell.getCellProps()}
286-
borderBottomWidth="inherit"
287-
borderColor="borderColor"
288-
maxW="sm"
289-
// biome-ignore lint/suspicious/noArrayIndexKey: FIXME
290-
key={cellIndex}
291-
>
292-
{cell.render("Cell")}
293-
</Td>
294-
))}
315+
{row.cells.map((cell, cellIndex) => {
316+
return (
317+
<Td
318+
{...cell.getCellProps()}
319+
borderBottomWidth="inherit"
320+
borderColor="borderColor"
321+
maxW="sm"
322+
// biome-ignore lint/suspicious/noArrayIndexKey: FIXME
323+
key={cellIndex}
324+
>
325+
<ErrorBoundary FallbackComponent={NFTCellErrorBoundary}>
326+
{cell.render("Cell")}
327+
</ErrorBoundary>
328+
</Td>
329+
);
330+
})}
295331
<Td borderBottomWidth="inherit" borderColor="borderColor">
296332
{!failedToLoad && <ArrowRightIcon className="size-4" />}
297333
</Td>
@@ -374,3 +410,25 @@ export const NFTGetAllTable: React.FC<ContractOverviewNFTGetAllProps> = ({
374410
</Flex>
375411
);
376412
};
413+
414+
function NFTCellErrorBoundary(errorProps: FallbackProps) {
415+
// eslint-disable-next-line no-restricted-syntax
416+
useEffect(() => {
417+
Sentry.withScope((scope) => {
418+
scope.setTag("component-crashed", "true");
419+
scope.setTag("component-crashed-boundary", "NFTCellErrorBoundary");
420+
scope.setLevel("fatal");
421+
Sentry.captureException(errorProps.error);
422+
});
423+
}, [errorProps.error]);
424+
425+
return (
426+
<CopyTextButton
427+
textToShow={errorProps.error.message}
428+
textToCopy={errorProps.error.message}
429+
copyIconPosition="left"
430+
tooltip={errorProps.error.message}
431+
className="max-w-[250px] truncate text-destructive-text"
432+
/>
433+
);
434+
}

pnpm-lock.yaml

Lines changed: 20 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)