Skip to content

Commit

Permalink
fixup: Address some more comments.
Browse files Browse the repository at this point in the history
- useExecutor exposes more info about mutation status, we leverage
  this for the play buttons.

- Early return style for validation labels.

- Strict equality for address validation.

- `<>` instead of `<div>`
  • Loading branch information
amnn committed Jul 9, 2024
1 parent 90a7c63 commit 2b4ed0d
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 25 deletions.
18 changes: 10 additions & 8 deletions examples/tic-tac-toe/ui/src/components/NewMultiSigGame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { PublicKey } from '@mysten/sui/cryptography';
import { fromB64, toB64 } from '@mysten/sui/utils';
import { publicKeyFromRawBytes } from '@mysten/sui/verify';
import { ExclamationTriangleIcon } from '@radix-ui/react-icons';
import { Box, Button, Em, Flex, Separator, Text, TextField } from '@radix-ui/themes';
import { Box, Button, Em, Flex, Separator, Spinner, Text, TextField } from '@radix-ui/themes';
import { ComputedField } from 'components/ComputedField';
import { useExecutor } from 'hooks/useExecutor';
import { useTransactions } from 'hooks/useTransactions';
Expand All @@ -15,11 +15,11 @@ import { ReactElement, useState } from 'react';
/**
* Form for creating a new multi-sig game.
*/
export function NewMultiSigGame() {
export function NewMultiSigGame(): ReactElement {
// SAFETY: <App /> tests that a package exists, so Transactions
// builder should be available.
const tx = useTransactions()!!;
const signAndExecute = useExecutor();
const { mutate: signAndExecute, isPending } = useExecutor();

const { address, publicKey: bytes } = useCurrentAccount() || {};
const [opponent, setOpponent] = useState<PublicKey | null>(null);
Expand Down Expand Up @@ -67,8 +67,8 @@ export function NewMultiSigGame() {
/>
<Flex justify="between" mt="4">
<Validation hasPlayer={hasPlayer} hasOpponent={hasOpponent} />
<Button variant="outline" disabled={!(publicKey && opponent)} onClick={onClick}>
Play
<Button variant="outline" disabled={!(publicKey && opponent) || isPending} onClick={onClick}>
{isPending ? <Spinner /> : null} Play
</Button>
</Flex>
<Separator orientation="horizontal" my="4" style={{ width: '100%' }} />
Expand Down Expand Up @@ -102,16 +102,18 @@ function Validation({
<Text color="red">Wallet not connected.</Text>
</Flex>
);
} else if (!hasOpponent) {
}

if (!hasOpponent) {
return (
<Flex align="center" gap="2">
<ExclamationTriangleIcon color="red" />
<Text color="red">Invalid opponent public key.</Text>
</Flex>
);
} else {
return <Box />;
}

return <Box />;
}

/**
Expand Down
20 changes: 11 additions & 9 deletions examples/tic-tac-toe/ui/src/components/NewSharedGame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import { useCurrentAccount } from '@mysten/dapp-kit';
import { isValidSuiAddress, normalizeSuiAddress } from '@mysten/sui/utils';
import { ExclamationTriangleIcon } from '@radix-ui/react-icons';
import { Box, Button, Em, Flex, Separator, Text, TextField } from '@radix-ui/themes';
import { Box, Button, Em, Flex, Separator, Spinner, Text, TextField } from '@radix-ui/themes';
import { useExecutor } from 'hooks/useExecutor';
import { useTransactions } from 'hooks/useTransactions';
import { ReactElement, useState } from 'react';
Expand All @@ -14,11 +14,11 @@ import { ComputedField } from './ComputedField';
/**
* Form for creating a new shared game.
*/
export function NewSharedGame() {
export function NewSharedGame(): ReactElement {
// SAFETY: <App /> tests that a package exists, so Transactions
// builder should be available.
const tx = useTransactions()!!;
const signAndExecute = useExecutor();
const { mutate: signAndExecute, isPending } = useExecutor();

const player = useCurrentAccount()?.address;
const [opponent, setOpponent] = useState<string | null>(null);
Expand Down Expand Up @@ -57,8 +57,8 @@ export function NewSharedGame() {
/>
<Flex justify="between" mt="4">
<Validation hasPlayer={hasPlayer} hasOpponent={hasOpponent} />
<Button variant="outline" disabled={!(player && opponent)} onClick={onClick}>
Play
<Button variant="outline" disabled={!(player && opponent) || isPending} onClick={onClick}>
{isPending ? <Spinner /> : null} Play
</Button>
</Flex>
<Separator orientation="horizontal" my="4" style={{ width: '100%' }} />
Expand Down Expand Up @@ -86,16 +86,18 @@ function Validation({
<Text color="red">Wallet not connected.</Text>
</Flex>
);
} else if (!hasOpponent) {
}

if (!hasOpponent) {
return (
<Flex align="center" gap="2">
<ExclamationTriangleIcon color="red" />
<Text color="red">Invalid opponent address.</Text>
</Flex>
);
} else {
return <Box />;
}

return <Box />;
}

/**
Expand All @@ -108,7 +110,7 @@ function normalizedAddress(address?: string): string | null {
}

address = address.trim();
if (address == '') {
if (address === '') {
return null;
}

Expand Down
37 changes: 34 additions & 3 deletions examples/tic-tac-toe/ui/src/hooks/useExecutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,47 @@ import { Transaction } from '@mysten/sui/transactions';
type Options = Omit<Parameters<SuiClient['getTransactionBlock']>[0], 'digest'> & {
tx: Transaction;
};

type ExecuteResponse = { digest: string; rawEffects?: number[] };

type ExecuteCallback = ({
bytes,
signature,
}: {
bytes: string;
signature: string;
}) => Promise<ExecuteResponse>;

type ResponseCallback = (tx: SuiTransactionBlockResponse) => void | Promise<void>;
type Executor = (options: Options, then: ResponseCallback) => void;

type ExecutorResult = {
mutate: Executor;
status: string;
isIdle: boolean;
isPending: boolean;
isSuccess: boolean;
isError: boolean;
isPaused: boolean;
};

/**
* Hook encapsulating running a transaction, waiting for its effects
* and then doing something with them.
*/
export function useExecutor({ execute }: { execute?: ExecuteCallback } = {}): Executor {
export function useExecutor({ execute }: { execute?: ExecuteCallback } = {}): ExecutorResult {
const client = useSuiClient();
const { mutate: signAndExecute } = useSignAndExecuteTransaction({ execute });
const {
mutate: signAndExecute,
status,
isIdle,
isPending,
isSuccess,
isError,
isPaused,
} = useSignAndExecuteTransaction({ execute });

return ({ tx, ...options }, then) => {
const mutate: Executor = ({ tx, ...options }, then) => {
signAndExecute(
{
transaction: tx,
Expand All @@ -43,4 +64,14 @@ export function useExecutor({ execute }: { execute?: ExecuteCallback } = {}): Ex
},
);
};

return {
mutate,
status,
isIdle,
isPending,
isSuccess,
isError,
isPaused,
};
}
4 changes: 2 additions & 2 deletions examples/tic-tac-toe/ui/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
<QueryClientProvider client={queryClient}>
<SuiClientProvider networks={networkConfig} defaultNetwork="testnet">
<WalletProvider autoConnect>
<div>
<>
<Toaster position="top-center" />
<App />
</div>
</>
</WalletProvider>
</SuiClientProvider>
</QueryClientProvider>
Expand Down
6 changes: 3 additions & 3 deletions examples/tic-tac-toe/ui/src/pages/Game.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ function SharedGame({
invalidateTrophy: InvalidateTrophyQuery;
}): ReactElement {
const account = useCurrentAccount();
const signAndExecute = useExecutor();
const { mutate: signAndExecute } = useExecutor();
const tx = useTransactions()!!;

const { id, board, turn, x, o } = game;
Expand Down Expand Up @@ -168,8 +168,8 @@ function OwnedGame({
const adminKey = game.admin ? new MultiSigPublicKey(new Uint8Array(game.admin)) : null;

const client = useSuiClient();
const signAndExecute = useExecutor();
const multiSignAndExecute = useExecutor({
const { mutate: signAndExecute } = useExecutor();
const { mutate: multiSignAndExecute } = useExecutor({
execute: ({ bytes, signature }) => {
// SAFETY: We check below whether the admin key is available,
// and only allow moves to be submitted when it is.
Expand Down

0 comments on commit 2b4ed0d

Please sign in to comment.