Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2025-07-18 10:24:36 -05:00
parent ead4252441
commit d2d9d9691b
3 changed files with 88 additions and 25 deletions

View File

@ -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) => (
<IonItemSliding key={singer.key}>
<IonItem detail={false}>
<IonLabel>
<h3 className="text-sm font-medium text-gray-900">
{singer.name}
</h3>
</IonLabel>
</IonItem>
<IonItem detail={false}>
<IonLabel>
<h3 className="text-sm font-medium text-gray-900">
{singer.name}
</h3>
</IonLabel>
{/* Swipe to Remove (Admin Only) */}
{/* Delete Icon (Admin Only) */}
{isAdmin && (
<IonItemOptions side="end">
<IonItemOption
color="danger"
onClick={() => handleRemoveSinger(singer)}
>
<IonIcon icon={trash} slot="icon-only" />
</IonItemOption>
</IonItemOptions>
<IonIcon
icon={trash}
slot="end"
color="danger"
className="cursor-pointer"
onClick={() => handleRemoveSinger(singer)}
/>
)}
</IonItemSliding>
</IonItem>
);
return (

View File

@ -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<string, QueueItem | null> = {};
// 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<string, Singer | null> = {};
// 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<string, Singer>) => void) => {
const singersRef = ref(database, `controllers/${controllerName}/player/singers`);
onValue(singersRef, (snapshot) => {
callback(snapshot.exists() ? snapshot.val() : {});
});
return () => off(singersRef);
}
};

View File

@ -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,