Skip to content

Commit

Permalink
Use tRPC for recent groups page (#253)
Browse files Browse the repository at this point in the history
* Use tRPC for recent groups page

* Use tRPC for adding group by URL

* Use tRPC for saving visited group

* Group context
  • Loading branch information
scastiel authored Oct 20, 2024
1 parent 39c1a2f commit db03fe4
Show file tree
Hide file tree
Showing 31 changed files with 314 additions and 262 deletions.
11 changes: 5 additions & 6 deletions src/app/groups/[groupId]/activity/activity-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import dayjs, { type Dayjs } from 'dayjs'
import { useTranslations } from 'next-intl'
import { forwardRef, useEffect } from 'react'
import { useInView } from 'react-intersection-observer'
import { useCurrentGroup } from '../current-group-context'

const PAGE_SIZE = 20

Expand Down Expand Up @@ -82,11 +83,9 @@ const ActivitiesLoading = forwardRef<HTMLDivElement>((_, ref) => {
})
ActivitiesLoading.displayName = 'ActivitiesLoading'

export function ActivityList({ groupId }: { groupId: string }) {
export function ActivityList() {
const t = useTranslations('Activity')

const { data: groupData, isLoading: groupIsLoading } =
trpc.groups.get.useQuery({ groupId })
const { group, groupId } = useCurrentGroup()

const {
data: activitiesData,
Expand All @@ -105,7 +104,7 @@ export function ActivityList({ groupId }: { groupId: string }) {
if (inView && hasMore && !isLoading) fetchNextPage()
}, [fetchNextPage, hasMore, inView, isLoading])

if (isLoading || !activities || !groupData) return <ActivitiesLoading />
if (isLoading || !activities || !group) return <ActivitiesLoading />

const groupedActivitiesByDate = getGroupedActivitiesByDate(activities)

Expand All @@ -131,7 +130,7 @@ export function ActivityList({ groupId }: { groupId: string }) {
{groupActivities.map((activity) => {
const participant =
activity.participantId !== null
? groupData.group.participants.find(
? group.participants.find(
(p) => p.id === activity.participantId,
)
: undefined
Expand Down
4 changes: 2 additions & 2 deletions src/app/groups/[groupId]/activity/page.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const metadata: Metadata = {
title: 'Activity',
}

export function ActivityPageClient({ groupId }: { groupId: string }) {
export function ActivityPageClient() {
const t = useTranslations('Activity')

return (
Expand All @@ -24,7 +24,7 @@ export function ActivityPageClient({ groupId }: { groupId: string }) {
<CardDescription>{t('description')}</CardDescription>
</CardHeader>
<CardContent className="flex flex-col space-y-4">
<ActivityList groupId={groupId} />
<ActivityList />
</CardContent>
</Card>
</>
Expand Down
8 changes: 2 additions & 6 deletions src/app/groups/[groupId]/activity/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ export const metadata: Metadata = {
title: 'Activity',
}

export default async function ActivityPage({
params: { groupId },
}: {
params: { groupId: string }
}) {
return <ActivityPageClient groupId={groupId} />
export default async function ActivityPage() {
return <ActivityPageClient />
}
44 changes: 20 additions & 24 deletions src/app/groups/[groupId]/balances/balances-and-reimbursements.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,12 @@ import { Skeleton } from '@/components/ui/skeleton'
import { trpc } from '@/trpc/client'
import { useTranslations } from 'next-intl'
import { Fragment, useEffect } from 'react'
import { match } from 'ts-pattern'
import { useCurrentGroup } from '../current-group-context'

export default function BalancesAndReimbursements({
groupId,
}: {
groupId: string
}) {
export default function BalancesAndReimbursements() {
const utils = trpc.useUtils()
const { data: groupData, isLoading: groupIsLoading } =
trpc.groups.get.useQuery({ groupId })
const { groupId, group } = useCurrentGroup()
const { data: balancesData, isLoading: balancesAreLoading } =
trpc.groups.balances.list.useQuery({
groupId,
Expand All @@ -34,8 +31,7 @@ export default function BalancesAndReimbursements({
utils.groups.balances.invalidate()
}, [utils])

const isLoading =
balancesAreLoading || !balancesData || groupIsLoading || !groupData?.group
const isLoading = balancesAreLoading || !balancesData || !group

return (
<>
Expand All @@ -46,14 +42,12 @@ export default function BalancesAndReimbursements({
</CardHeader>
<CardContent>
{isLoading ? (
<BalancesLoading
participantCount={groupData?.group.participants.length}
/>
<BalancesLoading participantCount={group?.participants.length} />
) : (
<BalancesList
balances={balancesData.balances}
participants={groupData.group.participants}
currency={groupData.group.currency}
participants={group?.participants}
currency={group?.currency}
/>
)}
</CardContent>
Expand All @@ -66,14 +60,14 @@ export default function BalancesAndReimbursements({
<CardContent>
{isLoading ? (
<ReimbursementsLoading
participantCount={groupData?.group.participants.length}
participantCount={group?.participants.length}
/>
) : (
<ReimbursementList
reimbursements={balancesData.reimbursements}
participants={groupData.group.participants}
currency={groupData.group.currency}
groupId={groupData.group.id}
participants={group?.participants}
currency={group?.currency}
groupId={groupId}
/>
)}
</CardContent>
Expand Down Expand Up @@ -109,6 +103,12 @@ const BalancesLoading = ({
}: {
participantCount?: number
}) => {
const barWidth = (index: number) =>
match(index % 3)
.with(0, () => 'w-1/3')
.with(1, () => 'w-2/3')
.otherwise(() => 'w-full')

return (
<div className="grid grid-cols-2 py-1 gap-y-2">
{Array(participantCount)
Expand All @@ -120,17 +120,13 @@ const BalancesLoading = ({
<Skeleton className="h-3 w-16" />
</div>
<div className="self-start">
<Skeleton
className={`h-7 w-${(index % 3) + 1}/3 rounded-l-none`}
/>
<Skeleton className={`h-7 ${barWidth(index)} rounded-l-none`} />
</div>
</Fragment>
) : (
<Fragment key={index}>
<div className="flex items-center justify-end">
<Skeleton
className={`h-7 w-${(index % 3) + 1}/3 rounded-r-none`}
/>
<Skeleton className={`h-7 ${barWidth(index)} rounded-r-none`} />
</div>
<div className="flex items-center pl-2">
<Skeleton className="h-3 w-16" />
Expand Down
13 changes: 2 additions & 11 deletions src/app/groups/[groupId]/balances/page.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
import { cached } from '@/app/cached-functions'
import BalancesAndReimbursements from '@/app/groups/[groupId]/balances/balances-and-reimbursements'
import { Metadata } from 'next'
import { notFound } from 'next/navigation'

export const metadata: Metadata = {
title: 'Balances',
}

export default async function GroupPage({
params: { groupId },
}: {
params: { groupId: string }
}) {
const group = await cached.getGroup(groupId)
if (!group) notFound()

return <BalancesAndReimbursements groupId={groupId} />
export default async function GroupPage() {
return <BalancesAndReimbursements />
}
30 changes: 30 additions & 0 deletions src/app/groups/[groupId]/current-group-context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { AppRouterOutput } from '@/trpc/routers/_app'
import { PropsWithChildren, createContext, useContext } from 'react'

type Group = NonNullable<AppRouterOutput['groups']['get']['group']>

type GroupContext =
| { isLoading: false; groupId: string; group: Group }
| { isLoading: true; groupId: string; group: undefined }

const CurrentGroupContext = createContext<GroupContext | null>(null)

export const useCurrentGroup = () => {
const context = useContext(CurrentGroupContext)
if (!context)
throw new Error(
'Missing context. Should be called inside a CurrentGroupProvider.',
)
return context
}

export const CurrentGroupProvider = ({
children,
...props
}: PropsWithChildren<GroupContext>) => {
return (
<CurrentGroupContext.Provider value={props}>
{children}
</CurrentGroupContext.Provider>
)
}
6 changes: 4 additions & 2 deletions src/app/groups/[groupId]/edit/edit-group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

import { GroupForm } from '@/components/group-form'
import { trpc } from '@/trpc/client'
import { useCurrentGroup } from '../current-group-context'

export const EditGroup = ({ groupId }: { groupId: string }) => {
const { data, isLoading } = trpc.groups.get.useQuery({ groupId })
export const EditGroup = () => {
const { groupId } = useCurrentGroup()
const { data, isLoading } = trpc.groups.getDetails.useQuery({ groupId })
const { mutateAsync } = trpc.groups.update.useMutation()
const utils = trpc.useUtils()

Expand Down
8 changes: 2 additions & 6 deletions src/app/groups/[groupId]/edit/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ export const metadata: Metadata = {
title: 'Settings',
}

export default async function EditGroupPage({
params: { groupId },
}: {
params: { groupId: string }
}) {
return <EditGroup groupId={groupId} />
export default async function EditGroupPage() {
return <EditGroup />
}
14 changes: 5 additions & 9 deletions src/app/groups/[groupId]/expenses/create-from-receipt-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,11 @@ import { getImageData, usePresignedUpload } from 'next-s3-upload'
import Image from 'next/image'
import { useRouter } from 'next/navigation'
import { PropsWithChildren, ReactNode, useState } from 'react'
import { useCurrentGroup } from '../current-group-context'

const MAX_FILE_SIZE = 5 * 1024 ** 2

export function CreateFromReceiptButton({ groupId }: { groupId: string }) {
return <CreateFromReceiptButton_ groupId={groupId} />
}

function CreateFromReceiptButton_({ groupId }: { groupId: string }) {
export function CreateFromReceiptButton() {
const t = useTranslations('CreateFromReceipt')
const isDesktop = useMediaQuery('(min-width: 640px)')

Expand Down Expand Up @@ -70,15 +67,14 @@ function CreateFromReceiptButton_({ groupId }: { groupId: string }) {
}
description={<>{t('Dialog.description')}</>}
>
<ReceiptDialogContent groupId={groupId} />
<ReceiptDialogContent />
</DialogOrDrawer>
)
}

function ReceiptDialogContent({ groupId }: { groupId: string }) {
const { data: groupData } = trpc.groups.get.useQuery({ groupId })
function ReceiptDialogContent() {
const { group } = useCurrentGroup()
const { data: categoriesData } = trpc.categories.list.useQuery()
const group = groupData?.group
const categories = categoriesData?.categories

const locale = useLocale()
Expand Down
5 changes: 2 additions & 3 deletions src/app/groups/[groupId]/expenses/expense-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const enforceCurrencyPattern = (value: string) =>
.replace(/[^-\d.]/g, '') // remove all non-numeric characters

const getDefaultSplittingOptions = (
group: AppRouterOutput['groups']['get']['group'],
group: NonNullable<AppRouterOutput['groups']['get']['group']>,
) => {
const defaultValue = {
splitMode: 'EVENLY' as const,
Expand Down Expand Up @@ -145,7 +145,7 @@ export function ExpenseForm({
onDelete,
runtimeFeatureFlags,
}: {
group: AppRouterOutput['groups']['get']['group']
group: NonNullable<AppRouterOutput['groups']['get']['group']>
categories: AppRouterOutput['categories']['list']['categories']
expense?: AppRouterOutput['groups']['expenses']['get']['expense']
onSubmit: (value: ExpenseFormValues, participantId?: string) => Promise<void>
Expand Down Expand Up @@ -250,7 +250,6 @@ export function ExpenseForm({
>(new Set())

const sExpense = isIncome ? 'Income' : 'Expense'
const sPaid = isIncome ? 'received' : 'paid'

useEffect(() => {
setManuallyEditedParticipants(new Set())
Expand Down
16 changes: 7 additions & 9 deletions src/app/groups/[groupId]/expenses/expense-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Link from 'next/link'
import { forwardRef, useEffect, useMemo, useState } from 'react'
import { useInView } from 'react-intersection-observer'
import { useDebounce } from 'use-debounce'
import { useCurrentGroup } from '../current-group-context'

const PAGE_SIZE = 20

Expand Down Expand Up @@ -56,12 +57,12 @@ function getGroupedExpensesByDate(expenses: ExpensesType) {
}, {})
}

export function ExpenseList({ groupId }: { groupId: string }) {
const { data: groupData } = trpc.groups.get.useQuery({ groupId })
export function ExpenseList() {
const { groupId, group } = useCurrentGroup()
const [searchText, setSearchText] = useState('')
const [debouncedSearchText] = useDebounce(searchText, 300)

const participants = groupData?.group.participants
const participants = group?.participants

useEffect(() => {
if (!participants) return
Expand Down Expand Up @@ -103,6 +104,7 @@ const ExpenseListForSearch = ({
searchText: string
}) => {
const utils = trpc.useUtils()
const { group } = useCurrentGroup()

useEffect(() => {
// Until we use tRPC more widely and can invalidate the cache on expense
Expand All @@ -124,11 +126,7 @@ const ExpenseListForSearch = ({
const expenses = data?.pages.flatMap((page) => page.expenses)
const hasMore = data?.pages.at(-1)?.hasMore ?? false

const { data: groupData, isLoading: groupIsLoading } =
trpc.groups.get.useQuery({ groupId })

const isLoading =
expensesAreLoading || !expenses || groupIsLoading || !groupData
const isLoading = expensesAreLoading || !expenses || !group

useEffect(() => {
if (inView && hasMore && !isLoading) fetchNextPage()
Expand Down Expand Up @@ -172,7 +170,7 @@ const ExpenseListForSearch = ({
<ExpenseCard
key={expense.id}
expense={expense}
currency={groupData.group.currency}
currency={group.currency}
groupId={groupId}
/>
))}
Expand Down
Loading

0 comments on commit db03fe4

Please sign in to comment.