Skip to content

Commit ce77009

Browse files
committed
Create game hooks
1 parent e70bbb6 commit ce77009

File tree

15 files changed

+450
-30
lines changed

15 files changed

+450
-30
lines changed

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,13 @@
1212
"prepare": "husky"
1313
},
1414
"dependencies": {
15+
"@noble/ed25519": "^2.1.0",
16+
"@noble/hashes": "^1.5.0",
1517
"@tanstack/react-query": "^5.55.4",
18+
"bech32-buffer": "^0.2.1",
1619
"classnames": "^2.5.1",
1720
"lodash": "^4.17.21",
21+
"lucid-cardano": "^0.10.7",
1822
"react": "^18.3.1",
1923
"react-dom": "^18.3.1",
2024
"use-custom-compare": "^1.4.0",
@@ -42,6 +46,8 @@
4246
"typescript": "^5.5.3",
4347
"typescript-eslint": "^8.0.1",
4448
"vite": "^5.4.1",
49+
"vite-plugin-top-level-await": "^1.4.4",
50+
"vite-plugin-wasm": "^3.3.0",
4551
"yarn": "^1.22.22"
4652
}
4753
}

src/App.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import InitialView from "./components/InitialView";
33
import GameView from "./components/GameView";
44
import AppContextProvider from "./context/AppContextProvider";
55
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
6+
import GameContextProvider from "./context/GameContextProvider";
67

78
const queryClient = new QueryClient();
89

@@ -13,7 +14,9 @@ export default function App() {
1314
<QueryClientProvider client={queryClient}>
1415
<AppContextProvider>
1516
{isGameStarted ? (
16-
<GameView />
17+
<GameContextProvider>
18+
<GameView />
19+
</GameContextProvider>
1720
) : (
1821
<InitialView startGame={() => setIsGameStarted(true)} />
1922
)}

src/components/InitialView/InitialView.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Modal from "../Modal";
77
import SelectContinentDialog from "../SelectContinentDialog";
88
import Layout from "../Layout";
99
import GlobalTotals from "../GlobalTotals";
10+
import { REGION } from "../../constants";
1011

1112
interface InitialViewProps {
1213
startGame: () => void;
@@ -17,18 +18,22 @@ const InitialView: FC<InitialViewProps> = ({ startGame }) => {
1718
const [isSelectContinentModalOpen, setIsSelectContinentModalOpen] =
1819
useState(false);
1920

21+
const handleClickPlay = () => {
22+
if (REGION) {
23+
startGame();
24+
} else {
25+
setIsSelectContinentModalOpen(true);
26+
}
27+
};
28+
2029
return (
2130
<Layout>
2231
<img
2332
src={hydraText}
2433
alt="Hydra"
2534
className="w-full max-w-5xl relative -bottom-14 -mt-14 z-10 pointer-events-none"
2635
/>
27-
<Button
28-
className="w-96 h-16"
29-
onClick={() => setIsSelectContinentModalOpen(true)}
30-
withDecoration
31-
>
36+
<Button className="w-96 h-16" onClick={handleClickPlay} withDecoration>
3237
Play Doom on Hydra
3338
</Button>
3439
<div className="grid grid-cols-2 max-w-6xl w-full mt-32 gap-8 py-6">

src/components/SelectContinentDialog/SelectContinentDialog.tsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,41 @@
1-
import { FC } from "react";
1+
import { ChangeEventHandler, FC } from "react";
22
import Modal from "../Modal";
33
import Button from "../Button";
4+
import { REGIONS } from "../../constants";
5+
import { useAppContext } from "../../context/useAppContext";
46

57
interface SelectContinentDialogProps {
68
close: () => void;
79
isOpen: boolean;
810
startGame: () => void;
911
}
1012

11-
const continents = [
12-
{ name: "Ohio, NA", value: "us-east-2" },
13-
{ name: "Oregon, NA", value: "us-west-2" },
14-
{ name: "Frankfurt, Europe", value: "eu-central-1" },
15-
{ name: "Cape Town, Africa", value: "af-south-1" },
16-
{ name: "Melbourne, Australia", value: "ap-southeast-4" },
17-
{ name: "Seoul, Asia", value: "ap-northeast-2" },
18-
{ name: "Sao Paulo, SA", value: "sa-east-1" },
19-
];
20-
2113
const SelectContinentDialog: FC<SelectContinentDialogProps> = ({
2214
close,
2315
isOpen,
2416
startGame,
2517
}) => {
18+
const { region, setRegion } = useAppContext();
19+
20+
const handleChange: ChangeEventHandler<HTMLInputElement> = (event) => {
21+
setRegion(REGIONS.find((r) => r.value === event.target.value)!);
22+
};
23+
2624
return (
2725
<Modal isOpen={isOpen} close={close}>
2826
<h1 className="text-5xl mb-20 text-center">Select your continent</h1>
2927
<form>
3028
<ul className="grid grid-cols-2 gap-y-8 text-3xl gap-x-40 mb-20">
31-
{continents.map((continent) => (
29+
{REGIONS.map((continent) => (
3230
<li key={continent.value}>
33-
<label className="flex gap-4 items-center">
31+
<label className="flex gap-4 items-center cursor-pointer">
3432
<input
35-
className="h-6 w-6"
33+
className="h-6 w-6 cursor-pointer"
3634
name="continent"
3735
type="radio"
3836
value={continent.value}
37+
checked={continent.value === region.value}
38+
onChange={handleChange}
3939
/>
4040
{continent.name}
4141
</label>

src/constants.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
export const CABINET_KEY = import.meta.env.VITE_CABINET_KEY;
22
export const GLOBAL_MAX_SPEED = 30 * 100;
3-
export const HANDLE_CACHE_KEY = "playerHandleCache";
3+
export const HANDLE_CACHE_KEY = "player-handle-cache";
4+
export const HYDRA_DOOM_SESSION_KEY = "hydra-doom-session-key";
45
export const MAX_SPEED = 40;
56
export const REGION = import.meta.env.VITE_REGION;
7+
export const REGIONS = [
8+
{ name: "Ohio, NA", value: "us-east-2" },
9+
{ name: "Oregon, NA", value: "us-west-2" },
10+
{ name: "Frankfurt, Europe", value: "eu-central-1" },
11+
{ name: "Cape Town, Africa", value: "af-south-1" },
12+
{ name: "Melbourne, Australia", value: "ap-southeast-4" },
13+
{ name: "Seoul, Asia", value: "ap-northeast-2" },
14+
{ name: "Sao Paulo, SA", value: "sa-east-1" },
15+
];
616
export const SERVER_URL = import.meta.env.VITE_SERVER_URL;
717
export const TIC_RATE_MAGIC = 35; // 35 is the ticrate in DOOM WASM they use to calculate time.

src/context/AppContextProvider.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
1-
import { FC, PropsWithChildren, useMemo } from "react";
1+
import { FC, PropsWithChildren, useMemo, useState } from "react";
22
import { useQuery } from "@tanstack/react-query";
33
import { AppContext } from "./useAppContext";
4-
import { GameStatistics } from "../types";
5-
import { SERVER_URL } from "../constants";
4+
import { GameStatistics, Region } from "../types";
5+
import { REGIONS, SERVER_URL } from "../constants";
66

77
const AppContextProvider: FC<PropsWithChildren> = ({ children }) => {
8+
const [region, setRegion] = useState<Region>(REGIONS[0]);
89
const globalQuery = useQuery<GameStatistics>({
910
queryKey: ["global"],
1011
queryFn: async () => {
11-
const response = await fetch(`${SERVER_URL}/global`);
12+
const response = await fetch(`${SERVER_URL}global`);
1213
return response.json();
1314
},
14-
// refetchInterval: 1000,
15+
refetchInterval: 1000,
1516
});
1617

17-
const value = useMemo(() => ({ globalQuery }), [globalQuery]);
18+
const value = useMemo(
19+
() => ({ globalQuery, region, setRegion }),
20+
[globalQuery, region],
21+
);
1822

1923
return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
2024
};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { FC, PropsWithChildren, useMemo } from "react";
2+
import { useQuery } from "@tanstack/react-query";
3+
import { GameContext } from "./useGameContext";
4+
import { GameStatistics } from "../types";
5+
import { CABINET_KEY, REGION, SERVER_URL } from "../constants";
6+
import useAddress from "../hooks/useAddress";
7+
import { useAppContext } from "./useAppContext";
8+
9+
const GameContextProvider: FC<PropsWithChildren> = ({ children }) => {
10+
const { region } = useAppContext();
11+
const address = useAddress();
12+
const newGameQuery = useQuery<GameStatistics>({
13+
queryKey: ["newGame", address, region.value],
14+
queryFn: async () => {
15+
const response = await fetch(
16+
`${SERVER_URL}new_game?address=${address}&region=${REGION ?? region.value}&reserved=${!!CABINET_KEY}`,
17+
);
18+
return response.json();
19+
},
20+
enabled: !!address,
21+
});
22+
23+
const value = useMemo(() => ({ newGameQuery }), [newGameQuery]);
24+
25+
return <GameContext.Provider value={value}>{children}</GameContext.Provider>;
26+
};
27+
28+
export default GameContextProvider;

src/context/useAppContext.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1-
import { createContext, useContext } from "react";
2-
import { GameStatistics } from "../types";
1+
import { createContext, Dispatch, useContext } from "react";
2+
import { GameStatistics, Region } from "../types";
33
import { UseQueryResult } from "@tanstack/react-query";
4+
import { REGIONS } from "../constants";
45

56
interface AppContextInterface {
67
globalQuery?: UseQueryResult<GameStatistics, Error>;
8+
region: Region;
9+
setRegion: Dispatch<React.SetStateAction<Region>>;
710
}
811

912
export const AppContext = createContext<AppContextInterface>({
1013
globalQuery: undefined,
14+
region: REGIONS[0],
15+
setRegion: () => {},
1116
});
1217

1318
export const useAppContext = () => {

src/context/useGameContext.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { createContext, useContext } from "react";
2+
import { GameStatistics } from "../types";
3+
import { UseQueryResult } from "@tanstack/react-query";
4+
5+
interface GameContextInterface {
6+
newGameQuery?: UseQueryResult<GameStatistics, Error>;
7+
}
8+
9+
export const GameContext = createContext<GameContextInterface>({
10+
newGameQuery: undefined,
11+
});
12+
13+
export const useGameContext = () => {
14+
const context = useContext(GameContext);
15+
return context;
16+
};

src/hooks/useAddress.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Lucid } from "lucid-cardano";
2+
import { useCallback, useEffect, useState } from "react";
3+
import useKeys from "./useKeys";
4+
5+
const useAddress = () => {
6+
const [address, setAddress] = useState<string>();
7+
const { sessionKey } = useKeys();
8+
9+
const generateAddress = useCallback(async () => {
10+
if (sessionKey) {
11+
const lucid = await Lucid.new(undefined, "Preprod");
12+
const val = await lucid
13+
.selectWalletFromPrivateKey(sessionKey)
14+
.wallet.address();
15+
setAddress(val);
16+
}
17+
}, [sessionKey]);
18+
19+
useEffect(() => {
20+
if (!address) {
21+
generateAddress();
22+
}
23+
}, [address, generateAddress]);
24+
25+
return address;
26+
};
27+
28+
export default useAddress;

0 commit comments

Comments
 (0)