add Sonner (and have some fun)
This commit is contained in:
parent
f985906395
commit
545bdf7bae
|
|
@ -13,6 +13,7 @@
|
||||||
"@tanstack/react-router": "^1.28.7",
|
"@tanstack/react-router": "^1.28.7",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
"sonner": "^1.4.41",
|
||||||
"web3": "^4.7.0"
|
"web3": "^4.7.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@ dependencies:
|
||||||
react-dom:
|
react-dom:
|
||||||
specifier: ^18.2.0
|
specifier: ^18.2.0
|
||||||
version: 18.2.0(react@18.2.0)
|
version: 18.2.0(react@18.2.0)
|
||||||
|
sonner:
|
||||||
|
specifier: ^1.4.41
|
||||||
|
version: 1.4.41(react-dom@18.2.0)(react@18.2.0)
|
||||||
web3:
|
web3:
|
||||||
specifier: ^4.7.0
|
specifier: ^4.7.0
|
||||||
version: 4.7.0(typescript@5.4.5)
|
version: 4.7.0(typescript@5.4.5)
|
||||||
|
|
@ -2614,6 +2617,16 @@ packages:
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/sonner@1.4.41(react-dom@18.2.0)(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-uG511ggnnsw6gcn/X+YKkWPo5ep9il9wYi3QJxHsYe7yTZ4+cOd1wuodOUmOpFuXL+/RE3R04LczdNCDygTDgQ==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^18.0.0
|
||||||
|
react-dom: ^18.0.0
|
||||||
|
dependencies:
|
||||||
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/source-map-js@1.2.0:
|
/source-map-js@1.2.0:
|
||||||
resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
|
resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,17 @@ import { Route as rootRoute } from './routes/__root'
|
||||||
|
|
||||||
// Create Virtual Routes
|
// Create Virtual Routes
|
||||||
|
|
||||||
|
const MessageBoxLazyImport = createFileRoute('/message-box')()
|
||||||
const AboutLazyImport = createFileRoute('/about')()
|
const AboutLazyImport = createFileRoute('/about')()
|
||||||
const IndexLazyImport = createFileRoute('/')()
|
const IndexLazyImport = createFileRoute('/')()
|
||||||
|
|
||||||
// Create/Update Routes
|
// Create/Update Routes
|
||||||
|
|
||||||
|
const MessageBoxLazyRoute = MessageBoxLazyImport.update({
|
||||||
|
path: '/message-box',
|
||||||
|
getParentRoute: () => rootRoute,
|
||||||
|
} as any).lazy(() => import('./routes/message-box.lazy').then((d) => d.Route))
|
||||||
|
|
||||||
const AboutLazyRoute = AboutLazyImport.update({
|
const AboutLazyRoute = AboutLazyImport.update({
|
||||||
path: '/about',
|
path: '/about',
|
||||||
getParentRoute: () => rootRoute,
|
getParentRoute: () => rootRoute,
|
||||||
|
|
@ -43,11 +49,19 @@ declare module '@tanstack/react-router' {
|
||||||
preLoaderRoute: typeof AboutLazyImport
|
preLoaderRoute: typeof AboutLazyImport
|
||||||
parentRoute: typeof rootRoute
|
parentRoute: typeof rootRoute
|
||||||
}
|
}
|
||||||
|
'/message-box': {
|
||||||
|
preLoaderRoute: typeof MessageBoxLazyImport
|
||||||
|
parentRoute: typeof rootRoute
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create and export the route tree
|
// Create and export the route tree
|
||||||
|
|
||||||
export const routeTree = rootRoute.addChildren([IndexLazyRoute, AboutLazyRoute])
|
export const routeTree = rootRoute.addChildren([
|
||||||
|
IndexLazyRoute,
|
||||||
|
AboutLazyRoute,
|
||||||
|
MessageBoxLazyRoute,
|
||||||
|
])
|
||||||
|
|
||||||
/* prettier-ignore-end */
|
/* prettier-ignore-end */
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { createRootRoute, Link, Outlet } from "@tanstack/react-router";
|
import { createRootRoute, Link, Outlet } from "@tanstack/react-router";
|
||||||
|
import { Toaster } from "sonner";
|
||||||
|
|
||||||
export const Route = createRootRoute({
|
export const Route = createRootRoute({
|
||||||
component: RootLayout,
|
component: RootLayout,
|
||||||
|
|
@ -17,10 +18,14 @@ function RootLayout() {
|
||||||
<Link to="/about" className="[&.active]:font-bold">
|
<Link to="/about" className="[&.active]:font-bold">
|
||||||
About
|
About
|
||||||
</Link>
|
</Link>
|
||||||
|
<Link to="/message-box" className="[&.active]:font-bold">
|
||||||
|
Message Box
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
|
<Toaster />
|
||||||
</main>
|
</main>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/about")({
|
export const Route = createLazyFileRoute("/about")({
|
||||||
component: About,
|
component: AboutPage,
|
||||||
});
|
});
|
||||||
|
|
||||||
function About() {
|
function AboutPage() {
|
||||||
return (
|
return (
|
||||||
<div className="p-4">
|
<div className="container mx-auto p-4">
|
||||||
<h1 className="text-3xl font-bold">About</h1>
|
<h1 className="text-3xl font-bold text-center">About</h1>
|
||||||
<p>
|
<p>
|
||||||
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Nemo nulla
|
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Nemo nulla
|
||||||
neque quo officiis eum quia voluptates ratione, tenetur assumenda ad
|
neque quo officiis eum quia voluptates ratione, tenetur assumenda ad
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1,25 @@
|
||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
import { useState } from "react";
|
|
||||||
import { messageBox, web3 } from "../web3";
|
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/")({
|
export const Route = createLazyFileRoute("/")({
|
||||||
component: Index,
|
component: IndexPage,
|
||||||
});
|
});
|
||||||
|
|
||||||
function Index() {
|
function IndexPage() {
|
||||||
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 (
|
return (
|
||||||
<div className="p-4">
|
<div className="container mx-auto p-4 text-center">
|
||||||
<button
|
<div className="my-6">
|
||||||
type="button"
|
<h1 className="text-7xl font-bold italic">This is a launchpad.</h1>
|
||||||
className="btn btn-primary"
|
<h2 className="text-3xl font-bold italic">
|
||||||
onClick={() => connectMetamask()}
|
(Don't you dare thinking otherwise.)
|
||||||
>
|
</h2>
|
||||||
Connect to MetaMask
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<span className="font-bold">Connected account address:</span>{" "}
|
|
||||||
{connectedAccount}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<p>
|
||||||
<span className="font-bold">Message:</span> {message}
|
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Adipisci
|
||||||
</div>
|
nesciunt ipsa possimus sed? Maxime eos iusto facere natus commodi
|
||||||
|
consectetur ad pariatur minima quisquam. Soluta esse minus porro nemo
|
||||||
|
at.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { tryWithToast } from "../utils";
|
||||||
|
import { messageBox, web3 } from "../web3";
|
||||||
|
|
||||||
|
export const Route = createLazyFileRoute("/message-box")({
|
||||||
|
component: MessageBoxPage,
|
||||||
|
});
|
||||||
|
|
||||||
|
function MessageBoxPage() {
|
||||||
|
const [connectedAccount, setConnectedAccount] = useState("(nothingness)");
|
||||||
|
const [message, setMessage] = useState("(nothingness)");
|
||||||
|
const [newMessage, setNewMessage] = useState("");
|
||||||
|
|
||||||
|
// https://docs.web3js.org/guides/getting_started/metamask/#react-app
|
||||||
|
async function connectMetamask() {
|
||||||
|
await tryWithToast("Connect MetaMask", async () => {
|
||||||
|
// 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]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getMessage() {
|
||||||
|
await tryWithToast("Get Message", async () => {
|
||||||
|
const _message: string = await messageBox.methods.getMessage().call();
|
||||||
|
setMessage(_message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _setMessage() {
|
||||||
|
await tryWithToast("Set Message", async () => {
|
||||||
|
await messageBox.methods
|
||||||
|
.setMessage(newMessage)
|
||||||
|
.send({ from: connectedAccount });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto p-4 grid md:grid-cols-2 xl:grid-cols-3 gap-4">
|
||||||
|
<div className="card bg-base-200">
|
||||||
|
<div className="card-body items-start">
|
||||||
|
<h2 className="text-2xl font-bold">✨Essentials✨</h2>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-primary"
|
||||||
|
onClick={() => connectMetamask()}
|
||||||
|
>
|
||||||
|
Connect to MetaMask
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span className="font-bold">Connected account address:</span>{" "}
|
||||||
|
{connectedAccount}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="card bg-base-200">
|
||||||
|
<div className="card-body items-start">
|
||||||
|
<h2 className="text-2xl font-bold">Get Message</h2>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-primary"
|
||||||
|
onClick={() => getMessage()}
|
||||||
|
>
|
||||||
|
Get
|
||||||
|
</button>
|
||||||
|
<div>
|
||||||
|
<span className="font-bold">Message:</span> {message}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="card bg-base-200">
|
||||||
|
<div className="card-body items-start">
|
||||||
|
<h2 className="text-2xl font-bold">Set Message</h2>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="input"
|
||||||
|
placeholder="New message"
|
||||||
|
value={newMessage}
|
||||||
|
onChange={(event) => setNewMessage(event.target.value)}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-primary"
|
||||||
|
onClick={() => _setMessage()}
|
||||||
|
>
|
||||||
|
Set
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
|
export async function tryWithToast(
|
||||||
|
taskName: string,
|
||||||
|
taskFn: () => Promise<void>
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
await taskFn();
|
||||||
|
toast.success(`${taskName}: Success`, {
|
||||||
|
className: "border-none bg-green-100",
|
||||||
|
});
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error(error);
|
||||||
|
toast.error(`${taskName}: Error`, {
|
||||||
|
description:
|
||||||
|
error.message ||
|
||||||
|
"No error message. Check the DevTools console for details.",
|
||||||
|
className: "border-none bg-red-100",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue