From 05b967bd01645350d498d853a96692c892cc74ba Mon Sep 17 00:00:00 2001 From: broody Date: Thu, 6 Jul 2023 19:39:27 -0700 Subject: [PATCH] feat(web): fetch game state react-query --- web/.env.development | 4 +- web/.env.production | 4 +- web/codegen.yaml | 6 +- web/package.json | 1 + web/src/components/Header.tsx | 28 +- web/src/constants.ts | 6 +- web/src/generated/graphql.ts | 403 ++++++++++++------ web/src/generated/introspection.ts | 12 +- web/src/graphql/components.graphql | 1 - web/src/graphql/entities.graphql | 46 +- web/src/hooks/dojo.tsx | 46 +- web/src/hooks/fetcher.tsx | 24 ++ web/src/hooks/useRyoWorld.tsx | 22 +- web/src/pages/{[game] => [gameId]}/index.tsx | 0 .../location/[locationSlug]/[drugSlug].tsx | 0 .../location/[locationSlug]/index.tsx | 0 web/src/pages/{[game] => [gameId]}/travel.tsx | 0 web/src/pages/{[game] => [gameId]}/turn.tsx | 0 web/src/pages/_app.tsx | 55 ++- web/src/pages/index.tsx | 21 +- web/yarn.lock | 85 +++- 21 files changed, 531 insertions(+), 233 deletions(-) create mode 100644 web/src/hooks/fetcher.tsx rename web/src/pages/{[game] => [gameId]}/index.tsx (100%) rename web/src/pages/{[game] => [gameId]}/location/[locationSlug]/[drugSlug].tsx (100%) rename web/src/pages/{[game] => [gameId]}/location/[locationSlug]/index.tsx (100%) rename web/src/pages/{[game] => [gameId]}/travel.tsx (100%) rename web/src/pages/{[game] => [gameId]}/turn.tsx (100%) diff --git a/web/.env.development b/web/.env.development index 635dd4ed..4637833e 100644 --- a/web/.env.development +++ b/web/.env.development @@ -1,3 +1,3 @@ NEXT_PUBLIC_DISABLE_MEDIAPLAYER_AUTOPLAY="true" -NEXT_PUBLIC_WORLD_ADDRESS="0x7d17bb24b59cb371c9ca36b79efca27fe53318e26340df3d8623dba5a7b9e5f" -NEXT_PUBLIC_KATANA_RPC="http://localhost:5050" \ No newline at end of file +NEXT_PUBLIC_KATANA_RPC="http://localhost:5050" +NEXT_PUBLIC_TORII_API="http://localhost:8080" \ No newline at end of file diff --git a/web/.env.production b/web/.env.production index 27e5168c..a4ff5089 100644 --- a/web/.env.production +++ b/web/.env.production @@ -1,2 +1,2 @@ -NEXT_PUBLIC_WORLD_ADDRESS="0x7d17bb24b59cb371c9ca36b79efca27fe53318e26340df3d8623dba5a7b9e5f" -NEXT_PUBLIC_KATANA_RPC="http://localhost:5050" \ No newline at end of file +NEXT_PUBLIC_KATANA_RPC="http://localhost:5050" +NEXT_PUBLIC_TORII_API="http://localhost:8080" \ No newline at end of file diff --git a/web/codegen.yaml b/web/codegen.yaml index 8b15bafb..462c4230 100644 --- a/web/codegen.yaml +++ b/web/codegen.yaml @@ -1,14 +1,18 @@ overwrite: true -schema: ${NEXT_PUBLIC_API_URL:http://127.0.0.1:8080} +schema: ${NEXT_PUBLIC_TORII_API:http://localhost:8080} documents: "src/**/*.graphql" generates: src/generated/graphql.ts: plugins: - "typescript" - "typescript-operations" + - "typescript-react-query" config: addInfiniteQuery: true exposeQueryKeys: true + fetcher: + func: "@/hooks/fetcher#useFetchData" + isReactHook: true src/generated/introspection.ts: plugins: - fragment-matcher diff --git a/web/package.json b/web/package.json index 23e26362..2210243d 100644 --- a/web/package.json +++ b/web/package.json @@ -28,6 +28,7 @@ "next": "13.2.4", "react": "18.2.0", "react-dom": "18.2.0", + "react-query": "^3.39.2", "starknet": "https://github.com/broody/starknet.js#2ca935e0062fc7b7a5fff2130c1255596179964e", "use-caret-position": "^0.0.2", "zustand": "^4.3.7" diff --git a/web/src/components/Header.tsx b/web/src/components/Header.tsx index a2737ced..23313ee2 100644 --- a/web/src/components/Header.tsx +++ b/web/src/components/Header.tsx @@ -19,6 +19,8 @@ import MediaPlayer from "@/components/MediaPlayer"; import MobileMenu from "@/components/MobileMenu"; import { play } from "@/hooks/media"; import { useGameStore, getInventoryInfos } from "@/hooks/state"; +import { usePlayerEntitiesQuery } from "@/generated/graphql"; +import { PLAYER_ADDRESS } from "@/constants"; export interface HeaderProps { back?: boolean; @@ -26,8 +28,14 @@ export interface HeaderProps { const Header = ({ back }: HeaderProps) => { const router = useRouter(); - const { address } = useAccount(); - const { connectors, connect, disconnect } = useConnectors(); + const { gameId } = router.query as { gameId: string }; + + const { data } = usePlayerEntitiesQuery( + { gameId, address: PLAYER_ADDRESS }, + { + enabled: !!gameId, + }, + ); const isMobile = IsMobile(); @@ -60,11 +68,6 @@ const Header = ({ back }: HeaderProps) => { zIndex="1" > - {!isBackButtonVisible && ( - router.push("/")}> - - - )} {isBackButtonVisible && ( router.back()}> @@ -104,12 +107,12 @@ const Header = ({ back }: HeaderProps) => { {!isMobile && } - - {!isMobile && ( + {/* Chat requires backend implementation */} + {/* {!isMobile && ( router.push("/chat")}> - )} + )} */} {isMobile && } @@ -122,7 +125,8 @@ const Header = ({ back }: HeaderProps) => { )} - + {/* For alpha, we're using burner wallets */} + {/* {!isMobile && } - + */} )} diff --git a/web/src/constants.ts b/web/src/constants.ts index 615cec43..7209af8c 100644 --- a/web/src/constants.ts +++ b/web/src/constants.ts @@ -1,2 +1,6 @@ export const RYO_WORLD_ADDRESS = - "0x357158d06624f4b5b25d92d49c95a1f07b0d520320b81e5c926fcdfc1decce4"; + "0x7d17bb24b59cb371c9ca36b79efca27fe53318e26340df3d8623dba5a7b9e5f"; +export const PLAYER_ADDRESS = + "0x3ee9e18edc71a6df30ac3aca2e0b02a198fbce19b7480a63a0d71cbd76652e0"; +export const PLAYER_PRIVATE_KEY = + "0x300001800000000300000180000000000030000000000003006001800006600"; diff --git a/web/src/generated/graphql.ts b/web/src/generated/graphql.ts index 8415ffb0..4f57d317 100644 --- a/web/src/generated/graphql.ts +++ b/web/src/generated/graphql.ts @@ -1,3 +1,11 @@ +import { + useQuery, + useInfiniteQuery, + UseQueryOptions, + UseInfiniteQueryOptions, + QueryFunctionContext, +} from "react-query"; +import { useFetchData } from "@/hooks/fetcher"; export type Maybe = T | null; export type InputMaybe = Maybe; export type Exact = { @@ -27,35 +35,11 @@ export type Scalars = { usize: any; }; -export type Authrole = { - __typename?: "Authrole"; - id: Scalars["felt252"]; -}; - -export type Authstatus = { - __typename?: "Authstatus"; - is_authorized: Scalars["bool"]; -}; - -export type Cash = { - __typename?: "Cash"; - amount: Scalars["u128"]; -}; - -export type ComponentUnion = - | Authrole - | Authstatus - | Cash - | Drug - | Game - | Location - | Market - | Risks - | Stats; +export type ComponentUnion = Drug | Game | Location | Market | Player | Risks; export type Drug = { __typename?: "Drug"; - id: Scalars["u32"]; + name: Scalars["felt252"]; quantity: Scalars["usize"]; }; @@ -84,7 +68,6 @@ export type Game = { creator: Scalars["felt252"]; game_id: Scalars["u32"]; is_finished: Scalars["bool"]; - max_locations: Scalars["usize"]; max_players: Scalars["usize"]; max_turns: Scalars["usize"]; num_players: Scalars["usize"]; @@ -93,7 +76,7 @@ export type Game = { export type Location = { __typename?: "Location"; - id: Scalars["u32"]; + name: Scalars["felt252"]; }; export type Market = { @@ -102,11 +85,17 @@ export type Market = { quantity: Scalars["usize"]; }; +export type Player = { + __typename?: "Player"; + arrested: Scalars["bool"]; + cash: Scalars["u128"]; + health: Scalars["u8"]; + name: Scalars["felt252"]; + turns_remaining: Scalars["usize"]; +}; + export type Query = { __typename?: "Query"; - authroleComponents?: Maybe>>; - authstatusComponents?: Maybe>>; - cashComponents?: Maybe>>; drugComponents?: Maybe>>; entities?: Maybe>>; entity: Entity; @@ -115,32 +104,17 @@ export type Query = { gameComponents?: Maybe>>; locationComponents?: Maybe>>; marketComponents?: Maybe>>; + playerComponents?: Maybe>>; risksComponents?: Maybe>>; - statsComponents?: Maybe>>; system: System; systemCall: SystemCall; systemCalls?: Maybe>>; systems?: Maybe>>; }; -export type QueryAuthroleComponentsArgs = { - id?: InputMaybe; - limit?: InputMaybe; -}; - -export type QueryAuthstatusComponentsArgs = { - is_authorized?: InputMaybe; - limit?: InputMaybe; -}; - -export type QueryCashComponentsArgs = { - amount?: InputMaybe; - limit?: InputMaybe; -}; - export type QueryDrugComponentsArgs = { - id?: InputMaybe; limit?: InputMaybe; + name?: InputMaybe; quantity?: InputMaybe; }; @@ -166,7 +140,6 @@ export type QueryGameComponentsArgs = { game_id?: InputMaybe; is_finished?: InputMaybe; limit?: InputMaybe; - max_locations?: InputMaybe; max_players?: InputMaybe; max_turns?: InputMaybe; num_players?: InputMaybe; @@ -174,8 +147,8 @@ export type QueryGameComponentsArgs = { }; export type QueryLocationComponentsArgs = { - id?: InputMaybe; limit?: InputMaybe; + name?: InputMaybe; }; export type QueryMarketComponentsArgs = { @@ -184,23 +157,23 @@ export type QueryMarketComponentsArgs = { quantity?: InputMaybe; }; +export type QueryPlayerComponentsArgs = { + arrested?: InputMaybe; + cash?: InputMaybe; + health?: InputMaybe; + limit?: InputMaybe; + name?: InputMaybe; + turns_remaining?: InputMaybe; +}; + export type QueryRisksComponentsArgs = { arrested?: InputMaybe; hurt?: InputMaybe; - killed?: InputMaybe; limit?: InputMaybe; mugged?: InputMaybe; travel?: InputMaybe; }; -export type QueryStatsComponentsArgs = { - arrested?: InputMaybe; - health?: InputMaybe; - limit?: InputMaybe; - respect?: InputMaybe; - turns_remaining?: InputMaybe; -}; - export type QuerySystemArgs = { id: Scalars["ID"]; }; @@ -217,19 +190,10 @@ export type Risks = { __typename?: "Risks"; arrested: Scalars["u8"]; hurt: Scalars["u8"]; - killed: Scalars["u8"]; mugged: Scalars["u8"]; travel: Scalars["u8"]; }; -export type Stats = { - __typename?: "Stats"; - arrested: Scalars["bool"]; - health: Scalars["u8"]; - respect: Scalars["u8"]; - turns_remaining: Scalars["usize"]; -}; - export type System = { __typename?: "System"; address: Scalars["Address"]; @@ -261,7 +225,6 @@ export type AvailableGamesQuery = { creator: any; num_players: any; max_players: any; - max_locations: any; max_turns: any; start_time: any; } | null> | null; @@ -276,78 +239,280 @@ export type UserGamesQuery = { gameComponents?: Array<{ __typename?: "Game"; game_id: any } | null> | null; }; -export type PlayerQueryVariables = Exact<{ - id: Scalars["ID"]; +export type PlayerEntitiesQueryVariables = Exact<{ + gameId: Scalars["String"]; + address: Scalars["String"]; }>; -export type PlayerQuery = { +export type PlayerEntitiesQuery = { __typename?: "Query"; - entity: { + entities?: Array<{ __typename?: "Entity"; components?: Array< - | { __typename: "Authrole" } - | { __typename: "Authstatus" } - | { __typename: "Cash"; amount: any } - | { __typename: "Drug" } + | { __typename: "Drug"; name: any; quantity: any } | { __typename: "Game" } - | { __typename: "Location"; id: any } + | { __typename: "Location"; name: any } | { __typename: "Market" } + | { + __typename: "Player"; + name: any; + cash: any; + health: any; + arrested: any; + turns_remaining: any; + } | { __typename: "Risks" } - | { __typename: "Stats" } | null > | null; - }; + } | null> | null; }; -export type LocationQueryVariables = Exact<{ - id: Scalars["ID"]; +export type LocationEntitiesQueryVariables = Exact<{ + gameId: Scalars["String"]; + location: Scalars["String"]; }>; -export type LocationQuery = { +export type LocationEntitiesQuery = { __typename?: "Query"; - entity: { + entities?: Array<{ __typename?: "Entity"; components?: Array< - | { __typename: "Authrole" } - | { __typename: "Authstatus" } - | { __typename: "Cash" } - | { __typename: "Drug" } - | { __typename: "Game" } - | { __typename: "Location"; id: any } - | { __typename: "Market" } + | { __typename?: "Drug" } + | { __typename?: "Game" } + | { __typename?: "Location"; name: any } + | { __typename?: "Market"; cash: any; quantity: any } + | { __typename?: "Player" } | { - __typename: "Risks"; - travel: any; + __typename?: "Risks"; + arrested: any; hurt: any; - killed: any; mugged: any; - arrested: any; + travel: any; } - | { __typename: "Stats" } | null > | null; - }; + } | null> | null; }; -export type MarketQueryVariables = Exact<{ - id: Scalars["ID"]; -}>; - -export type MarketQuery = { - __typename?: "Query"; - entity: { - __typename?: "Entity"; - components?: Array< - | { __typename: "Authrole" } - | { __typename: "Authstatus" } - | { __typename: "Cash" } - | { __typename: "Drug" } - | { __typename: "Game" } - | { __typename: "Location" } - | { __typename: "Market"; cash: any; quantity: any } - | { __typename: "Risks" } - | { __typename: "Stats" } - | null - > | null; - }; -}; +export const AvailableGamesDocument = ` + query AvailableGames { + gameComponents(limit: 10) { + game_id + creator + num_players + max_players + max_turns + start_time + } +} + `; +export const useAvailableGamesQuery = < + TData = AvailableGamesQuery, + TError = unknown, +>( + variables?: AvailableGamesQueryVariables, + options?: UseQueryOptions, +) => + useQuery( + variables === undefined + ? ["AvailableGames"] + : ["AvailableGames", variables], + useFetchData( + AvailableGamesDocument, + ).bind(null, variables), + options, + ); + +useAvailableGamesQuery.getKey = (variables?: AvailableGamesQueryVariables) => + variables === undefined ? ["AvailableGames"] : ["AvailableGames", variables]; +export const useInfiniteAvailableGamesQuery = < + TData = AvailableGamesQuery, + TError = unknown, +>( + variables?: AvailableGamesQueryVariables, + options?: UseInfiniteQueryOptions, +) => { + const query = useFetchData( + AvailableGamesDocument, + ); + return useInfiniteQuery( + variables === undefined + ? ["AvailableGames.infinite"] + : ["AvailableGames.infinite", variables], + (metaData) => query({ ...variables, ...(metaData.pageParam ?? {}) }), + options, + ); +}; + +useInfiniteAvailableGamesQuery.getKey = ( + variables?: AvailableGamesQueryVariables, +) => + variables === undefined + ? ["AvailableGames.infinite"] + : ["AvailableGames.infinite", variables]; +export const UserGamesDocument = ` + query UserGames($id: felt252!) { + gameComponents(creator: $id) { + game_id + } +} + `; +export const useUserGamesQuery = ( + variables: UserGamesQueryVariables, + options?: UseQueryOptions, +) => + useQuery( + ["UserGames", variables], + useFetchData( + UserGamesDocument, + ).bind(null, variables), + options, + ); + +useUserGamesQuery.getKey = (variables: UserGamesQueryVariables) => [ + "UserGames", + variables, +]; +export const useInfiniteUserGamesQuery = < + TData = UserGamesQuery, + TError = unknown, +>( + variables: UserGamesQueryVariables, + options?: UseInfiniteQueryOptions, +) => { + const query = useFetchData( + UserGamesDocument, + ); + return useInfiniteQuery( + ["UserGames.infinite", variables], + (metaData) => query({ ...variables, ...(metaData.pageParam ?? {}) }), + options, + ); +}; + +useInfiniteUserGamesQuery.getKey = (variables: UserGamesQueryVariables) => [ + "UserGames.infinite", + variables, +]; +export const PlayerEntitiesDocument = ` + query PlayerEntities($gameId: String!, $address: String!) { + entities(keys: [$gameId, $address]) { + components { + __typename + ... on Player { + name + cash + health + arrested + turns_remaining + } + ... on Location { + name + } + ... on Drug { + name + quantity + } + } + } +} + `; +export const usePlayerEntitiesQuery = < + TData = PlayerEntitiesQuery, + TError = unknown, +>( + variables: PlayerEntitiesQueryVariables, + options?: UseQueryOptions, +) => + useQuery( + ["PlayerEntities", variables], + useFetchData( + PlayerEntitiesDocument, + ).bind(null, variables), + options, + ); + +usePlayerEntitiesQuery.getKey = (variables: PlayerEntitiesQueryVariables) => [ + "PlayerEntities", + variables, +]; +export const useInfinitePlayerEntitiesQuery = < + TData = PlayerEntitiesQuery, + TError = unknown, +>( + variables: PlayerEntitiesQueryVariables, + options?: UseInfiniteQueryOptions, +) => { + const query = useFetchData( + PlayerEntitiesDocument, + ); + return useInfiniteQuery( + ["PlayerEntities.infinite", variables], + (metaData) => query({ ...variables, ...(metaData.pageParam ?? {}) }), + options, + ); +}; + +useInfinitePlayerEntitiesQuery.getKey = ( + variables: PlayerEntitiesQueryVariables, +) => ["PlayerEntities.infinite", variables]; +export const LocationEntitiesDocument = ` + query LocationEntities($gameId: String!, $location: String!) { + entities(keys: [$gameId, $location]) { + components { + ... on Market { + cash + quantity + } + ... on Location { + name + } + ... on Risks { + arrested + hurt + mugged + travel + } + } + } +} + `; +export const useLocationEntitiesQuery = < + TData = LocationEntitiesQuery, + TError = unknown, +>( + variables: LocationEntitiesQueryVariables, + options?: UseQueryOptions, +) => + useQuery( + ["LocationEntities", variables], + useFetchData( + LocationEntitiesDocument, + ).bind(null, variables), + options, + ); + +useLocationEntitiesQuery.getKey = ( + variables: LocationEntitiesQueryVariables, +) => ["LocationEntities", variables]; +export const useInfiniteLocationEntitiesQuery = < + TData = LocationEntitiesQuery, + TError = unknown, +>( + variables: LocationEntitiesQueryVariables, + options?: UseInfiniteQueryOptions, +) => { + const query = useFetchData< + LocationEntitiesQuery, + LocationEntitiesQueryVariables + >(LocationEntitiesDocument); + return useInfiniteQuery( + ["LocationEntities.infinite", variables], + (metaData) => query({ ...variables, ...(metaData.pageParam ?? {}) }), + options, + ); +}; + +useInfiniteLocationEntitiesQuery.getKey = ( + variables: LocationEntitiesQueryVariables, +) => ["LocationEntities.infinite", variables]; diff --git a/web/src/generated/introspection.ts b/web/src/generated/introspection.ts index e9a31d14..8339eba1 100644 --- a/web/src/generated/introspection.ts +++ b/web/src/generated/introspection.ts @@ -5,17 +5,7 @@ export interface PossibleTypesResultData { } const result: PossibleTypesResultData = { possibleTypes: { - ComponentUnion: [ - "Authrole", - "Authstatus", - "Cash", - "Drug", - "Game", - "Location", - "Market", - "Risks", - "Stats", - ], + ComponentUnion: ["Drug", "Game", "Location", "Market", "Player", "Risks"], }, }; export default result; diff --git a/web/src/graphql/components.graphql b/web/src/graphql/components.graphql index fba7ee45..015141c6 100644 --- a/web/src/graphql/components.graphql +++ b/web/src/graphql/components.graphql @@ -4,7 +4,6 @@ query AvailableGames { creator num_players max_players - max_locations max_turns start_time } diff --git a/web/src/graphql/entities.graphql b/web/src/graphql/entities.graphql index 2a847984..61363be2 100644 --- a/web/src/graphql/entities.graphql +++ b/web/src/graphql/entities.graphql @@ -1,42 +1,40 @@ -query Player($id: ID!) { - entity(id: $id) { +query PlayerEntities($gameId: String!, $address: String!) { + entities(keys: [$gameId, $address]) { components { __typename - ... on Cash { - amount + ... on Player { + name + cash + health + arrested + turns_remaining } ... on Location { - id + name + } + ... on Drug { + name + quantity } } } } -query Location($id: ID!) { - entity(id: $id) { +query LocationEntities($gameId: String!, $location: String!) { + entities(keys: [$gameId, $location]) { components { - __typename + ... on Market { + cash + quantity + } ... on Location { - id + name } ... on Risks { - travel + arrested hurt - killed mugged - arrested - } - } - } -} - -query Market($id: ID!) { - entity(id: $id) { - components { - __typename - ... on Market { - cash - quantity + travel } } } diff --git a/web/src/hooks/dojo.tsx b/web/src/hooks/dojo.tsx index c2e5b0a9..0a837d6e 100644 --- a/web/src/hooks/dojo.tsx +++ b/web/src/hooks/dojo.tsx @@ -1,9 +1,10 @@ -import { createContext, ReactNode, useContext, useEffect } from "react"; -import { Account, BigNumberish, CallData, num, shortString } from "starknet"; +import { createContext, ReactNode, useContext, useState } from "react"; +import { Account, BigNumberish, CallData, shortString } from "starknet"; interface DojoInterface { worldAddress: string; account: Account; + isPending: boolean; execute: (systemName: string, params: BigNumberish[]) => Promise; } @@ -27,27 +28,34 @@ export function DojoProvider({ account: Account; children?: ReactNode; }): JSX.Element { - const execute = async (systemName: string, params: BigNumberish[]) => { - const { transaction_hash } = await account.execute({ - contractAddress: worldAddress, - entrypoint: "execute", - calldata: CallData.compile([ - shortString.encodeShortString(systemName), - params.length, - ...params, - ]), - }); - - console.log("transaction hash: " + transaction_hash); + const [isPending, setIsPending] = useState(false); - // katana tx are instant - //await account.waitForTransaction(transaction_hash); - - return transaction_hash; + const execute = async (systemName: string, params: BigNumberish[]) => { + setIsPending(true); + + return account + .execute({ + contractAddress: worldAddress, + entrypoint: "execute", + calldata: CallData.compile([ + shortString.encodeShortString(systemName), + params.length, + ...params, + ]), + }) + .then(({ transaction_hash }) => { + console.log("transaction hash: " + transaction_hash); + return transaction_hash; + }) + .catch((e) => { + console.error(e); + throw e; + }) + .finally(() => setIsPending(false)); }; return ( - + {children} ); diff --git a/web/src/hooks/fetcher.tsx b/web/src/hooks/fetcher.tsx new file mode 100644 index 00000000..1bf5eb67 --- /dev/null +++ b/web/src/hooks/fetcher.tsx @@ -0,0 +1,24 @@ +export const useFetchData = ( + query: string, +): ((variables?: TVariables) => Promise) => { + return async (variables?: TVariables) => { + const res = await fetch(process.env.NEXT_PUBLIC_TORII_API!, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + query, + variables, + }), + }); + + const json = await res.json(); + + if (json.errors) { + throw new Error(JSON.stringify(json.errors)); + } + + return json.data; + }; +}; diff --git a/web/src/hooks/useRyoWorld.tsx b/web/src/hooks/useRyoWorld.tsx index 4300e124..0bd1f822 100644 --- a/web/src/hooks/useRyoWorld.tsx +++ b/web/src/hooks/useRyoWorld.tsx @@ -16,7 +16,7 @@ export interface RyoWorldInterface { startTime: number, maxPlayers: number, maxTurns: number, - ) => Promise; + ) => Promise<{ gameId: string; locationName: string }>; join: (gameId: string) => Promise; travel: (gameId: string, locationId: string) => Promise; trade: ( @@ -26,10 +26,11 @@ export interface RyoWorldInterface { quantity: number, direction: TradeDirection, ) => Promise; + isPending: boolean; } export const useRyoWorld = (): RyoWorldInterface => { - const { execute, account } = useDojo(); + const { execute, account, isPending } = useDojo(); const create = async ( startTime: number, @@ -39,7 +40,7 @@ export const useRyoWorld = (): RyoWorldInterface => { const txn = await execute("create_game", [startTime, maxPlayers, maxTurns]); const receipt = await account.getTransactionReceipt(txn); - return parseGameId(receipt); + return parseJoinedEvent(receipt); }; const join = async (gameId: string) => { @@ -65,18 +66,25 @@ export const useRyoWorld = (): RyoWorldInterface => { join, trade, travel, + isPending, }; }; -const parseGameId = (receipt: InvokeTransactionReceiptResponse): string => { +const parseJoinedEvent = ( + receipt: InvokeTransactionReceiptResponse, +): { gameId: string; playerId: string; locationName: string } => { const event = receipt.events?.find( (event) => - shortString.decodeShortString(event.keys[0]) === RyoEvents.GameCreated, + shortString.decodeShortString(event.keys[0]) === RyoEvents.PlayerJoined, ); if (!event) { - throw new Error("No GameCreated event found in receipt"); + throw new Error("No PlayerJoined event found in receipt"); } - return num.toHexString(event.data[0]); + return { + gameId: num.toHexString(event.data[0]), + playerId: num.toHexString(event.data[1]), + locationName: shortString.decodeShortString(num.toHexString(event.data[2])), + }; }; diff --git a/web/src/pages/[game]/index.tsx b/web/src/pages/[gameId]/index.tsx similarity index 100% rename from web/src/pages/[game]/index.tsx rename to web/src/pages/[gameId]/index.tsx diff --git a/web/src/pages/[game]/location/[locationSlug]/[drugSlug].tsx b/web/src/pages/[gameId]/location/[locationSlug]/[drugSlug].tsx similarity index 100% rename from web/src/pages/[game]/location/[locationSlug]/[drugSlug].tsx rename to web/src/pages/[gameId]/location/[locationSlug]/[drugSlug].tsx diff --git a/web/src/pages/[game]/location/[locationSlug]/index.tsx b/web/src/pages/[gameId]/location/[locationSlug]/index.tsx similarity index 100% rename from web/src/pages/[game]/location/[locationSlug]/index.tsx rename to web/src/pages/[gameId]/location/[locationSlug]/index.tsx diff --git a/web/src/pages/[game]/travel.tsx b/web/src/pages/[gameId]/travel.tsx similarity index 100% rename from web/src/pages/[game]/travel.tsx rename to web/src/pages/[gameId]/travel.tsx diff --git a/web/src/pages/[game]/turn.tsx b/web/src/pages/[gameId]/turn.tsx similarity index 100% rename from web/src/pages/[game]/turn.tsx rename to web/src/pages/[gameId]/turn.tsx diff --git a/web/src/pages/_app.tsx b/web/src/pages/_app.tsx index d91858cf..e2682015 100644 --- a/web/src/pages/_app.tsx +++ b/web/src/pages/_app.tsx @@ -15,6 +15,12 @@ import useKonamiCode, { starkpimpSequence } from "@/hooks/useKonamiCode"; import MakeItRain from "@/components/MakeItRain"; import { useEffect } from "react"; import { DojoProvider } from "@/hooks/dojo"; +import { + PLAYER_ADDRESS, + PLAYER_PRIVATE_KEY, + RYO_WORLD_ADDRESS, +} from "@/constants"; +import { QueryClient, QueryClientProvider } from "react-query"; export const controllerConnector = new ControllerConnector([ { @@ -29,14 +35,18 @@ export const argentConnector = new InjectedConnector({ }); export const connectors = [controllerConnector as any, argentConnector]; -const address = - "0x3ee9e18edc71a6df30ac3aca2e0b02a198fbce19b7480a63a0d71cbd76652e0"; -const privateKey = - "0x300001800000000300000180000000000030000000000003006001800006600"; const provider = new RpcProvider({ - nodeUrl: process.env.NEXT_PUBLIC_KATANA_RPC || "", + nodeUrl: process.env.NEXT_PUBLIC_KATANA_RPC!, +}); +const account = new Account(provider, PLAYER_ADDRESS, PLAYER_PRIVATE_KEY); + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 1000 * 20, + }, + }, }); -const account = new Account(provider, address, privateKey); export default function App({ Component, pageProps }: AppProps) { const { setSequence, isRightSequence, setIsRightSequence } = @@ -53,22 +63,21 @@ export default function App({ Component, pageProps }: AppProps) { }, [isRightSequence, setIsRightSequence, setSequence]); return ( - - - - - Roll your Own - - - {isRightSequence && } - - - + + + + + + Roll your Own + + + {isRightSequence && } + + + + ); } diff --git a/web/src/pages/index.tsx b/web/src/pages/index.tsx index f10c4394..d884b987 100644 --- a/web/src/pages/index.tsx +++ b/web/src/pages/index.tsx @@ -14,25 +14,22 @@ import { Footer } from "@/components/Footer"; import Content from "@/components/Content"; import { User } from "@/components/icons/archive"; import { startGame, useGameStore } from "@/hooks/state"; -import { useEffect, useState } from "react"; import { playSound, Sounds } from "@/hooks/sound"; import BorderImagePixelated from "@/components/icons/BorderImagePixelated"; import BorderImage from "@/components/icons/BorderImage"; import Link from "next/link"; import Leaderboard from "@/components/Leaderboard"; import { useRyoWorld } from "@/hooks/useRyoWorld"; +import { getLocationByName } from "@/hooks/ui"; // hardcode game params for now const START_TIME = 0; -const START_CASH = 2000 * 10000; // 2000 USD -const START_HEALTH = 100; const MAX_PLAYERS = 1; const NUM_TURNS = 10; export default function Home() { - const [creating, setCreating] = useState(false); const router = useRouter(); - const { create } = useRyoWorld(); + const { create, isPending } = useRyoWorld(); return ( diff --git a/web/yarn.lock b/web/yarn.lock index 44edc2bd..ebec037f 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -501,6 +501,13 @@ dependencies: regenerator-runtime "^0.13.11" +"@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438" + integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ== + dependencies: + regenerator-runtime "^0.13.11" + "@babel/template@^7.18.10", "@babel/template@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" @@ -2811,6 +2818,11 @@ base64url@^3.0.1: resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== +big-integer@^1.6.16: + version "1.6.51" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + bignumber.js@^9.0.0: version "9.1.1" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" @@ -2855,6 +2867,20 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.0.1" +broadcast-channel@^3.4.1: + version "3.7.0" + resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.7.0.tgz#2dfa5c7b4289547ac3f6705f9c00af8723889937" + integrity sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg== + dependencies: + "@babel/runtime" "^7.7.2" + detect-node "^2.1.0" + js-sha3 "0.8.0" + microseconds "0.2.0" + nano-time "1.0.0" + oblivious-set "1.0.0" + rimraf "3.0.2" + unload "2.2.0" + brorand@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" @@ -3351,6 +3377,11 @@ detect-node-es@^1.1.0: resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493" integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ== +detect-node@^2.0.4, detect-node@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -4664,6 +4695,11 @@ js-sdsl@^4.1.4: resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711" integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== +js-sha3@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -4884,6 +4920,14 @@ map-cache@^0.2.0: resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== +match-sorter@^6.0.2: + version "6.3.1" + resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-6.3.1.tgz#98cc37fda756093424ddf3cbc62bfe9c75b92bda" + integrity sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw== + dependencies: + "@babel/runtime" "^7.12.5" + remove-accents "0.4.2" + merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" @@ -4910,6 +4954,11 @@ micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" +microseconds@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39" + integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA== + mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" @@ -4971,6 +5020,13 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +nano-time@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef" + integrity sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA== + dependencies: + big-integer "^1.6.16" + nanoid@^3.3.4: version "3.3.4" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" @@ -5128,6 +5184,11 @@ object.values@^1.1.6: define-properties "^1.1.4" es-abstract "^1.20.4" +oblivious-set@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/oblivious-set/-/oblivious-set-1.0.0.tgz#c8316f2c2fb6ff7b11b6158db3234c49f733c566" + integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw== + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -5439,6 +5500,15 @@ react-is@^16.13.1, react-is@^16.7.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-query@^3.39.2: + version "3.39.3" + resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.39.3.tgz#4cea7127c6c26bdea2de5fb63e51044330b03f35" + integrity sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g== + dependencies: + "@babel/runtime" "^7.5.5" + broadcast-channel "^3.4.1" + match-sorter "^6.0.2" + react-remove-scroll-bar@^2.3.3: version "2.3.4" resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz#53e272d7a5cb8242990c7f144c44d8bd8ab5afd9" @@ -5518,6 +5588,11 @@ remedial@^1.0.7: resolved "https://registry.yarnpkg.com/remedial/-/remedial-1.0.8.tgz#a5e4fd52a0e4956adbaf62da63a5a46a78c578a0" integrity sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg== +remove-accents@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5" + integrity sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA== + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -5584,7 +5659,7 @@ rfdc@^1.3.0: resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== -rimraf@^3.0.2: +rimraf@3.0.2, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -6134,6 +6209,14 @@ unixify@^1.0.0: dependencies: normalize-path "^2.1.1" +unload@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7" + integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA== + dependencies: + "@babel/runtime" "^7.6.2" + detect-node "^2.0.4" + update-browserslist-db@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940"