Skip to content
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

feat: nextjs example #259

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
**/*/target
**/*/dist
**/*/.next
packages/torii-client/wasm
packages/torii-client/pkg
packages/torii-wasm/pkg/
Expand Down
Binary file added examples/clients/react/react-app/favicon.ico
Binary file not shown.
1 change: 1 addition & 0 deletions examples/clients/react/react-app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Dojo React Vite</title>
<link rel="icon" href="/favicon.ico" />
</head>
<body>
<div id="root"></div>
Expand Down
15 changes: 15 additions & 0 deletions examples/clients/react/react-nextjs/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module.exports = {
env: { browser: true, es2020: true },
extends: [
// "eslint:recommended",
// "plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended",
"plugin:@next/next/recommended",
],
parser: "@typescript-eslint/parser",
parserOptions: { ecmaVersion: "latest", sourceType: "module" },
plugins: ["react-refresh"],
rules: {
"react-refresh/only-export-components": "warn",
},
};
36 changes: 36 additions & 0 deletions examples/clients/react/react-nextjs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

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

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
7 changes: 7 additions & 0 deletions examples/clients/react/react-nextjs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Dojo React next.js app

[book](https://book.dojoengine.org/)

## Notes

- Using next.js 13, need to figure out how to use wasm in version 14
21 changes: 21 additions & 0 deletions examples/clients/react/react-nextjs/next.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
// future: {
// webpack5: false,
// },
// experimental: {
// esmExternals: 'loose',
// },
webpack: (config, { nextRuntime, isServer }) => {
// console.log(`nextRuntime:`,isServer,nextRuntime)
// allow wasm
config.experiments = {
asyncWebAssembly: true,
layers: true,
};
return config;
},
};

export default nextConfig;
4 changes: 4 additions & 0 deletions examples/clients/react/react-nextjs/next_fix.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export WASM_PACKAGE="./node_modules/@dojoengine/torii-client//node_modules/@dojoengine/torii-wasm/package.json"
ls -l $WASM_PACKAGE
sed -i '' -e 's/node/web/' $WASM_PACKAGE
ls -l $WASM_PACKAGE
41 changes: 41 additions & 0 deletions examples/clients/react/react-nextjs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"name": "react-nextjs",
"version": "0.1.0",
"private": false,
"type": "module",
"scripts": {
"dev": "NODE_OPTIONS='--experimental-wasm-modules' next dev",
"build": "tsc && NODE_OPTIONS='--experimental-wasm-modules' next build",
"start": "next start",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0"
},
"dependencies": {
"@dojoengine/core": "workspace:*",
"@dojoengine/create-burner": "workspace:*",
"@dojoengine/react": "workspace:*",
"@dojoengine/recs": "2.0.13",
"@dojoengine/state": "workspace:*",
"@dojoengine/torii-client": "workspace:*",
"@dojoengine/utils": "workspace:*",
"@latticexyz/react": "^2.0.12",
"@latticexyz/utils": "^2.0.12",
"next": "^13.5.6",
"react": "^18",
"react-dom": "^18",
"starknet": "6.11.0"
},
"devDependencies": {
"@next/eslint-plugin-next": "^14.0.3",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"@typescript-eslint/eslint-plugin": "^5.59.0",
"@typescript-eslint/parser": "^5.59.0",
"eslint": "^8.38.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.3.4",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5.5.4"
}
}
8 changes: 8 additions & 0 deletions examples/clients/react/react-nextjs/postcss.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
},
};

export default config;
Binary file not shown.
172 changes: 172 additions & 0 deletions examples/clients/react/react-nextjs/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { useComponentValue, useQuerySync } from "@dojoengine/react";
import { Entity } from "@dojoengine/recs";
import { useEffect, useState } from "react";
import { Direction } from "./utils";
import { getEntityIdFromKeys } from "@dojoengine/utils";
import { useDojo } from "./dojo/useDojo";

function App() {
const {
setup: {
systemCalls: { spawn, move },
clientComponents: { Position, Moves, DirectionsAvailable },
toriiClient,
contractComponents,
},
account,
} = useDojo();

useQuerySync(toriiClient, contractComponents as any, []);

const [clipboardStatus, setClipboardStatus] = useState({
message: "",
isError: false,
});

// entity id we are syncing
const entityId = getEntityIdFromKeys([
BigInt(account?.account.address),
]) as Entity;

// get current component values
const position = useComponentValue(Position, entityId);
const moves = useComponentValue(Moves, entityId);
const directions = useComponentValue(DirectionsAvailable, entityId);

const handleRestoreBurners = async () => {
try {
await account?.applyFromClipboard();
setClipboardStatus({
message: "Burners restored successfully!",
isError: false,
});
} catch (error) {
setClipboardStatus({
message: `Failed to restore burners from clipboard`,
isError: true,
});
}
};

useEffect(() => {
if (clipboardStatus.message) {
const timer = setTimeout(() => {
setClipboardStatus({ message: "", isError: false });
}, 3000);

return () => clearTimeout(timer);
}
}, [clipboardStatus.message]);

return (
<>
<button onClick={() => account?.create()}>
{account?.isDeploying ? "deploying burner" : "create burner"}
</button>
{account && account?.list().length > 0 && (
<button onClick={async () => await account?.copyToClipboard()}>
Save Burners to Clipboard
</button>
)}
<button onClick={handleRestoreBurners}>
Restore Burners from Clipboard
</button>
{clipboardStatus.message && (
<div className={clipboardStatus.isError ? "error" : "success"}>
{clipboardStatus.message}
</div>
)}

<div className="card">
<div>{`burners deployed: ${account.count}`}</div>
<div>
select signer:{" "}
<select
value={account ? account.account.address : ""}
onChange={(e) => account.select(e.target.value)}
>
{account?.list().map((account, index) => {
return (
<option value={account.address} key={index}>
{account.address}
</option>
);
})}
</select>
</div>
<div>
<button onClick={() => account.clear()}>
Clear burners
</button>
<p>
You will need to Authorise the contracts before you can
use a burner. See readme.
</p>
</div>
</div>

<div className="card">
<button onClick={() => spawn(account.account)}>Spawn</button>
<div>
Moves Left: {moves ? `${moves.remaining}` : "Need to Spawn"}
</div>
<div>
Position:{" "}
{position
? `${position?.vec.x}, ${position?.vec.y}`
: "Need to Spawn"}
</div>

<div>{moves && moves.last_direction}</div>

<div>
<div>Available Positions</div>
{directions?.directions.map((a: string, index: number) => (
<div key={index} className="">
{a}
</div>
))}
</div>
</div>

<div className="card">
<div>
<button
onClick={() =>
position && position.vec.y > 0
? move(account.account, Direction.Up)
: console.log("Reach the borders of the world.")
}
>
Move Up
</button>
</div>
<div>
<button
onClick={() =>
position && position.vec.x > 0
? move(account.account, Direction.Left)
: console.log("Reach the borders of the world.")
}
>
Move Left
</button>
<button
onClick={() => move(account.account, Direction.Right)}
>
Move Right
</button>
</div>
<div>
<button
onClick={() => move(account.account, Direction.Down)}
>
Move Down
</button>
</div>
</div>
</>
);
}

export default App;
Loading
Loading