From c9d323c83ff4249d11d556b71e199fae32ce5376 Mon Sep 17 00:00:00 2001 From: James Grugett Date: Tue, 13 Sep 2022 17:47:29 -0500 Subject: [PATCH] Small updates to experimental/home (#874) * Factor out section header * Remove daily balance change * Remove dead code * Layout, add streak * Fix visibility observer to work on server * Tweak * Formatting --- web/components/arrange-home.tsx | 2 +- web/components/bets-list.tsx | 5 +- web/components/visibility-observer.tsx | 12 ++-- web/hooks/use-contracts.ts | 37 +--------- web/pages/experimental/home/edit.tsx | 8 ++- web/pages/experimental/home/index.tsx | 99 ++++++++++++-------------- 6 files changed, 64 insertions(+), 99 deletions(-) diff --git a/web/components/arrange-home.tsx b/web/components/arrange-home.tsx index 646d30fe..6be187f8 100644 --- a/web/components/arrange-home.tsx +++ b/web/components/arrange-home.tsx @@ -111,9 +111,9 @@ export const getHomeItems = (groups: Group[], sections: string[]) => { if (!isArray(sections)) sections = [] const items = [ - { label: 'Daily movers', id: 'daily-movers' }, { label: 'Trending', id: 'score' }, { label: 'New for you', id: 'newest' }, + { label: 'Daily movers', id: 'daily-movers' }, ...groups.map((g) => ({ label: g.name, id: g.id, diff --git a/web/components/bets-list.tsx b/web/components/bets-list.tsx index ab232927..9c76174b 100644 --- a/web/components/bets-list.tsx +++ b/web/components/bets-list.tsx @@ -754,7 +754,10 @@ function SellButton(props: { ) } -function ProfitBadge(props: { profitPercent: number; className?: string }) { +export function ProfitBadge(props: { + profitPercent: number + className?: string +}) { const { profitPercent, className } = props if (!profitPercent) return null const colors = diff --git a/web/components/visibility-observer.tsx b/web/components/visibility-observer.tsx index aea2e41d..288d8f0e 100644 --- a/web/components/visibility-observer.tsx +++ b/web/components/visibility-observer.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react' +import { useEffect, useState } from 'react' import { useEvent } from '../hooks/use-event' export function VisibilityObserver(props: { @@ -8,18 +8,16 @@ export function VisibilityObserver(props: { const { className } = props const [elem, setElem] = useState(null) const onVisibilityUpdated = useEvent(props.onVisibilityUpdated) - const observer = useRef( - new IntersectionObserver(([entry]) => { - onVisibilityUpdated(entry.isIntersecting) - }, {}) - ).current useEffect(() => { if (elem) { + const observer = new IntersectionObserver(([entry]) => { + onVisibilityUpdated(entry.isIntersecting) + }, {}) observer.observe(elem) return () => observer.unobserve(elem) } - }, [elem, observer]) + }, [elem, onVisibilityUpdated]) return
} diff --git a/web/hooks/use-contracts.ts b/web/hooks/use-contracts.ts index 2f3bea7b..1ea2f232 100644 --- a/web/hooks/use-contracts.ts +++ b/web/hooks/use-contracts.ts @@ -1,10 +1,8 @@ import { useFirestoreQueryData } from '@react-query-firebase/firestore' -import { isEqual } from 'lodash' -import { useEffect, useRef, useState } from 'react' +import { useEffect, useState } from 'react' import { Contract, listenForActiveContracts, - listenForContract, listenForContracts, listenForHotContracts, listenForInactiveContracts, @@ -62,39 +60,6 @@ export const useHotContracts = () => { return hotContracts } -export const useUpdatedContracts = (contracts: Contract[] | undefined) => { - const [__, triggerUpdate] = useState(0) - const contractDict = useRef<{ [id: string]: Contract }>({}) - - useEffect(() => { - if (contracts === undefined) return - - contractDict.current = Object.fromEntries(contracts.map((c) => [c.id, c])) - - const disposes = contracts.map((contract) => { - const { id } = contract - - return listenForContract(id, (contract) => { - const curr = contractDict.current[id] - if (!isEqual(curr, contract)) { - contractDict.current[id] = contract as Contract - triggerUpdate((n) => n + 1) - } - }) - }) - - triggerUpdate((n) => n + 1) - - return () => { - disposes.forEach((dispose) => dispose()) - } - }, [!!contracts]) - - return contracts && Object.keys(contractDict.current).length > 0 - ? contracts.map((c) => contractDict.current[c.id]) - : undefined -} - export const usePrefetchUserBetContracts = (userId: string) => { const queryClient = useQueryClient() return queryClient.prefetchQuery( diff --git a/web/pages/experimental/home/edit.tsx b/web/pages/experimental/home/edit.tsx index 2ed9d2dd..8c242a34 100644 --- a/web/pages/experimental/home/edit.tsx +++ b/web/pages/experimental/home/edit.tsx @@ -28,7 +28,7 @@ export default function Home() { - + <Title text="Customize your home page" /> <DoneButton /> </Row> @@ -47,7 +47,11 @@ function DoneButton(props: { className?: string }) { return ( <SiteLink href="/experimental/home"> - <Button size="lg" color="blue" className={clsx(className, 'flex')}> + <Button + size="lg" + color="blue" + className={clsx(className, 'flex whitespace-nowrap')} + > Done </Button> </SiteLink> diff --git a/web/pages/experimental/home/index.tsx b/web/pages/experimental/home/index.tsx index 08f502b6..f5734918 100644 --- a/web/pages/experimental/home/index.tsx +++ b/web/pages/experimental/home/index.tsx @@ -1,7 +1,7 @@ import React from 'react' import Router from 'next/router' import { - PencilIcon, + AdjustmentsIcon, PlusSmIcon, ArrowSmRightIcon, } from '@heroicons/react/solid' @@ -26,11 +26,12 @@ import { Row } from 'web/components/layout/row' import { ProbChangeTable } from 'web/components/contract/prob-change-table' import { groupPath } from 'web/lib/firebase/groups' import { usePortfolioHistory } from 'web/hooks/use-portfolio-history' -import { calculatePortfolioProfit } from 'common/calculate-metrics' import { formatMoney } from 'common/util/format' import { useProbChanges } from 'web/hooks/use-prob-changes' +import { ProfitBadge } from 'web/components/bets-list' +import { calculatePortfolioProfit } from 'common/calculate-metrics' -const Home = () => { +export default function Home() { const user = useUser() useTracking('view home') @@ -44,14 +45,14 @@ const Home = () => { return ( <Page> <Col className="pm:mx-10 gap-4 px-4 pb-12"> - <Row className={'w-full items-center justify-between'}> - <Title className="!mb-0" text="Home" /> - - <EditButton /> + <Row className={'mt-4 w-full items-start justify-between'}> + <Row className="items-end gap-4"> + <Title className="!mb-1 !mt-0" text="Home" /> + <EditButton /> + </Row> + <DailyProfitAndBalance className="" user={user} /> </Row> - <DailyProfitAndBalance userId={user?.id} /> - {sections.map((item) => { const { id } = item if (id === 'daily-movers') { @@ -97,17 +98,10 @@ function SearchSection(props: { followed?: boolean }) { const { label, user, sort, yourBets, followed } = props - const href = `/home?s=${sort}` return ( <Col> - <SiteLink className="mb-2 text-xl" href={href}> - {label}{' '} - <ArrowSmRightIcon - className="mb-0.5 inline h-6 w-6 text-gray-500" - aria-hidden="true" - /> - </SiteLink> + <SectionHeader label={label} href={`/home?s=${sort}`} /> <ContractSearch user={user} defaultSort={sort} @@ -134,13 +128,7 @@ function GroupSection(props: { return ( <Col> - <SiteLink className="mb-2 text-xl" href={groupPath(group.slug)}> - {group.name}{' '} - <ArrowSmRightIcon - className="mb-0.5 inline h-6 w-6 text-gray-500" - aria-hidden="true" - /> - </SiteLink> + <SectionHeader label={group.name} href={groupPath(group.slug)} /> <ContractSearch user={user} defaultSort={'score'} @@ -159,15 +147,25 @@ function DailyMoversSection(props: { userId: string | null | undefined }) { return ( <Col className="gap-2"> - <SiteLink className="text-xl" href={'/daily-movers'}> - Daily movers{' '} + <SectionHeader label="Daily movers" href="daily-movers" /> + <ProbChangeTable changes={changes} /> + </Col> + ) +} + +function SectionHeader(props: { label: string; href: string }) { + const { label, href } = props + + return ( + <Row className="mb-3 items-center justify-between"> + <SiteLink className="text-xl" href={href}> + {label}{' '} <ArrowSmRightIcon className="mb-0.5 inline h-6 w-6 text-gray-500" aria-hidden="true" /> </SiteLink> - <ProbChangeTable changes={changes} /> - </Col> + </Row> ) } @@ -176,45 +174,42 @@ function EditButton(props: { className?: string }) { return ( <SiteLink href="/experimental/home/edit"> - <Button size="lg" color="gray-white" className={clsx(className, 'flex')}> - <PencilIcon className={clsx('mr-2 h-[24px] w-5')} aria-hidden="true" />{' '} - Edit + <Button size="sm" color="gray-white" className={clsx(className, 'flex')}> + <AdjustmentsIcon className={clsx('h-[24px] w-5')} aria-hidden="true" /> </Button> </SiteLink> ) } function DailyProfitAndBalance(props: { - userId: string | null | undefined + user: User | null | undefined className?: string }) { - const { userId, className } = props - const metrics = usePortfolioHistory(userId ?? '', 'daily') ?? [] + const { user, className } = props + const metrics = usePortfolioHistory(user?.id ?? '', 'daily') ?? [] const [first, last] = [metrics[0], metrics[metrics.length - 1]] if (first === undefined || last === undefined) return null const profit = calculatePortfolioProfit(last) - calculatePortfolioProfit(first) - - const balanceChange = last.balance - first.balance + const profitPercent = profit / first.investmentValue return ( - <div className={clsx(className, 'text-lg')}> - <span className={clsx(profit >= 0 ? 'text-green-500' : 'text-red-500')}> - {profit >= 0 && '+'} - {formatMoney(profit)} - </span>{' '} - profit and{' '} - <span - className={clsx(balanceChange >= 0 ? 'text-green-500' : 'text-red-500')} - > - {balanceChange >= 0 && '+'} - {formatMoney(balanceChange)} - </span>{' '} - balance today - </div> + <Row className={'gap-4'}> + <Col> + <div className="text-gray-500">Daily profit</div> + <Row className={clsx(className, 'items-center text-lg')}> + <span>{formatMoney(profit)}</span>{' '} + <ProfitBadge profitPercent={profitPercent * 100} /> + </Row> + </Col> + <Col> + <div className="text-gray-500">Streak</div> + <Row className={clsx(className, 'items-center text-lg')}> + <span>🔥 {user?.currentBettingStreak ?? 0}</span> + </Row> + </Col> + </Row> ) } - -export default Home