diff --git a/src/app/app.tsx b/src/app/app.tsx index b0487d5..62488d6 100644 --- a/src/app/app.tsx +++ b/src/app/app.tsx @@ -8,13 +8,14 @@ import { ErrorBoundary } from 'src/components/errors/ErrorBoundary'; import { Footer } from 'src/components/nav/Footer'; import { Header } from 'src/components/nav/Header'; import { WagmiContext } from 'src/config/wagmi'; +import { TransactionModal } from 'src/features/transactions/TransactionModal'; import { useIsSsr } from 'src/utils/ssr'; import 'src/vendor/inpage-metamask'; import 'src/vendor/polyfill'; function SafeHydrate({ children }: PropsWithChildren) { - // Disable app SSR for now as it's not needed and - // complicates wallet integrations + // Avoid SSR for now as it's not needed and it + // complicates wallet integrations and media query hooks const isSsr = useIsSsr(); if (isSsr) { return
; @@ -32,6 +33,7 @@ export function App({ children }: PropsWithChildren) { {children} + diff --git a/src/app/page.tsx b/src/app/page.tsx index c63f612..6520552 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -6,6 +6,8 @@ import { Section } from 'src/components/layout/Section'; import { Modal, useModal } from 'src/components/menus/Modal'; import { Amount } from 'src/components/numbers/Amount'; import { StakeForm } from 'src/features/staking/StakeForm'; +import { useTransactionModal } from 'src/features/transactions/TransactionModal'; +import { TxModalType } from 'src/features/transactions/types'; import { ValidatorGroupTable } from 'src/features/validators/ValidatorGroupTable'; import { ValidatorGroup, ValidatorStatus } from 'src/features/validators/types'; import { useValidatorGroups } from 'src/features/validators/useValidatorGroups'; @@ -44,13 +46,15 @@ function HeroSection({ totalVotes, groups }: { totalVotes?: bigint; groups?: Val return min; }, [groups]); + const showStakeModal = useTransactionModal(TxModalType.Stake); + return (

Discover Validators

Stake your CELO with validators to start earning rewards immediately.

- {`Stake and earn 4%`} + {`Stake and earn 4%`}
diff --git a/src/components/buttons/OutlineButton.tsx b/src/components/buttons/OutlineButton.tsx index 38d1e3d..66e06cf 100644 --- a/src/components/buttons/OutlineButton.tsx +++ b/src/components/buttons/OutlineButton.tsx @@ -1,3 +1,4 @@ +import clsx from 'clsx'; import { ButtonHTMLAttributes, PropsWithChildren } from 'react'; export function OutlineButton({ @@ -6,11 +7,10 @@ export function OutlineButton({ ...props }: PropsWithChildren>) { return ( - ); } + +export const OutlineButtonClassName = `btn btn-outline outline-none h-fit min-h-fit rounded-full border-taupe-300 px-4 py-2.5 font-semibold text-black hover:border-taupe-400 hover:bg-black/5 hover:text-black`; diff --git a/src/components/menus/Dropdown.tsx b/src/components/menus/Dropdown.tsx index dbd1922..b664b0c 100644 --- a/src/components/menus/Dropdown.tsx +++ b/src/components/menus/Dropdown.tsx @@ -1,6 +1,5 @@ import { Menu, Popover, Transition } from '@headlessui/react'; import { Fragment, ReactElement, ReactNode } from 'react'; -import { OutlineButton } from 'src/components/buttons/OutlineButton'; interface MenuProps { button: ReactNode; @@ -13,9 +12,7 @@ interface MenuProps { export function DropdownMenu({ button, buttonClasses, menuItems, menuClasses }: MenuProps) { return ( - - {button} - + {button} {({ open }) => ( <> - - {button({ open })} - + {button({ open })} void; - resetTransactions: () => void; - failUnconfirmedTransactions: () => void; + activeModal: { + type: TxModalType | null; + props?: object; + }; + setTransactionModal: (args: { type: TxModalType; props?: object }) => void; } // TODO is a store needed? export const useStore = create()( persist( (set) => ({ - transactions: [], - addTransactions: (t: any) => { - set((state) => ({ transactions: [...state.transactions, t] })); + activeModal: { + type: null, + props: {}, }, - resetTransactions: () => { - set(() => ({ transactions: [] })); - }, - failUnconfirmedTransactions: () => { - //TODO - set((state) => state); + setTransactionModal: (args: { type: TxModalType; props?: object }) => { + set(() => ({ activeModal: args })); }, }), { - name: 'app-state', - partialize: (state) => ({ transactions: state.transactions }), + name: 'celo-station-state', + partialize: (_state) => ({}), version: PERSIST_STATE_VERSION, - onRehydrateStorage: () => (state) => { - state?.failUnconfirmedTransactions(); - }, }, ), ); diff --git a/src/features/transactions/TransactionModal.tsx b/src/features/transactions/TransactionModal.tsx new file mode 100644 index 0000000..195a309 --- /dev/null +++ b/src/features/transactions/TransactionModal.tsx @@ -0,0 +1,46 @@ +import { useCallback, useEffect } from 'react'; +import { Modal, useModal } from 'src/components/menus/Modal'; +import { StakeForm } from 'src/features/staking/StakeForm'; +import { useStore } from 'src/features/store'; +import { TxModalType } from 'src/features/transactions/types'; + +const TypeToComponent: Record> = { + [TxModalType.Lock]: PlaceholderContent, + [TxModalType.Unlock]: PlaceholderContent, + [TxModalType.Withdraw]: PlaceholderContent, + [TxModalType.Stake]: StakeForm, + [TxModalType.Unstake]: PlaceholderContent, + [TxModalType.Vote]: PlaceholderContent, + [TxModalType.Unvote]: PlaceholderContent, + [TxModalType.Delegate]: PlaceholderContent, + [TxModalType.Undelegate]: PlaceholderContent, +}; + +export function useTransactionModal(type: TxModalType, props?: any) { + const setTxModal = useStore((state) => state.setTransactionModal); + return useCallback(() => setTxModal({ type, props }), [setTxModal, type, props]); +} + +export function TransactionModal() { + const { isModalOpen, closeModal, openModal } = useModal(); + + const activeModal = useStore((state) => state.activeModal); + const { type, props } = activeModal; + + const Component = type ? TypeToComponent[type] : PlaceholderContent; + + useEffect(() => { + if (!openModal || !activeModal?.type) return; + openModal(); + }, [activeModal, openModal]); + + return ( + + + + ); +} + +function PlaceholderContent() { + return
...
; +} diff --git a/src/features/transactions/types.ts b/src/features/transactions/types.ts new file mode 100644 index 0000000..db1a238 --- /dev/null +++ b/src/features/transactions/types.ts @@ -0,0 +1,11 @@ +export enum TxModalType { + Lock = 'lock', + Unlock = 'unlock', + Withdraw = 'withdraw', + Stake = 'stake', + Unstake = 'unstake', + Vote = 'vote', + Unvote = 'unvote', + Delegate = 'delegate', + Undelegate = 'undelegate', +} diff --git a/src/features/wallet/WalletDropdown.tsx b/src/features/wallet/WalletDropdown.tsx index abfcf7b..a421239 100644 --- a/src/features/wallet/WalletDropdown.tsx +++ b/src/features/wallet/WalletDropdown.tsx @@ -1,6 +1,6 @@ import { useConnectModal } from '@rainbow-me/rainbowkit'; import Link from 'next/link'; -import { OutlineButton } from 'src/components/buttons/OutlineButton'; +import { OutlineButton, OutlineButtonClassName } from 'src/components/buttons/OutlineButton'; import { SolidButton } from 'src/components/buttons/SolidButton'; import { Identicon } from 'src/components/icons/Identicon'; import { DropdownModal } from 'src/components/menus/Dropdown'; @@ -27,7 +27,7 @@ export function WalletDropdown() {
{shortenAddress(address, true)}
)} - buttonClasses="pl-1.5 pr-3 all:py-1" + buttonClasses={`${OutlineButtonClassName} pl-1.5 pr-3 all:py-1`} modal={({ close }) => ( )} @@ -58,7 +58,7 @@ function DropdownContent({ const totalBalance = (walletBalance?.value || 0n) + (lockedBalance?.value || 0n); - const onClickCopy = useCopyHandler(address, close); + const onClickCopy = useCopyHandler(address); return (