get distracted by non-essential frontend stuff

This commit is contained in:
osmannyildiz 2024-04-24 22:26:19 +03:00
parent fec688478b
commit 6ccaac1195
15 changed files with 139 additions and 39 deletions

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: UNLICENSED
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract MessageBox {

View File

@ -1,5 +1,6 @@
import { useStore } from "../store";
import { shortenAddress, tryWithToast } from "../utils";
import { tryWithToast } from "../utils/toast";
import { formatPadTokenAmount, shortenAddress } from "../utils/ui";
import { web3 } from "../web3";
export function ConnectMetamaskButton() {
@ -8,6 +9,7 @@ export function ConnectMetamaskButton() {
const unsetConnectedAccount = useStore(
(state) => state.unsetConnectedAccount
);
const padTokenBalance = useStore((state) => state.padTokenBalance);
// https://docs.web3js.org/guides/getting_started/metamask/#react-app
async function connectMetamask() {
@ -35,7 +37,18 @@ export function ConnectMetamaskButton() {
connectedAccount ? disconnectWallet() : connectMetamask()
}
>
{connectedAccount ? shortenAddress(connectedAccount) : "Connect MetaMask"}
{connectedAccount ? (
<>
{shortenAddress(connectedAccount)}
{padTokenBalance !== null && (
<div className="badge badge-secondary">
{formatPadTokenAmount(padTokenBalance)}
</div>
)}
</>
) : (
"Connect MetaMask"
)}
</button>
);
}

View File

@ -0,0 +1,15 @@
import { buildMainContextValue, MainContext } from "../contexts/MainContext";
interface Props {
children: React.ReactNode;
}
export function ContextsProvider({ children }: Props) {
const mainContextValue = buildMainContextValue();
return (
<MainContext.Provider value={mainContextValue}>
{children}
</MainContext.Provider>
);
}

View File

@ -0,0 +1,39 @@
/* eslint-disable react-hooks/rules-of-hooks */
import { useEffect } from "react";
import { useStore } from "../store";
import buildContext from "../utils/context";
import { padToken } from "../web3";
interface IMainContext {
fetchPadTokenBalance: () => Promise<void>;
}
export const [MainContext, useMainContext] = buildContext<IMainContext>();
export function buildMainContextValue(): IMainContext {
const connectedAccount = useStore((state) => state.connectedAccount);
const setPadTokenBalance = useStore((state) => state.setPadTokenBalance);
const unsetPadTokenBalance = useStore((state) => state.unsetPadTokenBalance);
const fetchPadTokenBalance = async () => {
const balance: bigint = await padToken.methods
.balanceOf(connectedAccount)
.call();
if (typeof balance === "bigint") {
setPadTokenBalance(balance);
} else {
unsetPadTokenBalance();
}
};
useEffect(() => {
if (connectedAccount) {
fetchPadTokenBalance();
}
}, [connectedAccount]);
return {
fetchPadTokenBalance,
};
}

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
{
"MessageBoxModule#MessageBox": "0x5FbDB2315678afecb367f032d93F642f64180aa3",
"MainModule#PADToken": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"
"MainModule#PADToken": "0x5FbDB2315678afecb367f032d93F642f64180aa3",
"MessageBoxModule#MessageBox": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512"
}

View File

@ -1,6 +1,7 @@
import { RouterProvider, createRouter } from "@tanstack/react-router";
import React from "react";
import ReactDOM from "react-dom/client";
import { ContextsProvider } from "./components/ContextsProvider";
import "./index.css";
import { routeTree } from "./routeTree.gen";
@ -13,6 +14,8 @@ declare module "@tanstack/react-router" {
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<ContextsProvider>
<RouterProvider router={router} />
</ContextsProvider>
</React.StrictMode>
);

View File

@ -1,7 +1,7 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import { useState } from "react";
import { useStore } from "../store";
import { toastError, tryWithToast } from "../utils";
import { toastError, tryWithToast } from "../utils/toast";
import { messageBox } from "../web3";
export const Route = createLazyFileRoute("/message-box")({

View File

@ -1,8 +1,9 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import { useState } from "react";
import type { Numbers } from "web3";
import { useStore } from "../store";
import { getFormInputValue, toastError, tryWithToast } from "../utils";
import { getFormInputValue } from "../utils/form";
import { toastError, tryWithToast } from "../utils/toast";
import { formatPadTokenAmount } from "../utils/ui";
import { padToken, web3 } from "../web3";
export const Route = createLazyFileRoute("/pad-token")({
@ -28,10 +29,8 @@ function PadTokenPage() {
}
await tryWithToast("Balance Of", async () => {
const balanceBN: Numbers = await padToken.methods
.balanceOf(address)
.call();
setBalance(web3.utils.fromWei(balanceBN, "ether"));
const _balance: bigint = await padToken.methods.balanceOf(address).call();
setBalance(formatPadTokenAmount(_balance));
});
};
@ -86,7 +85,7 @@ function PadTokenPage() {
Get
</button>
<div>
<span className="font-bold">Balance:</span> {balance} PAD
<span className="font-bold">Balance:</span> {balance}
</div>
</form>
</div>

19
frontend/src/store.ts Normal file
View File

@ -0,0 +1,19 @@
import { create } from "zustand";
interface State {
connectedAccount: string | null;
setConnectedAccount: (connectedAccount: string) => void;
unsetConnectedAccount: () => void;
padTokenBalance: bigint | null;
setPadTokenBalance: (padTokenBalance: bigint) => void;
unsetPadTokenBalance: () => void;
}
export const useStore = create<State>()((set, get) => ({
connectedAccount: null,
setConnectedAccount: (connectedAccount) => set({ connectedAccount }),
unsetConnectedAccount: () => set({ connectedAccount: null }),
padTokenBalance: null,
setPadTokenBalance: (padTokenBalance) => set({ padTokenBalance }),
unsetPadTokenBalance: () => set({ padTokenBalance: null }),
}));

View File

@ -1,13 +0,0 @@
import { create } from "zustand";
interface State {
connectedAccount: string | null;
setConnectedAccount: (newAccount: string) => void;
unsetConnectedAccount: () => void;
}
export const useStore = create<State>()((set) => ({
connectedAccount: null,
setConnectedAccount: (newAccount) => set({ connectedAccount: newAccount }),
unsetConnectedAccount: () => set({ connectedAccount: null }),
}));

View File

@ -0,0 +1,21 @@
import { createContext, useContext } from "react";
export default function buildContext<TContext>(): [
React.Context<TContext | null>,
() => TContext,
] {
const builtContext = createContext<TContext | null>(null);
const builtUseContext = () => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const contextInstance = useContext(builtContext);
if (contextInstance === null) {
throw new Error(
"You can use this hook only inside the context's provider."
);
}
return contextInstance;
};
return [builtContext, builtUseContext];
}

View File

@ -0,0 +1,5 @@
export function getFormInputValue(form: HTMLFormElement, name: string) {
// @ts-expect-error
const input = form.elements[name] as HTMLInputElement;
return input.value;
}

View File

@ -29,13 +29,3 @@ export async function tryWithToast(
);
}
}
export function shortenAddress(address: string) {
return address.slice(0, 6) + "..." + address.slice(-4);
}
export function getFormInputValue(form: HTMLFormElement, name: string) {
// @ts-expect-error
const input = form.elements[name] as HTMLInputElement;
return input.value;
}

9
frontend/src/utils/ui.ts Normal file
View File

@ -0,0 +1,9 @@
import { web3 } from "../web3";
export function shortenAddress(address: string) {
return address.slice(0, 6) + "..." + address.slice(-4);
}
export function formatPadTokenAmount(amount: bigint) {
return `${web3.utils.fromWei(amount, "ether").replace(/\.*$/, "")} PAD`;
}