Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions apps/dave/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@ import { Providers } from '../src/providers/Providers';
import theme from "../src/providers/theme";
import './global.css';

// @ts-expect-error JSON.stringify will try to call toJSON on bigints. ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#use_within_json
BigInt.prototype.toJSON = function () {
return this.toString();
};
try {
// @ts-expect-error JSON.stringify will try to call toJSON on bigints. ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#use_within_json
BigInt.prototype.toJSON = function () {
return this.toString();
};

} catch (error: unknown) {
console.info((error as Error).message)
}

const withLayout = (StoryFn: StoryFn, context: StoryContext) => {
const { title } = context;
Expand Down
5 changes: 3 additions & 2 deletions apps/dave/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
"format": "prettier --write \"**/*.{ts,tsx,md}\""
},
"dependencies": {
"@cartesi/viem": "2.0.0-alpha.24",
"@cartesi/wagmi": "2.0.0-alpha.27",
"@cartesi/viem": "2.0.0-alpha.26",
"@cartesi/wagmi": "2.0.0-alpha.30",
"@mantine/core": "^8.3.13",
"@mantine/form": "^8.3.13",
"@mantine/hooks": "^8.3.13",
Expand All @@ -31,6 +31,7 @@
"date-fns": "^4.1.0",
"humanize-duration": "^3.33.2",
"next": "^16.1.5",
"pretty-ms": "^8",
"ramda": "^0.32.0",
"ramda-adjunct": "^5.1.0",
"react": "catalog:",
Expand Down
80 changes: 80 additions & 0 deletions apps/dave/src/components/Address.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"use client";

import {
Anchor,
Group,
type GroupProps,
type MantineStyleProp,
Text,
Tooltip,
} from "@mantine/core";
import Link from "next/link";
import { type FC } from "react";
import { jsNumberForAddress } from "react-jazzicon";
import Jazzicon from "react-jazzicon/dist/Jazzicon";
import { type Address as AddressType, getAddress } from "viem";
import CopyButton from "./CopyButton";

import RollupContractResolver from "../lib/rollupContractResolver";
import { shortenHash } from "../lib/textUtils";

export interface AddressProps extends GroupProps {
value: AddressType;
href?: string;
hrefTarget?: "_self" | "_blank" | "_top" | "_parent";
icon?: boolean;
iconSize?: number;
shorten?: boolean;
canCopy?: boolean;
}

const Address: FC<AddressProps> = ({
href,
value,
icon,
iconSize,
shorten,
hrefTarget = "_self",
canCopy = true,
...restProps
}) => {
value = getAddress(value);
const name = RollupContractResolver.resolveName(value);
const text = shorten ? shortenHash(value) : value;
const textStyle: MantineStyleProp = { wordBreak: "break-all" };

const label = name ? (
<Tooltip label={value} withArrow>
<Text style={textStyle}>{name}</Text>
</Tooltip>
) : shorten ? (
<Tooltip label={value} withArrow>
<Text style={textStyle}>{text}</Text>
</Tooltip>
) : (
<Text style={textStyle}>{text}</Text>
);
return (
<Group gap={0} {...restProps}>
<Group gap={8}>
{icon && (
<Jazzicon
diameter={iconSize ?? 20}
seed={jsNumberForAddress(value)}
/>
)}

{href ? (
<Anchor href={href} component={Link} target={hrefTarget}>
{label}
</Anchor>
) : (
label
)}
</Group>
{canCopy && <CopyButton value={value} />}
</Group>
);
};

export default Address;
47 changes: 47 additions & 0 deletions apps/dave/src/components/CopyButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {
ActionIcon,
CopyButton as MantineCopyButton,
rem,
Tooltip,
} from "@mantine/core";
import { type FC } from "react";
import { TbCheck, TbCopy } from "react-icons/tb";

interface CopyButtonProps {
value: string;
}

const CopyButton: FC<CopyButtonProps> = ({ value }) => {
return (
<MantineCopyButton value={value} timeout={2000}>
{({ copied, copy }) => (
<Tooltip
label={copied ? "Copied" : "Copy"}
withArrow
position="right"
>
<ActionIcon
color={copied ? "teal" : "gray"}
variant="subtle"
aria-label="Copy address"
onClick={copy}
>
{copied ? (
<TbCheck
data-testid="copied-icon"
style={{ width: rem(16) }}
/>
) : (
<TbCopy
data-testid="copy-icon"
style={{ width: rem(16) }}
/>
)}
</ActionIcon>
</Tooltip>
)}
</MantineCopyButton>
);
};

export default CopyButton;
49 changes: 49 additions & 0 deletions apps/dave/src/components/PrettyTime.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Button, Text, type TextProps } from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import prettyMillis, { type Options } from "pretty-ms";
import { Activity, type FC } from "react";

interface PrettyTimeProps {
milliseconds: number;
options?: Options;
displayTimestampUTC?: boolean;
size?: TextProps["size"];
}

const defaultOpts = {
unitCount: 2,
secondsDecimalDigits: 0,
verbose: true,
};

export const PrettyTime: FC<PrettyTimeProps> = ({
milliseconds,
options,
displayTimestampUTC = false,
size,
}) => {
const opts: Options = Object.assign({ ...defaultOpts }, options);
const [asTimestamp, handlers] = useDisclosure(false);
const text = asTimestamp
? new Date(milliseconds).toISOString()
: `${prettyMillis(Date.now() - milliseconds, opts)} ago`;

return (
<>
<Activity mode={displayTimestampUTC ? "visible" : "hidden"}>
<Button
variant="transparent"
px={0}
onClick={() => {
if (displayTimestampUTC) handlers.toggle();
}}
>
{text}
</Button>
</Activity>
<Activity mode={!displayTimestampUTC ? "visible" : "hidden"}>
<Text size={size}>{text}</Text>
</Activity>
</>
);
};
44 changes: 44 additions & 0 deletions apps/dave/src/components/QueryPagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { Pagination as QPagination } from "@cartesi/viem";
import { Group, Pagination, type GroupProps } from "@mantine/core";
import type { FC } from "react";

const getActivePage = (offset: number, limit: number) => {
const safeLimit = limit === 0 ? 1 : limit;
return offset / safeLimit + 1;
};

type QueryPaginationProps = {
pagination: QPagination;
onPaginationChange?: (newOffset: number) => void;
groupProps?: GroupProps;
hideIfSinglePage?: boolean;
};

export const QueryPagination: FC<QueryPaginationProps> = ({
onPaginationChange,
pagination,
groupProps,
hideIfSinglePage = true,
}) => {
const totalPages = Math.ceil(pagination.totalCount / pagination.limit);
const activePage = getActivePage(pagination.offset, pagination.limit);
const displayPagination = totalPages > 1 && hideIfSinglePage;

if (!displayPagination) return "";

return (
<Group justify="flex-end" {...groupProps}>
<Pagination
total={totalPages}
value={activePage}
onChange={(newPageNumber) => {
if (newPageNumber !== activePage) {
onPaginationChange?.(
newPageNumber * pagination.limit - pagination.limit,
);
}
}}
/>
</Group>
);
};
33 changes: 33 additions & 0 deletions apps/dave/src/components/TransactionHash.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Flex, Text } from "@mantine/core";
import { type FC } from "react";
import { useConfig } from "wagmi";
import { shortenHash } from "../lib/textUtils";
import { BlockExplorerLink } from "./BlockExplorerLink";
import CopyButton from "./CopyButton";

interface TransactionHashProps {
transactionHash: string;
}

const TransactionHash: FC<TransactionHashProps> = ({ transactionHash }) => {
const config = useConfig();

const Link = BlockExplorerLink({
value: transactionHash,
type: "tx",
chain: config.chains[0],
});

return (
<Flex align="center">
{Link === null ? (
<Text>{shortenHash(transactionHash)}</Text>
) : (
<>{Link}</>
)}{" "}
<CopyButton value={transactionHash} />
</Flex>
);
};

export default TransactionHash;
Loading
Loading