Skip to content

Commit

Permalink
Merge pull request #7 from Numoen/ui
Browse files Browse the repository at this point in the history
ui
  • Loading branch information
kyscott18 authored Oct 9, 2023
2 parents 8979efa + 5f73296 commit 3e12528
Show file tree
Hide file tree
Showing 34 changed files with 3,958 additions and 92 deletions.
41 changes: 41 additions & 0 deletions packages/next-interface/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build
cache
dist

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local
.env

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

# @wagmi/cli
generated.ts
26 changes: 26 additions & 0 deletions packages/next-interface/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# reverse-mirage-next-example

## 0.1.3

### Patch Changes

- Updated dependencies [aef4b34]
- reverse-mirage@1.0.2

## 0.1.2

### Patch Changes

- Updated dependencies [041bcc9]
- reverse-mirage@1.0.1

## 0.1.1

### Patch Changes

- Updated dependencies [ad4046c]
- Updated dependencies [22f43d0]
- Updated dependencies [64ac88a]
- Updated dependencies [51e3d32]
- Updated dependencies [89add6a]
- reverse-mirage@1.0.0
34 changes: 34 additions & 0 deletions packages/next-interface/biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"$schema": "https://biomejs.dev/schemas/1.0.0/schema.json",
"files": {
"ignore": [
"**/node_modules",
"CHANGELOG.md",
"dist",
".next",
"tsconfig.json",
"gql",
"tsconfig.*.json",
"generated.ts",
"pnpm-lock.yaml"
]
},
"organizeImports": {
"enabled": true
},
"formatter": {
"enabled": true,
"formatWithErrors": true,
"indentStyle": "space",
"indentSize": 2,
"lineWidth": 80
},
"linter": {
"enabled": true,
"rules": {
"style": {
"noNonNullAssertion": "off"
}
}
}
}
18 changes: 18 additions & 0 deletions packages/next-interface/components/asyncButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useConnectModal } from "@rainbow-me/rainbowkit";
import { useAccount } from "wagmi";

import type { ButtonProps } from "./button";
import Button from "./button";

export default function AsyncButton(props: ButtonProps) {
const { isConnected } = useAccount();
const { openConnectModal } = useConnectModal();

return isConnected ? (
<Button {...props} />
) : openConnectModal ? (
<Button {...props} disabled={false} onClick={openConnectModal}>
{"Connect Wallet"}
</Button>
) : null;
}
165 changes: 165 additions & 0 deletions packages/next-interface/components/beet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import { clsx } from "clsx";
import Link from "next/link";
import React from "react";
import { toast } from "react-hot-toast";
import type { TransactionReceipt } from "viem";
import type { Address } from "wagmi";
import { useNetwork } from "wagmi";

export type BeetTx = {
title: string;
description: string;
callback: (toast: TxToast) => Promise<TransactionReceipt>;
};
export type BeetStage = { title: string; parallelTxs: readonly BeetTx[] };

export type TxToast = {
id: string;
title: string;
description: string;
humanCount: string;
};
type TxSending = TxToast & { status: "sending" };
type TxSuccess = TxToast & { status: "success"; receipt: TransactionReceipt };
type TxPending = TxToast & { status: "pending"; hash: string };
type TxError = TxToast & { status: "error"; error?: string };

const genRanHex = (size: number) => {
const chars = "0123456789abcdef";
let result = "";
for (let i = 0; i < size; i++) {
result += chars[Math.floor(Math.random() * chars.length)];
}
return result;
};

export const Beet = async (stages: readonly BeetStage[]) => {
const random = genRanHex(12); // to prevent toast collisions

const totaltx = stages.reduce((acc, cur) => acc + cur.parallelTxs.length, 0);

for (const [stageIndex, stage] of stages.entries()) {
if (stageIndex !== 0) await new Promise((r) => setTimeout(r, 150));

const previousTxs = [...Array(stageIndex).keys()].reduce(
(acc, i) => acc + (stages[i]?.parallelTxs.length ?? 0),
0,
);

try {
await Promise.all(
stage.parallelTxs.map(async (beetTx, i) =>
beetTx.callback({
id: `${random}-${stageIndex}-${i}`,
title: beetTx.title,
description: beetTx.description,
humanCount: `(${1 + i + previousTxs}/${totaltx})`,
}),
),
);
} catch (err) {
console.error(err);
return;
}
}
};

export class DefaultToasterWrapper {
txSending(tx: Omit<TxSending, "status">) {
toast.loading(this._buildToastContainer({ ...tx, status: "sending" }), {
id: tx.id,
duration: Infinity,
position: "bottom-left",
});
}

txPending(tx: Omit<TxPending, "status">) {
toast.loading(this._buildToastContainer({ ...tx, status: "pending" }), {
id: tx.id,
duration: 10_000,
position: "bottom-left",
});
}

txError(tx: Omit<TxError, "status">) {
toast.error(this._buildToastContainer({ ...tx, status: "error" }), {
id: tx.id,
duration: 6000,
position: "bottom-left",
});
return;
}

txSuccess(tx: Omit<TxSuccess, "status">) {
toast.success(this._buildToastContainer({ ...tx, status: "success" }), {
id: tx.id,
duration: 3000,
position: "bottom-left",
});
}

dismiss(id: string): void {
toast.dismiss(id);
}

private _buildToastContainer(
tx: TxSending | TxSuccess | TxError | TxPending,
) {
return (
<div className="flex w-full flex-col overflow-hidden font-mono">
<div className="flex items-center justify-between w-full">
<p className="flex items-center p2 gap-1 text-ellipsis ma">
{tx.title}
<span className="flex p5">{tx.humanCount}</span>
</p>
<button
type="button"
className="pointer text-xl text-secondary hover:text-black"
onClick={() => toast.dismiss(tx.id)}
>
×
</button>
</div>

<div className="flex p5">
{tx.status === "success" ? (
<div>
View Transaction:{" "}
<AddressLink address={tx.receipt.transactionHash} data="tx" />
</div>
) : tx.status === "pending" ? (
<div>
View Transaction: <AddressLink address={tx.hash} data="tx" />
</div>
) : tx.status === "error" ? (
tx.error ?? tx.description
) : (
tx.description
)}
</div>
</div>
);
}
}

export const toaster = new DefaultToasterWrapper();

export const AddressLink: React.FC<{
address: Address | string;
data: "tx" | "address";
className?: string;
}> = ({ address, className, data }) => {
const { chain } = useNetwork();
return (
<Link
href={`${
chain?.blockExplorers?.default.url ?? "https://arbiscan.io"
}/${data}/${address}`}
rel="noopener noreferrer"
target="_blank"
className={clsx(className, "underline")}
>
{address.slice(0, 6)}...{address.slice(address.length - 4)}
</Link>
);
};
63 changes: 63 additions & 0 deletions packages/next-interface/components/button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { clsx } from "clsx";
import type { DetailedHTMLProps } from "react";
import { useState } from "react";

type Variant = "danger" | "primary" | "inverse" | "connect";

interface AdditionalButtonProps {
variant?: Variant;
}

export interface ButtonProps
extends DetailedHTMLProps<
React.ButtonHTMLAttributes<HTMLButtonElement>,
HTMLButtonElement
>,
AdditionalButtonProps {
onClick?:
| ((e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void)
| ((e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => Promise<void>);
children?: React.ReactNode;
}

export default function Button({
children,
disabled,
className,
onClick,
variant = "primary",
...props
}: ButtonProps) {
const [loading, setLoading] = useState<boolean>(false);
return (
<button
{...props}
onClick={
onClick
? async (e) => {
setLoading(true);
await onClick(e);
setLoading(false);
}
: undefined
}
type="button"
disabled={disabled || loading}
className={clsx(
"p2 flex flex-row items-center justify-center rounded-lg px-4 py-2 leading-normal w-full",
"transistion-transform active:scale-98 hover:bg-opacity-90",
variant === "primary" && "bg-gray-1000 text-white",
variant === "inverse" && "bg-white",
variant === "danger" && "bg-red-500 text-white",
variant === "connect" && "bg-brand text-white",
"disabled:cursor-not-allowed disabled:bg-gray-200 disabled:text-secondary",
className,
)}
style={{
...props.style,
}}
>
{loading ? children : children}
</button>
);
}
Loading

0 comments on commit 3e12528

Please sign in to comment.