Improve Customize UI

This commit is contained in:
James Grugett 2022-09-17 18:30:27 -05:00
parent 438c12da57
commit 1fbadf8181
4 changed files with 105 additions and 46 deletions

View File

@ -1,14 +1,22 @@
import clsx from 'clsx'
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd'
import { MenuIcon } from '@heroicons/react/solid'
import { toast } from 'react-hot-toast'
import { Col } from 'web/components/layout/col'
import { Row } from 'web/components/layout/row'
import { Subtitle } from 'web/components/subtitle'
import { keyBy } from 'lodash'
import { XCircleIcon } from '@heroicons/react/outline'
import { Button } from './button'
import { updateUser } from 'web/lib/firebase/users'
import { leaveGroup } from 'web/lib/firebase/groups'
import { User } from 'common/user'
import { useUser } from 'web/hooks/use-user'
import { Group } from 'common/group'
export function ArrangeHome(props: {
sections: { label: string; id: string }[]
sections: { label: string; id: string; group?: Group }[]
setSectionIds: (sections: string[]) => void
}) {
const { sections, setSectionIds } = props
@ -40,8 +48,9 @@ export function ArrangeHome(props: {
function DraggableList(props: {
title: string
items: { id: string; label: string }[]
items: { id: string; label: string; group?: Group }[]
}) {
const user = useUser()
const { title, items } = props
return (
<Droppable droppableId={title.toLowerCase()}>
@ -66,6 +75,7 @@ function DraggableList(props: {
snapshot.isDragging && 'z-[9000] bg-gray-200'
)}
item={item}
user={user}
/>
</div>
)}
@ -79,23 +89,52 @@ function DraggableList(props: {
}
const SectionItem = (props: {
item: { id: string; label: string }
item: { id: string; label: string; group?: Group }
user: User | null | undefined
className?: string
}) => {
const { item, className } = props
const { item, user, className } = props
const { group } = item
return (
<div
<Row
className={clsx(
className,
'flex flex-row items-center gap-4 rounded bg-gray-50 p-2'
'items-center justify-between 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>
<Row className="items-center gap-4">
<MenuIcon
className="h-5 w-5 flex-shrink-0 text-gray-500"
aria-hidden="true"
/>{' '}
{item.label}
</Row>
{group && (
<Button
color="gray-white"
onClick={() => {
if (user) {
const homeSections = (user.homeSections ?? []).filter(
(id) => id !== group.id
)
updateUser(user.id, { homeSections })
toast.promise(leaveGroup(group, user.id), {
loading: 'Unfollowing group...',
success: `Unfollowed ${group.name}`,
error: "Couldn't unfollow group, try again?",
})
}
}}
>
<XCircleIcon
className={clsx('h-5 w-5 flex-shrink-0')}
aria-hidden="true"
/>
</Button>
)}
</Row>
)
}

View File

@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'
import { Group } from 'common/group'
import { User } from 'common/user'
import {
getGroup,
getMemberGroups,
GroupMemberDoc,
groupMembers,
@ -102,6 +103,21 @@ export const useMemberGroupIds = (user: User | null | undefined) => {
return memberGroupIds
}
export function useMemberGroupsSubscription(user: User | null | undefined) {
const cachedGroups = useMemberGroups(user?.id) ?? []
const groupIds = useMemberGroupIds(user)
const [groups, setGroups] = useState(cachedGroups)
useEffect(() => {
if (groupIds) {
Promise.all(groupIds.map((id) => getGroup(id))).then((groups) =>
setGroups(filterDefined(groups))
)
}
}, [groupIds])
return groups
}
export function useMembers(groupId: string | undefined) {
const [members, setMembers] = useState<User[]>([])
useEffect(() => {

View File

@ -7,12 +7,12 @@ import { Row } from 'web/components/layout/row'
import { Page } from 'web/components/page'
import { SiteLink } from 'web/components/site-link'
import { Title } from 'web/components/title'
import { useMemberGroups } from 'web/hooks/use-group'
import { useMemberGroupsSubscription } from 'web/hooks/use-group'
import { useTracking } from 'web/hooks/use-tracking'
import { useUser } from 'web/hooks/use-user'
import { updateUser } from 'web/lib/firebase/users'
import { track } from 'web/lib/service/analytics'
import { getHomeItems } from '.'
import { getHomeItems, TrendingGroupsSection } from '.'
export default function Home() {
const user = useUser()
@ -27,7 +27,7 @@ export default function Home() {
setHomeSections(newHomeSections)
}
const groups = useMemberGroups(user?.id) ?? []
const groups = useMemberGroupsSubscription(user)
const { sections } = getHomeItems(groups, homeSections)
return (
@ -38,7 +38,15 @@ export default function Home() {
<DoneButton />
</Row>
<ArrangeHome sections={sections} setSectionIds={updateHomeSections} />
<Col className="gap-8 md:flex-row">
<Col className="flex-1">
<ArrangeHome
sections={sections}
setSectionIds={updateHomeSections}
/>
</Col>
<TrendingGroupsSection className="flex-1" user={user} full />
</Col>
</Col>
</Page>
)

View File

@ -1,4 +1,4 @@
import React, { ReactNode, useEffect, useState } from 'react'
import React, { ReactNode } from 'react'
import Router from 'next/router'
import {
AdjustmentsIcon,
@ -22,18 +22,13 @@ import { SiteLink } from 'web/components/site-link'
import { usePrivateUser, useUser } from 'web/hooks/use-user'
import {
useMemberGroupIds,
useMemberGroups,
useMemberGroupsSubscription,
useTrendingGroups,
} from 'web/hooks/use-group'
import { Button } from 'web/components/button'
import { Row } from 'web/components/layout/row'
import { ProbChangeTable } from 'web/components/contract/prob-change-table'
import {
getGroup,
groupPath,
joinGroup,
leaveGroup,
} from 'web/lib/firebase/groups'
import { groupPath, joinGroup, leaveGroup } from 'web/lib/firebase/groups'
import { usePortfolioHistory } from 'web/hooks/use-portfolio-history'
import { formatMoney } from 'common/util/format'
import { useProbChanges } from 'web/hooks/use-prob-changes'
@ -57,17 +52,7 @@ export default function Home() {
useSaveReferral()
usePrefetch(user?.id)
const cachedGroups = useMemberGroups(user?.id) ?? []
const groupIds = useMemberGroupIds(user)
const [groups, setGroups] = useState(cachedGroups)
useEffect(() => {
if (groupIds) {
Promise.all(groupIds.map((id) => getGroup(id))).then((groups) =>
setGroups(filterDefined(groups))
)
}
}, [groupIds])
const groups = useMemberGroupsSubscription(user)
const { sections } = getHomeItems(groups, user?.homeSections ?? [])
@ -77,7 +62,10 @@ export default function Home() {
<Col className="pm:mx-10 gap-4 px-4 pb-12 pt-4 sm:pt-0">
<Row className={'mb-2 w-full items-center justify-between gap-8'}>
<Title className="!mt-0 !mb-0" text="Home" />
<Row className="items-center gap-2">
<Title className="!mt-0 !mb-0" text="Home" />
<CustomizeButton justIcon />
</Row>
<DailyStats user={user} />
</Row>
@ -110,11 +98,12 @@ export const getHomeItems = (groups: Group[], sections: string[]) => {
// Accommodate old home sections.
if (!isArray(sections)) sections = []
const items = [
const items: { id: string; label: string; group?: Group }[] = [
...HOME_SECTIONS,
...groups.map((g) => ({
label: g.name,
id: g.id,
group: g,
})),
]
const itemsById = keyBy(items, 'id')
@ -225,7 +214,6 @@ function GroupSection(props: {
<Col>
<SectionHeader label={group.name} href={groupPath(group.slug)}>
<Button
className=""
color="gray-white"
onClick={() => {
if (user) {
@ -312,20 +300,24 @@ function DailyStats(props: {
)
}
function TrendingGroupsSection(props: { user: User | null | undefined }) {
const { user } = props
export function TrendingGroupsSection(props: {
user: User | null | undefined
full?: boolean
className?: string
}) {
const { user, full, className } = props
const memberGroupIds = useMemberGroupIds(user) || []
const groups = useTrendingGroups().filter(
(g) => !memberGroupIds.includes(g.id)
)
const count = 25
const count = full ? 100 : 25
const chosenGroups = groups.slice(0, count)
return (
<Col>
<Col className={className}>
<SectionHeader label="Trending groups" href="/explore-groups">
<CustomizeButton />
{!full && <CustomizeButton className="mb-1" />}
</SectionHeader>
<Row className="flex-wrap gap-2">
{chosenGroups.map((g) => (
@ -359,10 +351,14 @@ function TrendingGroupsSection(props: { user: User | null | undefined }) {
)
}
function CustomizeButton() {
function CustomizeButton(props: { justIcon?: boolean; className?: string }) {
const { justIcon, className } = props
return (
<SiteLink
className="mb-2 flex flex-row items-center text-xl hover:no-underline"
className={clsx(
className,
'flex flex-row items-center text-xl hover:no-underline'
)}
href="/home/edit"
>
<Button size="lg" color="gray" className={clsx('flex gap-2')}>
@ -370,7 +366,7 @@ function CustomizeButton() {
className={clsx('h-[24px] w-5 text-gray-500')}
aria-hidden="true"
/>
Customize
{!justIcon && 'Customize'}
</Button>
</SiteLink>
)