Skip to content

Commit

Permalink
improve component and hook organization
Browse files Browse the repository at this point in the history
  • Loading branch information
MattPereira committed Aug 31, 2024
1 parent a2abf98 commit fa8242d
Show file tree
Hide file tree
Showing 31 changed files with 170 additions and 192 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

import { useState } from "react";
import Link from "next/link";
import { type HookDetails } from "../page";
import { type HookInfo } from "./page";
import { ArrowTopRightOnSquareIcon } from "@heroicons/react/24/outline";
import Modal from "~~/components/common/Modal";

export const HooksCards = ({ hooks }: { hooks: HookDetails[] }) => {
export const HooksDetails = ({ hooks }: { hooks: HookInfo[] }) => {
const [activeModal, setActiveModal] = useState<number | null>(null);

const modalContent = (id: number) => {
Expand All @@ -14,12 +15,16 @@ export const HooksCards = ({ hooks }: { hooks: HookDetails[] }) => {
if (!hook) return null;
const categories = hook.category.join(", ");
return (
<div className="text-lg">
<h1 className="text-3xl font-bold mb-1">{hook.title}</h1>
<div className="">Created By {hook.created_by}</div>
<p className="text-xl">{hook.description}</p>
<div>Audited: {hook.audited}</div>
<div className="text-lg flex flex-col gap-10">
<div>
<h1 className="text-3xl font-bold mb-1">{hook.title}</h1>
<div>Created By {hook.created_by}</div>
</div>

<div className="text-xl">{hook.description}</div>

<div className="flex justify-between">
<div>Audited: {hook.audited}</div>
<div>Categories: {categories}</div>
<Link
href={hook.github}
Expand All @@ -39,24 +44,24 @@ export const HooksCards = ({ hooks }: { hooks: HookDetails[] }) => {
{hooks.map(hook => (
<div
key={hook.id}
className="bg-base-200 hover:bg-base-100 p-5 rounded-lg w-full hover:cursor-pointer grid grid-cols-8"
className="text-lg bg-base-200 hover:bg-base-100 p-5 rounded-lg w-full hover:cursor-pointer grid grid-cols-5 shadow-md"
onClick={() => setActiveModal(hook.id)}
>
<div className="col-start-1 col-end-3 text-xl font-bold ">{hook.title}</div>
<div className="col-span-full lg:col-start-1 lg:col-end-3 text-xl font-bold ">{hook.title}</div>

<div className="hidden md:flex col-start-4 col-end-6 text-center">
<div className="hidden lg:flex text-center">
<Link
className="flex gap-2 items-center text-nowrap overflow-hidden whitespace-nowrap"
className="hover:underline flex gap-2 items-center text-nowrap overflow-hidden whitespace-nowrap"
target="_blank"
rel="noopener noreferrer"
href={hook.github}
>
{hook.github.slice(0, 40)}...
github <ArrowTopRightOnSquareIcon className="w-4 h-4" />
</Link>
</div>

<div className="col-start-7 col-end-7">{hook.category}</div>
<div className="col-start-8 col-end-8">{hook.created_by}</div>
<div className="hidden lg:flex">{hook.category}</div>
<div className="hidden lg:flex">{hook.created_by}</div>
</div>
))}

Expand Down
18 changes: 9 additions & 9 deletions packages/nextjs/app/hooks/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Link from "next/link";
import { HooksCards } from "./_components/HooksCards";
import { HooksDetails } from "./HookDetails";
import type { NextPage } from "next";

export type HookDetails = {
export type HookInfo = {
id: number;
title: string;
source: string;
Expand All @@ -15,7 +15,7 @@ export type HookDetails = {
};

const Hooks: NextPage = async () => {
let hooks: HookDetails[] | null = null;
let hooks: HookInfo[] | null = null;
const response = await fetch("https://raw.githubusercontent.com/burns2854/balancer-hooks/main/hook-data.json");
if (response.ok) {
hooks = await response.json();
Expand All @@ -40,13 +40,13 @@ const Hooks: NextPage = async () => {
</div>
</div>
<div className="w-full flex flex-col gap-3">
<div className="w-full grid grid-cols-8 font-bold text-lg">
<div className="col-start-1 col-end-4">Name</div>
<div className="col-start-4 col-end-6 text-start hidden md:flex">Repo URL</div>
<div className="col-start-7 col-end-7">Category</div>
<div className="col-start-8 col-end-8">Created By</div>
<div className="w-full grid grid-cols-5 font-bold text-lg">
<div className="col-auto lg:col-start-1 lg:col-end-3">Name</div>
<div className="hidden lg:flex">Repo URL</div>
<div className="hidden lg:flex">Category</div>
<div className="hidden lg:flex">Created By</div>
</div>
{hooks ? <HooksCards hooks={hooks} /> : <div className="text-xl text-error">Error fetching hooks data!</div>}
{hooks ? <HooksDetails hooks={hooks} /> : <div className="text-xl text-error">Error fetching hooks data!</div>}
</div>
</div>
);
Expand Down
11 changes: 11 additions & 0 deletions packages/nextjs/app/hooks/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export type HookDetails = {
id: number;
title: string;
source: string;
description: string;
github: string;
additional_link: string;
created_by: string;
audited: "Yes" | "No";
category: string[];
};
29 changes: 29 additions & 0 deletions packages/nextjs/app/pools/_components/PoolPageSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { SkeletonLoader } from "~~/components/common";

export const PoolPageSkeleton = () => {
return (
<div className="w-full">
<div className="grid grid-cols-1 xl:grid-cols-2 w-full gap-7 mb-5 mt-20">
<div className="flex flex-col gap-7">
<div className="w-full h-72">
<SkeletonLoader />
</div>
<div className="w-full h-48">
<SkeletonLoader />
</div>
<div className="w-full h-96">
<SkeletonLoader />
</div>
</div>
<div className="flex flex-col gap-7">
<div className="w-full h-96">
<SkeletonLoader />
</div>
<div className="w-full h-72">
<SkeletonLoader />
</div>
</div>
</div>
</div>
);
};
9 changes: 3 additions & 6 deletions packages/nextjs/app/pools/_components/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
export * from "./PoolActions";
export * from "./PoolComposition";
export * from "./PoolAttributes";
export * from "./UserLiquidity";
export * from "./PoolSelector";
export * from "./PoolConfig";
export * from "./HooksConfig";
export * from "./info";
export * from "./operations";
export * from "./PoolPageSkeleton";
5 changes: 5 additions & 0 deletions packages/nextjs/app/pools/_components/info/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from "./PoolComposition";
export * from "./PoolAttributes";
export * from "./UserLiquidity";
export * from "./PoolConfig";
export * from "./HooksConfig";
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React, { useState } from "react";
import { PoolActionButton, ResultsDisplay, TokenField } from ".";
import { ResultsDisplay, TokenField, TransactionButton } from ".";
import { InputAmount, calculateProportionalAmounts } from "@balancer/sdk";
import { useQueryClient } from "@tanstack/react-query";
import { formatUnits, parseUnits } from "viem";
import { useContractEvent } from "wagmi";
import { Alert } from "~~/components/common/";
import abis from "~~/contracts/abis";
import { useAddLiquidity, useApproveTokens, useQueryAddLiquidity, useReadTokens } from "~~/hooks/balancer/";
import { useAddLiquidity, useQueryAddLiquidity } from "~~/hooks/balancer/";
import { PoolActionsProps, PoolOperationReceipt, TokenAmountDetails } from "~~/hooks/balancer/types";
import { useApproveTokens, useReadTokens } from "~~/hooks/token/";

/**
* 1. Query adding some amount of liquidity to the pool
Expand Down Expand Up @@ -123,16 +124,16 @@ export const AddLiquidityForm: React.FC<PoolActionsProps> = ({
})}

{!queryResponse || addLiquidityReceipt || isFormEmpty ? (
<PoolActionButton
<TransactionButton
label="Query"
onClick={handleQueryAddLiquidity}
isDisabled={isQueryFetching}
isFormEmpty={isFormEmpty}
/>
) : !sufficientAllowances ? (
<PoolActionButton label="Approve" isDisabled={isApproving} onClick={approveTokens} />
<TransactionButton label="Approve" isDisabled={isApproving} onClick={approveTokens} />
) : (
<PoolActionButton label="Add Liquidity" isDisabled={isAddLiquidityPending} onClick={handleAddLiquidity} />
<TransactionButton label="Add Liquidity" isDisabled={isAddLiquidityPending} onClick={handleAddLiquidity} />
)}

{queryResponse && (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
import { useState } from "react";
import { AddLiquidityForm, RemoveLiquidityForm, SwapForm } from "./actions";
import { AddLiquidityForm, RemoveLiquidityForm, SwapForm } from ".";
import { useAccount } from "wagmi";
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
import { useReadTokens } from "~~/hooks/balancer";
import { type Pool, RefetchPool } from "~~/hooks/balancer";
import { Alert } from "~~/components/common";
import { Pool, RefetchPool, TokenBalances } from "~~/hooks/balancer";
import { useAccountBalance, useScaffoldContractWrite } from "~~/hooks/scaffold-eth";
import { useReadTokens } from "~~/hooks/token";

type Action = "Swap" | "AddLiquidity" | "RemoveLiquidity";
type Operation = "Swap" | "AddLiquidity" | "RemoveLiquidity";

/**
* Allow user to swap, add liquidity, and remove liquidity from a pool
*/
export const PoolActions: React.FC<{ pool: Pool; refetchPool: RefetchPool }> = ({ pool, refetchPool }) => {
const [activeTab, setActiveTab] = useState<Action>("Swap");

const { address } = useAccount();
const { balance } = useAccountBalance(address);
export const PoolOperations: React.FC<{ pool: Pool; refetchPool: RefetchPool }> = ({ pool, refetchPool }) => {
const [activeTab, setActiveTab] = useState<Operation>("Swap");

const tokens = pool.poolTokens.map(token => ({
address: token.address as `0x${string}`,
Expand All @@ -25,8 +22,6 @@ export const PoolActions: React.FC<{ pool: Pool; refetchPool: RefetchPool }> = (

const { tokenBalances, refetchTokenBalances } = useReadTokens(tokens);

const userHasNoTokens = Object.values(tokenBalances).every(balance => balance === 0n);

const tabs = {
Swap: (
<SwapForm
Expand Down Expand Up @@ -57,22 +52,22 @@ export const PoolActions: React.FC<{ pool: Pool; refetchPool: RefetchPool }> = (
return (
<div>
<div className="w-full bg-base-200 rounded-xl p-5 shadow-lg min-h-[516px]">
<div className="flex mb-3 items-center justify-between gap-5">
<h5 className="text-xl font-bold text-nowrap">Pool Actions</h5>
{address && !balance ? (
<Alert>Click the faucet button in the top right corner!</Alert>
) : balance !== 0 && userHasNoTokens ? (
<ZeroTokensAlert refetchTokenBalances={refetchTokenBalances} />
) : pool.poolConfig?.liquidityManagement.disableUnbalancedLiquidity ? (
<Alert>This pool only allows proportional liquidity operations</Alert>
) : null}
<div className="flex h-[44px] justify-between gap-5 relative">
<h5 className="text-xl font-bold text-nowrap">Pool Operations</h5>
<div className="absolute -top-2 right-0">
<PoolOperationsAlerts
tokenBalances={tokenBalances}
refetchTokenBalances={refetchTokenBalances}
isUnbalancedLiquidityDisabled={pool.poolConfig?.liquidityManagement.disableUnbalancedLiquidity ?? false}
/>
</div>
</div>
<div className="bg-neutral rounded-lg">
<div className="flex">
{Object.keys(tabs).map(tab => (
<button
key={tab}
onClick={() => setActiveTab(tab as Action)}
onClick={() => setActiveTab(tab as Operation)}
className={`font-bold py-3 flex-1 ${
activeTab === tab
? "rounded-tl-lg rounded-tr-lg text-neutral-700 bg-gradient-to-b from-custom-beige-start to-custom-beige-end to-100%"
Expand All @@ -90,7 +85,19 @@ export const PoolActions: React.FC<{ pool: Pool; refetchPool: RefetchPool }> = (
);
};

const ZeroTokensAlert = ({ refetchTokenBalances }: { refetchTokenBalances: () => void }) => {
const PoolOperationsAlerts = ({
tokenBalances,
refetchTokenBalances,
isUnbalancedLiquidityDisabled,
}: {
tokenBalances: TokenBalances;
refetchTokenBalances: () => void;
isUnbalancedLiquidityDisabled: boolean;
}) => {
const { address } = useAccount();
const { balance } = useAccountBalance(address);
const userHasNoTokens = Object.values(tokenBalances).every(balance => balance === 0n);

const { writeAsync: mintToken1 } = useScaffoldContractWrite({
contractName: "MockToken1",
functionName: "mint",
Expand All @@ -103,30 +110,30 @@ const ZeroTokensAlert = ({ refetchTokenBalances }: { refetchTokenBalances: () =>
args: [100000000000000000000n],
});

return (
<Alert>
To mint 100 of each mock token{" "}
<span
className="link"
onClick={async () => {
await mintToken1();
await mintToken2();
refetchTokenBalances();
}}
>
click here
</span>
</Alert>
);
};
const handleMintTokens = async () => {
await mintToken1();
await mintToken2();
refetchTokenBalances();
};

const Alert = ({ children }: { children: React.ReactNode }) => {
return (
<div className="text-warning bg-warning-tint border border-warning rounded-lg py-1 px-5 flex gap-2 items-center justify-center">
<div>
<ExclamationTriangleIcon className="w-5 h-5" />
</div>
<div className="">{children}</div>
</div>
);
if (address && !balance) {
return <Alert type="warning">Click the faucet button in the top right corner!</Alert>;
}

if (balance !== 0 && userHasNoTokens) {
return (
<Alert type="info">
To mint 100 of each mock token:{" "}
<span className="link" onClick={handleMintTokens}>
click here
</span>
</Alert>
);
}

if (isUnbalancedLiquidityDisabled) {
return <Alert type="info">This pool only allows proportional liquidity operations</Alert>;
}

return null;
};
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
import { useState } from "react";
import { PoolActionButton, ResultsDisplay, TokenField } from ".";
import { ResultsDisplay, TokenField, TransactionButton } from ".";
import { BALANCER_ROUTER, VAULT_V3, vaultV3Abi } from "@balancer/sdk";
import { useQueryClient } from "@tanstack/react-query";
import { parseUnits } from "viem";
import { useContractEvent } from "wagmi";
import { Alert } from "~~/components/common/";
import {
PoolActionsProps,
PoolOperationReceipt,
TokenAmountDetails,
useAllowanceOnToken,
useApproveOnToken,
useQueryRemoveLiquidity,
useRemoveLiquidity,
useTargetFork,
} from "~~/hooks/balancer/";
import { useQueryRemoveLiquidity, useRemoveLiquidity, useTargetFork } from "~~/hooks/balancer/";
import { PoolActionsProps, PoolOperationReceipt, TokenAmountDetails } from "~~/hooks/balancer/types";
import { useAllowanceOnToken, useApproveOnToken } from "~~/hooks/token";
import { formatToHuman } from "~~/utils/";

/**
Expand Down Expand Up @@ -106,9 +99,9 @@ export const RemoveLiquidityForm: React.FC<PoolActionsProps> = ({ pool, refetchP
/>

{!queryResponse || removeLiquidityReceipt || isFormEmpty ? (
<PoolActionButton label="Query" onClick={handleQuery} isDisabled={isQueryFetching} isFormEmpty={isFormEmpty} />
<TransactionButton label="Query" onClick={handleQuery} isDisabled={isQueryFetching} isFormEmpty={isFormEmpty} />
) : (
<PoolActionButton
<TransactionButton
label="Remove Liquidity"
isDisabled={isRemoveLiquidityPending}
onClick={handleRemoveLiquidity}
Expand Down
Loading

0 comments on commit fa8242d

Please sign in to comment.