diff --git a/frontend/package.json b/frontend/package.json index 952b294..8bb1635 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,11 +10,13 @@ "preview": "vite preview" }, "dependencies": { + "@tanstack/react-router": "^1.28.7", "react": "^18.2.0", "react-dom": "^18.2.0", "web3": "^4.7.0" }, "devDependencies": { + "@tanstack/router-vite-plugin": "^1.28.2", "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22", "@typescript-eslint/eslint-plugin": "^7.2.0", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 7e26865..b899697 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -5,6 +5,9 @@ settings: excludeLinksFromLockfile: false dependencies: + '@tanstack/react-router': + specifier: ^1.28.7 + version: 1.28.7(react-dom@18.2.0)(react@18.2.0) react: specifier: ^18.2.0 version: 18.2.0 @@ -16,6 +19,9 @@ dependencies: version: 4.7.0(typescript@5.4.5) devDependencies: + '@tanstack/router-vite-plugin': + specifier: ^1.28.2 + version: 1.28.2(vite@5.2.9) '@types/react': specifier: ^18.2.66 version: 18.2.79 @@ -129,6 +135,13 @@ packages: jsesc: 2.5.2 dev: true + /@babel/helper-annotate-as-pure@7.22.5: + resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: true + /@babel/helper-compilation-targets@7.23.6: resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} engines: {node: '>=6.9.0'} @@ -140,6 +153,24 @@ packages: semver: 6.3.1 dev: true + /@babel/helper-create-class-features-plugin@7.24.4(@babel/core@7.24.4): + resolution: {integrity: sha512-lG75yeuUSVu0pIcbhiYMXBXANHrpUPaOfu7ryAzskCgKUHuAxRQI5ssrtmF0X9UXldPlvT0XM/A4F44OXRt6iQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.24.4 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-member-expression-to-functions': 7.23.0 + '@babel/helper-optimise-call-expression': 7.22.5 + '@babel/helper-replace-supers': 7.24.1(@babel/core@7.24.4) + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + semver: 6.3.1 + dev: true + /@babel/helper-environment-visitor@7.22.20: resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} engines: {node: '>=6.9.0'} @@ -160,6 +191,13 @@ packages: '@babel/types': 7.24.0 dev: true + /@babel/helper-member-expression-to-functions@7.23.0: + resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: true + /@babel/helper-module-imports@7.24.3: resolution: {integrity: sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==} engines: {node: '>=6.9.0'} @@ -181,11 +219,30 @@ packages: '@babel/helper-validator-identifier': 7.22.20 dev: true + /@babel/helper-optimise-call-expression@7.22.5: + resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: true + /@babel/helper-plugin-utils@7.24.0: resolution: {integrity: sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==} engines: {node: '>=6.9.0'} dev: true + /@babel/helper-replace-supers@7.24.1(@babel/core@7.24.4): + resolution: {integrity: sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.24.4 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-member-expression-to-functions': 7.23.0 + '@babel/helper-optimise-call-expression': 7.22.5 + dev: true + /@babel/helper-simple-access@7.22.5: resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} engines: {node: '>=6.9.0'} @@ -193,6 +250,13 @@ packages: '@babel/types': 7.24.0 dev: true + /@babel/helper-skip-transparent-expression-wrappers@7.22.5: + resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: true + /@babel/helper-split-export-declaration@7.22.6: resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} engines: {node: '>=6.9.0'} @@ -244,6 +308,26 @@ packages: '@babel/types': 7.24.0 dev: true + /@babel/plugin-syntax-jsx@7.24.1(@babel/core@7.24.4): + resolution: {integrity: sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.4 + '@babel/helper-plugin-utils': 7.24.0 + dev: true + + /@babel/plugin-syntax-typescript@7.24.1(@babel/core@7.24.4): + resolution: {integrity: sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.4 + '@babel/helper-plugin-utils': 7.24.0 + dev: true + /@babel/plugin-transform-react-jsx-self@7.24.1(@babel/core@7.24.4): resolution: {integrity: sha512-kDJgnPujTmAZ/9q2CN4m2/lRsUUPDvsG3+tSHWUJIzMGTt5U/b/fwWd3RO3n+5mjLrsBrVa5eKFRVSQbi3dF1w==} engines: {node: '>=6.9.0'} @@ -264,6 +348,33 @@ packages: '@babel/helper-plugin-utils': 7.24.0 dev: true + /@babel/plugin-transform-react-jsx@7.23.4(@babel/core@7.24.4): + resolution: {integrity: sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.4 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-module-imports': 7.24.3 + '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-jsx': 7.24.1(@babel/core@7.24.4) + '@babel/types': 7.24.0 + dev: true + + /@babel/plugin-transform-typescript@7.24.4(@babel/core@7.24.4): + resolution: {integrity: sha512-79t3CQ8+oBGk/80SQ8MN3Bs3obf83zJ0YZjDmDaEZN8MqhMI760apl5z6a20kFeMXBwJX99VpKT8CKxEBp5H1g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.4 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-create-class-features-plugin': 7.24.4(@babel/core@7.24.4) + '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-typescript': 7.24.1(@babel/core@7.24.4) + dev: true + /@babel/template@7.24.0: resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} engines: {node: '>=6.9.0'} @@ -798,6 +909,75 @@ packages: '@scure/base': 1.1.6 dev: false + /@tanstack/history@1.26.10: + resolution: {integrity: sha512-fHx8RQ3liEDhueIemUggBGmqYnK6vOxtxCduolW7r6ExBEQVwKdLEcaUobxp6BxcXLQ7z/qhXAptlOlYi4FFXg==} + engines: {node: '>=12'} + dev: false + + /@tanstack/react-router@1.28.7(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-eSOg/ffG8KJ9E3oAJnhiiQtz5dNM/7M5PL2BItZeAZF+0CSIGi0eI97orGDknY42iMdE+1KRkCKdfuutln2dxw==} + engines: {node: '>=12'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + dependencies: + '@tanstack/history': 1.26.10 + '@tanstack/react-store': 0.2.1(react-dom@18.2.0)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 + dev: false + + /@tanstack/react-store@0.2.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-tEbMCQjbeVw9KOP/202LfqZMSNAVi6zYkkp1kBom8nFuMx/965Hzes3+6G6b/comCwVxoJU8Gg9IrcF8yRPthw==} + peerDependencies: + react: '>=16' + react-dom: '>=16' + dependencies: + '@tanstack/store': 0.1.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + use-sync-external-store: 1.2.0(react@18.2.0) + dev: false + + /@tanstack/router-generator@1.28.2: + resolution: {integrity: sha512-CBvLWduHLwr7EIMUqPFGWrHMR3+GsSWTk+zjlEmHT0IWVKhQyTAz21V3OE3Y6RJ+yD6S6WZV1sq8+09HFA0pYw==} + engines: {node: '>=12'} + dependencies: + prettier: 3.2.5 + zod: 3.22.4 + dev: true + + /@tanstack/router-vite-plugin@1.28.2(vite@5.2.9): + resolution: {integrity: sha512-LJLGg1apPo9KLgaib+JNcy7rSPXT0fVp8qvU9nQKf28xdTyeEk9bVDQRGCjy4pKflopufSoM0nXjFJjpGf+5Aw==} + engines: {node: '>=12'} + dependencies: + '@babel/core': 7.24.4 + '@babel/generator': 7.24.4 + '@babel/plugin-syntax-jsx': 7.24.1(@babel/core@7.24.4) + '@babel/plugin-syntax-typescript': 7.24.1(@babel/core@7.24.4) + '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.24.4) + '@babel/plugin-transform-typescript': 7.24.4(@babel/core@7.24.4) + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.1 + '@babel/types': 7.24.0 + '@tanstack/router-generator': 1.28.2 + '@types/babel__core': 7.20.5 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.5 + '@vitejs/plugin-react': 4.2.1(vite@5.2.9) + zod: 3.22.4 + transitivePeerDependencies: + - supports-color + - vite + dev: true + + /@tanstack/store@0.1.3: + resolution: {integrity: sha512-GnolmC8Fr4mvsHE1fGQmR3Nm0eBO3KnZjDU0a+P3TeQNM/dDscFGxtA7p31NplQNW3KwBw4t1RVFmz0VeKLxcw==} + dev: false + /@types/babel__core@7.20.5: resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} dependencies: @@ -2269,6 +2449,12 @@ packages: engines: {node: '>= 0.8.0'} dev: true + /prettier@3.2.5: + resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} + engines: {node: '>=14'} + hasBin: true + dev: true + /punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -2551,6 +2737,14 @@ packages: any-promise: 1.3.0 dev: true + /tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + dev: false + + /tiny-warning@1.0.3: + resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} + dev: false + /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} @@ -2618,6 +2812,14 @@ packages: punycode: 2.3.1 dev: true + /use-sync-external-store@1.2.0(react@18.2.0): + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true @@ -3018,4 +3220,3 @@ packages: /zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} - dev: false diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx deleted file mode 100644 index a4ef7a5..0000000 --- a/frontend/src/App.tsx +++ /dev/null @@ -1,55 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { useState } from "react"; -import { messageBox, web3 } from "./web3"; - -function App() { - const [connectedAccount, setConnectedAccount] = useState(""); - const [message, setMessage] = useState(""); - - // https://docs.web3js.org/guides/getting_started/metamask/#react-app - async function connectMetamask() { - // Request user to connect accounts (MetaMask will prompt) - await window.ethereum.request({ method: "eth_requestAccounts" }); - - // Get the connected accounts - const accounts = await web3.eth.getAccounts(); - - // Show the first connected account in the page - setConnectedAccount(accounts[0]); - - const _message: string = await messageBox.methods.getMessage().call(); - setMessage(_message); - } - - return ( -
-

OsiPad

-

- Lorem ipsum, dolor sit amet consectetur adipisicing elit. Adipisci ad - error eius recusandae voluptatibus modi id repellat amet, optio porro - magni ducimus consequatur voluptas nisi aperiam enim eligendi excepturi - et! -

- -
- -
- {/* Button to trigger MetaMask connection */} - - - {/* Display the connected account */} -

Connected account address: {connectedAccount}

- -

Message: {message}

-
-
- ); -} - -export default App; diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 3f507e0..b32ba6c 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1,10 +1,18 @@ +import { RouterProvider, createRouter } from "@tanstack/react-router"; import React from "react"; import ReactDOM from "react-dom/client"; -import App from "./App.tsx"; import "./index.css"; +import { routeTree } from "./routeTree.gen"; + +const router = createRouter({ routeTree }); +declare module "@tanstack/react-router" { + interface Register { + router: typeof router; + } +} ReactDOM.createRoot(document.getElementById("root")!).render( - + ); diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts new file mode 100644 index 0000000..d9869aa --- /dev/null +++ b/frontend/src/routeTree.gen.ts @@ -0,0 +1,53 @@ +/* prettier-ignore-start */ + +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file is auto-generated by TanStack Router + +import { createFileRoute } from '@tanstack/react-router' + +// Import Routes + +import { Route as rootRoute } from './routes/__root' + +// Create Virtual Routes + +const AboutLazyImport = createFileRoute('/about')() +const IndexLazyImport = createFileRoute('/')() + +// Create/Update Routes + +const AboutLazyRoute = AboutLazyImport.update({ + path: '/about', + getParentRoute: () => rootRoute, +} as any).lazy(() => import('./routes/about.lazy').then((d) => d.Route)) + +const IndexLazyRoute = IndexLazyImport.update({ + path: '/', + getParentRoute: () => rootRoute, +} as any).lazy(() => import('./routes/index.lazy').then((d) => d.Route)) + +// Populate the FileRoutesByPath interface + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/': { + preLoaderRoute: typeof IndexLazyImport + parentRoute: typeof rootRoute + } + '/about': { + preLoaderRoute: typeof AboutLazyImport + parentRoute: typeof rootRoute + } + } +} + +// Create and export the route tree + +export const routeTree = rootRoute.addChildren([IndexLazyRoute, AboutLazyRoute]) + +/* prettier-ignore-end */ diff --git a/frontend/src/routes/__root.tsx b/frontend/src/routes/__root.tsx new file mode 100644 index 0000000..bc1bd8c --- /dev/null +++ b/frontend/src/routes/__root.tsx @@ -0,0 +1,27 @@ +import { createRootRoute, Link, Outlet } from "@tanstack/react-router"; + +export const Route = createRootRoute({ + component: RootLayout, +}); + +function RootLayout() { + return ( + <> +
+ OsiPad™ +
+
+ + Home + + + About + +
+
+
+ +
+ + ); +} diff --git a/frontend/src/routes/about.lazy.tsx b/frontend/src/routes/about.lazy.tsx new file mode 100644 index 0000000..fef69ec --- /dev/null +++ b/frontend/src/routes/about.lazy.tsx @@ -0,0 +1,19 @@ +import { createLazyFileRoute } from "@tanstack/react-router"; + +export const Route = createLazyFileRoute("/about")({ + component: About, +}); + +function About() { + return ( +
+

About

+

+ Lorem ipsum dolor sit, amet consectetur adipisicing elit. Nemo nulla + neque quo officiis eum quia voluptates ratione, tenetur assumenda ad + error et! Libero mollitia eaque praesentium? Velit consectetur ratione + sunt? +

+
+ ); +} diff --git a/frontend/src/routes/index.lazy.tsx b/frontend/src/routes/index.lazy.tsx new file mode 100644 index 0000000..f17c4bb --- /dev/null +++ b/frontend/src/routes/index.lazy.tsx @@ -0,0 +1,48 @@ +import { createLazyFileRoute } from "@tanstack/react-router"; +import { useState } from "react"; +import { messageBox, web3 } from "../web3"; + +export const Route = createLazyFileRoute("/")({ + component: Index, +}); + +function Index() { + const [connectedAccount, setConnectedAccount] = useState("(nothingness)"); + const [message, setMessage] = useState("(nothingness)"); + + // https://docs.web3js.org/guides/getting_started/metamask/#react-app + async function connectMetamask() { + // Request user to connect accounts (MetaMask will prompt) + await window.ethereum.request({ method: "eth_requestAccounts" }); + + // Get the connected accounts + const accounts = await web3.eth.getAccounts(); + + // Show the first connected account in the page + setConnectedAccount(accounts[0]); + + const _message: string = await messageBox.methods.getMessage().call(); + setMessage(_message); + } + + return ( +
+ + +
+ Connected account address:{" "} + {connectedAccount} +
+ +
+ Message: {message} +
+
+ ); +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 5a33944..366d300 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,7 +1,8 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { TanStackRouterVite } from "@tanstack/router-vite-plugin"; +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; // https://vitejs.dev/config/ export default defineConfig({ - plugins: [react()], -}) + plugins: [react(), TanStackRouterVite()], +});