Skip to content
Merged
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
10 changes: 10 additions & 0 deletions examples/next/next.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/** @type {import('next').NextConfig} */
const path = require("path");

const nextConfig = {
productionBrowserSourceMaps: true,
reactStrictMode: true,
Expand All @@ -24,6 +26,14 @@ const nextConfig = {
config.plugins.push(new WasmChunksFixPlugin());
}

config.resolve.alias = {
...config.resolve.alias,
[path.resolve(__dirname, "src/assets")]: path.resolve(
path.dirname(require.resolve("@cartridge/ui")),
"assets",
),
};

return config;
},
};
Expand Down
1 change: 1 addition & 0 deletions examples/next/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
"**/*.otf",
".next/types/**/*.ts",
"**/*.wasm"
],
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
},
"dependencies": {
"@cartridge/presets": "github:cartridge-gg/presets#c064e82",
"@cartridge/ui": "github:cartridge-gg/ui#e5405d6",
"@cartridge/ui": "catalog:",
"tailwindcss": "catalog:",
"@graphql-codegen/cli": "^2.6.2",
"@graphql-codegen/typescript": "^2.4.8",
Expand Down
54 changes: 23 additions & 31 deletions packages/keychain/src/components/activity.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useCallback, useMemo, useState } from "react";
import { Link } from "react-router-dom";
import {
ActivityAchievementCard,
ActivityCollectibleCard,
Expand All @@ -12,30 +14,10 @@ import {
import { cn } from "@cartridge/ui/utils";
import { useExplorer } from "@starknet-react/core";
import { useData } from "@/hooks/data";
import { useCallback, useMemo, useState } from "react";
import { Link } from "react-router-dom";
import { CardProps } from "@/components/provider/data";

const OFFSET = 100;

interface CardProps {
variant: "token" | "collectible" | "game" | "achievement";
key: string;
transactionHash: string;
amount: string;
address: string;
value: string;
name: string;
collection: string;
image: string;
title: string;
website: string;
certified: boolean;
action: "send" | "receive" | "mint";
timestamp: number;
date: string;
points?: number;
}

export function Activity() {
const [cap, setCap] = useState(OFFSET);
const explorer = useExplorer();
Expand Down Expand Up @@ -66,25 +48,29 @@ export function Activity() {
{dates.map((current) => {
return (
<div key={current} className="flex flex-col gap-y-2 select-none">
<p className="text-foreground-300 text-sm font-medium">{current}</p>
<div className="flex flex-col gap-y-px">
<p className="text-foreground-400 text-xs font-bold uppercase mb-2 leading-none">
{current}
</p>
<div className="flex flex-col gap-y-2">
{events
.filter((event) => event.date === current)
.map((props: CardProps, index: number) => {
switch (props.variant) {
case "token":
return (
<Link
key={`${index}-${props.key}`}
key={`${index}-${props.key}-${props.username}`}
to={to(props.transactionHash)}
target="_blank"
>
<ActivityTokenCard
amount={props.amount}
address={props.address}
username={props.username}
value={props.value}
image={props.image}
action={props.action}
timestamp={props.timestamp * 1000}
/>
</Link>
);
Expand All @@ -99,8 +85,10 @@ export function Activity() {
name={props.name}
collection={props.collection}
address={props.address}
username={props.username}
image={props.image}
action={props.action}
timestamp={props.timestamp * 1000}
/>
</Link>
);
Expand All @@ -112,10 +100,12 @@ export function Activity() {
target="_blank"
>
<ActivityGameCard
title={props.title}
action={props.title}
name={props.name}
themeColor={props.color}
website={props.website}
image={props.image}
certified={props.certified}
timestamp={props.timestamp * 1000}
/>
</Link>
);
Expand All @@ -125,10 +115,12 @@ export function Activity() {
key={`${index}-${props.key}`}
title={"Achievement"}
topic={props.title}
website={props.website}
image={props.image}
certified={props.certified}
points={props.points || 0}
themeColor={props.color}
website={props.website}
certified={props.certified}
timestamp={props.timestamp * 1000}
/>
);
}
Expand All @@ -152,20 +144,20 @@ export function Activity() {
);
}

const LoadingState = () => {
export const LoadingState = ({ rowCount = 5 }: { rowCount?: number }) => {
return (
<LayoutContent>
<Skeleton className="w-1/5 h-4 py-4 rounded" />
<div className="flex flex-col gap-2">
{Array.from({ length: 20 }).map((_, index) => (
{Array.from({ length: rowCount }).map((_, index) => (
<Skeleton key={index} className="w-full h-16 rounded" />
))}
</div>
</LayoutContent>
);
};

const EmptyState = () => {
export const EmptyState = () => {
return (
<LayoutContent>
<Empty
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ export function CollectibleAsset() {
</CollectibleItems>
</TabsContent>
<TabsContent
className="m-0 p-0 flex flex-col gap-px"
className="m-0 p-0 flex flex-col gap-2"
value="activity"
>
{events.map((props: CardProps, index: number) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export function Collectible() {
? asset.name
: `${asset.name} #${parseInt(BigInt(asset.tokenId).toString())}`
}
clickable
selectable={false}
images={[...asset.imageUrls, placeholder]}
totalCount={asset.amount}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ export function CollectionAsset() {
/>
</TabsContent>
<TabsContent
className="m-0 p-0 flex flex-col gap-px"
className="m-0 p-0 flex flex-col gap-2"
value="activity"
>
{events.map((props: CardProps, index: number) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ function LazyAsset({
: `${asset.name} #${parseInt(BigInt(asset.tokenId).toString())}`
}
images={[...asset.imageUrls, placeholder]}
clickable
selectable
selected={isSelected}
listingCount={listingCount}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export function Collections() {
totalCount={collection.totalCount}
listingCount={listingCount}
selectable={false}
clickable
/>
</Link>
);
Expand Down
114 changes: 73 additions & 41 deletions packages/keychain/src/components/inventory/token/token.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ import { useData } from "@/hooks/data";
import { getDate, isPublicChain, useCreditBalance } from "@cartridge/ui/utils";
import { useExplorer } from "@starknet-react/core";
import { constants, getChecksumAddress } from "starknet";
import { useAccount } from "@/hooks/account";
import { useAccount, useUsernames } from "@/hooks/account";
import { useToken } from "@/hooks/token";
import { useCallback, useMemo } from "react";
import { useConnection } from "@/hooks/connection";
import { useVersion } from "@/hooks/version";
import { useNavigation } from "@/context/navigation";
import { EmptyState, LoadingState } from "@/components/activity";

export function Token() {
const { address } = useParams<{ address: string }>();
Expand Down Expand Up @@ -116,7 +117,7 @@ function ERC20() {
const accountAddress = account?.address || "";
const { controller } = useConnection();
const explorer = useExplorer();
const { transfers } = useData();
const { transfers, status } = useData();
const { isControllerGte } = useVersion();

const chainId = constants.StarknetChainId.SN_MAIN; // Use mainnet as default
Expand All @@ -127,6 +128,22 @@ function ERC20() {
return isControllerGte("0.5.6");
}, [isControllerGte]);

const addresses = useMemo<string[]>(() => {
const accounts =
transfers?.transfers?.items.flatMap((item) =>
item.transfers.reduce(
(acc, item) => [
...acc,
`0x${BigInt(item.fromAddress).toString(16)}`,
`0x${BigInt(item.toAddress).toString(16)}`,
],
[] as string[],
),
) ?? [];
return Array.from(new Set(accounts));
}, [transfers]);
const { getUsername } = useUsernames({ addresses });

const txs = useMemo(() => {
if (!transfers || !token?.metadata?.image) {
return [];
Expand All @@ -147,11 +164,14 @@ function ERC20() {
transactionHash: transfer.transactionHash,
amount: value,
to: transfer.toAddress,
toUsername: getUsername(transfer.toAddress),
from: transfer.fromAddress,
fromUsername: getUsername(transfer.fromAddress),
contractAddress: transfer.contractAddress,
symbol: transfer.symbol,
eventId: transfer.eventId,
date: date,
timestamp,
image,
action:
getChecksumAddress(transfer.fromAddress) ===
Expand All @@ -161,7 +181,7 @@ function ERC20() {
};
});
});
}, [transfers, accountAddress, token?.metadata?.image]);
}, [transfers, accountAddress, getUsername, token?.metadata?.image]);

const to = useCallback(
(transactionHash: string) => {
Expand All @@ -185,44 +205,56 @@ function ERC20() {
chainId={chainId as constants.StarknetChainId}
/>

<div className="flex flex-col gap-3">
{Object.entries(
txs
.filter((tx) => tx?.symbol === token.metadata.symbol)
.reduce(
(acc, tx) => {
if (!acc[tx.date]) {
acc[tx.date] = [];
}
acc[tx.date].push(tx);
return acc;
},
{} as Record<string, typeof txs>,
),
).map(([date, transactions]) => (
<div key={date} className="flex flex-col gap-3">
<p className="text-foreground-400 font-semibold text-xs py-3 tracking-wider">
{date}
</p>
{transactions.map((item) => (
<Link
key={item.key}
to={to(item.transactionHash)}
target="_blank"
>
<ActivityTokenCard
amount={item.amount}
// no price available from the oracle for $PAPER
value=""
address={item.action === "send" ? item.to : item.from}
image={token.metadata.image!}
action={item.action}
/>
</Link>
))}
</div>
))}
</div>
{status === "loading" ? (
<LoadingState rowCount={2} />
) : status === "error" || !txs.length ? (
<EmptyState />
) : (
<div className="flex flex-col gap-2">
{Object.entries(
txs
.filter((tx) => tx?.symbol === token.metadata.symbol)
.reduce(
(acc, tx) => {
if (!acc[tx.date]) {
acc[tx.date] = [];
}
acc[tx.date].push(tx);
return acc;
},
{} as Record<string, typeof txs>,
),
).map(([date, transactions]) => (
<div key={date} className="flex flex-col gap-2">
<p className="text-foreground-400 text-xs font-bold uppercase py-2">
{date}
</p>
{transactions.map((item) => (
<Link
key={item.key}
to={to(item.transactionHash)}
target="_blank"
>
<ActivityTokenCard
amount={item.amount}
// no price available from the oracle for $PAPER
value=""
address={item.action === "send" ? item.to : item.from}
username={
item.action === "send"
? item.toUsername
: item.fromUsername
}
image={token.metadata.image!}
action={item.action}
timestamp={item.timestamp}
/>
</Link>
))}
</div>
))}
</div>
)}
</LayoutContent>

{compatibility && controller && (
Expand Down
Loading
Loading