Skip to content

Commit

Permalink
Add a /test-utils page for updating arena state on a local network;…
Browse files Browse the repository at this point in the history
… add shadcn for prototyping components
  • Loading branch information
xbtmatt committed Feb 12, 2025
1 parent a6b349c commit 115d911
Show file tree
Hide file tree
Showing 13 changed files with 375 additions and 65 deletions.
1 change: 1 addition & 0 deletions src/typescript/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@pontem/wallet-adapter-plugin": "^0.2.1",
"@popperjs/core": "^2.11.8",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-progress": "^1.1.1",
"@radix-ui/react-select": "^2.1.4",
"@radix-ui/react-slot": "^1.1.0",
Expand Down
22 changes: 22 additions & 0 deletions src/typescript/frontend/src/app/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,28 @@
--pink: #cd2f8d;
--warning: #ffb119;
--error: #f3263e;

/* Currently only used for testing and prototyping. */
--background: 224 71% 4%;
--foreground: 213 31% 91%;
--muted: 223 47% 11%;
--muted-foreground: 215.4 16.3% 56.9%;
--accent: 216 34% 17%;
--accent-foreground: 210 40% 98%;
--popover: 224 71% 4%;
--popover-foreground: 215 20.2% 65.1%;
--border: 216 34% 17%;
--input: 216 34% 17%;
--card: 224 71% 4%;
--card-foreground: 213 31% 91%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 1.2%;
--secondary: 222.2 47.4% 11.2%;
--secondary-foreground: 210 40% 98%;
--destructive: 0 63% 31%;
--destructive-foreground: 210 40% 98%;
--ring: 216 34% 17%;
--radius: 0.5rem;
}

.med-pixel-text {
Expand Down
2 changes: 0 additions & 2 deletions src/typescript/frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import DisplayDebugData from "@/store/server-to-client/FetchFromServer";
import { fontsStyle, notoColorEmoji } from "styles/fonts";
import { headers } from "next/headers";
import "@react95/core/themes/win95.css";
import { RandomEmojiBg } from "components/RandomEmojiBg";

export const metadata: Metadata = getDefaultMetadata();
export const viewport: Viewport = {
Expand All @@ -24,7 +23,6 @@ export default async function RootLayout({ children }: { children: React.ReactNo
<style dangerouslySetInnerHTML={{ __html: fontsStyle }} />
<StyledComponentsRegistry>
<Providers userAgent={userAgent}>
<RandomEmojiBg />
<DisplayDebugData />
{children}
</Providers>
Expand Down
17 changes: 17 additions & 0 deletions src/typescript/frontend/src/app/test-utils/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { SetMeleeDurationForm } from "@/components/pages/test-utils/SetNewMeleeDuration";
import { Network } from "@aptos-labs/ts-sdk";
import { APTOS_NETWORK } from "@sdk/const";

export const dynamic = "force-static";

export default function TestUtilsPage() {
return (
<>
{APTOS_NETWORK === Network.LOCAL && (
<div className="w-full h-full flex flex-col">
<SetMeleeDurationForm className="" />
</div>
)}
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"use client";

import { Button } from "@/components/ui/Button";
import { Input } from "@/components/ui/Input";
import { cn } from "lib/utils/class-name";
import { useState } from "react";
import { Ed25519PrivateKey, Hex, Account, Network } from "@aptos-labs/ts-sdk";
import { EmojicoinArena } from "@/contract-apis";
import { useAptos } from "context/wallet-context/AptosContextProvider";
import { isNumberInConstruction } from "@sdk/utils";
import { Label } from "@/components/ui/Label";
import { successfulTransactionToast } from "@/components/wallet/toasts";
import { toast } from "react-toastify";

const publisher = (() => {
// This is the publisher private key used in test.
const privateKeyString =
process.env.PUBLISHER_PRIVATE_KEY ??
"eaa964d1353b075ac63b0c5a0c1e92aa93355be1402f6077581e37e2a846105e";
const privateKey = new Ed25519PrivateKey(Hex.fromHexString(privateKeyString).toUint8Array());
return Account.fromPrivateKey({ privateKey });
})();

const MICROSECONDS_PER_MINUTE = 60 * 1000 * 1000;

export const SetMeleeDurationForm = ({ className }: React.HTMLAttributes<HTMLDivElement>) => {
const [duration, setDuration] = useState("");
const { aptos } = useAptos();

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => setDuration(e.target.value);

const handleClick = () => {
const dur = Number.parseFloat(duration);
const asMicroseconds = dur * MICROSECONDS_PER_MINUTE;
const floored = Math.floor(asMicroseconds);
const durationAsMicroseconds = BigInt(floored);
EmojicoinArena.SetNextMeleeDuration.submit({
aptosConfig: aptos.config,
emojicoinArena: publisher,
duration: durationAsMicroseconds,
}).then((res) => {
if (res.success) {
successfulTransactionToast(res, { name: Network.LOCAL });
} else {
toast.error("Fail.");
}
});
};

return (
<div
className={cn("flex flex-col w-full max-w-48 m-auto space-x-2 gap-1 font-forma", className)}
>
<div className="grid w-full max-w-sm items-center gap-1.5">
<Label className="text-white font-forma" htmlFor="duration">
{"Duration (minutes)"}
</Label>
<Input
id="duration"
value={duration}
className="text-lighter-gray"
type="text"
placeholder="in minutes"
onChange={handleChange}
/>
</div>
<Button
className="m-auto"
disabled={!isNumberInConstruction(duration)}
onClick={handleClick}
type="submit"
>
{"set next melee duration"}
</Button>
</div>
);
};
49 changes: 49 additions & 0 deletions src/typescript/frontend/src/components/ui/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "lib/utils/class-name";

const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline:
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);

export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />
);
}
);
Button.displayName = "Button";

export { Button, buttonVariants };
21 changes: 21 additions & 0 deletions src/typescript/frontend/src/components/ui/Input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { cn } from "lib/utils/class-name";
import * as React from "react";

const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
className
)}
ref={ref}
{...props}
/>
);
}
);
Input.displayName = "Input";

export { Input };
20 changes: 20 additions & 0 deletions src/typescript/frontend/src/components/ui/Label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"use client";

import * as React from "react";
import * as LabelPrimitive from "@radix-ui/react-label";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "lib/utils/class-name";

const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
);

const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />
));
Label.displayName = LabelPrimitive.Root.displayName;

export { Label };
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
"use client";
import { useWallet, type WalletName } from "@aptos-labs/wallet-adapter-react";

// cspell:word mkts
import { useWallet, type WalletName } from "@aptos-labs/wallet-adapter-react";

import { ONE_APT } from "@sdk/const";
import { CloseIconWithHover } from "components/svg";
import { useEventStore } from "context/event-store-context";
import { useAptos } from "context/wallet-context/AptosContextProvider";
import { Minus, X } from "lucide-react";
import { usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import { ROUTES } from "router/routes";
import { emoji } from "utils";

const iconClassName = "p-2 !text-white cursor-pointer !h-[40px] !w-[40px]";

export const StoreOnClient = () => {
const { account, connect, connected, wallet } = useWallet();
const { aptos, forceRefetch } = useAptos();
const storeMarkets = useEventStore((s) => s.markets);
const subscriptions = useEventStore((s) => s.subscriptions);
const pathname = usePathname();
const [showDebugger, setShowDebugger] = useState(false);
const registeredMarkets = useEventStore((s) => s.markets);
Expand All @@ -25,65 +28,60 @@ export const StoreOnClient = () => {
setEvents(storeMarkets.get(pathname.split("/market/")?.[1]));
}, [pathname, storeMarkets]);

const handleToggle = () => setShowDebugger((v) => !v);

return (
<>
<div className="fixed flex bottom-0 right-0 p-2 bg-black text-green text-xl">
<div className="m-auto">{registeredMarkets.size} mkts</div>
</div>

<div className="flex flex-col gap-1 fixed top-0 left-0 z-[50]">
<CloseIconWithHover
className="p-2 !text-white cursor-pointer !h-[40px] !w-[40px]"
onClick={() => setShowDebugger((v) => !v)}
/>
{showDebugger && subscriptions && (
<div
className={
"fixed flex flex-col top-0 left-0 bg-transparent " +
"text-xl max-h-[500px] w-[600px] overflow-x-clip overflow-y-scroll " +
"border-white border-solid border"
}
>
<div className="mt-[36px]">
<div className="relative left-[3ch] flex flex-row items-center bg-black">
<div className="text-green">
{"marketIDs:"}&nbsp;{Array.from(subscriptions.marketIDs).join(", ")}
</div>
<div className="text-green">
{"eventTypes:"}&nbsp;{Array.from(subscriptions.eventTypes).join(", ")}
</div>
</div>
</div>
</div>
{showDebugger ? (
<X onClick={handleToggle} className={iconClassName} />
) : (
<Minus onClick={handleToggle} className={iconClassName} />
)}
<button
className="p-3 !text-white h-auto w-auto m-auto border border-sky-100 border-solid uppercase"
onClick={async () => {
if (!account || !connected) {
if (wallet) {
connect("Petra" as WalletName);
} else {
try {
connect("Petra" as WalletName);
} catch (e) {
try {
connect("Connect with Google" as WalletName);
} catch (e) {
alert("No valid wallet to connect to.");
{showDebugger && (
<>
<button
className="flex p-3 !text-white h-auto m-auto border border-sky-100 border-solid uppercase min-w-[15ch]"
onClick={async () => {
if (!account || !connected) {
if (wallet) {
connect("Petra" as WalletName);
} else {
try {
connect("Petra" as WalletName);
} catch (e) {
try {
connect("Connect with Google" as WalletName);
} catch (e) {
alert("No valid wallet to connect to.");
}
}
}
} else {
await aptos.fundAccount({
accountAddress: account.address,
amount: ONE_APT * 10000000,
});
forceRefetch("apt");
}
}
} else {
await aptos.fundAccount({
accountAddress: account.address,
amount: ONE_APT * 10000000,
});
forceRefetch("apt");
}
}}
>
{"Fund me"} {emoji("money bag")}
</button>
}}
>
<span className="justify-start">
{"Fund me"} {emoji("money bag")}
</span>
</button>
<a
className="flex p-3 !text-white h-auto m-auto border border-sky-100 border-solid uppercase min-w-[15ch]"
href={ROUTES["test-utils"]}
>
<span className="justify-start">Test utils</span>
</a>
</>
)}
</div>
</>
);
Expand Down
1 change: 1 addition & 0 deletions src/typescript/frontend/src/router/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ export const ROUTES = {
market: "/market",
notFound: "/not-found",
pools: "/pools",
"test-utils": "/test-utils",
verify: "/verify",
} as const;
Loading

0 comments on commit 115d911

Please sign in to comment.