Skip to content

Commit

Permalink
Activity page (#1057)
Browse files Browse the repository at this point in the history
* Use infinity activities query

* Add explorer link for transfer tx

* Style see more button

* Only show activity tab under slot path

* Distinguish send and receive transfer
  • Loading branch information
JunichiSugiura authored Nov 22, 2024
1 parent 3e8531a commit bfe69cf
Show file tree
Hide file tree
Showing 15 changed files with 230 additions and 61 deletions.
4 changes: 2 additions & 2 deletions examples/next/src/components/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ export function Profile() {
>
Trophies
</Button>
{/* <Button
<Button
onClick={() => ctrlConnector.controller.openProfile("activity")}
>
Activity
</Button> */}
</Button>
</div>
</div>
);
Expand Down
3 changes: 2 additions & 1 deletion packages/controller/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ export type ProfileOptions = IFrameOptions & {
export type ProfileContextTypeVariant =
| "inventory"
| "trophies"
| "achievements";
| "achievements"
| "activity";

export type ColorMode = "light" | "dark";

Expand Down
7 changes: 2 additions & 5 deletions packages/profile/src/components/achievements/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
LayoutHeader,
} from "@/components/layout";
import { Link } from "react-router-dom";
import { ScrollArea, Button, ArrowIcon, SpinnerIcon } from "@cartridge/ui-next";
import { ScrollArea, Button, ArrowIcon, Spinner } from "@cartridge/ui-next";
import { TrophiesTab, LeaderboardTab, Scoreboard } from "./tab";
import { useAccount, useUsername } from "@/hooks/account";
import { CopyAddress } from "@cartridge/ui-next";
Expand Down Expand Up @@ -129,10 +129,7 @@ export function Achievements() {
) : isLoading ? (
<LayoutContent className="pb-4 select-none">
<div className="flex justify-center items-center h-full border border-dashed rounded-md text-muted-foreground/10 mb-4">
<SpinnerIcon
className="animate-spin text-muted-foreground/30"
size="lg"
/>
<Spinner className="text-muted-foreground/30" size="lg" />
</div>
</LayoutContent>
) : (
Expand Down
137 changes: 105 additions & 32 deletions packages/profile/src/components/activity.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,42 @@
import { Card, CardContent, CheckIcon, CopyAddress } from "@cartridge/ui-next";
import { useTokenTransfersQuery } from "@cartridge/utils/api/indexer";
import {
ArrowFromLineIcon,
ArrowToLineIcon,
Button,
Card,
CardContent,
CopyAddress,
ExternalIcon,
ScrollArea,
} from "@cartridge/ui-next";
import { useInfiniteTokenTransfersQuery } from "@cartridge/utils/api/indexer";
import {
LayoutContainer,
LayoutContent,
LayoutContentError,
LayoutContentLoader,
LayoutHeader,
} from "@/components/layout";
import { Navigation } from "@/components/navigation";
import { useAccount } from "@/hooks/account";
import { Link } from "react-router-dom";
import { StarkscanUrl } from "@cartridge/utils";
import { useConnection } from "@/hooks/context";
import { constants } from "starknet";

export function Activity() {
const { address, username } = useAccount();
const { data } = useTokenTransfersQuery({ address });
const { chainId } = useConnection();
const { status, data, hasNextPage, fetchNextPage } =
useInfiniteTokenTransfersQuery(
{
address,
first: 30,
},
{
getNextPageParam: (lastPage) =>
lastPage.tokenTransfers?.pageInfo.endCursor,
},
);

return (
<LayoutContainer>
Expand All @@ -20,35 +46,82 @@ export function Activity() {
right={<Navigation />}
/>

<LayoutContent>
{data?.tokenTransfers?.edges ? (
<Card>
{data.tokenTransfers.edges.map(({ node: t }) => {
switch (t.tokenMetadata.__typename) {
case "ERC20__Token": {
return (
<CardContent className="flex items-center gap-1">
<CheckIcon size="sm" />
<div>
Send{" "}
{Number(t.tokenMetadata.amount) /
10 ** Number(t.tokenMetadata?.decimals)}{" "}
{t.tokenMetadata?.symbol}
</div>
</CardContent>
);
}
case "ERC721__Token":
return null;
}
})}
</Card>
) : (
<Card>
<CardContent>No data</CardContent>
</Card>
)}
</LayoutContent>
{(() => {
switch (status) {
case "loading": {
return <LayoutContentLoader />;
}
case "error": {
return <LayoutContentError />;
}
case "success": {
return (
<LayoutContent>
<ScrollArea>
<Card>
{data.pages.map((p) =>
p.tokenTransfers?.edges.length ? (
p.tokenTransfers.edges.map(({ node: t }) => {
switch (t.tokenMetadata.__typename) {
case "ERC20__Token": {
const isSend = t.from === address;
return (
<Link
to={StarkscanUrl(
chainId as constants.StarknetChainId,
).transaction(t.transactionHash)}
target="_blank"
key={t.transactionHash}
>
<CardContent className="flex items-center justify-between text-accent-foreground">
<div className="flex items-center gap-1">
{isSend ? (
<ArrowFromLineIcon variant="up" />
) : (
<ArrowToLineIcon variant="down" />
)}
<div>
{isSend ? "Send" : "Receive"}{" "}
{Number(t.tokenMetadata.amount) /
10 **
Number(
t.tokenMetadata?.decimals,
)}{" "}
{t.tokenMetadata?.symbol}
</div>
</div>

<ExternalIcon />
</CardContent>
</Link>
);
}
case "ERC721__Token":
return null;
}
})
) : (
<CardContent>No data</CardContent>
),
)}
</Card>

{hasNextPage && (
<Button
className="w-full my-2"
variant="ghost"
size="sm"
onClick={() => fetchNextPage()}
>
See More
</Button>
)}
</ScrollArea>
</LayoutContent>
);
}
}
})()}
</LayoutContainer>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,9 @@ export function Asset() {
<div className="text-muted-foreground">Contract</div>
{isPublicChain(chainId) ? (
<Link
to={`${StarkscanUrl(
chainId as constants.StarknetChainId,
).contract(collection.address)} `}
to={StarkscanUrl(chainId as constants.StarknetChainId).contract(
collection.address,
)}
className="flex items-center gap-1 text-sm"
target="_blank"
>
Expand Down
29 changes: 28 additions & 1 deletion packages/profile/src/components/layout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { Button, cn, TimesIcon, Network, DotsIcon } from "@cartridge/ui-next";
import {
Button,
cn,
TimesIcon,
Network,
DotsIcon,
Spinner,
ErrorImage,
} from "@cartridge/ui-next";
import { PropsWithChildren, useCallback } from "react";
import { useConnection } from "@/hooks/context";
import { isIframe } from "@cartridge/utils";
Expand Down Expand Up @@ -122,6 +130,25 @@ export function LayoutContent({
);
}

export function LayoutContentLoader() {
return (
<LayoutContent className="h-full flex items-center justify-center">
<Spinner size="lg" />
</LayoutContent>
);
}

export function LayoutContentError({
children = "Oops! Something went wrong.",
}: PropsWithChildren) {
return (
<div className="h-full flex flex-col items-center gap-8 p-8">
<div className="text-semibold">{children}</div>
<ErrorImage />
</div>
);
}

export function LayoutFooter({
children,
className,
Expand Down
3 changes: 2 additions & 1 deletion packages/profile/src/components/navigation.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
ClockIcon,
cn,
CoinsIcon,
StateIconProps,
Expand All @@ -22,7 +23,7 @@ export function Navigation() {
{project && namespace && (
<Item Icon={TrophyIcon} variant="achievements" />
)}
{/* <Item Icon={ClockIcon} variant="activity" /> */}
{project && <Item Icon={ClockIcon} variant="activity" />}
</div>
);
}
Expand Down
57 changes: 57 additions & 0 deletions packages/ui-next/src/components/error-image.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { cn } from "@/utils";
import { forwardRef } from "react";
import { memo } from "react";

export const ErrorImage = memo(
forwardRef<SVGSVGElement, { className?: string }>(
({ className, ...props }, forwardedRef) => (
<svg
viewBox="0 0 120 140"
className={cn("w-[120px] h-[140px] text-muted", className)}
ref={forwardedRef}
{...props}
>
<path
d="M66.9133 18.5224V54.9275C66.9133 60.1861 71.1474 64.4199 76.4058 64.4199C81.6671 64.4199 85.8347 60.1858 85.8347 54.9247C85.8347 50.6906 83.1027 47.0701 79.074 45.84C78.0495 45.5679 77.025 46.1122 76.7529 47.07C76.4808 48.0945 77.025 49.119 77.9828 49.3911C80.3734 50.1435 82.0809 52.3953 82.0809 54.8552C82.0809 57.9981 79.4849 60.5914 76.3447 60.5914C73.2018 60.5914 70.6085 57.9954 70.6085 54.8552L70.6032 18.5225C70.6032 17.498 69.7841 16.6789 68.7596 16.6789C67.7351 16.6763 66.9133 17.4979 66.9133 18.5224Z"
fill="currentColor"
/>
<path
d="M49.9733 43.1759V18.5206C49.9733 17.4961 49.1542 16.677 48.1297 16.677C47.1052 16.677 46.2861 17.4961 46.2861 18.5206V43.1786C46.2861 46.3215 43.6901 48.9148 40.5499 48.9148C37.407 48.9148 34.8138 46.3188 34.8138 43.1786C34.8138 40.652 36.5213 38.3975 38.9118 37.7145C39.9363 37.4424 40.4833 36.3485 40.1418 35.3934C39.8696 34.3689 38.7758 33.8219 37.8206 34.1634C33.792 35.3934 31.0599 39.0139 31.0599 43.2481C31.0599 48.5068 35.294 52.7406 40.5524 52.7406C45.8057 52.7379 49.9733 48.4346 49.9733 43.1759Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M45.9097 8.05898C41.4507 10.5298 36.4117 13.104 27.868 13.104C26.9165 13.104 26.1451 12.3326 26.1451 11.3811C26.1451 10.4296 26.9165 9.65821 27.868 9.65821C35.5548 9.65821 39.9836 7.40331 44.2395 5.045C44.4734 4.91541 44.7073 4.7851 44.9417 4.65453C49.0462 2.3679 53.2965 0 60.0222 0C66.7939 0 70.677 2.40184 74.4011 4.70536C74.6061 4.83216 74.8107 4.95867 75.0152 5.08446C78.8097 7.41854 82.7837 9.65811 90.42 9.65811C91.3716 9.65811 92.1429 10.4295 92.1429 11.381C92.1429 12.3325 91.3716 13.1039 90.42 13.1039C81.8815 13.1039 77.2659 10.5144 73.2098 8.01943C73.0201 7.90277 72.8322 7.78673 72.6457 7.67151C68.9648 5.39833 65.8032 3.44578 60.0222 3.44578C54.2062 3.44578 50.6478 5.42457 46.5353 7.71146C46.3283 7.82659 46.1199 7.9425 45.9097 8.05898Z"
fill="currentColor"
/>
<path
d="M24.3295 136.037C26.3612 136.037 73.676 136.037 95.6715 136.037L95.6715 72.6214H91.7081V84.5118C91.7081 88.8913 88.1608 92.4387 83.7812 92.4387L36.2198 92.4386C31.8403 92.4386 28.293 88.8913 28.293 84.5118L28.293 72.6214H24.3295L24.3295 136.037ZM20.3661 116.219V104.329H4.51228L4.51228 100.366H20.3661L20.3661 88.4752H4.51228L4.51228 84.5118H20.3661L20.3661 72.6214H4.51228L4.51227 132.073H20.3661L20.3661 120.183L4.51228 120.183L4.51228 116.219H20.3661ZM115.489 120.183H99.635V116.219H115.489L115.489 104.329H99.635V100.366H115.489V88.4752H99.635V84.5118H115.489V72.6214H99.635V132.073H115.489V120.183ZM99.635 136.037C99.635 138.226 97.8614 140 95.6715 140L24.3295 140C22.1397 140 20.3661 138.226 20.3661 136.037L4.51227 136.037C2.32242 136.037 0.548828 134.263 0.548828 132.073L0.548833 72.6214C0.548834 70.4316 2.32243 68.658 4.51228 68.658L115.489 68.658C117.679 68.658 119.452 70.4316 119.452 72.6214L119.452 132.073C119.452 134.263 117.679 136.037 115.489 136.037H99.635ZM87.7446 72.6214L32.2564 72.6214V84.5118C32.2564 86.7016 34.03 88.4752 36.2198 88.4752H83.7812C85.971 88.4752 87.7446 86.7016 87.7446 84.5118V72.6214Z"
fill="currentColor"
/>
<path
d="M60.2886 13.8057L60.2886 66.8749L60.2894 66.8758C60.2894 67.4872 60.2338 68.0867 60.1273 68.6694H56.3144C56.5008 68.0811 56.6014 67.4559 56.6014 66.8086L56.6014 13.8057C56.6014 12.7812 57.4205 11.9621 58.445 11.9621C59.4695 11.9621 60.2886 12.7812 60.2886 13.8057Z"
fill="currentColor"
/>
<path
d="M44.3758 68.6694C44.1979 68.1026 44.1018 67.5004 44.1018 66.8753C44.1018 64.1433 45.9454 61.6834 48.5413 60.9337C49.5659 60.6616 50.1128 59.5677 49.7713 58.6126C49.4992 57.5881 48.4053 57.0411 47.4502 57.3826C43.2827 58.6126 40.3482 62.5746 40.3482 66.9447C40.3482 67.5333 40.3993 68.1096 40.497 68.6694H44.3758Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M39.0655 125.37C38.9035 125.475 38.7538 125.572 38.6164 125.659C37.814 126.171 36.7489 125.935 36.2374 125.133C35.726 124.33 35.9617 123.265 36.7641 122.754C36.9023 122.666 37.054 122.568 37.2192 122.461C40.3151 120.46 48.1557 115.392 60.6367 115.392H60.6461L60.6555 115.392C67.1035 115.463 72.3657 116.844 76.224 118.413C80.0353 119.963 82.5682 121.735 83.5155 122.682C84.1883 123.355 84.1883 124.446 83.5155 125.119C82.8427 125.791 81.7518 125.791 81.079 125.119C80.5616 124.601 78.474 123.048 74.926 121.605C71.4269 120.182 66.5944 118.904 60.6273 118.838C49.1715 118.84 42.0425 123.447 39.0655 125.37Z"
fill="currentColor"
/>
<path
d="M48.6834 104.328C48.6834 106.19 47.1738 107.7 45.3115 107.7C43.4492 107.7 41.9396 106.19 41.9396 104.328C41.9396 102.466 43.4492 100.956 45.3115 100.956C47.1738 100.956 48.6834 102.466 48.6834 104.328Z"
fill="currentColor"
/>
<path
d="M78.7609 104.328C78.7609 106.19 77.2512 107.7 75.389 107.7C73.5267 107.7 72.017 106.19 72.017 104.328C72.017 102.466 73.5267 100.956 75.389 100.956C77.2512 100.956 78.7609 102.466 78.7609 104.328Z"
fill="currentColor"
/>
</svg>
),
),
);
2 changes: 1 addition & 1 deletion packages/ui-next/src/components/icons/state/checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const CheckboxIcon = memo(
return (
<path
fill-rule="evenodd"
clip-rule="evenodd"
clipRule="evenodd"
d="M4 6.28571C4 5.025 5.025 4 6.28571 4H17.7143C18.975 4 20 5.025 20 6.28571V17.7143C20 18.975 18.975 20 17.7143 20H6.28571C5.025 20 4 18.975 4 17.7143V6.28571ZM7.75 11.1C7.33579 11.1 7 11.5029 7 12C7 12.497 7.33579 12.9 7.75 12.9H16.25C16.6642 12.9 17 12.497 17 12C17 11.5029 16.6642 11.1 16.25 11.1H7.75Z"
className="fill-current"
/>
Expand Down
2 changes: 2 additions & 0 deletions packages/ui-next/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export * from "./copy-address";
export * from "./copy-text";
export * from "./error-image";
export * from "./icons";
export * from "./network";
export * from "./primitives";
export * from "./spinner";
6 changes: 3 additions & 3 deletions packages/ui-next/src/components/primitives/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/utils";
import { SpinnerIcon } from "../icons";
import { Spinner } from "../spinner";

const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 uppercase font-mono",
Expand All @@ -23,7 +23,7 @@ const buttonVariants = cva(
},
size: {
default: "h-10 px-4 tracking-wide text-base",
// sm: "h-9 px-3 text-sm",
sm: "h-9 px-3 text-sm",
// lg: "h-11 px-8 tracking-widest text-lg",
icon: "h-10 w-10",
},
Expand Down Expand Up @@ -64,7 +64,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
disabled={disabled || isLoading}
{...props}
>
{isLoading ? <SpinnerIcon className="animate-spin" /> : children}
{isLoading ? <Spinner /> : children}
</Comp>
);
},
Expand Down
11 changes: 11 additions & 0 deletions packages/ui-next/src/components/spinner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { cn } from "@/utils";
import { IconProps, SpinnerIcon } from "./icons";

export function Spinner({ className, ...props }: IconProps) {
return (
<SpinnerIcon
className={cn("animate-spin text-muted-foreground", className)}
{...props}
/>
);
}
Loading

0 comments on commit bfe69cf

Please sign in to comment.