mirror of
https://github.com/spliit-app/spliit.git
synced 2026-02-24 16:36:12 +01:00
* Use tRPC for recent groups page * Use tRPC for adding group by URL * Use tRPC for saving visited group * Group context
245 lines
6.2 KiB
TypeScript
245 lines
6.2 KiB
TypeScript
'use client'
|
|
import { AddGroupByUrlButton } from '@/app/groups/add-group-by-url-button'
|
|
import {
|
|
RecentGroups,
|
|
getArchivedGroups,
|
|
getRecentGroups,
|
|
getStarredGroups,
|
|
} from '@/app/groups/recent-groups-helpers'
|
|
import { Button } from '@/components/ui/button'
|
|
import { getGroups } from '@/lib/api'
|
|
import { trpc } from '@/trpc/client'
|
|
import { AppRouterOutput } from '@/trpc/routers/_app'
|
|
import { Loader2 } from 'lucide-react'
|
|
import { useTranslations } from 'next-intl'
|
|
import Link from 'next/link'
|
|
import { PropsWithChildren, useEffect, useState } from 'react'
|
|
import { RecentGroupListCard } from './recent-group-list-card'
|
|
|
|
export type RecentGroupsState =
|
|
| { status: 'pending' }
|
|
| {
|
|
status: 'partial'
|
|
groups: RecentGroups
|
|
starredGroups: string[]
|
|
archivedGroups: string[]
|
|
}
|
|
| {
|
|
status: 'complete'
|
|
groups: RecentGroups
|
|
groupsDetails: Awaited<ReturnType<typeof getGroups>>
|
|
starredGroups: string[]
|
|
archivedGroups: string[]
|
|
}
|
|
|
|
function sortGroups({
|
|
groups,
|
|
starredGroups,
|
|
archivedGroups,
|
|
}: {
|
|
groups: RecentGroups
|
|
starredGroups: string[]
|
|
archivedGroups: string[]
|
|
}) {
|
|
const starredGroupInfo = []
|
|
const groupInfo = []
|
|
const archivedGroupInfo = []
|
|
for (const group of groups) {
|
|
if (starredGroups.includes(group.id)) {
|
|
starredGroupInfo.push(group)
|
|
} else if (archivedGroups.includes(group.id)) {
|
|
archivedGroupInfo.push(group)
|
|
} else {
|
|
groupInfo.push(group)
|
|
}
|
|
}
|
|
return {
|
|
starredGroupInfo,
|
|
groupInfo,
|
|
archivedGroupInfo,
|
|
}
|
|
}
|
|
|
|
export function RecentGroupList() {
|
|
const [state, setState] = useState<RecentGroupsState>({ status: 'pending' })
|
|
|
|
function loadGroups() {
|
|
const groupsInStorage = getRecentGroups()
|
|
const starredGroups = getStarredGroups()
|
|
const archivedGroups = getArchivedGroups()
|
|
setState({
|
|
status: 'partial',
|
|
groups: groupsInStorage,
|
|
starredGroups,
|
|
archivedGroups,
|
|
})
|
|
}
|
|
|
|
useEffect(() => {
|
|
loadGroups()
|
|
}, [])
|
|
|
|
if (state.status === 'pending') return null
|
|
|
|
return (
|
|
<RecentGroupList_
|
|
groups={state.groups}
|
|
starredGroups={state.starredGroups}
|
|
archivedGroups={state.archivedGroups}
|
|
refreshGroupsFromStorage={() => loadGroups()}
|
|
/>
|
|
)
|
|
}
|
|
|
|
function RecentGroupList_({
|
|
groups,
|
|
starredGroups,
|
|
archivedGroups,
|
|
refreshGroupsFromStorage,
|
|
}: {
|
|
groups: RecentGroups
|
|
starredGroups: string[]
|
|
archivedGroups: string[]
|
|
refreshGroupsFromStorage: () => void
|
|
}) {
|
|
const t = useTranslations('Groups')
|
|
const { data, isLoading } = trpc.groups.list.useQuery({
|
|
groupIds: groups.map((group) => group.id),
|
|
})
|
|
|
|
if (isLoading || !data) {
|
|
return (
|
|
<GroupsPage reload={refreshGroupsFromStorage}>
|
|
<p>
|
|
<Loader2 className="w-4 m-4 mr-2 inline animate-spin" />{' '}
|
|
{t('loadingRecent')}
|
|
</p>
|
|
</GroupsPage>
|
|
)
|
|
}
|
|
|
|
if (data.groups.length === 0) {
|
|
return (
|
|
<GroupsPage reload={refreshGroupsFromStorage}>
|
|
<div className="text-sm space-y-2">
|
|
<p>{t('NoRecent.description')}</p>
|
|
<p>
|
|
<Button variant="link" asChild className="-m-4">
|
|
<Link href={`/groups/create`}>{t('NoRecent.create')}</Link>
|
|
</Button>{' '}
|
|
{t('NoRecent.orAsk')}
|
|
</p>
|
|
</div>
|
|
</GroupsPage>
|
|
)
|
|
}
|
|
|
|
const { starredGroupInfo, groupInfo, archivedGroupInfo } = sortGroups({
|
|
groups,
|
|
starredGroups,
|
|
archivedGroups,
|
|
})
|
|
|
|
return (
|
|
<GroupsPage reload={refreshGroupsFromStorage}>
|
|
{starredGroupInfo.length > 0 && (
|
|
<>
|
|
<h2 className="mb-2">{t('starred')}</h2>
|
|
<GroupList
|
|
groups={starredGroupInfo}
|
|
groupDetails={data.groups}
|
|
archivedGroups={archivedGroups}
|
|
starredGroups={starredGroups}
|
|
refreshGroupsFromStorage={refreshGroupsFromStorage}
|
|
/>
|
|
</>
|
|
)}
|
|
|
|
{groupInfo.length > 0 && (
|
|
<>
|
|
<h2 className="mt-6 mb-2">{t('recent')}</h2>
|
|
<GroupList
|
|
groups={groupInfo}
|
|
groupDetails={data.groups}
|
|
archivedGroups={archivedGroups}
|
|
starredGroups={starredGroups}
|
|
refreshGroupsFromStorage={refreshGroupsFromStorage}
|
|
/>
|
|
</>
|
|
)}
|
|
|
|
{archivedGroupInfo.length > 0 && (
|
|
<>
|
|
<h2 className="mt-6 mb-2 opacity-50">{t('archived')}</h2>
|
|
<div className="opacity-50">
|
|
<GroupList
|
|
groups={archivedGroupInfo}
|
|
groupDetails={data.groups}
|
|
archivedGroups={archivedGroups}
|
|
starredGroups={starredGroups}
|
|
refreshGroupsFromStorage={refreshGroupsFromStorage}
|
|
/>
|
|
</div>
|
|
</>
|
|
)}
|
|
</GroupsPage>
|
|
)
|
|
}
|
|
|
|
function GroupList({
|
|
groups,
|
|
groupDetails,
|
|
starredGroups,
|
|
archivedGroups,
|
|
refreshGroupsFromStorage,
|
|
}: {
|
|
groups: RecentGroups
|
|
groupDetails?: AppRouterOutput['groups']['list']['groups']
|
|
starredGroups: string[]
|
|
archivedGroups: string[]
|
|
refreshGroupsFromStorage: () => void
|
|
}) {
|
|
return (
|
|
<ul className="grid gap-2 sm:grid-cols-2">
|
|
{groups.map((group) => (
|
|
<RecentGroupListCard
|
|
key={group.id}
|
|
group={group}
|
|
groupDetail={groupDetails?.find(
|
|
(groupDetail) => groupDetail.id === group.id,
|
|
)}
|
|
isStarred={starredGroups.includes(group.id)}
|
|
isArchived={archivedGroups.includes(group.id)}
|
|
refreshGroupsFromStorage={refreshGroupsFromStorage}
|
|
/>
|
|
))}
|
|
</ul>
|
|
)
|
|
}
|
|
|
|
function GroupsPage({
|
|
children,
|
|
reload,
|
|
}: PropsWithChildren<{ reload: () => void }>) {
|
|
const t = useTranslations('Groups')
|
|
return (
|
|
<>
|
|
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-2">
|
|
<h1 className="font-bold text-2xl flex-1">
|
|
<Link href="/groups">{t('myGroups')}</Link>
|
|
</h1>
|
|
<div className="flex gap-2">
|
|
<AddGroupByUrlButton reload={reload} />
|
|
<Button asChild>
|
|
<Link href="/groups/create">
|
|
{/* <Plus className="w-4 h-4 mr-2" /> */}
|
|
{t('create')}
|
|
</Link>
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<div>{children}</div>
|
|
</>
|
|
)
|
|
}
|