From 488f8bc8aea3fcd6ebd21a8fd614025d480684d6 Mon Sep 17 00:00:00 2001 From: glen coden Date: Tue, 14 Jan 2025 22:15:04 +0100 Subject: [PATCH] feat (session): add realtime lips hook --- apps/cloud/app/components/DragDropList.tsx | 18 ++-- apps/cloud/app/hooks/useLips.ts | 30 ++++++ apps/cloud/app/routes/session.$sessionId.tsx | 62 +++++------ packages/db/src/queries/createDemoLip.ts | 107 +++++++++---------- packages/db/src/queries/moveLip.ts | 26 ++++- packages/ui/src/components/SongLip.tsx | 2 +- 6 files changed, 146 insertions(+), 99 deletions(-) create mode 100644 apps/cloud/app/hooks/useLips.ts diff --git a/apps/cloud/app/components/DragDropList.tsx b/apps/cloud/app/components/DragDropList.tsx index 9868b50..9ace8d2 100644 --- a/apps/cloud/app/components/DragDropList.tsx +++ b/apps/cloud/app/components/DragDropList.tsx @@ -32,7 +32,7 @@ const DragDropList = forwardRef<
-
-
- {header} -
+
+
+ {header}
+ {fixTop !== null &&
} {springs.map((spring, index) => { const lip = lips[index] diff --git a/apps/cloud/app/hooks/useLips.ts b/apps/cloud/app/hooks/useLips.ts new file mode 100644 index 0000000..8ab013a --- /dev/null +++ b/apps/cloud/app/hooks/useLips.ts @@ -0,0 +1,30 @@ +import { api } from '@repo/api/client' +import { Session } from '@repo/db' +import { useEffect } from 'react' +import { supabase } from '~/lib/supabase.client' + +export const useLips = (sessionId: Session['id']) => { + const utils = api.useUtils() + + useEffect(() => { + const channel = supabase + .channel('lip') + .on( + 'postgres_changes', + { + event: '*', + schema: 'public', + table: 'lip', + }, + () => { + void utils.lip.getBySessionId.invalidate({ id: sessionId }) + }, + ) + .subscribe() + return () => { + void supabase.removeChannel(channel) + } + }, [utils, sessionId]) + + return api.lip.getBySessionId.useQuery({ id: sessionId }) +} diff --git a/apps/cloud/app/routes/session.$sessionId.tsx b/apps/cloud/app/routes/session.$sessionId.tsx index 7946f74..9933438 100644 --- a/apps/cloud/app/routes/session.$sessionId.tsx +++ b/apps/cloud/app/routes/session.$sessionId.tsx @@ -19,6 +19,7 @@ import DragDropList from '~/components/DragDropList' import DragDropListItem from '~/components/DragDropListItem' import SessionMenu from '~/components/SessionMenu' import { createSpringEffect } from '~/helpers/create-spring-effect' +import { useLips } from '~/hooks/useLips' const FULL_LIP_HEIGHT = 108 // px, lip height 96 + list gap 12 const FULL_ACTION_LIP_BOX_HEIGHT = 124 // px, lip box height 112 + list gap 12 @@ -98,12 +99,7 @@ export default function ActiveSession() { data, isLoading: isLipsLoading, isFetching: isLipsFetching, - } = api.lip.getBySessionId.useQuery( - { id: session.id }, - { - refetchInterval: 1000 * 60, - }, - ) + } = useLips(session.id) const lips = data ?? ([] as LipDTO[]) @@ -300,31 +296,13 @@ export default function ActiveSession() { return } - // TODO: add this to move update to avoid having two requests - // ALSO let staged lip be moved away - // ALSO mobile page indicator dots - // Song select loading spinner + // TODO: + // Mobile page indicator dots // Session start buttons loading spinner rather than page pulse - // Refactor drop shadows by the example of SongLip + // Refactor Button drop shadows by the example of SongLip // Always five demo lips - // Remove shadow from lips on live stack + // Remove shadow from lips on live stack? // Hook up stripe and tool ws locally - if (actionLip && status === 'staged') { - const actionStatus = - actionLip.status === 'staged' ? 'no-show' : 'done' - utils.lip.getBySessionId.setData({ id: session.id }, (prevLips) => { - return prevLips?.map((lip) => { - if (lip.id !== actionLip.id) { - return lip - } - return { ...lip, status: actionStatus } - }) - }) - updateLip({ - id: actionLip.id, - status: actionStatus, - }) - } // Optimistic update void utils.lip.getBySessionId.setData( @@ -335,6 +313,7 @@ export default function ActiveSession() { if (lip.sortNumber === null) { return lip } + // Update moved lip if (lip.id === id) { return { ...lip, @@ -342,6 +321,16 @@ export default function ActiveSession() { sortNumber, } } + // Update current lip on live stack + if (lip.id === actionLip?.id && status === 'staged') { + return { + ...lip, + status: + lip.status === 'staged' + ? 'no-show' + : ('done' as LipDTO['status']), + } + } if (fromLip.status === status) { if (lip.status !== status) { return lip @@ -733,9 +722,6 @@ export default function ActiveSession() { console.log('DRAG INDEX', dragIndex) console.log('TARGET INDEX', targetIndex) - // staged - live toggle on lip - // no moving lip away from live stack > implement no-show status - let status: LipDTO['status'] = 'idle' if (deleteOnDrop) { @@ -827,11 +813,21 @@ export default function ActiveSession() { lip={actionLip} spring={actionSpring} bind={bindLipDrag} - isLocked + isLocked={ + actionLip.status === 'live' + } hideTime hideFavorites /> -
+
{actionLip.status === 'staged' && (