From d2d9d9691bf19a4aebc5a152d4f986ab7ebb4cdc Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 18 Jul 2025 10:24:36 -0500 Subject: [PATCH] Signed-off-by: Matt Bruce --- src/features/Singers/Singers.tsx | 35 +++++++++---------- src/firebase/services.ts | 60 +++++++++++++++++++++++++++++++- src/hooks/useSingers.ts | 18 +++++++--- 3 files changed, 88 insertions(+), 25 deletions(-) diff --git a/src/features/Singers/Singers.tsx b/src/features/Singers/Singers.tsx index 66600f2..a218ef2 100644 --- a/src/features/Singers/Singers.tsx +++ b/src/features/Singers/Singers.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { IonItem, IonLabel, IonIcon, IonItemSliding, IonItemOptions, IonItemOption } from '@ionic/react'; +import { IonItem, IonLabel, IonIcon } from '@ionic/react'; import { trash } from 'ionicons/icons'; import { InfiniteScrollList, PageHeader } from '../../components/common'; import { useSingers } from '../../hooks'; @@ -23,27 +23,24 @@ const Singers: React.FC = () => { // Render singer item for InfiniteScrollList const renderSingerItem = (singer: Singer) => ( - - - -

- {singer.name} -

-
-
+ + +

+ {singer.name} +

+
- {/* Swipe to Remove (Admin Only) */} + {/* Delete Icon (Admin Only) */} {isAdmin && ( - - handleRemoveSinger(singer)} - > - - - + handleRemoveSinger(singer)} + /> )} -
+ ); return ( diff --git a/src/firebase/services.ts b/src/firebase/services.ts index 0a0c351..6f3b095 100644 --- a/src/firebase/services.ts +++ b/src/firebase/services.ts @@ -9,7 +9,7 @@ import { update } from 'firebase/database'; import { database } from './config'; -import type { Song, QueueItem, Controller } from '../types'; +import type { Song, QueueItem, Controller, Singer } from '../types'; // Basic CRUD operations for controllers export const controllerService = { @@ -199,4 +199,62 @@ export const favoritesService = { return () => off(favoritesRef); } +}; + +// Singer management operations +export const singerService = { + // Remove singer and all their queue items + removeSinger: async (controllerName: string, singerName: string) => { + // First, remove all queue items for this singer + const queueRef = ref(database, `controllers/${controllerName}/player/queue`); + const queueSnapshot = await get(queueRef); + + if (queueSnapshot.exists()) { + const queue = queueSnapshot.val(); + const updates: Record = {}; + + // Find all queue items for this singer and mark them for removal + Object.entries(queue).forEach(([key, item]) => { + if (item && (item as QueueItem).singer.name === singerName) { + updates[key] = null; // Mark for removal + } + }); + + // Remove the queue items + if (Object.keys(updates).length > 0) { + await update(queueRef, updates); + } + } + + // Then, remove the singer from the singers list + const singersRef = ref(database, `controllers/${controllerName}/player/singers`); + const singersSnapshot = await get(singersRef); + + if (singersSnapshot.exists()) { + const singers = singersSnapshot.val(); + const updates: Record = {}; + + // Find the singer by name and mark for removal + Object.entries(singers).forEach(([key, singer]) => { + if (singer && (singer as Singer).name === singerName) { + updates[key] = null; // Mark for removal + } + }); + + // Remove the singer + if (Object.keys(updates).length > 0) { + await update(singersRef, updates); + } + } + }, + + // Listen to singers changes + subscribeToSingers: (controllerName: string, callback: (data: Record) => void) => { + const singersRef = ref(database, `controllers/${controllerName}/player/singers`); + onValue(singersRef, (snapshot) => { + callback(snapshot.exists() ? snapshot.val() : {}); + }); + + return () => off(singersRef); + } }; \ No newline at end of file diff --git a/src/hooks/useSingers.ts b/src/hooks/useSingers.ts index 4fb7b02..1fa49f4 100644 --- a/src/hooks/useSingers.ts +++ b/src/hooks/useSingers.ts @@ -1,11 +1,13 @@ import { useCallback } from 'react'; -import { useAppSelector, selectSingersArray, selectIsAdmin } from '../redux'; +import { useAppSelector, selectSingersArray, selectIsAdmin, selectControllerName } from '../redux'; import { useToast } from './useToast'; +import { singerService } from '../firebase/services'; import type { Singer } from '../types'; export const useSingers = () => { const singers = useAppSelector(selectSingersArray); const isAdmin = useAppSelector(selectIsAdmin); + const controllerName = useAppSelector(selectControllerName); const { showSuccess, showError } = useToast(); const handleRemoveSinger = useCallback(async (singer: Singer) => { @@ -14,13 +16,19 @@ export const useSingers = () => { return; } + if (!controllerName) { + showError('Controller not found'); + return; + } + try { - // TODO: Implement remove singer functionality - showSuccess(`${singer.name} removed from singers list`); - } catch { + await singerService.removeSinger(controllerName, singer.name); + showSuccess(`${singer.name} removed from singers list and queue`); + } catch (error) { + console.error('Failed to remove singer:', error); showError('Failed to remove singer'); } - }, [isAdmin, showSuccess, showError]); + }, [isAdmin, controllerName, showSuccess, showError]); return { singers,