add Sonner (and have some fun)

This commit is contained in:
osmannyildiz 2024-04-20 19:51:19 +03:00
parent f985906395
commit 545bdf7bae
8 changed files with 174 additions and 42 deletions

View File

@ -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": {

View File

@ -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'}

View File

@ -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 */

View File

@ -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>
</> </>
); );

View File

@ -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

View File

@ -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>
); );
} }

View File

@ -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>
);
}

21
frontend/src/utils.ts Normal file
View File

@ -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",
});
}
}