-
Notifications
You must be signed in to change notification settings - Fork 11.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Examples/Front-end] Tic-tac-toe #18526
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
3 Skipped Deployments
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leaving some notes to help direct the review.
export type InvalidateGameQuery = () => void; | ||
|
||
/** Refetch games every 5 seconds */ | ||
const REFETCH_INTERVAL = 5000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refetching ... I'm using refetching in react-query
to make sure the tic tac toe board gets updated when someone else (another player) makes a move. At the moment, I have to go to each query that's feeds the main game screen and add a refetch interval, which is not great. I would rather tell the root query (this one) to refetch, and then have the effects of the cascade everywhere (e.g. when the refetch spots that the game has changed, we trigger the other refetches).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So there are two different ways that probably answer this question:
- Prepend a "key" element into all query keys (so when you invalidate that, you also invalidate all the nested ones)
e.g. queryKey: ['core', 'inner', 'more']
When you invalidate core
, it will invalidate all queries that start with core
.
- You can add global settings when you instatiate the QueryClient on your
main.tsx
file.
new QueryClient({
defaultOptions: {
queries: { ... }
}
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like option (1) a fair bit, but it doesn't play too nicely with how dapp-kit
currently handles keys (you can only supply a key suffix, not a prefix) -- I wonder if there's a way we can tweak the way dapp-kit
works to allow these kind of patterns, cc @hayes-mysten . (2) seems less ideal to me, because it sounds like we would switch things around to make refetching the default, and then have to manually turn it off in places, which would work for our case here, but I'm not sure how generalisable that is.
What I was originally hoping was that there was a way to describe the data dependency, so that react-query
could detect that there was a change in the query input and therefore re-run it, in which case we could treat our game as a redundant input to all the other queries, and use that mechanism to trigger the refetches -- is that feasible at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Going to defer to @williamrobertson13 here who has some more react/front-end expertise
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is definitely something we should have a good (and well documented) solution for
arguments: [ | ||
tx.pure.address(player.toSuiAddress()), | ||
tx.pure.address(opponent.toSuiAddress()), | ||
tx.pure(bcs.vector(bcs.u8()).serialize(admin.toRawBytes()).toBytes()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a better way to serialize a Uint8Array
as a vector<u8>
parameter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think what you did is the recommended way. @hayes-mysten to confirm!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is mostly the correct way to do this, but the last toBytes isn't needed: tx.pure(bcs.vector(bcs.u8()).serialize(admin.toRawBytes()))
bcs.bytes()
is an existing method that is used for fixedLength bytes, so I think also calling vector bytes would be confusing.
I just opened a PR to make this a little better though: https://github.com/MystenLabs/sui/pull/18561/files
This will add tx.pure.vector('u8', admin.toRawBytes())
const account = useCurrentAccount(); | ||
|
||
const queryKey = [ctx.network, 'turn-cap', account?.address, game]; | ||
const response = useQuery({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My attempt at trying to fetch a single object from among an address' owned objects. Relying on a vanilla useQuery
instead of useSuiClientQuery
of the infinite variety because I want to read potentially multiple pages in one query to find the value I want -- let me know if there's a better way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The lines on the board are actually the background of the .board
class showing through (and the white squares are the background of each cell). I'm a CSS/flexbox noob, so this seemed to me the cleanest way to do this, but let me know if there's a more straightforward way.
@hayes-mysten, as promised, here's the experience report from modernising this demo! I'm going to focus on the areas where I struggled, but I wanted to start by saying that dApp Kit, Radix UI, React, and React Query were great -- they significantly sped things up for me, and I'm confident I wouldn't have been able to deliver anything near the quality I was able to without them. A big 👍 for starting with an opinionated stack of tools and libraries that work well together, because they taught me a lot about what's available, and what the best practices are. Some of the things I found tricky are specific to me being a front-end/TypeScript noob. I tried to filter those things out, but I may not have succeeded (don't know what I don't know!), but here goes: Wallet <-> NetworkConfig syncingThis was an early stumbling block because NetworkConfig defaults to testnet and I was building on localnet, so without a way to switch networks, I was a bit stuck. Initially, I was able to unblock myself by changing the default to localnet, but this may not be a sensible default. Eventually, I added some logic to query the current account, its network, check whether we have a config for that network, and set it if so (or error otherwise). I wonder whether we can package this behaviour up as a default? So that out of the box, the app will switch its config according to the network the wallet sets. Similarly, I had to roll my own script to deploy a package, and update the dApp's configs to pick up the newly deployed package -- is this something we can bake into Address <-> App StickinessThis one's a known issue, I got stuck trying to connect some other address to the app during testing. My understanding is that you need to go into the wallet settings and dissociate the app from that address and then reconnect. My expectation here is that once you connect a wallet, you should be able to select addresses from that wallet to connect to the app using the drop-down, but only one address ever shows here. Query InvalidationMain issue here was that it was not clear what the default query key was -- I was supplying a key as a parameter, but I didn't realise that it was being prefixed with some other stuff. There's a couple of ways I could see this being solved:
Docs in LSPFinding docs in the LSP was a struggle, and it is made more complicated by the fact that the symbols the language server like to jump to are usually just type declarations, and there are some pretty exotic types here. This wasn't a problem that was unique to our libraries though -- while the standard library and maybe core React hooks had good documentation, many other libraries didn't, which is a shame. Transforming an Object on LoadWhen querying the Game object over RPC, I needed to perform some extra checks and transformations. In some cases the checks would yield errors. I ended up doing this in a custom hook, but it might be nice to allow users to provide a transformation function in a call to Radix UI is now at 3.x
Importing modulesI found the pattern of always using relative imports to be quite jarring -- is that the best practice? In the end, I installed a vite plugin that would let me depend on modules relative to the Aliased typesThere are some types (the ones I can think of are
|
const { address, publicKey: bytes } = useCurrentAccount() || {}; | ||
const [opponent, setOpponent] = useState<PublicKey | null>(null); | ||
|
||
const publicKey = bytes && publicKeyFromRawBytes('ED25519', bytes); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here useCurrentAccount
has returned some bytes for the public key, and they seem to be raw -- how would a wallet return the public key when the signature scheme was not ED25519?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the walletStandard doesn't expose enough information to make this work. Generally dapps are built around addresses, not public keys, and some wallet accounts won't return meaningful publicKey bytes (eg stashed).
That being said, you theoretically could try each supported schema, and see if it produces a pubKey that has the same address as the account
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be feasible to have the wallet standard return a public key in "SuiBytes" form? (I believe it's just an enum, so it's the raw bytes prefixed with a variant index), or add an optional signature scheme field, so I could write something like:
const { address, publicKey: bytes, keySignature } = useCurrentAccount() || {};
const publicKey = keySignature && bytes && publicKeyFromRawBytes(keySignature, bytes);
const response = useQuery({ | ||
enabled: !!game, | ||
refetchInterval: REFETCH_INTERVAL, | ||
queryKey: ['game-end-state', game?.id], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What might be convenient is to export all queryKey
names so you could theoretically invalidate keys more generic throughout the app, if you needed to. E.g. QueryKeys { GameEndState, ...}
, instead of having to expose an "invalidate" function on each of these.
Returning the "useQuery" hook as is, also exports a refetch
function that you can use to re-fetch the values, instead of re-defining a new invalidate
function on every hook.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about this, but I think again it kind of runs counter to the way dapp-kit
is working, which abstracts away the query keys in its queries. Maybe if we can align on that, this would be a good way to go.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot Ashok. Approving to unblock, overall this looks amazing. UX is super smooth! 🚀
Added some comments / suggestions here and there!
Just finishing up addressing the other comments, but I'm noticing that something is still not quite right with the workspace set-up, so could use some advice on that @manolisliolios / @hayes-mysten . It looks like I was trying to follow the path that the Is the right thing to do here to exclude all the examples from the workspace, and ensure they each have their own lock files? |
Couldn't get SuiScan's local explorer to work.
This will change for each person, so don't check-in an actual package.
- 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>`
## Description Write a CLI to talk to the shared and multi-sig tic-tac-toe example Move packages. ## Test plan Manual testing: ``` $ tic-tac-toe view 0xab06bb92cc1e6c95f9e6eed7e6c01fade6bb5eaa241e2100eb54d914597295ed X | O | ---+---+--- O | X | ---+---+--- | | X X: <address> -> O: <address> GAME: 0xab06bb92cc1e6c95f9e6eed7e6c01fade6bb5eaa241e2100eb54d914597295ed ADMIN: <public key> X WINS! ``` Also, confirmed that the CLI works with the React front-end. ## Stack - #18525 - #18526 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK:
## Description A README for the tic-tac-toe app example, derived from: https://github.com/MystenLabs/multisig_tic-tac-toe/blob/main/README.md And updates to the docs for the tic-tac-toe app example to reflect changes to modernise the guide and bring all its source code into the `sui` mono-repo. This is the last major example that lived in the `sui_programmability` directory. ## Test plan :eyes: ## Stack - #18525 - #18526 - #18557 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: --------- Co-authored-by: Ronny Roland <[email protected]>
…8595) ## Description Replace all references to Move packages in `sui_programmability` with equivalents in `./examples/move`, in preparation for deleting the programmability directory. In the process, I also: - removed the tic-tac-toe example from the Sui SDK, as it has been replaced by a more full-featured E2E example. - ported some modernised versions of the `basics` packages into the new `examples/move/basics` for use in tests. ## Test plan CI and, ``` sui$ cargo simtest ``` ## Stack - #18525 - #18526 - #18557 - #18558 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: --------- Co-authored-by: Ronny Roland <[email protected]>
…18609) ## Description Port over the following examples from sui_programmability/examples: - crypto/sources/ecdsa.move - crypto/sources/groth16.move - ~games/sources/drand_lib.move~ - ~games/sources/drand_based_lottery.move~ - ~games/sources/drand_based_scratch_card.move~ - games/sources/vdf_based_lottery.move Modernising and cleaning them up in the process: - Applying wrapping consistently at 100 characters, and cleaning up comments. - Removing unnecessary use of `entry` functions, including returning values instead of transfering to sender in some cases. - Using receiver functions where possible. - Standardising file order and adding titles for sections. - Standardising use of doc comments vs regular comments. - Using clever errors. This marks the final set of examples to be moved out of sui-programmability, which will then be deleted. ## Test plan ``` sui-framework-tests$ cargo nextest run -- run_examples_move_unit_tests ``` ## Stack - #18525 - #18526 - #18557 - #18558 - #18595 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: --------- Co-authored-by: Ronny Roland <[email protected]>
## Description Remove the `sui_programmability` folder as all examples have been ported and modernised to `examples/move`, or elsewhere. ## Test plan CI ## Stack - #18525 - #18526 - #18557 - #18558 - #18595 - #18609 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: --------- Co-authored-by: Ronny Roland <[email protected]>
## Description Build front-ends for shared and multi-sig tic-tac-toe, using `create-react-dapp`. ## Test plan Manually tested (screenshots below). #### New Shared game  #### New MultiSig game  #### Game in-progress  #### Game won, viewed by player  #### Game won, viewed by spectator (no wallet connected)  #### Delete game dialog  ## Stack - MystenLabs#18525
## Description Write a CLI to talk to the shared and multi-sig tic-tac-toe example Move packages. ## Test plan Manual testing: ``` $ tic-tac-toe view 0xab06bb92cc1e6c95f9e6eed7e6c01fade6bb5eaa241e2100eb54d914597295ed X | O | ---+---+--- O | X | ---+---+--- | | X X: <address> -> O: <address> GAME: 0xab06bb92cc1e6c95f9e6eed7e6c01fade6bb5eaa241e2100eb54d914597295ed ADMIN: <public key> X WINS! ``` Also, confirmed that the CLI works with the React front-end. ## Stack - MystenLabs#18525 - MystenLabs#18526 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK:
## Description A README for the tic-tac-toe app example, derived from: https://github.com/MystenLabs/multisig_tic-tac-toe/blob/main/README.md And updates to the docs for the tic-tac-toe app example to reflect changes to modernise the guide and bring all its source code into the `sui` mono-repo. This is the last major example that lived in the `sui_programmability` directory. ## Test plan :eyes: ## Stack - MystenLabs#18525 - MystenLabs#18526 - MystenLabs#18557 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: --------- Co-authored-by: Ronny Roland <[email protected]>
…stenLabs#18595) ## Description Replace all references to Move packages in `sui_programmability` with equivalents in `./examples/move`, in preparation for deleting the programmability directory. In the process, I also: - removed the tic-tac-toe example from the Sui SDK, as it has been replaced by a more full-featured E2E example. - ported some modernised versions of the `basics` packages into the new `examples/move/basics` for use in tests. ## Test plan CI and, ``` sui$ cargo simtest ``` ## Stack - MystenLabs#18525 - MystenLabs#18526 - MystenLabs#18557 - MystenLabs#18558 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: --------- Co-authored-by: Ronny Roland <[email protected]>
…ystenLabs#18609) ## Description Port over the following examples from sui_programmability/examples: - crypto/sources/ecdsa.move - crypto/sources/groth16.move - ~games/sources/drand_lib.move~ - ~games/sources/drand_based_lottery.move~ - ~games/sources/drand_based_scratch_card.move~ - games/sources/vdf_based_lottery.move Modernising and cleaning them up in the process: - Applying wrapping consistently at 100 characters, and cleaning up comments. - Removing unnecessary use of `entry` functions, including returning values instead of transfering to sender in some cases. - Using receiver functions where possible. - Standardising file order and adding titles for sections. - Standardising use of doc comments vs regular comments. - Using clever errors. This marks the final set of examples to be moved out of sui-programmability, which will then be deleted. ## Test plan ``` sui-framework-tests$ cargo nextest run -- run_examples_move_unit_tests ``` ## Stack - MystenLabs#18525 - MystenLabs#18526 - MystenLabs#18557 - MystenLabs#18558 - MystenLabs#18595 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: --------- Co-authored-by: Ronny Roland <[email protected]>
## Description Remove the `sui_programmability` folder as all examples have been ported and modernised to `examples/move`, or elsewhere. ## Test plan CI ## Stack - MystenLabs#18525 - MystenLabs#18526 - MystenLabs#18557 - MystenLabs#18558 - MystenLabs#18595 - MystenLabs#18609 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: --------- Co-authored-by: Ronny Roland <[email protected]>
Description
Build front-ends for shared and multi-sig tic-tac-toe, using
create-react-dapp
.Test plan
Manually tested (screenshots below).
New Shared game
New MultiSig game
Game in-progress
Game won, viewed by player
Game won, viewed by spectator (no wallet connected)
Delete game dialog
Stack