From 605bcfc7fe3bcb1fd1c56134123243b5afbe8a6e Mon Sep 17 00:00:00 2001 From: Richard Gregory Date: Mon, 4 Sep 2023 12:04:45 +0100 Subject: [PATCH 1/2] Implement Volume Contest - Dashboard Fixes: #49 Signed-off-by: Richard Gregory --- src/App.js | 9 +-- src/pages/VolumeContestDashboard.js | 92 +++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 src/pages/VolumeContestDashboard.js diff --git a/src/App.js b/src/App.js index d66f860..58f61fe 100755 --- a/src/App.js +++ b/src/App.js @@ -18,6 +18,7 @@ import PinnedData from './components/PinnedData' import SideNav from './components/SideNav' import AccountLookup from './pages/AccountLookup' import LpContestLookup from './pages/LpContestLookup' +import VolumeContestDashboard from './pages/VolumeContestDashboard' import LpContestAccountPage from './pages/LpContestPage' import LocalLoader from './components/LocalLoader' import { useLatestBlocks, useWhitelistedTokens } from './contexts/Application' @@ -123,10 +124,10 @@ function App() { )} {globalData && - Object.keys(globalData).length > 0 && - globalChartData && - Object.keys(globalChartData).length > 0 && - !isEmpty(whitelistedTokens) ? ( + Object.keys(globalData).length > 0 && + globalChartData && + Object.keys(globalChartData).length > 0 && + !isEmpty(whitelistedTokens) ? ( diff --git a/src/pages/VolumeContestDashboard.js b/src/pages/VolumeContestDashboard.js new file mode 100644 index 0000000..9a5ef0f --- /dev/null +++ b/src/pages/VolumeContestDashboard.js @@ -0,0 +1,92 @@ +import React, { useMemo } from 'react' +import 'feather-icons' +import { withRouter } from 'react-router-dom' +import dayjs from 'dayjs' + +import { TYPE } from '../Theme' +import { PageWrapper, FullWrapper } from '../components' + +import styled from 'styled-components' + +import { AutoRow, RowBetween } from '../components/Row' +import { Banner } from '../components/Banner' +import { Flag, Watch, Box } from 'react-feather' +//import { useVolumeContestPlayersData } from '../contexts/LpContestData' + +import contestFlagIcon from '../../src/assets/flag.svg' +import { AutoColumn } from '../components/Column' +//import VolumeContestNftCategories from '../components/LpContestNftCategories' + +const Title = styled.div` + display: flex; + align-items: center; + font-size: 16px; + font-weight: 500; + margin-bottom: 14px; +` + +const TitleIconWrapper = styled.div` + display: flex; + margin-right: 0.75rem; + font-size: 30px; + + img { + width: 1em; + } +` + +const BannerGridRow = styled.div` + display: grid; + width: 100%; + grid-template-columns: 1fr 1fr 1fr; + column-gap: 20px; + align-items: start; + justify-content: space-between; + + @media screen and (max-width: 800px) { + grid-template-columns: 1fr; + row-gap: 20px; + } +` + +const LeaderboardGridRow = styled.div` + display: grid; + width: 100%; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + column-gap: 20px; + align-items: flex-start; + justify-content: space-between; + + @media screen and (max-width: 1180px) { + grid-template-columns: minmax(0, 1fr); + row-gap: 20px; + } +` + +const ListOptions = styled(AutoRow)` + height: 40px; + width: 100%; + font-size: 1.25rem; + font-weight: 600; + + @media screen and (max-width: 640px) { + font-size: 1rem; + } +` + +function VolumeContestDashboard() { + return ( + + + + <TitleIconWrapper> + <img src={contestFlagIcon} alt={''} /> + </TitleIconWrapper> + <TYPE.largeHeader style={{ fontWeight: 700 }}>The Trade Federation Awakens</TYPE.largeHeader> + + + + ) +} + +export default withRouter(VolumeContestDashboard) From 0e3d4cc4f4fa351bcde16f8e3bed18e645fa49ee Mon Sep 17 00:00:00 2001 From: Richard Gregory Date: Mon, 11 Sep 2023 17:56:30 +0100 Subject: [PATCH 2/2] Add LP contest search component --- src/App.js | 23 ++ src/components/VolumeContestSearch/index.js | 253 ++++++++++++++++++++ src/pages/LpContestPage.js | 2 +- src/pages/VolumeContestDashboard.js | 56 ++++- 4 files changed, 332 insertions(+), 2 deletions(-) create mode 100644 src/components/VolumeContestSearch/index.js diff --git a/src/App.js b/src/App.js index 58f61fe..1b36813 100755 --- a/src/App.js +++ b/src/App.js @@ -227,6 +227,29 @@ function App() { + { + if (isStarknetAddress(match.params.accountAddress.toLowerCase())) { + return ( + + + + ) + } else { + return + } + }} + /> + + + + + + + diff --git a/src/components/VolumeContestSearch/index.js b/src/components/VolumeContestSearch/index.js new file mode 100644 index 0000000..84fd62e --- /dev/null +++ b/src/components/VolumeContestSearch/index.js @@ -0,0 +1,253 @@ +import React, { useState, useEffect, useMemo, useCallback } from 'react' +import dayjs from 'dayjs' +import utc from 'dayjs/plugin/utc' +import { Box, Flex } from 'rebass' +import styled from 'styled-components' + +import Switch from 'react-switch' +import 'react-tooltip/dist/react-tooltip.css' + +import { Tooltip as ReactTooltip } from 'react-tooltip' +import { Search as SearchIcon } from 'react-feather' + +import { CustomLink } from '../Link' +import { Divider, EmptyCard } from '..' +import { formattedNum, isStarknetAddress, shortenStraknetAddress, zeroStarknetAddress } from '../../utils' +import { TYPE } from '../../Theme' +import Panel from '../Panel' + +import eligibilityBadgeIcon from '../../../src/assets/starBadge.svg' +import { AutoRow } from '../Row' +import { AutoColumn } from '../Column' +import { ButtonDark } from '../ButtonStyled' +import { withRouter } from 'react-router-dom' + +dayjs.extend(utc) + +const PageButtons = styled.div` + width: 100%; + display: flex; + justify-content: center; + margin-top: 2em; + margin-bottom: 0.5em; +` + +const Arrow = styled.div` + color: ${({ theme }) => theme.primary1}; + opacity: ${(props) => (props.faded ? 0.3 : 1)}; + padding: 0 20px; + user-select: none; + + :hover { + cursor: pointer; + } +` + +const SearchWrapper = styled.div` + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-end; + width: 100%; + border-radius: 12px; +` + +const Input = styled.input` + position: relative; + display: flex; + align-items: center; + width: 100%; + white-space: nowrap; + outline: none; + padding: 12px 16px; + border-radius: 12px; + color: ${({ theme }) => theme.text1}; + background-color: ${({ theme }) => theme.bg1}; + font-size: 16px; + + border: 1px solid ${({ theme }) => theme.bg3}; + + ::placeholder { + color: ${({ theme }) => theme.text3}; + font-size: 14px; + } + + @media screen and (max-width: 640px) { + ::placeholder { + font-size: 1rem; + } + } +` + +const EligibilityBadge = styled.img`` + +const EligibilityBadgeWrapper = styled.a` + display: flex; + cursor: help; +` + +const List = styled(Box)` + -webkit-overflow-scrolling: touch; +` + +const DashGrid = styled.div` + display: grid; + grid-gap: 1em; + grid-template-columns: 0.5fr 1fr 1fr; + grid-template-areas: 'number name value'; + padding: 0 4px; + + > * { + justify-content: flex-end; + } + + @media screen and (max-width: 1080px) { + grid-template-columns: 0.5fr 1fr 1fr; + grid-template-areas: 'number name value'; + } + + @media screen and (max-width: 600px) { + grid-template-columns: 0.5fr 1fr 1fr; + grid-template-areas: 'number name value'; + } +` + +const Switcher = styled(Switch)` + .react-switch-bg { + opacity: 0.32; + } +` + +const ListWrapper = styled.div`` + +const LeaderboardNote = styled.div` + display: flex; + align-items: center; + font-size: 12px; + font-weight: 400; + color: #fff; +` + +const DataText = styled(Flex)` + align-items: center; + text-align: center; + color: ${({ theme }) => theme.text1}; + + & > * { + font-size: 14px; + } + + @media screen and (max-width: 600px) { + font-size: 13px; + } +` + +function VolumeContestSearch({ history, players, maxItems = 10 }) { + const [isEligibilityFilterChecked, setIsEligibilityFilterChecked] = useState(false) + const [checkAccountQuery, setCheckAccountQuery] = useState('') + const [isCheckAccountAddressValid, setIsCheckAccountAddressValid] = useState(false) + const [page, setPage] = useState(1) + const [maxPage, setMaxPage] = useState(1) + const ITEMS_PER_PAGE = maxItems + const arePlayersAvailable = !!Object.keys(players).length + + const filteredPlayers = useMemo(() => { + if (!arePlayersAvailable) { + return + } + return Object.keys(players) + .filter((playerId) => { + return isEligibilityFilterChecked ? players[playerId]?.isEligible : true + }) + .map((key) => players[key]) + }, [arePlayersAvailable, players, isEligibilityFilterChecked]) + + const handleCheckAccountInputChange = useCallback( + (e) => { + const value = e.currentTarget.value + if (!value) { + setCheckAccountQuery('') + setIsCheckAccountAddressValid(false) + return + } + setCheckAccountQuery(value) + setIsCheckAccountAddressValid(isStarknetAddress(value, true)) + }, + [setCheckAccountQuery] + ) + + const handleAccountSearch = useCallback( + (e) => { + if (!(isCheckAccountAddressValid && checkAccountQuery)) { + return + } + history.push('/lp-contest/' + checkAccountQuery) + }, + [isCheckAccountAddressValid, checkAccountQuery, history] + ) + + useEffect(() => { + setMaxPage(1) // edit this to do modular + setPage(1) + }, [filteredPlayers]) + + useEffect(() => { + if (filteredPlayers?.length) { + let extraPages = 1 + if (filteredPlayers.length % ITEMS_PER_PAGE === 0) { + extraPages = 0 + } + setMaxPage(Math.floor(filteredPlayers.length / ITEMS_PER_PAGE) + extraPages) + } + }, [ITEMS_PER_PAGE, filteredPlayers]) + + const ListItem = ({ player, index }) => { + return ( + + + {index} + + + + {player.starknetIdDomain ? player.starknetIdDomain : shortenStraknetAddress(player.user.id, 6)} + + {player?.isEligible && ( + + + + )} + + + {formattedNum(player.contestValue)} + + + ) + } + + return ( + <> + + + + + {} + +
+ + View Profile + +
+
+
+ + + ) +} + +export default withRouter(VolumeContestSearch) diff --git a/src/pages/LpContestPage.js b/src/pages/LpContestPage.js index 2805f5d..9b7190d 100755 --- a/src/pages/LpContestPage.js +++ b/src/pages/LpContestPage.js @@ -103,7 +103,7 @@ function LpContestAccountPage({ account }) { if (account) { try { fetchData() - } catch (e) {} + } catch (e) { } } }, [account]) diff --git a/src/pages/VolumeContestDashboard.js b/src/pages/VolumeContestDashboard.js index 9a5ef0f..cc28697 100644 --- a/src/pages/VolumeContestDashboard.js +++ b/src/pages/VolumeContestDashboard.js @@ -11,7 +11,8 @@ import styled from 'styled-components' import { AutoRow, RowBetween } from '../components/Row' import { Banner } from '../components/Banner' import { Flag, Watch, Box } from 'react-feather' -//import { useVolumeContestPlayersData } from '../contexts/LpContestData' +import VolumeContestSearch from '../components/VolumeContestSearch' +import { useLpContestPlayersData } from '../contexts/LpContestData' import contestFlagIcon from '../../src/assets/flag.svg' import { AutoColumn } from '../components/Column' @@ -74,7 +75,19 @@ const ListOptions = styled(AutoRow)` } ` +const ONGOING_WEEK_START_DATE_ISO = '2023-09-17T00:00:00.000Z' +const ONGOING_WEEK_END_DATE_ISO = '2023-09-24T07:24:00.000Z' + function VolumeContestDashboard() { + const allPlayersData = useLpContestPlayersData() + + let latestLpBlockNumber = useMemo(() => { + if (!allPlayersData) { + return null + } + const data = Object.values(allPlayersData) + return data?.length ? data[0].block : null + }, [allPlayersData]) return ( @@ -84,6 +97,47 @@ function VolumeContestDashboard() { The Trade Federation Awakens +
+ + + } showPollingDot={false} /> + + + + + + + + NFT Rewards + + + + {/* */} + + {/* +
+ +
+
+ +
+
*/} +
+
)