From 88bfb0c8f2375e8a189560e414c2b7c0b17dab3d Mon Sep 17 00:00:00 2001 From: jordan Date: Tue, 25 Jul 2023 22:13:54 -0700 Subject: [PATCH 01/60] next auction component in topSection component, switch smart contracts to contracts --- apps/web/src/layouts/TopSection.tsx | 54 +++++++++++++++++++ .../pages/dao/[network]/[token]/[tokenId].tsx | 22 ++++---- 2 files changed, 63 insertions(+), 13 deletions(-) create mode 100644 apps/web/src/layouts/TopSection.tsx diff --git a/apps/web/src/layouts/TopSection.tsx b/apps/web/src/layouts/TopSection.tsx new file mode 100644 index 00000000..5ce6b3dd --- /dev/null +++ b/apps/web/src/layouts/TopSection.tsx @@ -0,0 +1,54 @@ +import React from 'react' + +import { TokenWithWinner } from 'src/data/contract/requests/getToken' +import { Auction } from 'src/modules/auction' +import { AuctionSkeleton } from 'src/modules/auction/components/AuctionSkeleton' +import { Chain } from 'src/typings' + +type TopSectionProps = { + chain: Chain + collection: string + auctionAddress?: string + token?: TokenWithWinner +} + +enum TopSectionView { + Auction = 'auction', + Chart = 'chart', +} + +export const TopSection = ({ + chain, + auctionAddress, + collection, + token, +}: TopSectionProps) => { + const [topSectionView, setTopSectionView] = React.useState( + TopSectionView.Auction + ) + if (topSectionView === TopSectionView.Auction) { + return token && auctionAddress ? ( + + ) : ( + + ) + } + if (topSectionView === TopSectionView.Chart) { + return + } + + return <>'error' +} + +const Chart = () => { + return ( +
+

Chart

+
+ ) +} diff --git a/apps/web/src/pages/dao/[network]/[token]/[tokenId].tsx b/apps/web/src/pages/dao/[network]/[token]/[tokenId].tsx index f8e5b5d5..7196cab7 100644 --- a/apps/web/src/pages/dao/[network]/[token]/[tokenId].tsx +++ b/apps/web/src/pages/dao/[network]/[token]/[tokenId].tsx @@ -17,8 +17,7 @@ import SWR_KEYS from 'src/constants/swrKeys' import { TokenWithWinner } from 'src/data/contract/requests/getToken' import { useVotes } from 'src/hooks' import { getDaoLayout } from 'src/layouts/DaoLayout' -import { Auction } from 'src/modules/auction' -import { AuctionSkeleton } from 'src/modules/auction/components/AuctionSkeleton' +import { TopSection } from 'src/layouts/TopSection' import { About, Activity, @@ -93,7 +92,7 @@ const TokenPage: NextPageWithLayout = ({ component: [], } const smartContractsSection = { - title: 'Smart Contracts', + title: 'Contracts', component: [], } const daoFeed = { @@ -124,16 +123,13 @@ const TokenPage: NextPageWithLayout = ({ slug={url} description={ogDescription} /> - {token && addresses?.auction ? ( - - ) : ( - - )} + + Date: Tue, 25 Jul 2023 23:29:26 -0700 Subject: [PATCH 02/60] build rough view toggle --- apps/web/src/layouts/TopSection.tsx | 51 +++++++++++++++++-- .../modules/auction/components/Auction.tsx | 6 ++- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/apps/web/src/layouts/TopSection.tsx b/apps/web/src/layouts/TopSection.tsx index 5ce6b3dd..7fca2de5 100644 --- a/apps/web/src/layouts/TopSection.tsx +++ b/apps/web/src/layouts/TopSection.tsx @@ -1,4 +1,5 @@ -import React from 'react' +import { Button, Flex } from '@zoralabs/zord' +import React, { ReactNode } from 'react' import { TokenWithWinner } from 'src/data/contract/requests/getToken' import { Auction } from 'src/modules/auction' @@ -12,7 +13,7 @@ type TopSectionProps = { token?: TokenWithWinner } -enum TopSectionView { +export enum TopSectionView { Auction = 'auction', Chart = 'chart', } @@ -33,22 +34,64 @@ export const TopSection = ({ auctionAddress={auctionAddress} collection={collection} token={token} + viewSwitcher={ + + } /> ) : ( ) } if (topSectionView === TopSectionView.Chart) { - return + return ( + + } + /> + ) } return <>'error' } -const Chart = () => { +const Chart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { return (
+ {viewSwitcher}

Chart

) } + +const ViewSwitcher = ({ + topSectionView, + setTopSectionView, +}: { + topSectionView: TopSectionView + setTopSectionView: (view: TopSectionView) => void +}) => { + return ( + + + {Object.values(TopSectionView).map((view) => ( + + ))} + + + ) +} diff --git a/apps/web/src/modules/auction/components/Auction.tsx b/apps/web/src/modules/auction/components/Auction.tsx index 8ced8450..df55c4d3 100644 --- a/apps/web/src/modules/auction/components/Auction.tsx +++ b/apps/web/src/modules/auction/components/Auction.tsx @@ -1,6 +1,6 @@ import { readContract } from '@wagmi/core' import { Flex, Grid } from '@zoralabs/zord' -import React, { Fragment } from 'react' +import React, { Fragment, ReactNode } from 'react' import useSWR from 'swr' import SWR_KEYS from 'src/constants/swrKeys' @@ -25,6 +25,7 @@ interface AuctionControllerProps { auctionAddress: string collection: string token: TokenWithWinner + viewSwitcher?: ReactNode } export const Auction: React.FC = ({ @@ -32,6 +33,7 @@ export const Auction: React.FC = ({ auctionAddress, collection, token, + viewSwitcher, }) => { const { mintDate, name, image, price: tokenPrice, owner: tokenOwner } = token @@ -64,13 +66,13 @@ export const Auction: React.FC = ({ return ( + {viewSwitcher} - Date: Wed, 26 Jul 2023 14:52:32 -0700 Subject: [PATCH 03/60] write simple auction history query --- .../src/data/subgraph/queries/auctionSales.graphql | 11 +++++++++++ apps/web/src/layouts/TopSection.tsx | 7 ++++--- 2 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 apps/web/src/data/subgraph/queries/auctionSales.graphql diff --git a/apps/web/src/data/subgraph/queries/auctionSales.graphql b/apps/web/src/data/subgraph/queries/auctionSales.graphql new file mode 100644 index 00000000..28947a19 --- /dev/null +++ b/apps/web/src/data/subgraph/queries/auctionSales.graphql @@ -0,0 +1,11 @@ +query auctionSales($startTime: BigInt!, $daoId: String!) { + dao(id: $daoId) { + auctions(where: { startTime_gt: $startTime }) { + id + endTime + winningBid { + amount + } + } + } +} diff --git a/apps/web/src/layouts/TopSection.tsx b/apps/web/src/layouts/TopSection.tsx index 7fca2de5..7b4fd86c 100644 --- a/apps/web/src/layouts/TopSection.tsx +++ b/apps/web/src/layouts/TopSection.tsx @@ -3,6 +3,7 @@ import React, { ReactNode } from 'react' import { TokenWithWinner } from 'src/data/contract/requests/getToken' import { Auction } from 'src/modules/auction' +import { auctionWrapVariants } from 'src/modules/auction/components/Auction.css' import { AuctionSkeleton } from 'src/modules/auction/components/AuctionSkeleton' import { Chain } from 'src/typings' @@ -63,10 +64,10 @@ export const TopSection = ({ const Chart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { return ( -
+ {viewSwitcher} -

Chart

-
+ +
) } From 598e154d3ca6e166c994b39b78441036c618d842 Mon Sep 17 00:00:00 2001 From: jordan Date: Thu, 27 Jul 2023 16:13:45 -0700 Subject: [PATCH 04/60] build query, request, handler, SWR call from Chart component --- .../subgraph/queries/auctionHistory.graphql | 22 +++++++ .../subgraph/queries/auctionSales.graphql | 11 ---- .../data/subgraph/requests/auctionHistory.ts | 40 +++++++++++++ apps/web/src/data/subgraph/sdk.generated.ts | 59 +++++++++++++++++++ apps/web/src/layouts/TopSection.tsx | 41 ++++++++++++- .../pages/api/auctionHistory/[tokenId].tsx | 25 ++++++++ 6 files changed, 186 insertions(+), 12 deletions(-) create mode 100644 apps/web/src/data/subgraph/queries/auctionHistory.graphql delete mode 100644 apps/web/src/data/subgraph/queries/auctionSales.graphql create mode 100644 apps/web/src/data/subgraph/requests/auctionHistory.ts create mode 100644 apps/web/src/pages/api/auctionHistory/[tokenId].tsx diff --git a/apps/web/src/data/subgraph/queries/auctionHistory.graphql b/apps/web/src/data/subgraph/queries/auctionHistory.graphql new file mode 100644 index 00000000..d6c20e2d --- /dev/null +++ b/apps/web/src/data/subgraph/queries/auctionHistory.graphql @@ -0,0 +1,22 @@ +query auctionHistory( + $startTime: BigInt! + $daoId: ID! + $orderBy: Auction_orderBy + $orderDirection: OrderDirection + $first: Int +) { + dao(id: $daoId) { + auctions( + where: { endTime_gt: $startTime } + orderBy: $orderBy + orderDirection: $orderDirection + first: $first + ) { + id + endTime + winningBid { + amount + } + } + } +} diff --git a/apps/web/src/data/subgraph/queries/auctionSales.graphql b/apps/web/src/data/subgraph/queries/auctionSales.graphql deleted file mode 100644 index 28947a19..00000000 --- a/apps/web/src/data/subgraph/queries/auctionSales.graphql +++ /dev/null @@ -1,11 +0,0 @@ -query auctionSales($startTime: BigInt!, $daoId: String!) { - dao(id: $daoId) { - auctions(where: { startTime_gt: $startTime }) { - id - endTime - winningBid { - amount - } - } - } -} diff --git a/apps/web/src/data/subgraph/requests/auctionHistory.ts b/apps/web/src/data/subgraph/requests/auctionHistory.ts new file mode 100644 index 00000000..c6880f04 --- /dev/null +++ b/apps/web/src/data/subgraph/requests/auctionHistory.ts @@ -0,0 +1,40 @@ +import * as Sentry from '@sentry/nextjs' + +import { SDK } from 'src/data/subgraph/client' +import { CHAIN_ID } from 'src/typings' + +import { Auction_OrderBy, OrderDirection } from '../sdk.generated' + +export type AuctionHistory = { + id: string + endTime: number + winningBidAmt: string +} + +export const auctionHistoryRequest = async ( + chainId: CHAIN_ID, + collectionAddress: string, + startTime: number +): Promise => { + try { + const data = await SDK.connect(chainId).auctionHistory({ + startTime, + daoId: collectionAddress, + orderDirection: OrderDirection.Desc, + orderBy: Auction_OrderBy.EndTime, + first: 1000, + }) + + return data.dao?.auctions + .filter((auction) => auction.winningBid) + .map((auction) => ({ + id: auction.id, + endTime: Number(auction.endTime), + winningBidAmt: auction?.winningBid?.amount as string, + })) + } catch (error) { + console.error(error) + Sentry.captureException(error) + await Sentry.flush(2000) + } +} diff --git a/apps/web/src/data/subgraph/sdk.generated.ts b/apps/web/src/data/subgraph/sdk.generated.ts index 760a6496..fde94c9d 100644 --- a/apps/web/src/data/subgraph/sdk.generated.ts +++ b/apps/web/src/data/subgraph/sdk.generated.ts @@ -1920,6 +1920,27 @@ export type ActiveAuctionsQuery = { }> } +export type AuctionHistoryQueryVariables = Exact<{ + startTime: Scalars['BigInt'] + daoId: Scalars['ID'] + orderBy?: InputMaybe + orderDirection?: InputMaybe + first?: InputMaybe +}> + +export type AuctionHistoryQuery = { + __typename?: 'Query' + dao?: { + __typename?: 'DAO' + auctions: Array<{ + __typename?: 'Auction' + id: string + endTime: any + winningBid?: { __typename?: 'AuctionBid'; amount: any } | null + }> + } | null +} + export type DaoInfoQueryVariables = Exact<{ tokenAddress: Scalars['ID'] }> @@ -2238,6 +2259,30 @@ export const ActiveAuctionsDocument = gql` } ${AuctionFragmentDoc} ` +export const AuctionHistoryDocument = gql` + query auctionHistory( + $startTime: BigInt! + $daoId: ID! + $orderBy: Auction_orderBy + $orderDirection: OrderDirection + $first: Int + ) { + dao(id: $daoId) { + auctions( + where: { endTime_gt: $startTime } + orderBy: $orderBy + orderDirection: $orderDirection + first: $first + ) { + id + endTime + winningBid { + amount + } + } + } + } +` export const DaoInfoDocument = gql` query daoInfo($tokenAddress: ID!) { dao(id: $tokenAddress) { @@ -2418,6 +2463,20 @@ export function getSdk( 'query' ) }, + auctionHistory( + variables: AuctionHistoryQueryVariables, + requestHeaders?: Dom.RequestInit['headers'] + ): Promise { + return withWrapper( + (wrappedRequestHeaders) => + client.request(AuctionHistoryDocument, variables, { + ...requestHeaders, + ...wrappedRequestHeaders, + }), + 'auctionHistory', + 'query' + ) + }, daoInfo( variables: DaoInfoQueryVariables, requestHeaders?: Dom.RequestInit['headers'] diff --git a/apps/web/src/layouts/TopSection.tsx b/apps/web/src/layouts/TopSection.tsx index 7b4fd86c..b0cc3492 100644 --- a/apps/web/src/layouts/TopSection.tsx +++ b/apps/web/src/layouts/TopSection.tsx @@ -1,10 +1,16 @@ import { Button, Flex } from '@zoralabs/zord' -import React, { ReactNode } from 'react' +import axios from 'axios' +import { useRouter } from 'next/router' +import React, { ReactNode, useState } from 'react' +import useSWR from 'swr' import { TokenWithWinner } from 'src/data/contract/requests/getToken' +import { AuctionHistory } from 'src/data/subgraph/requests/auctionHistory' import { Auction } from 'src/modules/auction' import { auctionWrapVariants } from 'src/modules/auction/components/Auction.css' import { AuctionSkeleton } from 'src/modules/auction/components/AuctionSkeleton' +import { useDaoStore } from 'src/modules/dao' +import { useChainStore } from 'src/stores/useChainStore' import { Chain } from 'src/typings' type TopSectionProps = { @@ -62,7 +68,40 @@ export const TopSection = ({ return <>'error' } +enum StartTimes { + '30 days' = '30', + '60 days' = '60', + '90 days' = '90', + 'All' = '0', +} + +const startTimeFromNow = (startTime: StartTimes) => { + if (startTime === '0') return 0 + + const nowInSeconds = Math.floor(Date.now() / 1000) + + return nowInSeconds - parseInt(startTime) * 24 * 60 * 60 +} + const Chart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { + const { query, isReady } = useRouter() + const chain = useChainStore((x) => x.chain) + const { + addresses: { token }, + } = useDaoStore() + + const [startTime, setStartTime] = useState(startTimeFromNow(StartTimes['All'])) + + const { data, error, isValidating } = useSWR( + isReady ? [token, chain.id, startTime] : undefined, + () => + axios + .get<{ auctionHistory: AuctionHistory }>( + `/api/auctionHistory/${token}?chainId=${chain.id}&startTime=${startTime}` + ) + .then((x) => x.data.auctionHistory) + ) + console.log('data', data) return ( {viewSwitcher} diff --git a/apps/web/src/pages/api/auctionHistory/[tokenId].tsx b/apps/web/src/pages/api/auctionHistory/[tokenId].tsx new file mode 100644 index 00000000..5c9f7c98 --- /dev/null +++ b/apps/web/src/pages/api/auctionHistory/[tokenId].tsx @@ -0,0 +1,25 @@ +import { NextApiRequest, NextApiResponse } from 'next' + +import { auctionHistoryRequest } from 'src/data/subgraph/requests/auctionHistory' +import { CHAIN_ID } from 'src/typings' + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + const { tokenId, chainId, startTime } = req.query + console.log('tokenId', tokenId) + console.log('chainId', chainId) + console.log('startTime', startTime) + try { + if (!tokenId || !chainId || !startTime) { + throw new Error('Invalid query') + } + const auctionHistory = await auctionHistoryRequest( + Number(chainId) as CHAIN_ID, + (tokenId as string).toLowerCase(), + Number(startTime) + ) + res.status(200).json({ auctionHistory }) + } catch (error) { + res.status(500).json({ error }) + } +} +export default handler From f404287f87c28c80d46e5d885884ef4a3098bc3c Mon Sep 17 00:00:00 2001 From: jordan Date: Thu, 27 Jul 2023 16:21:34 -0700 Subject: [PATCH 05/60] clean, break out into separate components --- apps/web/src/layouts/TopSection.tsx | 54 ++----------------- .../components/AuctionChart/AuctionChart.tsx | 52 ++++++++++++++++++ .../dao/components/AuctionChart/index.ts | 1 + .../pages/api/auctionHistory/[tokenId].tsx | 4 +- 4 files changed, 57 insertions(+), 54 deletions(-) create mode 100644 apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx create mode 100644 apps/web/src/modules/dao/components/AuctionChart/index.ts diff --git a/apps/web/src/layouts/TopSection.tsx b/apps/web/src/layouts/TopSection.tsx index b0cc3492..9254ec73 100644 --- a/apps/web/src/layouts/TopSection.tsx +++ b/apps/web/src/layouts/TopSection.tsx @@ -1,16 +1,10 @@ import { Button, Flex } from '@zoralabs/zord' -import axios from 'axios' -import { useRouter } from 'next/router' -import React, { ReactNode, useState } from 'react' -import useSWR from 'swr' +import React from 'react' import { TokenWithWinner } from 'src/data/contract/requests/getToken' -import { AuctionHistory } from 'src/data/subgraph/requests/auctionHistory' import { Auction } from 'src/modules/auction' -import { auctionWrapVariants } from 'src/modules/auction/components/Auction.css' import { AuctionSkeleton } from 'src/modules/auction/components/AuctionSkeleton' -import { useDaoStore } from 'src/modules/dao' -import { useChainStore } from 'src/stores/useChainStore' +import { AuctionChart } from 'src/modules/dao/components/AuctionChart/AuctionChart' import { Chain } from 'src/typings' type TopSectionProps = { @@ -54,7 +48,7 @@ export const TopSection = ({ } if (topSectionView === TopSectionView.Chart) { return ( - 'error' } -enum StartTimes { - '30 days' = '30', - '60 days' = '60', - '90 days' = '90', - 'All' = '0', -} - -const startTimeFromNow = (startTime: StartTimes) => { - if (startTime === '0') return 0 - - const nowInSeconds = Math.floor(Date.now() / 1000) - - return nowInSeconds - parseInt(startTime) * 24 * 60 * 60 -} - -const Chart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { - const { query, isReady } = useRouter() - const chain = useChainStore((x) => x.chain) - const { - addresses: { token }, - } = useDaoStore() - - const [startTime, setStartTime] = useState(startTimeFromNow(StartTimes['All'])) - - const { data, error, isValidating } = useSWR( - isReady ? [token, chain.id, startTime] : undefined, - () => - axios - .get<{ auctionHistory: AuctionHistory }>( - `/api/auctionHistory/${token}?chainId=${chain.id}&startTime=${startTime}` - ) - .then((x) => x.data.auctionHistory) - ) - console.log('data', data) - return ( - - {viewSwitcher} - - - ) -} - const ViewSwitcher = ({ topSectionView, setTopSectionView, diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx new file mode 100644 index 00000000..f0a9cfd4 --- /dev/null +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx @@ -0,0 +1,52 @@ +import { Flex } from '@zoralabs/zord' +import axios from 'axios' +import { useRouter } from 'next/router' +import React, { ReactNode, useState } from 'react' +import useSWR from 'swr' + +import { AuctionHistory } from 'src/data/subgraph/requests/auctionHistory' +import { auctionWrapVariants } from 'src/modules/auction/components/Auction.css' +import { useDaoStore } from 'src/modules/dao' +import { useChainStore } from 'src/stores/useChainStore' + +enum StartTimes { + '30 days' = '30', + '60 days' = '60', + '90 days' = '90', + 'All' = '0', +} + +const startTimeFromNow = (startTime: StartTimes) => { + if (startTime === '0') return 0 + + const nowInSeconds = Math.floor(Date.now() / 1000) + + return nowInSeconds - parseInt(startTime) * 24 * 60 * 60 +} + +export const AuctionChart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { + const { query, isReady } = useRouter() + const chain = useChainStore((x) => x.chain) + const { + addresses: { token }, + } = useDaoStore() + + const [startTime, setStartTime] = useState(startTimeFromNow(StartTimes['All'])) + + const { data, error, isValidating } = useSWR( + isReady ? [token, chain.id, startTime] : undefined, + () => + axios + .get<{ auctionHistory: AuctionHistory }>( + `/api/auctionHistory/${token}?chainId=${chain.id}&startTime=${startTime}` + ) + .then((x) => x.data.auctionHistory) + ) + console.log('data', data) + return ( + + {viewSwitcher} + + + ) +} diff --git a/apps/web/src/modules/dao/components/AuctionChart/index.ts b/apps/web/src/modules/dao/components/AuctionChart/index.ts new file mode 100644 index 00000000..8f41e06d --- /dev/null +++ b/apps/web/src/modules/dao/components/AuctionChart/index.ts @@ -0,0 +1 @@ +export * from './AuctionChart' diff --git a/apps/web/src/pages/api/auctionHistory/[tokenId].tsx b/apps/web/src/pages/api/auctionHistory/[tokenId].tsx index 5c9f7c98..b3ff8801 100644 --- a/apps/web/src/pages/api/auctionHistory/[tokenId].tsx +++ b/apps/web/src/pages/api/auctionHistory/[tokenId].tsx @@ -5,9 +5,7 @@ import { CHAIN_ID } from 'src/typings' const handler = async (req: NextApiRequest, res: NextApiResponse) => { const { tokenId, chainId, startTime } = req.query - console.log('tokenId', tokenId) - console.log('chainId', chainId) - console.log('startTime', startTime) + try { if (!tokenId || !chainId || !startTime) { throw new Error('Invalid query') From 438e1ead710e5c36549af0aa7f9deb57264d513c Mon Sep 17 00:00:00 2001 From: jordan Date: Mon, 31 Jul 2023 11:12:41 -0700 Subject: [PATCH 06/60] rough in chart interface, still broken --- .../subgraph/queries/auctionHistory.graphql | 1 + .../data/subgraph/requests/auctionHistory.ts | 2 +- apps/web/src/data/subgraph/sdk.generated.ts | 2 + .../components/AuctionChart/AuctionChart.tsx | 10 +- .../components/AuctionChart/AuctionGraph.tsx | 137 ++++++++++++++++++ 5 files changed, 147 insertions(+), 5 deletions(-) create mode 100644 apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx diff --git a/apps/web/src/data/subgraph/queries/auctionHistory.graphql b/apps/web/src/data/subgraph/queries/auctionHistory.graphql index d6c20e2d..81971044 100644 --- a/apps/web/src/data/subgraph/queries/auctionHistory.graphql +++ b/apps/web/src/data/subgraph/queries/auctionHistory.graphql @@ -17,6 +17,7 @@ query auctionHistory( winningBid { amount } + settled } } } diff --git a/apps/web/src/data/subgraph/requests/auctionHistory.ts b/apps/web/src/data/subgraph/requests/auctionHistory.ts index c6880f04..8f44bb32 100644 --- a/apps/web/src/data/subgraph/requests/auctionHistory.ts +++ b/apps/web/src/data/subgraph/requests/auctionHistory.ts @@ -26,7 +26,7 @@ export const auctionHistoryRequest = async ( }) return data.dao?.auctions - .filter((auction) => auction.winningBid) + .filter((auction) => auction.settled) .map((auction) => ({ id: auction.id, endTime: Number(auction.endTime), diff --git a/apps/web/src/data/subgraph/sdk.generated.ts b/apps/web/src/data/subgraph/sdk.generated.ts index fde94c9d..23314684 100644 --- a/apps/web/src/data/subgraph/sdk.generated.ts +++ b/apps/web/src/data/subgraph/sdk.generated.ts @@ -1936,6 +1936,7 @@ export type AuctionHistoryQuery = { __typename?: 'Auction' id: string endTime: any + settled: boolean winningBid?: { __typename?: 'AuctionBid'; amount: any } | null }> } | null @@ -2279,6 +2280,7 @@ export const AuctionHistoryDocument = gql` winningBid { amount } + settled } } } diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx index f0a9cfd4..d005ce6b 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx @@ -1,4 +1,4 @@ -import { Flex } from '@zoralabs/zord' +import { Box, Flex } from '@zoralabs/zord' import axios from 'axios' import { useRouter } from 'next/router' import React, { ReactNode, useState } from 'react' @@ -9,6 +9,8 @@ import { auctionWrapVariants } from 'src/modules/auction/components/Auction.css' import { useDaoStore } from 'src/modules/dao' import { useChainStore } from 'src/stores/useChainStore' +import { AuctionGraph } from './AuctionGraph' + enum StartTimes { '30 days' = '30', '60 days' = '60', @@ -37,16 +39,16 @@ export const AuctionChart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { isReady ? [token, chain.id, startTime] : undefined, () => axios - .get<{ auctionHistory: AuctionHistory }>( + .get<{ auctionHistory: AuctionHistory[] }>( `/api/auctionHistory/${token}?chainId=${chain.id}&startTime=${startTime}` ) .then((x) => x.data.auctionHistory) ) - console.log('data', data) + return ( {viewSwitcher} - + {data && } ) } diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx new file mode 100644 index 00000000..c364a469 --- /dev/null +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx @@ -0,0 +1,137 @@ +import { Box, Text } from '@zoralabs/zord' +import { color } from '@zoralabs/zord' +import { useState } from 'react' + +import { AuctionHistory } from 'src/data/subgraph/requests/auctionHistory' +import { formatCryptoVal } from 'src/utils/numbers' + +const STROKE = 1 + +interface AuctionGraphProps { + height?: number + width?: number + chartData?: AuctionHistory[] +} + +export const AuctionGraph = ({ + height = 200, + width = 500, + chartData, +}: AuctionGraphProps) => { + const [visibleIndex, setVisibleIndex] = useState(10) + + if (!chartData || !chartData.length) return null + + const paddingX = 10 + const paddingY = 30 + const chartWidth = width - paddingX * 2 + const chartHeight = height - paddingY * 2 + + const FONT_SIZE = width / 60 + + const maximumXFromData = Math.max(...chartData.map((e) => e.endTime)) + const maximumYFromData = Math.max(...chartData.map((e) => Number(e.winningBidAmt))) + + const handleMouseMove = (e: any) => { + const event = e.targetTouches ? e.targetTouches[0] : e + const y = Math.max( + 0, + event.pageX - e.currentTarget.getBoundingClientRect().left - paddingX + ) + const index = Math.round( + ((y - paddingY / 2) / (e.currentTarget.getBoundingClientRect().width - paddingY)) * + chartData.length + ) + setVisibleIndex(Math.max(0, Math.min(index, chartData.length - 1))) + } + + const points = chartData + .map((element) => { + const x = (element.endTime / maximumXFromData) * chartWidth + paddingX + console.log('x', x) + const y = + chartHeight - + (Number(element.winningBidAmt) / maximumYFromData) * chartHeight + + paddingY + return `${x},${y}` + }) + .join(' ') + + const XValues = () => { + const PARTS = chartData.length + return ( + <> + {new Array(PARTS).fill(0).map((_, index) => { + const x = index * (chartWidth / PARTS) + paddingX - FONT_SIZE / 2 + const y = + chartHeight - + (Number(chartData[index].winningBidAmt) / maximumYFromData) * chartHeight + + paddingY - + FONT_SIZE + return ( + + {formatCryptoVal(chartData[index]?.winningBidAmt)} + + ) + })} + + ) + } + + return ( + + + + + + + + + + {/* + {formatDistance(chartData[0].time, Date.now(), { addSuffix: true })} + */} + + Now + + + ) +} From 1547a09f2f75181fe92da62a30f047e8141bf780 Mon Sep 17 00:00:00 2001 From: jordan Date: Mon, 31 Jul 2023 13:16:38 -0700 Subject: [PATCH 07/60] displays intelligible graph based on evenly-spaced auction times --- .../src/data/subgraph/requests/auctionHistory.ts | 2 +- .../dao/components/AuctionChart/AuctionChart.tsx | 13 +++++++++++-- .../dao/components/AuctionChart/AuctionGraph.tsx | 8 +++++--- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/apps/web/src/data/subgraph/requests/auctionHistory.ts b/apps/web/src/data/subgraph/requests/auctionHistory.ts index 8f44bb32..0b230bfb 100644 --- a/apps/web/src/data/subgraph/requests/auctionHistory.ts +++ b/apps/web/src/data/subgraph/requests/auctionHistory.ts @@ -20,7 +20,7 @@ export const auctionHistoryRequest = async ( const data = await SDK.connect(chainId).auctionHistory({ startTime, daoId: collectionAddress, - orderDirection: OrderDirection.Desc, + orderDirection: OrderDirection.Asc, orderBy: Auction_OrderBy.EndTime, first: 1000, }) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx index d005ce6b..8c0390de 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx @@ -33,7 +33,7 @@ export const AuctionChart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { addresses: { token }, } = useDaoStore() - const [startTime, setStartTime] = useState(startTimeFromNow(StartTimes['All'])) + const [startTime, setStartTime] = useState(startTimeFromNow(StartTimes['30 days'])) const { data, error, isValidating } = useSWR( isReady ? [token, chain.id, startTime] : undefined, @@ -48,7 +48,16 @@ export const AuctionChart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { return ( {viewSwitcher} - {data && } + + + {data && } + + ) } diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx index c364a469..a5637112 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx @@ -30,6 +30,7 @@ export const AuctionGraph = ({ const FONT_SIZE = width / 60 const maximumXFromData = Math.max(...chartData.map((e) => e.endTime)) + console.log(maximumXFromData) const maximumYFromData = Math.max(...chartData.map((e) => Number(e.winningBidAmt))) const handleMouseMove = (e: any) => { @@ -46,9 +47,10 @@ export const AuctionGraph = ({ } const points = chartData - .map((element) => { - const x = (element.endTime / maximumXFromData) * chartWidth + paddingX - console.log('x', x) + .map((element, index) => { + const PARTS = chartData.length + const x = index * (chartWidth / PARTS) + paddingX + // const x = (element.endTime / maximumXFromData) * chartWidth + paddingX const y = chartHeight - (Number(element.winningBidAmt) / maximumYFromData) * chartHeight + From 0a5b89079a06df643942c56513e484d10f0cfa34 Mon Sep 17 00:00:00 2001 From: jordan Date: Tue, 1 Aug 2023 21:05:52 -0700 Subject: [PATCH 08/60] pre zora API --- .../components/AuctionChart/AuctionChart.tsx | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx index 8c0390de..736767d0 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx @@ -1,4 +1,4 @@ -import { Box, Flex } from '@zoralabs/zord' +import { Flex } from '@zoralabs/zord' import axios from 'axios' import { useRouter } from 'next/router' import React, { ReactNode, useState } from 'react' @@ -33,7 +33,7 @@ export const AuctionChart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { addresses: { token }, } = useDaoStore() - const [startTime, setStartTime] = useState(startTimeFromNow(StartTimes['30 days'])) + const [startTime, setStartTime] = useState(startTimeFromNow(StartTimes['All'])) const { data, error, isValidating } = useSWR( isReady ? [token, chain.id, startTime] : undefined, @@ -48,15 +48,16 @@ export const AuctionChart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { return ( {viewSwitcher} - - - {data && } - + + + {data && } ) From 67e183f2f80d4194f0d35934b45d1403c116b35f Mon Sep 17 00:00:00 2001 From: jordan Date: Wed, 2 Aug 2023 21:45:43 -0700 Subject: [PATCH 09/60] fix cursor values --- .../dao/components/AuctionChart/AuctionGraph.tsx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx index a5637112..f0f7859e 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx @@ -1,9 +1,9 @@ import { Box, Text } from '@zoralabs/zord' import { color } from '@zoralabs/zord' +import { ethers } from 'ethers' import { useState } from 'react' import { AuctionHistory } from 'src/data/subgraph/requests/auctionHistory' -import { formatCryptoVal } from 'src/utils/numbers' const STROKE = 1 @@ -30,7 +30,6 @@ export const AuctionGraph = ({ const FONT_SIZE = width / 60 const maximumXFromData = Math.max(...chartData.map((e) => e.endTime)) - console.log(maximumXFromData) const maximumYFromData = Math.max(...chartData.map((e) => Number(e.winningBidAmt))) const handleMouseMove = (e: any) => { @@ -77,12 +76,12 @@ export const AuctionGraph = ({ x={x} y={y} variant="eyebrow" - style={{ - fontFamily: 'Helvetica', - display: visibleIndex === index ? 'block' : 'none', - }} + display={visibleIndex === index ? 'block' : 'none'} > - {formatCryptoVal(chartData[index]?.winningBidAmt)} + {Number(ethers.utils.formatEther(chartData[index]?.winningBidAmt)).toFixed( + 2 + )}{' '} + ETH ) })} From 7225c3d809d5a6dce8da80fd5ab00adcb7afbbe7 Mon Sep 17 00:00:00 2001 From: jordan Date: Wed, 2 Aug 2023 21:51:10 -0700 Subject: [PATCH 10/60] fix overflow issues --- .../web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx index f0f7859e..c650da01 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx @@ -95,6 +95,7 @@ export const AuctionGraph = ({ viewBox={`0 0 ${width} ${height}`} onTouchMove={handleMouseMove} onMouseMove={handleMouseMove} + style={{ overflow: 'visible' }} > Date: Wed, 2 Aug 2023 22:00:15 -0700 Subject: [PATCH 11/60] fix SVG sizing/collapse issue --- .../modules/dao/components/AuctionChart/AuctionChart.tsx | 9 ++++++--- .../modules/dao/components/AuctionChart/AuctionGraph.tsx | 7 ++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx index 736767d0..f4b6d156 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx @@ -51,10 +51,13 @@ export const AuctionChart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { {data && } diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx index c650da01..26b72099 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx @@ -90,7 +90,12 @@ export const AuctionGraph = ({ } return ( - + Date: Wed, 2 Aug 2023 23:24:49 -0700 Subject: [PATCH 12/60] basic mobile styles, will refine --- .../components/AuctionChart/AuctionChart.tsx | 8 ++-- .../components/AuctionChart/AuctionGraph.tsx | 38 ++++++++++++------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx index f4b6d156..f5440ab6 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx @@ -7,6 +7,7 @@ import useSWR from 'swr' import { AuctionHistory } from 'src/data/subgraph/requests/auctionHistory' import { auctionWrapVariants } from 'src/modules/auction/components/Auction.css' import { useDaoStore } from 'src/modules/dao' +import { useLayoutStore } from 'src/stores' import { useChainStore } from 'src/stores/useChainStore' import { AuctionGraph } from './AuctionGraph' @@ -27,12 +28,12 @@ const startTimeFromNow = (startTime: StartTimes) => { } export const AuctionChart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { - const { query, isReady } = useRouter() + const { isReady } = useRouter() const chain = useChainStore((x) => x.chain) const { addresses: { token }, } = useDaoStore() - + const { isMobile } = useLayoutStore() const [startTime, setStartTime] = useState(startTimeFromNow(StartTimes['All'])) const { data, error, isValidating } = useSWR( @@ -48,7 +49,6 @@ export const AuctionChart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { return ( {viewSwitcher} - { // backgroundColor="negative" w={'100%'} style={{ - height: '464px', + height: isMobile ? 'fit-content' : '464px', maxWidth: '912px', }} > diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx index 26b72099..bcea3cdc 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx @@ -1,16 +1,18 @@ -import { Box, Text } from '@zoralabs/zord' +import { Box, Flex, Text } from '@zoralabs/zord' import { color } from '@zoralabs/zord' +import dayjs from 'dayjs' +import relativeTime from 'dayjs/plugin/relativeTime' import { ethers } from 'ethers' -import { useState } from 'react' +import { useMemo, useState } from 'react' import { AuctionHistory } from 'src/data/subgraph/requests/auctionHistory' -const STROKE = 1 +const STROKE = 1.25 interface AuctionGraphProps { height?: number width?: number - chartData?: AuctionHistory[] + chartData: AuctionHistory[] } export const AuctionGraph = ({ @@ -20,8 +22,6 @@ export const AuctionGraph = ({ }: AuctionGraphProps) => { const [visibleIndex, setVisibleIndex] = useState(10) - if (!chartData || !chartData.length) return null - const paddingX = 10 const paddingY = 30 const chartWidth = width - paddingX * 2 @@ -29,9 +29,16 @@ export const AuctionGraph = ({ const FONT_SIZE = width / 60 - const maximumXFromData = Math.max(...chartData.map((e) => e.endTime)) const maximumYFromData = Math.max(...chartData.map((e) => Number(e.winningBidAmt))) + const startTime = useMemo(() => { + if (!chartData || !chartData.length) return '--' + + dayjs.extend(relativeTime) + const date = dayjs.unix(chartData[0].endTime).fromNow() + return date + }, [chartData]) + const handleMouseMove = (e: any) => { const event = e.targetTouches ? e.targetTouches[0] : e const y = Math.max( @@ -92,10 +99,17 @@ export const AuctionGraph = ({ return ( + Auction History + + {startTime} - {/* - {formatDistance(chartData[0].time, Date.now(), { addSuffix: true })} - */} - - Now - + Now + ) } From 80b4bf7c12ff33fea6154c5998145700191fd7c3 Mon Sep 17 00:00:00 2001 From: jordan Date: Mon, 7 Aug 2023 11:17:02 -0700 Subject: [PATCH 13/60] install artifacts From 2187f0224f6435cee2a2bd22c657aae875933594 Mon Sep 17 00:00:00 2001 From: jordan Date: Mon, 7 Aug 2023 11:23:09 -0700 Subject: [PATCH 14/60] reconstruct on old git history. --- .../data/subgraph/requests/auctionHistory.ts | 2 +- apps/web/src/layouts/TopSection.tsx | 2 +- .../AuctionChart/AuctionChart.css.ts | 12 ++ .../components/AuctionChart/AuctionChart.tsx | 125 +++++++++++++-- .../components/AuctionChart/AuctionGraph.tsx | 146 +++++++++--------- 5 files changed, 204 insertions(+), 83 deletions(-) create mode 100644 apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts diff --git a/apps/web/src/data/subgraph/requests/auctionHistory.ts b/apps/web/src/data/subgraph/requests/auctionHistory.ts index 0b230bfb..4e8a1a3f 100644 --- a/apps/web/src/data/subgraph/requests/auctionHistory.ts +++ b/apps/web/src/data/subgraph/requests/auctionHistory.ts @@ -30,7 +30,7 @@ export const auctionHistoryRequest = async ( .map((auction) => ({ id: auction.id, endTime: Number(auction.endTime), - winningBidAmt: auction?.winningBid?.amount as string, + winningBidAmt: auction?.winningBid?.amount || ('1' as string), })) } catch (error) { console.error(error) diff --git a/apps/web/src/layouts/TopSection.tsx b/apps/web/src/layouts/TopSection.tsx index 9254ec73..0479bfe8 100644 --- a/apps/web/src/layouts/TopSection.tsx +++ b/apps/web/src/layouts/TopSection.tsx @@ -59,7 +59,7 @@ export const TopSection = ({ ) } - return <>'error' + return null } const ViewSwitcher = ({ diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts new file mode 100644 index 00000000..c6337962 --- /dev/null +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts @@ -0,0 +1,12 @@ +import { style } from '@vanilla-extract/css' + +export const activeFilter = style({ + borderBottom: '2px solid black', + borderBottomLeftRadius: '0px', + borderBottomRightRadius: '0px', +}) +export const inactiveFilter = style({ + borderBottom: '2px solid transparent', + borderBottomLeftRadius: '0px', + borderBottomRightRadius: '0px', +}) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx index f5440ab6..5e8a4053 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx @@ -1,7 +1,9 @@ -import { Flex } from '@zoralabs/zord' +import { Box, Button, Flex, Text } from '@zoralabs/zord' import axios from 'axios' +import dayjs from 'dayjs' +import relativeTime from 'dayjs/plugin/relativeTime' import { useRouter } from 'next/router' -import React, { ReactNode, useState } from 'react' +import React, { ReactNode, useMemo, useState } from 'react' import useSWR from 'swr' import { AuctionHistory } from 'src/data/subgraph/requests/auctionHistory' @@ -10,9 +12,10 @@ import { useDaoStore } from 'src/modules/dao' import { useLayoutStore } from 'src/stores' import { useChainStore } from 'src/stores/useChainStore' +import { activeFilter, inactiveFilter } from './AuctionChart.css' import { AuctionGraph } from './AuctionGraph' -enum StartTimes { +export enum StartTimes { '30 days' = '30', '60 days' = '60', '90 days' = '90', @@ -33,19 +36,85 @@ export const AuctionChart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { const { addresses: { token }, } = useDaoStore() - const { isMobile } = useLayoutStore() - const [startTime, setStartTime] = useState(startTimeFromNow(StartTimes['All'])) + + const [startTime, setStartTime] = useState(StartTimes['All']) + + const startSeconds = startTimeFromNow(startTime) const { data, error, isValidating } = useSWR( - isReady ? [token, chain.id, startTime] : undefined, + isReady ? [token, chain.id, startSeconds] : undefined, () => axios .get<{ auctionHistory: AuctionHistory[] }>( - `/api/auctionHistory/${token}?chainId=${chain.id}&startTime=${startTime}` + `/api/auctionHistory/${token}?chainId=${chain.id}&startTime=${startSeconds}` ) .then((x) => x.data.auctionHistory) ) + if (isValidating) { + return ( + + ) + } + + if (error) { + return null + } + + if (!data || !data.length) { + return ( + + ) + } + + return ( + + } + /> + ) +} +const AuctionGraphLayout = ({ + chart, + viewSwitcher, + startTime, + setStartTime, + chartData, +}: { + chart?: ReactNode + viewSwitcher?: ReactNode + startTime: StartTimes + setStartTime: (startTime: StartTimes) => void + chartData?: AuctionHistory[] +}) => { + const { isMobile } = useLayoutStore() + const startTimeText = useMemo(() => { + if (!chartData || !chartData.length) return '--' + + dayjs.extend(relativeTime) + const date = dayjs.unix(chartData[0].endTime).fromNow() + return date + }, [chartData]) + return ( {viewSwitcher} @@ -53,14 +122,52 @@ export const AuctionChart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { direction="column" alignSelf="center" justify={'center'} - // backgroundColor="negative" + borderRadius={'phat'} + borderStyle={'solid'} + borderWidth={'normal'} + borderColor={'border'} w={'100%'} style={{ height: isMobile ? 'fit-content' : '464px', maxWidth: '912px', }} > - {data && } + + + {isMobile || Auction History} + + {Object.entries(StartTimes).map(([label, value]) => { + const isActive = startTime === value + + return ( + + ) + })} + + + {chart} + + {startTimeText} + Now + + ) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx index bcea3cdc..c52cc86c 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx @@ -1,27 +1,32 @@ -import { Box, Flex, Text } from '@zoralabs/zord' +import { Box, Text } from '@zoralabs/zord' import { color } from '@zoralabs/zord' -import dayjs from 'dayjs' -import relativeTime from 'dayjs/plugin/relativeTime' import { ethers } from 'ethers' -import { useMemo, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import { AuctionHistory } from 'src/data/subgraph/requests/auctionHistory' -const STROKE = 1.25 +import { StartTimes } from './AuctionChart' + +const STROKE = 1.5 +const STROKE_MOBILE = 2 interface AuctionGraphProps { height?: number width?: number + startTime: StartTimes + setStartTime: (startTime: StartTimes) => void chartData: AuctionHistory[] } export const AuctionGraph = ({ - height = 200, + height = 220, width = 500, chartData, + startTime, + setStartTime, }: AuctionGraphProps) => { - const [visibleIndex, setVisibleIndex] = useState(10) - + const [visibleIndex, setVisibleIndex] = useState(0) + console.log('chartData', chartData) const paddingX = 10 const paddingY = 30 const chartWidth = width - paddingX * 2 @@ -30,14 +35,22 @@ export const AuctionGraph = ({ const FONT_SIZE = width / 60 const maximumYFromData = Math.max(...chartData.map((e) => Number(e.winningBidAmt))) + const lineRef = useRef(null) - const startTime = useMemo(() => { - if (!chartData || !chartData.length) return '--' - - dayjs.extend(relativeTime) - const date = dayjs.unix(chartData[0].endTime).fromNow() - return date - }, [chartData]) + useEffect(() => { + if (lineRef.current) { + const length = lineRef.current.getTotalLength() + lineRef.current.style.strokeDasharray = `${length} ${length}` + lineRef.current.style.strokeDashoffset = length.toString() + lineRef.current.style.opacity = '0' + setTimeout(() => { + lineRef.current!.style.transition = + 'stroke-dashoffset 1.5s ease-in-out, opacity 1.5s ease-in-out' + lineRef.current!.style.strokeDashoffset = '0' + lineRef.current!.style.opacity = '1' + }, 100) + } + }, []) const handleMouseMove = (e: any) => { const event = e.targetTouches ? e.targetTouches[0] : e @@ -56,11 +69,11 @@ export const AuctionGraph = ({ .map((element, index) => { const PARTS = chartData.length const x = index * (chartWidth / PARTS) + paddingX - // const x = (element.endTime / maximumXFromData) * chartWidth + paddingX const y = chartHeight - (Number(element.winningBidAmt) / maximumYFromData) * chartHeight + paddingY + return `${x},${y}` }) .join(' ') @@ -80,13 +93,14 @@ export const AuctionGraph = ({ {Number(ethers.utils.formatEther(chartData[index]?.winningBidAmt)).toFixed( - 2 + 4 )}{' '} ETH @@ -97,60 +111,48 @@ export const AuctionGraph = ({ } return ( - - Auction History - - - - - - - - - {startTime} + + + - Now - - + + ) } From d9f4efff377241c4d97993d608089f0a15721d23 Mon Sep 17 00:00:00 2001 From: jordan Date: Mon, 7 Aug 2023 13:28:12 -0700 Subject: [PATCH 15/60] pre merge --- apps/web/src/layouts/TopSection.tsx | 36 ++++++++++++++--------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/apps/web/src/layouts/TopSection.tsx b/apps/web/src/layouts/TopSection.tsx index 0479bfe8..92fcaf20 100644 --- a/apps/web/src/layouts/TopSection.tsx +++ b/apps/web/src/layouts/TopSection.tsx @@ -28,24 +28,7 @@ export const TopSection = ({ const [topSectionView, setTopSectionView] = React.useState( TopSectionView.Auction ) - if (topSectionView === TopSectionView.Auction) { - return token && auctionAddress ? ( - - } - /> - ) : ( - - ) - } + if (topSectionView === TopSectionView.Chart) { return ( + } + /> + ) : ( + + ) } const ViewSwitcher = ({ From 1c89ce88e9e34fc5afcc77836677f35890583d49 Mon Sep 17 00:00:00 2001 From: jordan Date: Mon, 7 Aug 2023 15:04:44 -0700 Subject: [PATCH 16/60] low data, no data, and error display --- apps/web/src/layouts/TopSection.tsx | 12 +++- .../components/AuctionChart/AuctionChart.tsx | 56 ++++++++++++++++--- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/apps/web/src/layouts/TopSection.tsx b/apps/web/src/layouts/TopSection.tsx index 92fcaf20..f7027060 100644 --- a/apps/web/src/layouts/TopSection.tsx +++ b/apps/web/src/layouts/TopSection.tsx @@ -5,6 +5,10 @@ import { TokenWithWinner } from 'src/data/contract/requests/getToken' import { Auction } from 'src/modules/auction' import { AuctionSkeleton } from 'src/modules/auction/components/AuctionSkeleton' import { AuctionChart } from 'src/modules/dao/components/AuctionChart/AuctionChart' +import { + activeFilter, + inactiveFilter, +} from 'src/modules/dao/components/AuctionChart/AuctionChart.css' import { Chain } from 'src/typings' type TopSectionProps = { @@ -72,11 +76,15 @@ const ViewSwitcher = ({ {Object.values(TopSectionView).map((view) => ( diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx index 5e8a4053..7e7b9716 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx @@ -16,9 +16,9 @@ import { activeFilter, inactiveFilter } from './AuctionChart.css' import { AuctionGraph } from './AuctionGraph' export enum StartTimes { - '30 days' = '30', - '60 days' = '60', - '90 days' = '90', + '30d' = '30', + '60d' = '60', + '90d' = '90', 'All' = '0', } @@ -63,16 +63,33 @@ export const AuctionChart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { } if (error) { - return null + return ( + + } + /> + ) } - if (!data || !data.length) { + if (!data || !data.length || data.length < 2) { return ( + } /> ) } @@ -141,7 +158,7 @@ const AuctionGraphLayout = ({ py={{ '@initial': 'x2', '@768': 'x6' }} px={{ '@initial': 'x2', '@768': 'x6' }} > - + {isMobile || Auction History} {Object.entries(StartTimes).map(([label, value]) => { @@ -152,7 +169,8 @@ const AuctionGraphLayout = ({ key={label} variant="ghost" size="xs" - px={'x3'} + px={'x0'} + mr={'x2'} className={isActive ? activeFilter : inactiveFilter} onClick={() => setStartTime(value)} > @@ -172,3 +190,25 @@ const AuctionGraphLayout = ({ ) } + +const DisplayPanel = ({ title, description }: { title: string; description: string }) => { + return ( + + + {title} + + {description} + + ) +} From 5a2ec38dcaa1910a1cd21568399a149e6a7100f5 Mon Sep 17 00:00:00 2001 From: jordan Date: Mon, 7 Aug 2023 15:22:24 -0700 Subject: [PATCH 17/60] refactor all skeleton amimation styles --- .../src/modules/auction/components/Auction.css.ts | 10 +++------- .../modules/dao/components/Explore/Explore.css.ts | 10 +++------- apps/web/src/modules/dao/components/Feed/Feed.css.ts | 12 ++++-------- .../dao/components/MembersList/MembersList.css.ts | 10 +++------- apps/web/src/styles/Artwork.css.ts | 10 +++------- apps/web/src/styles/animations.css.ts | 9 +++++++++ 6 files changed, 25 insertions(+), 36 deletions(-) create mode 100644 apps/web/src/styles/animations.css.ts diff --git a/apps/web/src/modules/auction/components/Auction.css.ts b/apps/web/src/modules/auction/components/Auction.css.ts index 0a58d312..a52a28eb 100644 --- a/apps/web/src/modules/auction/components/Auction.css.ts +++ b/apps/web/src/modules/auction/components/Auction.css.ts @@ -1,14 +1,10 @@ -import { keyframes, style, styleVariants } from '@vanilla-extract/css' +import { style, styleVariants } from '@vanilla-extract/css' import { atoms, media, theme, vars } from '@zoralabs/zord' -const pulse = keyframes({ - '0%': { opacity: '1' }, - '100%': { opacity: '1' }, - '50%': { opacity: '.5' }, -}) +import { skeletonAnimation } from 'src/styles/animations.css' export const auctionSkeleton = style({ - animation: `${pulse} 2s cubic-bezier(0.4, 0, 0.6, 1) infinite`, + animation: skeletonAnimation, }) export const auctionWrap = atoms({ diff --git a/apps/web/src/modules/dao/components/Explore/Explore.css.ts b/apps/web/src/modules/dao/components/Explore/Explore.css.ts index 84bfefd8..cb528bcc 100644 --- a/apps/web/src/modules/dao/components/Explore/Explore.css.ts +++ b/apps/web/src/modules/dao/components/Explore/Explore.css.ts @@ -1,14 +1,10 @@ -import { keyframes, style } from '@vanilla-extract/css' +import { style } from '@vanilla-extract/css' import { atoms } from '@zoralabs/zord' -const pulse = keyframes({ - '0%': { opacity: '1' }, - '100%': { opacity: '1' }, - '50%': { opacity: '.5' }, -}) +import { skeletonAnimation } from 'src/styles/animations.css' export const exploreSkeleton = style({ - animation: `${pulse} 2s cubic-bezier(0.4, 0, 0.6, 1) infinite`, + animation: skeletonAnimation, height: '390px', }) diff --git a/apps/web/src/modules/dao/components/Feed/Feed.css.ts b/apps/web/src/modules/dao/components/Feed/Feed.css.ts index 047c081b..c6c5b555 100644 --- a/apps/web/src/modules/dao/components/Feed/Feed.css.ts +++ b/apps/web/src/modules/dao/components/Feed/Feed.css.ts @@ -1,6 +1,8 @@ -import { keyframes, style } from '@vanilla-extract/css' +import { style } from '@vanilla-extract/css' import { atoms } from '@zoralabs/zord' +import { skeletonAnimation } from 'src/styles/animations.css' + export const feed = style([ atoms({ m: 'auto', @@ -12,14 +14,8 @@ export const feed = style([ export const castCardStyle = style({}) -const pulse = keyframes({ - '0%': { opacity: '1' }, - '100%': { opacity: '1' }, - '50%': { opacity: '.5' }, -}) - export const cardSkeleton = style({ - animation: `${pulse} 2s cubic-bezier(0.4, 0, 0.6, 1) infinite`, + animation: skeletonAnimation, height: '10rem', marginBottom: '1.6rem', }) diff --git a/apps/web/src/modules/dao/components/MembersList/MembersList.css.ts b/apps/web/src/modules/dao/components/MembersList/MembersList.css.ts index 6db0fc98..bb5fcc6a 100644 --- a/apps/web/src/modules/dao/components/MembersList/MembersList.css.ts +++ b/apps/web/src/modules/dao/components/MembersList/MembersList.css.ts @@ -1,10 +1,6 @@ -import { keyframes, style } from '@vanilla-extract/css' +import { style } from '@vanilla-extract/css' -const pulse = keyframes({ - '0%': { opacity: '1' }, - '100%': { opacity: '1' }, - '50%': { opacity: '.5' }, -}) +import { skeletonAnimation } from 'src/styles/animations.css' export const row = style({ width: '100%', @@ -31,7 +27,7 @@ export const lastRowItem = style({ }) export const cardSkeleton = style({ width: '100%', - animation: `${pulse} 2s cubic-bezier(0.4, 0, 0.6, 1) infinite`, + animation: skeletonAnimation, height: '32px', '@media': { 'screen and (max-width: 768px)': { diff --git a/apps/web/src/styles/Artwork.css.ts b/apps/web/src/styles/Artwork.css.ts index 37f17007..a29ce2f2 100644 --- a/apps/web/src/styles/Artwork.css.ts +++ b/apps/web/src/styles/Artwork.css.ts @@ -1,14 +1,10 @@ -import { keyframes, style } from '@vanilla-extract/css' +import { style } from '@vanilla-extract/css' import { atoms } from '@zoralabs/zord' -const pulse = keyframes({ - '0%': { opacity: '1' }, - '100%': { opacity: '1' }, - '50%': { opacity: '.5' }, -}) +import { skeletonAnimation } from './animations.css' export const artworkSkeleton = style({ - animation: `${pulse} 2s cubic-bezier(0.4, 0, 0.6, 1) infinite`, + animation: skeletonAnimation, }) export const artworkSettingsBox = style({ diff --git a/apps/web/src/styles/animations.css.ts b/apps/web/src/styles/animations.css.ts new file mode 100644 index 00000000..37422e5c --- /dev/null +++ b/apps/web/src/styles/animations.css.ts @@ -0,0 +1,9 @@ +import { keyframes } from '@vanilla-extract/css' + +export const pulse = keyframes({ + '0%': { opacity: '1' }, + '100%': { opacity: '1' }, + '50%': { opacity: '.5' }, +}) + +export const skeletonAnimation = `${pulse} 2s cubic-bezier(0.4, 0, 0.6, 1) infinite` From f094c34a5641e4568414c0b780fad1e07d172384 Mon Sep 17 00:00:00 2001 From: jordan Date: Mon, 7 Aug 2023 16:00:45 -0700 Subject: [PATCH 18/60] loading state, try to mitigate SVG stair-stepping with blur and round caps --- .../components/AuctionChart/AuctionChart.css.ts | 12 ++++++++++++ .../dao/components/AuctionChart/AuctionChart.tsx | 15 +++++++++++++-- .../dao/components/AuctionChart/AuctionGraph.tsx | 13 ++++++++++--- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts index c6337962..467c1030 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts @@ -1,5 +1,7 @@ import { style } from '@vanilla-extract/css' +import { skeletonAnimation } from 'src/styles/animations.css' + export const activeFilter = style({ borderBottom: '2px solid black', borderBottomLeftRadius: '0px', @@ -10,3 +12,13 @@ export const inactiveFilter = style({ borderBottomLeftRadius: '0px', borderBottomRightRadius: '0px', }) +export const chartSkeleton = style({ + animation: skeletonAnimation, + height: '350px', + maxWidth: '912px', + '@media': { + 'screen and (max-width: 768px)': { + height: '300px', + }, + }, +}) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx index 7e7b9716..f39f9e42 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx @@ -12,7 +12,7 @@ import { useDaoStore } from 'src/modules/dao' import { useLayoutStore } from 'src/stores' import { useChainStore } from 'src/stores/useChainStore' -import { activeFilter, inactiveFilter } from './AuctionChart.css' +import { activeFilter, chartSkeleton, inactiveFilter } from './AuctionChart.css' import { AuctionGraph } from './AuctionGraph' export enum StartTimes { @@ -57,7 +57,7 @@ export const AuctionChart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { viewSwitcher={viewSwitcher} startTime={startTime} setStartTime={setStartTime} - chart={null} + chart={} /> ) } @@ -212,3 +212,14 @@ const DisplayPanel = ({ title, description }: { title: string; description: stri ) } + +const SkeletonPanel = () => { + return ( + + ) +} diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx index c52cc86c..4136e5f1 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx @@ -7,8 +7,7 @@ import { AuctionHistory } from 'src/data/subgraph/requests/auctionHistory' import { StartTimes } from './AuctionChart' -const STROKE = 1.5 -const STROKE_MOBILE = 2 +const STROKE = 1 interface AuctionGraphProps { height?: number @@ -26,7 +25,6 @@ export const AuctionGraph = ({ setStartTime, }: AuctionGraphProps) => { const [visibleIndex, setVisibleIndex] = useState(0) - console.log('chartData', chartData) const paddingX = 10 const paddingY = 30 const chartWidth = width - paddingX * 2 @@ -91,6 +89,7 @@ export const AuctionGraph = ({ FONT_SIZE return ( + + + + + ) From 586bc0c233d51eeccdb24833712c3b33301558d1 Mon Sep 17 00:00:00 2001 From: jordan Date: Mon, 7 Aug 2023 16:13:07 -0700 Subject: [PATCH 19/60] adjust mobile styles --- .../modules/dao/components/AuctionChart/AuctionChart.css.ts | 2 +- .../src/modules/dao/components/AuctionChart/AuctionGraph.tsx | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts index 467c1030..3f069ffb 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts @@ -18,7 +18,7 @@ export const chartSkeleton = style({ maxWidth: '912px', '@media': { 'screen and (max-width: 768px)': { - height: '300px', + height: '145px', }, }, }) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx index 4136e5f1..07ded2ff 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx @@ -4,6 +4,7 @@ import { ethers } from 'ethers' import { useEffect, useRef, useState } from 'react' import { AuctionHistory } from 'src/data/subgraph/requests/auctionHistory' +import { useLayoutStore } from 'src/stores' import { StartTimes } from './AuctionChart' @@ -31,7 +32,7 @@ export const AuctionGraph = ({ const chartHeight = height - paddingY * 2 const FONT_SIZE = width / 60 - + const { isMobile } = useLayoutStore() const maximumYFromData = Math.max(...chartData.map((e) => Number(e.winningBidAmt))) const lineRef = useRef(null) @@ -89,7 +90,7 @@ export const AuctionGraph = ({ FONT_SIZE return ( Date: Tue, 8 Aug 2023 14:08:59 -0700 Subject: [PATCH 20/60] refactor to smaller files, remove inline styles --- apps/web/src/layouts/TopSection.tsx | 36 +---- .../modules/auction/components/Auction.css.ts | 1 + .../auction/components/ViewSwitcher.tsx | 37 +++++ .../AuctionChart/AuctionChart.css.ts | 36 ++++- .../components/AuctionChart/AuctionChart.tsx | 122 +---------------- .../components/AuctionChart/AuctionGraph.tsx | 10 +- .../dao/components/AuctionChart/Layouts.tsx | 126 ++++++++++++++++++ 7 files changed, 204 insertions(+), 164 deletions(-) create mode 100644 apps/web/src/modules/auction/components/ViewSwitcher.tsx create mode 100644 apps/web/src/modules/dao/components/AuctionChart/Layouts.tsx diff --git a/apps/web/src/layouts/TopSection.tsx b/apps/web/src/layouts/TopSection.tsx index f7027060..96b32edd 100644 --- a/apps/web/src/layouts/TopSection.tsx +++ b/apps/web/src/layouts/TopSection.tsx @@ -1,14 +1,10 @@ -import { Button, Flex } from '@zoralabs/zord' import React from 'react' import { TokenWithWinner } from 'src/data/contract/requests/getToken' import { Auction } from 'src/modules/auction' import { AuctionSkeleton } from 'src/modules/auction/components/AuctionSkeleton' +import { ViewSwitcher } from 'src/modules/auction/components/ViewSwitcher' import { AuctionChart } from 'src/modules/dao/components/AuctionChart/AuctionChart' -import { - activeFilter, - inactiveFilter, -} from 'src/modules/dao/components/AuctionChart/AuctionChart.css' import { Chain } from 'src/typings' type TopSectionProps = { @@ -63,33 +59,3 @@ export const TopSection = ({ ) } - -const ViewSwitcher = ({ - topSectionView, - setTopSectionView, -}: { - topSectionView: TopSectionView - setTopSectionView: (view: TopSectionView) => void -}) => { - return ( - - - {Object.values(TopSectionView).map((view) => ( - - ))} - - - ) -} diff --git a/apps/web/src/modules/auction/components/Auction.css.ts b/apps/web/src/modules/auction/components/Auction.css.ts index a52a28eb..04fc48b2 100644 --- a/apps/web/src/modules/auction/components/Auction.css.ts +++ b/apps/web/src/modules/auction/components/Auction.css.ts @@ -277,3 +277,4 @@ export const tokenImage = style({ borderRadius: 12, }, }) +export const switcherBox = style({ width: '100%', maxWidth: '912px' }) diff --git a/apps/web/src/modules/auction/components/ViewSwitcher.tsx b/apps/web/src/modules/auction/components/ViewSwitcher.tsx new file mode 100644 index 00000000..72a181d1 --- /dev/null +++ b/apps/web/src/modules/auction/components/ViewSwitcher.tsx @@ -0,0 +1,37 @@ +import { Button, Flex } from '@zoralabs/zord' +import React from 'react' + +import { TopSectionView } from 'src/layouts/TopSection' +import { + selectedTab, + unselectedTab, +} from 'src/modules/dao/components/AuctionChart/AuctionChart.css' + +import { switcherBox } from './Auction.css' + +export const ViewSwitcher = ({ + topSectionView, + setTopSectionView, +}: { + topSectionView: TopSectionView + setTopSectionView: (view: TopSectionView) => void +}) => ( + + + {Object.values(TopSectionView).map((view) => ( + + ))} + + +) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts index 3f069ffb..94b48ff2 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts @@ -2,15 +2,17 @@ import { style } from '@vanilla-extract/css' import { skeletonAnimation } from 'src/styles/animations.css' -export const activeFilter = style({ +export const selectedTab = style({ borderBottom: '2px solid black', borderBottomLeftRadius: '0px', borderBottomRightRadius: '0px', + textTransform: 'capitalize', }) -export const inactiveFilter = style({ +export const unselectedTab = style({ borderBottom: '2px solid transparent', borderBottomLeftRadius: '0px', borderBottomRightRadius: '0px', + textTransform: 'capitalize', }) export const chartSkeleton = style({ animation: skeletonAnimation, @@ -22,3 +24,33 @@ export const chartSkeleton = style({ }, }, }) +export const outerBox = style({ + height: '464px', + maxWidth: '912px', + '@media': { + 'screen and (max-width: 768px)': { + height: 'fit-content', + }, + }, +}) + +export const viewBox = style({ + height: '100%', + maxWidth: '912px', +}) +export const innerBox = style({ + height: '90%', +}) +export const displayPanelBox = style({ + height: '300px', + maxWidth: '912px', +}) + +export const graphOnLoadStyles = style({ + opacity: '0', + strokeDasharray: '1000 1000', // Some large number to initially hide the line + strokeDashoffset: '1000', // Same large number to initially hide the line +}) +export const svgBox = style({ + overflow: 'visible', +}) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx index f39f9e42..41137a58 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx @@ -1,19 +1,14 @@ -import { Box, Button, Flex, Text } from '@zoralabs/zord' import axios from 'axios' -import dayjs from 'dayjs' -import relativeTime from 'dayjs/plugin/relativeTime' import { useRouter } from 'next/router' -import React, { ReactNode, useMemo, useState } from 'react' +import React, { ReactNode, useState } from 'react' import useSWR from 'swr' import { AuctionHistory } from 'src/data/subgraph/requests/auctionHistory' -import { auctionWrapVariants } from 'src/modules/auction/components/Auction.css' import { useDaoStore } from 'src/modules/dao' -import { useLayoutStore } from 'src/stores' import { useChainStore } from 'src/stores/useChainStore' -import { activeFilter, chartSkeleton, inactiveFilter } from './AuctionChart.css' import { AuctionGraph } from './AuctionGraph' +import { AuctionGraphLayout, DisplayPanel, SkeletonPanel } from './Layouts' export enum StartTimes { '30d' = '30', @@ -110,116 +105,3 @@ export const AuctionChart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { /> ) } -const AuctionGraphLayout = ({ - chart, - viewSwitcher, - startTime, - setStartTime, - chartData, -}: { - chart?: ReactNode - viewSwitcher?: ReactNode - startTime: StartTimes - setStartTime: (startTime: StartTimes) => void - chartData?: AuctionHistory[] -}) => { - const { isMobile } = useLayoutStore() - const startTimeText = useMemo(() => { - if (!chartData || !chartData.length) return '--' - - dayjs.extend(relativeTime) - const date = dayjs.unix(chartData[0].endTime).fromNow() - return date - }, [chartData]) - - return ( - - {viewSwitcher} - - - - {isMobile || Auction History} - - {Object.entries(StartTimes).map(([label, value]) => { - const isActive = startTime === value - - return ( - - ) - })} - - - {chart} - - {startTimeText} - Now - - - - - ) -} - -const DisplayPanel = ({ title, description }: { title: string; description: string }) => { - return ( - - - {title} - - {description} - - ) -} - -const SkeletonPanel = () => { - return ( - - ) -} diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx index 07ded2ff..9b3090af 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx @@ -7,6 +7,7 @@ import { AuctionHistory } from 'src/data/subgraph/requests/auctionHistory' import { useLayoutStore } from 'src/stores' import { StartTimes } from './AuctionChart' +import { graphOnLoadStyles, svgBox } from './AuctionChart.css' const STROKE = 1 @@ -23,7 +24,6 @@ export const AuctionGraph = ({ width = 500, chartData, startTime, - setStartTime, }: AuctionGraphProps) => { const [visibleIndex, setVisibleIndex] = useState(0) const paddingX = 10 @@ -115,7 +115,7 @@ export const AuctionGraph = ({ viewBox={`0 0 ${width} ${height}`} onTouchMove={handleMouseMove} onMouseMove={handleMouseMove} - style={{ overflow: 'visible' }} + className={svgBox} > @@ -152,11 +152,7 @@ export const AuctionGraph = ({ stroke={chartData ? color.accent : 'transparent'} strokeWidth={STROKE} points={points} - style={{ - opacity: '0', - strokeDasharray: '1000 1000', // Some large number to initially hide the line - strokeDashoffset: '1000', // Same large number to initially hide the line - }} + className={graphOnLoadStyles} strokeLinejoin="round" strokeLinecap="round" filter="url(#smooth)" diff --git a/apps/web/src/modules/dao/components/AuctionChart/Layouts.tsx b/apps/web/src/modules/dao/components/AuctionChart/Layouts.tsx new file mode 100644 index 00000000..0325eedc --- /dev/null +++ b/apps/web/src/modules/dao/components/AuctionChart/Layouts.tsx @@ -0,0 +1,126 @@ +import { Box, Button, Flex, Text } from '@zoralabs/zord' +import dayjs from 'dayjs' +import relativeTime from 'dayjs/plugin/relativeTime' +import React, { ReactNode, useMemo } from 'react' + +import { AuctionHistory } from 'src/data/subgraph/requests/auctionHistory' +import { auctionWrapVariants } from 'src/modules/auction/components/Auction.css' +import { useLayoutStore } from 'src/stores' + +import { StartTimes } from './AuctionChart' +import { + chartSkeleton, + displayPanelBox, + innerBox, + outerBox, + selectedTab, + unselectedTab, + viewBox, +} from './AuctionChart.css' + +export const AuctionGraphLayout = ({ + chart, + viewSwitcher, + startTime, + setStartTime, + chartData, +}: { + chart?: ReactNode + viewSwitcher?: ReactNode + startTime: StartTimes + setStartTime: (startTime: StartTimes) => void + chartData?: AuctionHistory[] +}) => { + const { isMobile } = useLayoutStore() + const startTimeText = useMemo(() => { + if (!chartData || !chartData.length) return '--' + + dayjs.extend(relativeTime) + const date = dayjs.unix(chartData[0].endTime).fromNow() + return date + }, [chartData]) + + return ( + + {viewSwitcher} + + + + {isMobile || Auction History} + + {Object.entries(StartTimes).map(([label, value]) => { + const isActive = startTime === value + + return ( + + ) + })} + + + {chart} + + {startTimeText} + Now + + + + + ) +} + +export const DisplayPanel = ({ + title, + description, +}: { + title: string + description: string +}) => ( + + + {title} + + {description} + +) + +export const SkeletonPanel = () => ( + +) From e225ee50dbbc13ea4b0e7649ea0f53807a335ebc Mon Sep 17 00:00:00 2001 From: jordan Date: Tue, 8 Aug 2023 14:51:11 -0700 Subject: [PATCH 21/60] graph perf adjustments --- .../components/AuctionChart/AuctionGraph.tsx | 163 +++++++++++------- .../AuctionChart/auctionChartUtils.ts | 32 ++++ 2 files changed, 134 insertions(+), 61 deletions(-) create mode 100644 apps/web/src/modules/dao/components/AuctionChart/auctionChartUtils.ts diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx index 9b3090af..8ebdbcf2 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx @@ -1,17 +1,26 @@ import { Box, Text } from '@zoralabs/zord' import { color } from '@zoralabs/zord' import { ethers } from 'ethers' -import { useEffect, useRef, useState } from 'react' +import React, { useEffect, useMemo, useRef, useState } from 'react' import { AuctionHistory } from 'src/data/subgraph/requests/auctionHistory' import { useLayoutStore } from 'src/stores' import { StartTimes } from './AuctionChart' import { graphOnLoadStyles, svgBox } from './AuctionChart.css' +import { + calculateVisibleIndex, + calculateY, + getMouseEventSource, + getTouchEventSource, + isTouchEvent, +} from './auctionChartUtils' const STROKE = 1 +const paddingX = 10 +const paddingY = 30 -interface AuctionGraphProps { +type AuctionGraphProps = { height?: number width?: number startTime: StartTimes @@ -26,12 +35,9 @@ export const AuctionGraph = ({ startTime, }: AuctionGraphProps) => { const [visibleIndex, setVisibleIndex] = useState(0) - const paddingX = 10 - const paddingY = 30 const chartWidth = width - paddingX * 2 const chartHeight = height - paddingY * 2 - const FONT_SIZE = width / 60 const { isMobile } = useLayoutStore() const maximumYFromData = Math.max(...chartData.map((e) => Number(e.winningBidAmt))) const lineRef = useRef(null) @@ -51,65 +57,32 @@ export const AuctionGraph = ({ } }, []) - const handleMouseMove = (e: any) => { - const event = e.targetTouches ? e.targetTouches[0] : e - const y = Math.max( - 0, - event.pageX - e.currentTarget.getBoundingClientRect().left - paddingX - ) - const index = Math.round( - ((y - paddingY / 2) / (e.currentTarget.getBoundingClientRect().width - paddingY)) * - chartData.length - ) - setVisibleIndex(Math.max(0, Math.min(index, chartData.length - 1))) - } + const points = useMemo( + () => + chartData + .map((element, index) => { + const parts = chartData.length + const x = index * (chartWidth / parts) + paddingX + const y = + chartHeight - + (Number(element.winningBidAmt) / maximumYFromData) * chartHeight + + paddingY - const points = chartData - .map((element, index) => { - const PARTS = chartData.length - const x = index * (chartWidth / PARTS) + paddingX - const y = - chartHeight - - (Number(element.winningBidAmt) / maximumYFromData) * chartHeight + - paddingY + return `${x},${y}` + }) + .join(' '), + [chartData, chartWidth, paddingX, paddingY, chartHeight, maximumYFromData] + ) - return `${x},${y}` - }) - .join(' ') + const handleMouseMove = ( + e: React.MouseEvent | React.TouchEvent + ) => { + const event = isTouchEvent(e) ? getTouchEventSource(e) : getMouseEventSource(e) + const y = calculateY(event, e, paddingX) + const visibleIndex = calculateVisibleIndex(y, e, paddingY, chartData.length) - const XValues = () => { - const PARTS = chartData.length - return ( - <> - {new Array(PARTS).fill(0).map((_, index) => { - const x = index * (chartWidth / PARTS) + paddingX - FONT_SIZE / 2 - const y = - chartHeight - - (Number(chartData[index].winningBidAmt) / maximumYFromData) * chartHeight + - paddingY - - FONT_SIZE - return ( - - {Number(ethers.utils.formatEther(chartData[index]?.winningBidAmt)).toFixed( - 4 - )}{' '} - ETH - - ) - })} - - ) + setVisibleIndex(visibleIndex) } - return ( - + ) } + +type XValuesProps = { + chartData: AuctionHistory[] + chartWidth: number + paddingX: number + paddingY: number + width: number + visibleIndex: number + maximumYFromData: number + chartHeight: number + isMobile: boolean +} + +const XValues = React.memo( + ({ + chartData, + chartWidth, + paddingX, + paddingY, + width, + visibleIndex, + maximumYFromData, + chartHeight, + isMobile, + }: XValuesProps) => { + const FONT_SIZE = width / 60 + const parts = chartData.length + return ( + <> + {new Array(parts).fill(0).map((_, index) => { + const x = index * (chartWidth / parts) + paddingX - FONT_SIZE / 2 + const y = + chartHeight - + (Number(chartData[index].winningBidAmt) / maximumYFromData) * chartHeight + + paddingY - + FONT_SIZE + return ( + + {Number(ethers.utils.formatEther(chartData[index]?.winningBidAmt)).toFixed( + 4 + )}{' '} + ETH + + ) + })} + + ) + } +) diff --git a/apps/web/src/modules/dao/components/AuctionChart/auctionChartUtils.ts b/apps/web/src/modules/dao/components/AuctionChart/auctionChartUtils.ts new file mode 100644 index 00000000..0e101060 --- /dev/null +++ b/apps/web/src/modules/dao/components/AuctionChart/auctionChartUtils.ts @@ -0,0 +1,32 @@ +import React from 'react' + +export const isTouchEvent = ( + e: React.MouseEvent | React.TouchEvent +): e is React.TouchEvent => 'touches' in e + +export const getMouseEventSource = ( + e: React.MouseEvent +): React.MouseEvent => e + +export const getTouchEventSource = (e: React.TouchEvent): React.Touch => + e.touches[0] + +export const calculateY = ( + event: { clientX: number; pageX?: number }, + e: React.MouseEvent | React.TouchEvent, + paddingX: number +): number => + Math.max(0, event.clientX - e.currentTarget.getBoundingClientRect().left - paddingX) + +export const calculateVisibleIndex = ( + y: number, + e: React.MouseEvent | React.TouchEvent, + paddingY: number, + chartDataLength: number +): number => { + const index = Math.round( + ((y - paddingY / 2) / (e.currentTarget.getBoundingClientRect().width - paddingY)) * + chartDataLength + ) + return Math.max(0, Math.min(index, chartDataLength - 1)) +} From 0288726fcd8c306df0184180bfd9c4edceb8d4a7 Mon Sep 17 00:00:00 2001 From: jordan Date: Tue, 8 Aug 2023 14:59:29 -0700 Subject: [PATCH 22/60] cleanup --- .../dao/components/AuctionChart/AuctionChart.css.ts | 8 ++++++++ .../modules/dao/components/AuctionChart/AuctionGraph.tsx | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts index 94b48ff2..27d0ea6f 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts @@ -54,3 +54,11 @@ export const graphOnLoadStyles = style({ export const svgBox = style({ overflow: 'visible', }) +export const cursorText = style({ + fontSize: '20px', + '@media': { + 'screen and (max-width: 768px)': { + fontSize: '12px', + }, + }, +}) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx index 8ebdbcf2..85ccc85c 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx @@ -168,7 +168,7 @@ const XValues = React.memo( chartHeight, isMobile, }: XValuesProps) => { - const FONT_SIZE = width / 60 + const FONT_SIZE = width / (isMobile ? 36 : 60) const parts = chartData.length return ( <> From 43c1d62df7736d9245735365ad50aa1734d12828 Mon Sep 17 00:00:00 2001 From: jordan Date: Tue, 8 Aug 2023 15:01:08 -0700 Subject: [PATCH 23/60] fix memo warning --- .../src/modules/dao/components/AuctionChart/AuctionGraph.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx index 85ccc85c..5b210b22 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx @@ -71,7 +71,7 @@ export const AuctionGraph = ({ return `${x},${y}` }) .join(' '), - [chartData, chartWidth, paddingX, paddingY, chartHeight, maximumYFromData] + [chartData, chartWidth, chartHeight, maximumYFromData] ) const handleMouseMove = ( From d3e207999991ae79896bb9d80fdbeb2491c884b7 Mon Sep 17 00:00:00 2001 From: jordan Date: Wed, 9 Aug 2023 22:32:06 -0700 Subject: [PATCH 24/60] fix bottom padding, add new tag --- .../modules/auction/components/ViewSwitcher.tsx | 14 +++++++++++++- .../components/AuctionChart/AuctionChart.css.ts | 12 ++++++++++-- .../dao/components/AuctionChart/AuctionChart.tsx | 2 +- .../dao/components/AuctionChart/AuctionGraph.tsx | 2 +- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/apps/web/src/modules/auction/components/ViewSwitcher.tsx b/apps/web/src/modules/auction/components/ViewSwitcher.tsx index 72a181d1..390241bd 100644 --- a/apps/web/src/modules/auction/components/ViewSwitcher.tsx +++ b/apps/web/src/modules/auction/components/ViewSwitcher.tsx @@ -1,8 +1,9 @@ -import { Button, Flex } from '@zoralabs/zord' +import { Button, Flex, Text } from '@zoralabs/zord' import React from 'react' import { TopSectionView } from 'src/layouts/TopSection' import { + newTag, selectedTab, unselectedTab, } from 'src/modules/dao/components/AuctionChart/AuctionChart.css' @@ -21,6 +22,7 @@ export const ViewSwitcher = ({ {Object.values(TopSectionView).map((view) => ( ))} diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts index 27d0ea6f..f1427e7f 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts @@ -20,7 +20,7 @@ export const chartSkeleton = style({ maxWidth: '912px', '@media': { 'screen and (max-width: 768px)': { - height: '145px', + height: '125px', }, }, }) @@ -39,7 +39,7 @@ export const viewBox = style({ maxWidth: '912px', }) export const innerBox = style({ - height: '90%', + height: '83%', }) export const displayPanelBox = style({ height: '300px', @@ -62,3 +62,11 @@ export const cursorText = style({ }, }, }) +export const newTag = style({ + color: 'white', + position: 'absolute', + top: '0', + right: '-20px', + zIndex: 100, + padding: '2px 5px', +}) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx index 41137a58..782b3858 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx @@ -32,7 +32,7 @@ export const AuctionChart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { addresses: { token }, } = useDaoStore() - const [startTime, setStartTime] = useState(StartTimes['All']) + const [startTime, setStartTime] = useState(StartTimes['30d']) const startSeconds = startTimeFromNow(startTime) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx index 5b210b22..374626dd 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx @@ -92,7 +92,7 @@ export const AuctionGraph = ({ > - + Date: Wed, 9 Aug 2023 22:44:43 -0700 Subject: [PATCH 25/60] mouse away from SVG to fade cursor --- .../components/AuctionChart/AuctionGraph.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx index 374626dd..69efd6f2 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx @@ -35,6 +35,7 @@ export const AuctionGraph = ({ startTime, }: AuctionGraphProps) => { const [visibleIndex, setVisibleIndex] = useState(0) + const [cursorOpacity, setCursorOpacity] = useState(0) const chartWidth = width - paddingX * 2 const chartHeight = height - paddingY * 2 @@ -77,17 +78,27 @@ export const AuctionGraph = ({ const handleMouseMove = ( e: React.MouseEvent | React.TouchEvent ) => { + if (cursorOpacity === 0) { + setCursorOpacity(1) + } const event = isTouchEvent(e) ? getTouchEventSource(e) : getMouseEventSource(e) const y = calculateY(event, e, paddingX) const visibleIndex = calculateVisibleIndex(y, e, paddingY, chartData.length) setVisibleIndex(visibleIndex) } + + const handleMouseLeaveOrTouchEnd = () => { + setCursorOpacity(0) + } + return ( @@ -105,6 +116,7 @@ export const AuctionGraph = ({ maximumYFromData={maximumYFromData} chartHeight={chartHeight} isMobile={isMobile} + cursorOpacity={cursorOpacity} /> { const FONT_SIZE = width / (isMobile ? 36 : 60) const parts = chartData.length @@ -181,6 +197,7 @@ const XValues = React.memo( FONT_SIZE return ( Date: Thu, 10 Aug 2023 01:37:00 -0700 Subject: [PATCH 26/60] animate switcher --- apps/web/src/layouts/TopSection.tsx | 70 ++++++++++---- .../modules/auction/components/Auction.tsx | 91 +++++++++---------- .../auction/components/ViewSwitcher.tsx | 63 +++++++------ .../AuctionChart/AuctionChart.css.ts | 4 +- .../components/AuctionChart/AuctionChart.tsx | 14 +-- .../components/AuctionChart/AuctionGraph.tsx | 14 +-- .../dao/components/AuctionChart/Layouts.tsx | 88 +++++++++--------- 7 files changed, 188 insertions(+), 156 deletions(-) diff --git a/apps/web/src/layouts/TopSection.tsx b/apps/web/src/layouts/TopSection.tsx index 96b32edd..5db5fe07 100644 --- a/apps/web/src/layouts/TopSection.tsx +++ b/apps/web/src/layouts/TopSection.tsx @@ -1,4 +1,5 @@ -import React from 'react' +import { AnimatePresence, motion } from 'framer-motion' +import React, { ReactElement } from 'react' import { TokenWithWinner } from 'src/data/contract/requests/getToken' import { Auction } from 'src/modules/auction' @@ -31,31 +32,60 @@ export const TopSection = ({ if (topSectionView === TopSectionView.Chart) { return ( - - } - /> + + + + + ) } return token && auctionAddress ? ( - + + - } - /> + +
) : ( ) } + +const TabSwitchAnimation = ({ + children, + topSectionView, +}: { + children: ReactElement + topSectionView: string +}) => { + return ( + + + {children} + + + ) +} diff --git a/apps/web/src/modules/auction/components/Auction.tsx b/apps/web/src/modules/auction/components/Auction.tsx index 71f74e50..34c40849 100644 --- a/apps/web/src/modules/auction/components/Auction.tsx +++ b/apps/web/src/modules/auction/components/Auction.tsx @@ -12,7 +12,7 @@ import { AuctionBidFragment } from 'src/data/subgraph/sdk.generated' import { AddressType, Chain } from 'src/typings' import { useAuctionEvents } from '../hooks' -import { auctionGrid, auctionWrapVariants, auctionWrapper } from './Auction.css' +import { auctionGrid, auctionWrapper } from './Auction.css' import { AuctionDetails } from './AuctionDetails' import { AuctionImage } from './AuctionImage' import { AuctionPaused } from './AuctionPaused' @@ -75,54 +75,51 @@ export const Auction: React.FC = ({ ) return ( - - {viewSwitcher} - - + + + - - - {isTokenActiveAuction && !!auction && ( - - )} + {isTokenActiveAuction && !!auction && ( + + )} - {!isTokenActiveAuction && !!auction && ( - - - - - - - - - - - )} - - - + {!isTokenActiveAuction && !!auction && ( + + + + + + + + + + + )} + + ) } diff --git a/apps/web/src/modules/auction/components/ViewSwitcher.tsx b/apps/web/src/modules/auction/components/ViewSwitcher.tsx index 390241bd..6650e9b5 100644 --- a/apps/web/src/modules/auction/components/ViewSwitcher.tsx +++ b/apps/web/src/modules/auction/components/ViewSwitcher.tsx @@ -1,5 +1,5 @@ import { Button, Flex, Text } from '@zoralabs/zord' -import React from 'react' +import React, { ReactNode } from 'react' import { TopSectionView } from 'src/layouts/TopSection' import { @@ -8,42 +8,47 @@ import { unselectedTab, } from 'src/modules/dao/components/AuctionChart/AuctionChart.css' -import { switcherBox } from './Auction.css' +import { auctionWrapVariants, switcherBox } from './Auction.css' export const ViewSwitcher = ({ topSectionView, setTopSectionView, + children, }: { topSectionView: TopSectionView setTopSectionView: (view: TopSectionView) => void + children: ReactNode }) => ( - - - {Object.values(TopSectionView).map((view) => ( - - ))} + + + + {Object.values(TopSectionView).map((view) => ( + + ))} + + {children} ) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts index f1427e7f..bd1fdc5e 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.css.ts @@ -16,7 +16,7 @@ export const unselectedTab = style({ }) export const chartSkeleton = style({ animation: skeletonAnimation, - height: '350px', + height: '330px', maxWidth: '912px', '@media': { 'screen and (max-width: 768px)': { @@ -37,9 +37,11 @@ export const outerBox = style({ export const viewBox = style({ height: '100%', maxWidth: '912px', + width: '100%', }) export const innerBox = style({ height: '83%', + width: '100%', }) export const displayPanelBox = style({ height: '300px', diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx index 782b3858..c68bf05e 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx @@ -1,6 +1,6 @@ import axios from 'axios' import { useRouter } from 'next/router' -import React, { ReactNode, useState } from 'react' +import React, { useState } from 'react' import useSWR from 'swr' import { AuctionHistory } from 'src/data/subgraph/requests/auctionHistory' @@ -25,7 +25,7 @@ const startTimeFromNow = (startTime: StartTimes) => { return nowInSeconds - parseInt(startTime) * 24 * 60 * 60 } -export const AuctionChart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { +export const AuctionChart = () => { const { isReady } = useRouter() const chain = useChainStore((x) => x.chain) const { @@ -43,13 +43,14 @@ export const AuctionChart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { .get<{ auctionHistory: AuctionHistory[] }>( `/api/auctionHistory/${token}?chainId=${chain.id}&startTime=${startSeconds}` ) - .then((x) => x.data.auctionHistory) + .then((x) => x.data.auctionHistory), + { revalidateOnFocus: false } ) if (isValidating) { return ( } @@ -60,7 +61,7 @@ export const AuctionChart = ({ viewSwitcher }: { viewSwitcher: ReactNode }) => { if (error) { return ( { if (!data || !data.length || data.length < 2) { return ( { return ( | React.TouchEvent ) => { - if (cursorOpacity === 0) { - setCursorOpacity(1) - } const event = isTouchEvent(e) ? getTouchEventSource(e) : getMouseEventSource(e) const y = calculateY(event, e, paddingX) const visibleIndex = calculateVisibleIndex(y, e, paddingY, chartData.length) @@ -91,6 +88,9 @@ export const AuctionGraph = ({ const handleMouseLeaveOrTouchEnd = () => { setCursorOpacity(0) } + const handleMouseLeaveOrTouchEnter = () => { + setCursorOpacity(1) + } return ( @@ -129,7 +131,7 @@ export const AuctionGraph = ({ } r="3" fill={color.accent} - style={{ transition: 'opacity 0.3s', opacity: cursorOpacity }} + style={{ transition: 'opacity 0.5s', opacity: cursorOpacity }} /> - {viewSwitcher} - + - - - {isMobile || Auction History} - - {Object.entries(StartTimes).map(([label, value]) => { - const isActive = startTime === value + + {isMobile || Auction History} + + {Object.entries(StartTimes).map(([label, value]) => { + const isActive = startTime === value - return ( - - ) - })} - + return ( + + ) + })} - {chart} - - {startTimeText} - Now - - - + + {chart} + + {startTimeText} + Now + + ) } From f223dca142cc37b856a069bc1e92bbe78512ee6d Mon Sep 17 00:00:00 2001 From: jordan Date: Thu, 10 Aug 2023 01:44:35 -0700 Subject: [PATCH 27/60] include slide-in animation --- apps/web/src/layouts/TopSection.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/web/src/layouts/TopSection.tsx b/apps/web/src/layouts/TopSection.tsx index 5db5fe07..0bc679d4 100644 --- a/apps/web/src/layouts/TopSection.tsx +++ b/apps/web/src/layouts/TopSection.tsx @@ -74,9 +74,11 @@ const TabSwitchAnimation = ({ key={topSectionView} variants={{ closed: { + y: 10, opacity: 0, }, open: { + y: 0, opacity: 1, }, }} From 2c3ed607c8f41bf18907e95702433fdcb49d22c3 Mon Sep 17 00:00:00 2001 From: jordan Date: Fri, 11 Aug 2023 22:57:37 -0700 Subject: [PATCH 28/60] filter for settled with query --- .../src/data/subgraph/queries/auctionHistory.graphql | 2 +- .../web/src/data/subgraph/requests/auctionHistory.ts | 12 +++++------- apps/web/src/data/subgraph/sdk.generated.ts | 2 +- .../dao/components/AuctionChart/AuctionChart.tsx | 3 --- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/apps/web/src/data/subgraph/queries/auctionHistory.graphql b/apps/web/src/data/subgraph/queries/auctionHistory.graphql index 81971044..c9dea524 100644 --- a/apps/web/src/data/subgraph/queries/auctionHistory.graphql +++ b/apps/web/src/data/subgraph/queries/auctionHistory.graphql @@ -7,7 +7,7 @@ query auctionHistory( ) { dao(id: $daoId) { auctions( - where: { endTime_gt: $startTime } + where: { endTime_gt: $startTime, settled: true } orderBy: $orderBy orderDirection: $orderDirection first: $first diff --git a/apps/web/src/data/subgraph/requests/auctionHistory.ts b/apps/web/src/data/subgraph/requests/auctionHistory.ts index 4e8a1a3f..23007c14 100644 --- a/apps/web/src/data/subgraph/requests/auctionHistory.ts +++ b/apps/web/src/data/subgraph/requests/auctionHistory.ts @@ -25,13 +25,11 @@ export const auctionHistoryRequest = async ( first: 1000, }) - return data.dao?.auctions - .filter((auction) => auction.settled) - .map((auction) => ({ - id: auction.id, - endTime: Number(auction.endTime), - winningBidAmt: auction?.winningBid?.amount || ('1' as string), - })) + return data.dao?.auctions.map((auction) => ({ + id: auction.id, + endTime: Number(auction.endTime), + winningBidAmt: auction?.winningBid?.amount || ('1' as string), + })) } catch (error) { console.error(error) Sentry.captureException(error) diff --git a/apps/web/src/data/subgraph/sdk.generated.ts b/apps/web/src/data/subgraph/sdk.generated.ts index 5dca5928..728951dd 100644 --- a/apps/web/src/data/subgraph/sdk.generated.ts +++ b/apps/web/src/data/subgraph/sdk.generated.ts @@ -2311,7 +2311,7 @@ export const AuctionHistoryDocument = gql` ) { dao(id: $daoId) { auctions( - where: { endTime_gt: $startTime } + where: { endTime_gt: $startTime, settled: true } orderBy: $orderBy orderDirection: $orderDirection first: $first diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx index c68bf05e..9a566159 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx @@ -50,7 +50,6 @@ export const AuctionChart = () => { if (isValidating) { return ( } @@ -61,7 +60,6 @@ export const AuctionChart = () => { if (error) { return ( { if (!data || !data.length || data.length < 2) { return ( Date: Fri, 11 Aug 2023 23:09:07 -0700 Subject: [PATCH 29/60] rename and move some files --- apps/web/src/modules/auction/components/ViewSwitcher.tsx | 2 +- .../dao/components/DaoTopSection.tsx} | 2 +- .../pages/api/auctionHistory/{[tokenId].tsx => [token].tsx} | 6 +++--- apps/web/src/pages/dao/[network]/[token]/[tokenId].tsx | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) rename apps/web/src/{layouts/TopSection.tsx => modules/dao/components/DaoTopSection.tsx} (98%) rename apps/web/src/pages/api/auctionHistory/{[tokenId].tsx => [token].tsx} (79%) diff --git a/apps/web/src/modules/auction/components/ViewSwitcher.tsx b/apps/web/src/modules/auction/components/ViewSwitcher.tsx index 6650e9b5..c98966dd 100644 --- a/apps/web/src/modules/auction/components/ViewSwitcher.tsx +++ b/apps/web/src/modules/auction/components/ViewSwitcher.tsx @@ -1,12 +1,12 @@ import { Button, Flex, Text } from '@zoralabs/zord' import React, { ReactNode } from 'react' -import { TopSectionView } from 'src/layouts/TopSection' import { newTag, selectedTab, unselectedTab, } from 'src/modules/dao/components/AuctionChart/AuctionChart.css' +import { TopSectionView } from 'src/modules/dao/components/DaoTopSection' import { auctionWrapVariants, switcherBox } from './Auction.css' diff --git a/apps/web/src/layouts/TopSection.tsx b/apps/web/src/modules/dao/components/DaoTopSection.tsx similarity index 98% rename from apps/web/src/layouts/TopSection.tsx rename to apps/web/src/modules/dao/components/DaoTopSection.tsx index 0bc679d4..e8274a18 100644 --- a/apps/web/src/layouts/TopSection.tsx +++ b/apps/web/src/modules/dao/components/DaoTopSection.tsx @@ -20,7 +20,7 @@ export enum TopSectionView { Chart = 'chart', } -export const TopSection = ({ +export const DaoTopSection = ({ chain, auctionAddress, collection, diff --git a/apps/web/src/pages/api/auctionHistory/[tokenId].tsx b/apps/web/src/pages/api/auctionHistory/[token].tsx similarity index 79% rename from apps/web/src/pages/api/auctionHistory/[tokenId].tsx rename to apps/web/src/pages/api/auctionHistory/[token].tsx index b3ff8801..95f32071 100644 --- a/apps/web/src/pages/api/auctionHistory/[tokenId].tsx +++ b/apps/web/src/pages/api/auctionHistory/[token].tsx @@ -4,15 +4,15 @@ import { auctionHistoryRequest } from 'src/data/subgraph/requests/auctionHistory import { CHAIN_ID } from 'src/typings' const handler = async (req: NextApiRequest, res: NextApiResponse) => { - const { tokenId, chainId, startTime } = req.query + const { token, chainId, startTime } = req.query try { - if (!tokenId || !chainId || !startTime) { + if (!token || !chainId || !startTime) { throw new Error('Invalid query') } const auctionHistory = await auctionHistoryRequest( Number(chainId) as CHAIN_ID, - (tokenId as string).toLowerCase(), + (token as string).toLowerCase(), Number(startTime) ) res.status(200).json({ auctionHistory }) diff --git a/apps/web/src/pages/dao/[network]/[token]/[tokenId].tsx b/apps/web/src/pages/dao/[network]/[token]/[tokenId].tsx index 7196cab7..b0ae974c 100644 --- a/apps/web/src/pages/dao/[network]/[token]/[tokenId].tsx +++ b/apps/web/src/pages/dao/[network]/[token]/[tokenId].tsx @@ -17,7 +17,6 @@ import SWR_KEYS from 'src/constants/swrKeys' import { TokenWithWinner } from 'src/data/contract/requests/getToken' import { useVotes } from 'src/hooks' import { getDaoLayout } from 'src/layouts/DaoLayout' -import { TopSection } from 'src/layouts/TopSection' import { About, Activity, @@ -26,6 +25,7 @@ import { SectionHandler, SmartContracts, } from 'src/modules/dao' +import { DaoTopSection } from 'src/modules/dao/components/DaoTopSection' import FeedTab from 'src/modules/dao/components/Feed/Feed' import { NextPageWithLayout } from 'src/pages/_app' import { DaoResponse } from 'src/pages/api/dao/[network]/[token]' @@ -124,7 +124,7 @@ const TokenPage: NextPageWithLayout = ({ description={ogDescription} /> - Date: Fri, 11 Aug 2023 23:11:05 -0700 Subject: [PATCH 30/60] remove slide in --- apps/web/src/modules/dao/components/DaoTopSection.tsx | 2 -- .../src/pages/api/auctionHistory/{[token].tsx => [token].ts} | 0 2 files changed, 2 deletions(-) rename apps/web/src/pages/api/auctionHistory/{[token].tsx => [token].ts} (100%) diff --git a/apps/web/src/modules/dao/components/DaoTopSection.tsx b/apps/web/src/modules/dao/components/DaoTopSection.tsx index e8274a18..23e6f9c7 100644 --- a/apps/web/src/modules/dao/components/DaoTopSection.tsx +++ b/apps/web/src/modules/dao/components/DaoTopSection.tsx @@ -74,11 +74,9 @@ const TabSwitchAnimation = ({ key={topSectionView} variants={{ closed: { - y: 10, opacity: 0, }, open: { - y: 0, opacity: 1, }, }} diff --git a/apps/web/src/pages/api/auctionHistory/[token].tsx b/apps/web/src/pages/api/auctionHistory/[token].ts similarity index 100% rename from apps/web/src/pages/api/auctionHistory/[token].tsx rename to apps/web/src/pages/api/auctionHistory/[token].ts From c8f7da11f388efa8468174f8a2cd619d2ffa3f7d Mon Sep 17 00:00:00 2001 From: jordan Date: Fri, 11 Aug 2023 23:17:11 -0700 Subject: [PATCH 31/60] fix empty cursor on loading bug --- apps/web/src/modules/auction/components/Auction.tsx | 1 - .../src/modules/dao/components/AuctionChart/AuctionGraph.tsx | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/web/src/modules/auction/components/Auction.tsx b/apps/web/src/modules/auction/components/Auction.tsx index 34c40849..455c8b81 100644 --- a/apps/web/src/modules/auction/components/Auction.tsx +++ b/apps/web/src/modules/auction/components/Auction.tsx @@ -35,7 +35,6 @@ export const Auction: React.FC = ({ auctionAddress, collection, token, - viewSwitcher, }) => { const { mintDate, name, image, price: tokenPrice, owner: tokenOwner } = token diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx index a889195e..5b693101 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx @@ -78,6 +78,9 @@ export const AuctionGraph = ({ const handleMouseMove = ( e: React.MouseEvent | React.TouchEvent ) => { + if (cursorOpacity === 0) { + setCursorOpacity(1) + } const event = isTouchEvent(e) ? getTouchEventSource(e) : getMouseEventSource(e) const y = calculateY(event, e, paddingX) const visibleIndex = calculateVisibleIndex(y, e, paddingY, chartData.length) From 9d6bef0667a225bd7ad6bff3db8eb3a476461a58 Mon Sep 17 00:00:00 2001 From: jordan Date: Fri, 11 Aug 2023 23:37:16 -0700 Subject: [PATCH 32/60] remove AuctionHistory and data transformation from request and bring them down the client --- .../data/subgraph/requests/auctionHistory.ts | 14 ++------------ .../components/AuctionChart/AuctionChart.tsx | 18 +++++++++++++++--- .../components/AuctionChart/AuctionGraph.tsx | 3 +-- .../dao/components/AuctionChart/Layouts.tsx | 3 +-- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/apps/web/src/data/subgraph/requests/auctionHistory.ts b/apps/web/src/data/subgraph/requests/auctionHistory.ts index 23007c14..d2c6f406 100644 --- a/apps/web/src/data/subgraph/requests/auctionHistory.ts +++ b/apps/web/src/data/subgraph/requests/auctionHistory.ts @@ -5,17 +5,11 @@ import { CHAIN_ID } from 'src/typings' import { Auction_OrderBy, OrderDirection } from '../sdk.generated' -export type AuctionHistory = { - id: string - endTime: number - winningBidAmt: string -} - export const auctionHistoryRequest = async ( chainId: CHAIN_ID, collectionAddress: string, startTime: number -): Promise => { +) => { try { const data = await SDK.connect(chainId).auctionHistory({ startTime, @@ -25,11 +19,7 @@ export const auctionHistoryRequest = async ( first: 1000, }) - return data.dao?.auctions.map((auction) => ({ - id: auction.id, - endTime: Number(auction.endTime), - winningBidAmt: auction?.winningBid?.amount || ('1' as string), - })) + return data } catch (error) { console.error(error) Sentry.captureException(error) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx index 9a566159..69d54e61 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionChart.tsx @@ -3,7 +3,7 @@ import { useRouter } from 'next/router' import React, { useState } from 'react' import useSWR from 'swr' -import { AuctionHistory } from 'src/data/subgraph/requests/auctionHistory' +import { AuctionHistoryQuery } from 'src/data/subgraph/sdk.generated' import { useDaoStore } from 'src/modules/dao' import { useChainStore } from 'src/stores/useChainStore' @@ -17,6 +17,12 @@ export enum StartTimes { 'All' = '0', } +export type AuctionHistory = { + id: string + endTime: number + winningBidAmt: string +} + const startTimeFromNow = (startTime: StartTimes) => { if (startTime === '0') return 0 @@ -40,10 +46,16 @@ export const AuctionChart = () => { isReady ? [token, chain.id, startSeconds] : undefined, () => axios - .get<{ auctionHistory: AuctionHistory[] }>( + .get<{ auctionHistory: AuctionHistoryQuery }>( `/api/auctionHistory/${token}?chainId=${chain.id}&startTime=${startSeconds}` ) - .then((x) => x.data.auctionHistory), + .then((x) => + x.data.auctionHistory.dao?.auctions.map((auction) => ({ + id: auction.id, + endTime: Number(auction.endTime), + winningBidAmt: auction?.winningBid?.amount || ('1' as string), + })) + ), { revalidateOnFocus: false } ) diff --git a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx index 5b693101..de3375ac 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/AuctionGraph.tsx @@ -3,10 +3,9 @@ import { color } from '@zoralabs/zord' import { ethers } from 'ethers' import React, { useEffect, useMemo, useRef, useState } from 'react' -import { AuctionHistory } from 'src/data/subgraph/requests/auctionHistory' import { useLayoutStore } from 'src/stores' -import { StartTimes } from './AuctionChart' +import { AuctionHistory, StartTimes } from './AuctionChart' import { graphOnLoadStyles, svgBox } from './AuctionChart.css' import { calculateVisibleIndex, diff --git a/apps/web/src/modules/dao/components/AuctionChart/Layouts.tsx b/apps/web/src/modules/dao/components/AuctionChart/Layouts.tsx index df798044..1c20f7ff 100644 --- a/apps/web/src/modules/dao/components/AuctionChart/Layouts.tsx +++ b/apps/web/src/modules/dao/components/AuctionChart/Layouts.tsx @@ -3,10 +3,9 @@ import dayjs from 'dayjs' import relativeTime from 'dayjs/plugin/relativeTime' import React, { ReactNode, useMemo } from 'react' -import { AuctionHistory } from 'src/data/subgraph/requests/auctionHistory' import { useLayoutStore } from 'src/stores' -import { StartTimes } from './AuctionChart' +import { AuctionHistory, StartTimes } from './AuctionChart' import { chartSkeleton, displayPanelBox, From 58fe6a3edb08641e91c90145ee52ed793390403b Mon Sep 17 00:00:00 2001 From: neokry Date: Mon, 14 Aug 2023 16:15:42 +0900 Subject: [PATCH 33/60] Add inline bridging for EOA accounts --- .../BridgeModal/BridgeForm.schema.ts | 20 ++ .../src/components/BridgeModal/BridgeForm.tsx | 203 ++++++++++++++++++ .../components/BridgeModal/BridgeModal.tsx | 21 ++ apps/web/src/components/ContractButton.tsx | 26 ++- .../src/components/Icon/assets/arrow-down.svg | 3 + apps/web/src/components/Icon/icons.ts | 2 + apps/web/src/constants/addresses.ts | 17 ++ apps/web/src/hooks/useBridgeModal.ts | 48 +++++ apps/web/src/hooks/useIsContract.ts | 18 ++ .../layouts/DefaultLayout/DefaultLayout.tsx | 3 + apps/web/src/layouts/DefaultLayout/Nav.tsx | 16 +- .../web/src/layouts/DefaultLayout/NavMenu.tsx | 27 ++- 12 files changed, 382 insertions(+), 22 deletions(-) create mode 100644 apps/web/src/components/BridgeModal/BridgeForm.schema.ts create mode 100644 apps/web/src/components/BridgeModal/BridgeForm.tsx create mode 100644 apps/web/src/components/BridgeModal/BridgeModal.tsx create mode 100644 apps/web/src/components/Icon/assets/arrow-down.svg create mode 100644 apps/web/src/hooks/useBridgeModal.ts create mode 100644 apps/web/src/hooks/useIsContract.ts diff --git a/apps/web/src/components/BridgeModal/BridgeForm.schema.ts b/apps/web/src/components/BridgeModal/BridgeForm.schema.ts new file mode 100644 index 00000000..c9f3c38f --- /dev/null +++ b/apps/web/src/components/BridgeModal/BridgeForm.schema.ts @@ -0,0 +1,20 @@ +import * as yup from 'yup' + +export interface BridgeFormValues { + amount?: number +} + +const bridgeFormSchema = (userL1Balance: number) => + yup.object({ + amount: yup + .number() + .required() + .max(userL1Balance, 'Your balance is insufficient to send ETH.') + .test( + 'is-greater-than-0', + 'Must send more than 0 ETH', + (value) => !!value && value > 0 + ), + }) + +export default bridgeFormSchema diff --git a/apps/web/src/components/BridgeModal/BridgeForm.tsx b/apps/web/src/components/BridgeModal/BridgeForm.tsx new file mode 100644 index 00000000..28fa73e4 --- /dev/null +++ b/apps/web/src/components/BridgeModal/BridgeForm.tsx @@ -0,0 +1,203 @@ +import { sendTransaction } from '@wagmi/core' +import { Box, Button, Flex, Heading, Text } from '@zoralabs/zord' +import { parseEther } from 'ethers/lib/utils.js' +import { Formik } from 'formik' +import Image from 'next/image' +import { useAccount, useBalance, useNetwork, useSwitchNetwork } from 'wagmi' + +import Input from 'src/components/Input/Input' +import { L2ChainType, PUBLIC_L1_BRIDGE_ADDRESS } from 'src/constants/addresses' +import { PUBLIC_DEFAULT_CHAINS } from 'src/constants/defaultChains' +import { useBridgeModal } from 'src/hooks/useBridgeModal' +import { useChainStore } from 'src/stores/useChainStore' +import { formatCryptoVal } from 'src/utils/numbers' + +import { Icon } from '../Icon' +import bridgeFormSchema, { BridgeFormValues } from './BridgeForm.schema' + +export const BridgeForm = () => { + const { chain: l2Chain } = useChainStore() + const { address } = useAccount() + const { chain: userChain } = useNetwork() + const { switchNetwork } = useSwitchNetwork() + const { closeBridgeModal } = useBridgeModal() + + const l1Chain = PUBLIC_DEFAULT_CHAINS[0] + + const isWalletOnL1 = userChain?.id === l1Chain.id + + const { data: userL1Balance } = useBalance({ + address, + chainId: l1Chain.id, + }) + + const { data: userL2Balance } = useBalance({ + address, + chainId: l2Chain.id, + }) + + const initialValues: BridgeFormValues = { + amount: 0, + } + + const handleSubmit = async (values: BridgeFormValues) => { + const bridge = PUBLIC_L1_BRIDGE_ADDRESS[l2Chain.id as L2ChainType] + + if (!values.amount || !bridge) return + const { wait } = await sendTransaction({ + request: { + to: PUBLIC_L1_BRIDGE_ADDRESS[l2Chain.id as L2ChainType], + value: parseEther(values.amount.toString()), + }, + mode: 'recklesslyUnprepared', + }) + await wait() + } + + const formattedL1Balance = userL1Balance ? parseFloat(userL1Balance.formatted) : 0 + const formattedL2Balance = userL2Balance ? parseFloat(userL2Balance.formatted) : 0 + + return ( + + + + + + Bridge + + + Bridge ETH to participate in onchain governance and auctions on L2 chains. + + + + {({ errors, touched, isValid, submitForm }) => { + const isAmountInvalid = !!errors.amount && touched.amount + + return ( + + + + + From + + + L1 Chain + + {l1Chain.name} + + } + secondaryLabel={'ETH'} + autoComplete={'off'} + type={'number'} + placeholder={0} + min={0} + max={userL1Balance?.formatted} + step={'any'} + /> + + Balance: {formatCryptoVal(formattedL1Balance)} ETH + + + + + + + + + + + To + + + L2 Chain + + {l2Chain.name} + + } + secondaryLabel={'ETH'} + autoComplete={'off'} + type={'number'} + placeholder={0} + min={0} + max={userL2Balance?.formatted} + step={'any'} + /> + + Balance: {formatCryptoVal(formattedL2Balance)} ETH + + + + {isWalletOnL1 ? ( + + ) : ( + + )} + + ) + }} + + + ) +} diff --git a/apps/web/src/components/BridgeModal/BridgeModal.tsx b/apps/web/src/components/BridgeModal/BridgeModal.tsx new file mode 100644 index 00000000..034a8fdc --- /dev/null +++ b/apps/web/src/components/BridgeModal/BridgeModal.tsx @@ -0,0 +1,21 @@ +import { useRouter } from 'next/router' +import { useEffect } from 'react' + +import AnimatedModal from '../Modal/AnimatedModal' +import { BridgeForm } from './BridgeForm' + +export const BridgeModal = () => { + const { + query: { bridge }, + } = useRouter() + + useEffect(() => { + document.body.style.overflow = !!bridge ? 'hidden' : 'unset' + }, [bridge]) + + return ( + + + + ) +} diff --git a/apps/web/src/components/ContractButton.tsx b/apps/web/src/components/ContractButton.tsx index 6f3c7530..a77b78c5 100644 --- a/apps/web/src/components/ContractButton.tsx +++ b/apps/web/src/components/ContractButton.tsx @@ -1,7 +1,8 @@ import { useConnectModal } from '@rainbow-me/rainbowkit' import { Button, ButtonProps } from '@zoralabs/zord' -import { useAccount, useNetwork, useSwitchNetwork } from 'wagmi' +import { useAccount, useBalance, useNetwork, useSwitchNetwork } from 'wagmi' +import { useBridgeModal } from 'src/hooks/useBridgeModal' import { useChainStore } from 'src/stores/useChainStore' interface ContractButtonProps extends ButtonProps { @@ -16,23 +17,26 @@ export const ContractButton = ({ const { address: userAddress } = useAccount() const { chain: userChain } = useNetwork() const appChain = useChainStore((x) => x.chain) + const { canUserBridge, openBridgeModal } = useBridgeModal() + const { data: userBalance } = useBalance({ + address: userAddress, + chainId: appChain.id, + }) const { openConnectModal } = useConnectModal() const { switchNetwork } = useSwitchNetwork() const handleSwitchNetwork = () => switchNetwork?.(appChain.id) + const handleClickWithValidation = () => { + if (!userAddress) return openConnectModal?.() + if (canUserBridge && userBalance?.value.eq(0)) return openBridgeModal() + if (userChain?.id !== appChain.id) return handleSwitchNetwork() + handleClick() + } + return ( - ) diff --git a/apps/web/src/components/Icon/assets/arrow-down.svg b/apps/web/src/components/Icon/assets/arrow-down.svg new file mode 100644 index 00000000..4dc5da4c --- /dev/null +++ b/apps/web/src/components/Icon/assets/arrow-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/web/src/components/Icon/icons.ts b/apps/web/src/components/Icon/icons.ts index 81729b37..8f4f10b3 100644 --- a/apps/web/src/components/Icon/icons.ts +++ b/apps/web/src/components/Icon/icons.ts @@ -1,4 +1,5 @@ import Airdrop from './assets/airdrop.svg' +import ArrowDown from './assets/arrow-down.svg' import ArrowLeft from './assets/arrow-left.svg' import ArrowRight from './assets/arrow-right.svg' import ArrowTopRight from './assets/arrow-top-right.svg' @@ -40,6 +41,7 @@ import Warning from './assets/warning.svg' export const icons = { airdrop: Airdrop, + arrowDown: ArrowDown, arrowLeft: ArrowLeft, arrowRight: ArrowRight, arrowTopRight: ArrowTopRight, diff --git a/apps/web/src/constants/addresses.ts b/apps/web/src/constants/addresses.ts index 70b8c84c..48448728 100644 --- a/apps/web/src/constants/addresses.ts +++ b/apps/web/src/constants/addresses.ts @@ -1,5 +1,13 @@ import { AddressType, CHAIN_ID } from 'src/typings' +export type L2ChainType = + | CHAIN_ID.OPTIMISM + | CHAIN_ID.OPTIMISM_GOERLI + | CHAIN_ID.BASE + | CHAIN_ID.BASE_GOERLI + | CHAIN_ID.ZORA + | CHAIN_ID.ZORA_GOERLI + export const PUBLIC_MANAGER_ADDRESS = { [CHAIN_ID.ETHEREUM]: '0xd310a3041dfcf14def5ccbc508668974b5da7174' as AddressType, [CHAIN_ID.OPTIMISM]: '0x3ac0E64Fe2931f8e082C6Bb29283540DE9b5371C' as AddressType, @@ -12,6 +20,15 @@ export const PUBLIC_MANAGER_ADDRESS = { [CHAIN_ID.FOUNDRY]: '0xd310a3041dfcf14def5ccbc508668974b5da7174' as AddressType, } +export const PUBLIC_L1_BRIDGE_ADDRESS = { + [CHAIN_ID.OPTIMISM]: '0xbEb5Fc579115071764c7423A4f12eDde41f106Ed' as AddressType, + [CHAIN_ID.OPTIMISM_GOERLI]: '0x5b47E1A08Ea6d985D6649300584e6722Ec4B1383' as AddressType, + [CHAIN_ID.BASE]: '0x49048044D57e1C92A77f79988d21Fa8fAF74E97e' as AddressType, + [CHAIN_ID.BASE_GOERLI]: '0xe93c8cD0D409341205A592f8c4Ac1A5fe5585cfA' as AddressType, + [CHAIN_ID.ZORA]: '0x1a0ad011913A150f69f6A19DF447A0CfD9551054' as AddressType, + [CHAIN_ID.ZORA_GOERLI]: '0xDb9F51790365e7dc196e7D072728df39Be958ACe' as AddressType, +} + export const PUBLIC_BUILDER_ADDRESS = { [CHAIN_ID.ETHEREUM]: '0xDC9b96Ea4966d063Dd5c8dbaf08fe59062091B6D' as AddressType, // builder treasury address [CHAIN_ID.GOERLI]: '0xc2fff40D3e3468fD85dca6B09e41961edd9381cD' as AddressType, diff --git a/apps/web/src/hooks/useBridgeModal.ts b/apps/web/src/hooks/useBridgeModal.ts new file mode 100644 index 00000000..f16b2b7d --- /dev/null +++ b/apps/web/src/hooks/useBridgeModal.ts @@ -0,0 +1,48 @@ +import { omit } from 'lodash' +import { useRouter } from 'next/router' +import { useAccount } from 'wagmi' + +import { PUBLIC_DEFAULT_CHAINS } from 'src/constants/defaultChains' +import { useChainStore } from 'src/stores/useChainStore' + +import { useIsContract } from './useIsContract' + +export const useBridgeModal = () => { + const router = useRouter() + const { chain: selectedChain } = useChainStore() + + const { address } = useAccount() + const { data: isContractWallet } = useIsContract({ address }) + const isL1Selected = selectedChain.id === PUBLIC_DEFAULT_CHAINS[0].id + + const openBridgeModal = () => { + router.push( + { + pathname: router.pathname, + query: { + ...router.query, + bridge: true, + }, + }, + undefined, + { shallow: true } + ) + } + + const closeBridgeModal = () => { + router.push( + { + pathname: router.pathname, + query: omit(router.query, 'bridge'), + }, + undefined, + { shallow: true } + ) + } + + return { + canUserBridge: !isContractWallet && !isL1Selected, + openBridgeModal, + closeBridgeModal, + } +} diff --git a/apps/web/src/hooks/useIsContract.ts b/apps/web/src/hooks/useIsContract.ts new file mode 100644 index 00000000..c2feff58 --- /dev/null +++ b/apps/web/src/hooks/useIsContract.ts @@ -0,0 +1,18 @@ +import { ethers } from 'ethers' +import useSWRImmutable from 'swr/immutable' + +import { RPC_URL } from 'src/constants/rpc' +import { AddressType, CHAIN_ID } from 'src/typings' + +export const useIsContract = ({ + address, + chainId = CHAIN_ID.ETHEREUM, +}: { + address?: AddressType + chainId?: CHAIN_ID +}) => { + return useSWRImmutable(address ? [address, chainId] : undefined, async (address) => { + const provider = new ethers.providers.JsonRpcProvider(RPC_URL[chainId]) + return await provider.getCode(address).then((x) => x !== '0x') + }) +} diff --git a/apps/web/src/layouts/DefaultLayout/DefaultLayout.tsx b/apps/web/src/layouts/DefaultLayout/DefaultLayout.tsx index 35e82170..bfe3bd4d 100644 --- a/apps/web/src/layouts/DefaultLayout/DefaultLayout.tsx +++ b/apps/web/src/layouts/DefaultLayout/DefaultLayout.tsx @@ -1,6 +1,8 @@ import { Box } from '@zoralabs/zord' import React, { ReactElement, ReactNode } from 'react' +import { BridgeModal } from 'src/components/BridgeModal/BridgeModal' + import { LayoutWrapper } from '../LayoutWrapper' import { Footer } from './Footer' import { Nav } from './Nav' @@ -9,6 +11,7 @@ export function DefaultLayout({ children }: { children: ReactNode }) { return (