Small updates to experimental home (#870)
* Line clamp question in prob change table * Tweaks * Expand option for daily movers * Snap scrolling for carousel * Add arrows to section headers * Remove carousel from experimental/home * React querify fetching your groups * Edit home is its own page * Add daily profit and balance * Merge branch 'main' into new-home * Make experimental search by your followed groups/creators * Just submit, allow xs on pills * Weigh in * Use next/future/image component to optimize avatar images * Inga/challenge icon (#857) * changed challenge icon to custom icon * fixed tip button alignment * weighing in and trading "weigh in" for "trade" * Delete closing soon, mark new as New for you, trending is site-wide * Delete your trades. Factor out section item * Don't allow hiding of home sections * Convert daily movers into a section * Tweaks for loading daily movers * Prob change table shows variable number of rows * Fix double negative Co-authored-by: Ian Philips <iansphilips@gmail.com> Co-authored-by: Austin Chen <akrolsmir@gmail.com> Co-authored-by: ingawei <46611122+ingawei@users.noreply.github.com> Co-authored-by: mantikoros <sgrugett@gmail.com>
This commit is contained in:
parent
f8d346a404
commit
c1287a4a25
|
@ -34,7 +34,7 @@ export type User = {
|
|||
followerCountCached: number
|
||||
|
||||
followedCategories?: string[]
|
||||
homeSections?: { visible: string[]; hidden: string[] }
|
||||
homeSections?: string[]
|
||||
|
||||
referredByUserId?: string
|
||||
referredByContractId?: string
|
||||
|
|
|
@ -13,19 +13,13 @@ import { Group } from 'common/group'
|
|||
|
||||
export function ArrangeHome(props: {
|
||||
user: User | null | undefined
|
||||
homeSections: { visible: string[]; hidden: string[] }
|
||||
setHomeSections: (homeSections: {
|
||||
visible: string[]
|
||||
hidden: string[]
|
||||
}) => void
|
||||
homeSections: string[]
|
||||
setHomeSections: (sections: string[]) => void
|
||||
}) {
|
||||
const { user, homeSections, setHomeSections } = props
|
||||
|
||||
const groups = useMemberGroups(user?.id) ?? []
|
||||
const { itemsById, visibleItems, hiddenItems } = getHomeItems(
|
||||
groups,
|
||||
homeSections
|
||||
)
|
||||
const { itemsById, sections } = getHomeItems(groups, homeSections)
|
||||
|
||||
return (
|
||||
<DragDropContext
|
||||
|
@ -35,23 +29,16 @@ export function ArrangeHome(props: {
|
|||
|
||||
const item = itemsById[draggableId]
|
||||
|
||||
const newHomeSections = {
|
||||
visible: visibleItems.map((item) => item.id),
|
||||
hidden: hiddenItems.map((item) => item.id),
|
||||
}
|
||||
const newHomeSections = sections.map((section) => section.id)
|
||||
|
||||
const sourceSection = source.droppableId as 'visible' | 'hidden'
|
||||
newHomeSections[sourceSection].splice(source.index, 1)
|
||||
|
||||
const destSection = destination.droppableId as 'visible' | 'hidden'
|
||||
newHomeSections[destSection].splice(destination.index, 0, item.id)
|
||||
newHomeSections.splice(source.index, 1)
|
||||
newHomeSections.splice(destination.index, 0, item.id)
|
||||
|
||||
setHomeSections(newHomeSections)
|
||||
}}
|
||||
>
|
||||
<Row className="relative max-w-lg gap-4">
|
||||
<DraggableList items={visibleItems} title="Visible" />
|
||||
<DraggableList items={hiddenItems} title="Hidden" />
|
||||
<Row className="relative max-w-md gap-4">
|
||||
<DraggableList items={sections} title="Sections" />
|
||||
</Row>
|
||||
</DragDropContext>
|
||||
)
|
||||
|
@ -64,16 +51,13 @@ function DraggableList(props: {
|
|||
const { title, items } = props
|
||||
return (
|
||||
<Droppable droppableId={title.toLowerCase()}>
|
||||
{(provided, snapshot) => (
|
||||
{(provided) => (
|
||||
<Col
|
||||
{...provided.droppableProps}
|
||||
ref={provided.innerRef}
|
||||
className={clsx(
|
||||
'width-[220px] flex-1 items-start rounded bg-gray-50 p-2',
|
||||
snapshot.isDraggingOver && 'bg-gray-100'
|
||||
)}
|
||||
className={clsx('flex-1 items-stretch gap-1 rounded bg-gray-100 p-4')}
|
||||
>
|
||||
<Subtitle text={title} className="mx-2 !my-2" />
|
||||
<Subtitle text={title} className="mx-2 !mt-0 !mb-4" />
|
||||
{items.map((item, index) => (
|
||||
<Draggable key={item.id} draggableId={item.id} index={index}>
|
||||
{(provided, snapshot) => (
|
||||
|
@ -82,16 +66,13 @@ function DraggableList(props: {
|
|||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
style={provided.draggableProps.style}
|
||||
className={clsx(
|
||||
'flex flex-row items-center gap-4 rounded bg-gray-50 p-2',
|
||||
snapshot.isDragging && 'z-[9000] bg-gray-300'
|
||||
)}
|
||||
>
|
||||
<MenuIcon
|
||||
className="h-5 w-5 flex-shrink-0 text-gray-500"
|
||||
aria-hidden="true"
|
||||
/>{' '}
|
||||
{item.label}
|
||||
<SectionItem
|
||||
className={clsx(
|
||||
snapshot.isDragging && 'z-[9000] bg-gray-200'
|
||||
)}
|
||||
item={item}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
|
@ -103,15 +84,33 @@ function DraggableList(props: {
|
|||
)
|
||||
}
|
||||
|
||||
export const getHomeItems = (
|
||||
groups: Group[],
|
||||
homeSections: { visible: string[]; hidden: string[] }
|
||||
) => {
|
||||
const SectionItem = (props: {
|
||||
item: { id: string; label: string }
|
||||
className?: string
|
||||
}) => {
|
||||
const { item, className } = props
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
className,
|
||||
'flex flex-row items-center gap-4 rounded bg-gray-50 p-2'
|
||||
)}
|
||||
>
|
||||
<MenuIcon
|
||||
className="h-5 w-5 flex-shrink-0 text-gray-500"
|
||||
aria-hidden="true"
|
||||
/>{' '}
|
||||
{item.label}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const getHomeItems = (groups: Group[], sections: string[]) => {
|
||||
const items = [
|
||||
{ label: 'Daily movers', id: 'daily-movers' },
|
||||
{ label: 'Trending', id: 'score' },
|
||||
{ label: 'Newest', id: 'newest' },
|
||||
{ label: 'Close date', id: 'close-date' },
|
||||
{ label: 'Your trades', id: 'your-bets' },
|
||||
{ label: 'New for you', id: 'newest' },
|
||||
...groups.map((g) => ({
|
||||
label: g.name,
|
||||
id: g.id,
|
||||
|
@ -119,23 +118,13 @@ export const getHomeItems = (
|
|||
]
|
||||
const itemsById = keyBy(items, 'id')
|
||||
|
||||
const { visible, hidden } = homeSections
|
||||
const sectionItems = filterDefined(sections.map((id) => itemsById[id]))
|
||||
|
||||
const [visibleItems, hiddenItems] = [
|
||||
filterDefined(visible.map((id) => itemsById[id])),
|
||||
filterDefined(hidden.map((id) => itemsById[id])),
|
||||
]
|
||||
|
||||
// Add unmentioned items to the visible list.
|
||||
visibleItems.push(
|
||||
...items.filter(
|
||||
(item) => !visibleItems.includes(item) && !hiddenItems.includes(item)
|
||||
)
|
||||
)
|
||||
// Add unmentioned items to the end.
|
||||
sectionItems.push(...items.filter((item) => !sectionItems.includes(item)))
|
||||
|
||||
return {
|
||||
visibleItems,
|
||||
hiddenItems,
|
||||
sections: sectionItems,
|
||||
itemsById,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,74 +2,69 @@ import clsx from 'clsx'
|
|||
import { contractPath } from 'web/lib/firebase/contracts'
|
||||
import { CPMMContract } from 'common/contract'
|
||||
import { formatPercent } from 'common/util/format'
|
||||
import { useProbChanges } from 'web/hooks/use-prob-changes'
|
||||
import { linkClass, SiteLink } from '../site-link'
|
||||
import { SiteLink } from '../site-link'
|
||||
import { Col } from '../layout/col'
|
||||
import { Row } from '../layout/row'
|
||||
import { useState } from 'react'
|
||||
import { LoadingIndicator } from '../loading-indicator'
|
||||
|
||||
export function ProbChangeTable(props: { userId: string | undefined }) {
|
||||
const { userId } = props
|
||||
export function ProbChangeTable(props: {
|
||||
changes:
|
||||
| { positiveChanges: CPMMContract[]; negativeChanges: CPMMContract[] }
|
||||
| undefined
|
||||
}) {
|
||||
const { changes } = props
|
||||
|
||||
const changes = useProbChanges(userId ?? '')
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
|
||||
if (!changes) {
|
||||
return null
|
||||
}
|
||||
|
||||
const count = expanded ? 16 : 4
|
||||
if (!changes) return <LoadingIndicator />
|
||||
|
||||
const { positiveChanges, negativeChanges } = changes
|
||||
const filteredPositiveChanges = positiveChanges.slice(0, count / 2)
|
||||
const filteredNegativeChanges = negativeChanges.slice(0, count / 2)
|
||||
const filteredChanges = [
|
||||
...filteredPositiveChanges,
|
||||
...filteredNegativeChanges,
|
||||
]
|
||||
|
||||
const threshold = 0.075
|
||||
const countOverThreshold = Math.max(
|
||||
positiveChanges.findIndex((c) => c.probChanges.day < threshold) + 1,
|
||||
negativeChanges.findIndex((c) => c.probChanges.day > -threshold) + 1
|
||||
)
|
||||
const maxRows = Math.min(positiveChanges.length, negativeChanges.length)
|
||||
const rows = Math.min(3, Math.min(maxRows, countOverThreshold))
|
||||
|
||||
const filteredPositiveChanges = positiveChanges.slice(0, rows)
|
||||
const filteredNegativeChanges = negativeChanges.slice(0, rows)
|
||||
|
||||
if (rows === 0) return <div className="px-4 text-gray-500">None</div>
|
||||
|
||||
return (
|
||||
<Col>
|
||||
<Col className="mb-4 w-full divide-x-2 divide-y rounded-lg bg-white shadow-md md:flex-row md:divide-y-0">
|
||||
<Col className="flex-1 divide-y">
|
||||
{filteredChanges.slice(0, count / 2).map((contract) => (
|
||||
<Row className="items-center hover:bg-gray-100">
|
||||
<ProbChange
|
||||
className="p-4 text-right text-xl"
|
||||
contract={contract}
|
||||
/>
|
||||
<SiteLink
|
||||
className="p-4 pl-2 font-semibold text-indigo-700"
|
||||
href={contractPath(contract)}
|
||||
>
|
||||
<span className="line-clamp-2">{contract.question}</span>
|
||||
</SiteLink>
|
||||
</Row>
|
||||
))}
|
||||
</Col>
|
||||
<Col className="flex-1 divide-y">
|
||||
{filteredChanges.slice(count / 2).map((contract) => (
|
||||
<Row className="items-center hover:bg-gray-100">
|
||||
<ProbChange
|
||||
className="p-4 text-right text-xl"
|
||||
contract={contract}
|
||||
/>
|
||||
<SiteLink
|
||||
className="p-4 pl-2 font-semibold text-indigo-700"
|
||||
href={contractPath(contract)}
|
||||
>
|
||||
<span className="line-clamp-2">{contract.question}</span>
|
||||
</SiteLink>
|
||||
</Row>
|
||||
))}
|
||||
</Col>
|
||||
<Col className="mb-4 w-full divide-x-2 divide-y rounded-lg bg-white shadow-md md:flex-row md:divide-y-0">
|
||||
<Col className="flex-1 divide-y">
|
||||
{filteredPositiveChanges.map((contract) => (
|
||||
<Row className="items-center hover:bg-gray-100">
|
||||
<ProbChange
|
||||
className="p-4 text-right text-xl"
|
||||
contract={contract}
|
||||
/>
|
||||
<SiteLink
|
||||
className="p-4 pl-2 font-semibold text-indigo-700"
|
||||
href={contractPath(contract)}
|
||||
>
|
||||
<span className="line-clamp-2">{contract.question}</span>
|
||||
</SiteLink>
|
||||
</Row>
|
||||
))}
|
||||
</Col>
|
||||
<Col className="flex-1 divide-y">
|
||||
{filteredNegativeChanges.map((contract) => (
|
||||
<Row className="items-center hover:bg-gray-100">
|
||||
<ProbChange
|
||||
className="p-4 text-right text-xl"
|
||||
contract={contract}
|
||||
/>
|
||||
<SiteLink
|
||||
className="p-4 pl-2 font-semibold text-indigo-700"
|
||||
href={contractPath(contract)}
|
||||
>
|
||||
<span className="line-clamp-2">{contract.question}</span>
|
||||
</SiteLink>
|
||||
</Row>
|
||||
))}
|
||||
</Col>
|
||||
<div
|
||||
className={clsx(linkClass, 'cursor-pointer self-end')}
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
>
|
||||
{expanded ? 'Show less' : 'Show more'}
|
||||
</div>
|
||||
</Col>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -16,14 +16,9 @@ export default function Home() {
|
|||
|
||||
useTracking('edit home')
|
||||
|
||||
const [homeSections, setHomeSections] = useState(
|
||||
user?.homeSections ?? { visible: [], hidden: [] }
|
||||
)
|
||||
const [homeSections, setHomeSections] = useState(user?.homeSections ?? [])
|
||||
|
||||
const updateHomeSections = (newHomeSections: {
|
||||
visible: string[]
|
||||
hidden: string[]
|
||||
}) => {
|
||||
const updateHomeSections = (newHomeSections: string[]) => {
|
||||
if (!user) return
|
||||
updateUser(user.id, { homeSections: newHomeSections })
|
||||
setHomeSections(newHomeSections)
|
||||
|
@ -31,7 +26,7 @@ export default function Home() {
|
|||
|
||||
return (
|
||||
<Page>
|
||||
<Col className="pm:mx-10 gap-4 px-4 pb-12">
|
||||
<Col className="pm:mx-10 gap-4 px-4 pb-6 pt-2">
|
||||
<Row className={'w-full items-center justify-between'}>
|
||||
<Title text="Edit your home page" />
|
||||
<DoneButton />
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from 'react'
|
||||
import React from 'react'
|
||||
import Router from 'next/router'
|
||||
import {
|
||||
PencilIcon,
|
||||
|
@ -28,6 +28,7 @@ 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'
|
||||
|
||||
const Home = () => {
|
||||
const user = useUser()
|
||||
|
@ -38,10 +39,7 @@ const Home = () => {
|
|||
|
||||
const groups = useMemberGroups(user?.id) ?? []
|
||||
|
||||
const [homeSections] = useState(
|
||||
user?.homeSections ?? { visible: [], hidden: [] }
|
||||
)
|
||||
const { visibleItems } = getHomeItems(groups, homeSections)
|
||||
const { sections } = getHomeItems(groups, user?.homeSections ?? [])
|
||||
|
||||
return (
|
||||
<Page>
|
||||
|
@ -54,29 +52,19 @@ const Home = () => {
|
|||
|
||||
<DailyProfitAndBalance userId={user?.id} />
|
||||
|
||||
<div className="text-xl text-gray-800">Daily movers</div>
|
||||
<ProbChangeTable userId={user?.id} />
|
||||
|
||||
{visibleItems.map((item) => {
|
||||
{sections.map((item) => {
|
||||
const { id } = item
|
||||
if (id === 'your-bets') {
|
||||
return (
|
||||
<SearchSection
|
||||
key={id}
|
||||
label={'Your trades'}
|
||||
sort={'newest'}
|
||||
user={user}
|
||||
yourBets
|
||||
/>
|
||||
)
|
||||
if (id === 'daily-movers') {
|
||||
return <DailyMoversSection key={id} userId={user?.id} />
|
||||
}
|
||||
const sort = SORTS.find((sort) => sort.value === id)
|
||||
if (sort)
|
||||
return (
|
||||
<SearchSection
|
||||
key={id}
|
||||
label={sort.label}
|
||||
label={sort.value === 'newest' ? 'New for you' : sort.label}
|
||||
sort={sort.value}
|
||||
followed={sort.value === 'newest'}
|
||||
user={user}
|
||||
/>
|
||||
)
|
||||
|
@ -103,11 +91,12 @@ const Home = () => {
|
|||
|
||||
function SearchSection(props: {
|
||||
label: string
|
||||
user: User | null | undefined
|
||||
user: User | null | undefined | undefined
|
||||
sort: Sort
|
||||
yourBets?: boolean
|
||||
followed?: boolean
|
||||
}) {
|
||||
const { label, user, sort, yourBets } = props
|
||||
const { label, user, sort, yourBets, followed } = props
|
||||
const href = `/home?s=${sort}`
|
||||
|
||||
return (
|
||||
|
@ -122,7 +111,13 @@ function SearchSection(props: {
|
|||
<ContractSearch
|
||||
user={user}
|
||||
defaultSort={sort}
|
||||
additionalFilter={yourBets ? { yourBets: true } : { followed: true }}
|
||||
additionalFilter={
|
||||
yourBets
|
||||
? { yourBets: true }
|
||||
: followed
|
||||
? { followed: true }
|
||||
: undefined
|
||||
}
|
||||
noControls
|
||||
maxResults={6}
|
||||
persistPrefix={`experimental-home-${sort}`}
|
||||
|
@ -131,7 +126,10 @@ function SearchSection(props: {
|
|||
)
|
||||
}
|
||||
|
||||
function GroupSection(props: { group: Group; user: User | null | undefined }) {
|
||||
function GroupSection(props: {
|
||||
group: Group
|
||||
user: User | null | undefined | undefined
|
||||
}) {
|
||||
const { group, user } = props
|
||||
|
||||
return (
|
||||
|
@ -155,6 +153,24 @@ function GroupSection(props: { group: Group; user: User | null | undefined }) {
|
|||
)
|
||||
}
|
||||
|
||||
function DailyMoversSection(props: { userId: string | null | undefined }) {
|
||||
const { userId } = props
|
||||
const changes = useProbChanges(userId ?? '')
|
||||
|
||||
return (
|
||||
<Col className="gap-2">
|
||||
<SiteLink className="text-xl" href={'/daily-movers'}>
|
||||
Daily movers{' '}
|
||||
<ArrowSmRightIcon
|
||||
className="mb-0.5 inline h-6 w-6 text-gray-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</SiteLink>
|
||||
<ProbChangeTable changes={changes} />
|
||||
</Col>
|
||||
)
|
||||
}
|
||||
|
||||
function EditButton(props: { className?: string }) {
|
||||
const { className } = props
|
||||
|
||||
|
@ -186,14 +202,14 @@ function DailyProfitAndBalance(props: {
|
|||
return (
|
||||
<div className={clsx(className, 'text-lg')}>
|
||||
<span className={clsx(profit >= 0 ? 'text-green-500' : 'text-red-500')}>
|
||||
{profit >= 0 ? '+' : '-'}
|
||||
{profit >= 0 && '+'}
|
||||
{formatMoney(profit)}
|
||||
</span>{' '}
|
||||
profit and{' '}
|
||||
<span
|
||||
className={clsx(balanceChange >= 0 ? 'text-green-500' : 'text-red-500')}
|
||||
>
|
||||
{balanceChange >= 0 ? '+' : '-'}
|
||||
{balanceChange >= 0 && '+'}
|
||||
{formatMoney(balanceChange)}
|
||||
</span>{' '}
|
||||
balance today
|
||||
|
|
Loading…
Reference in New Issue
Block a user