/** * Moderation Lock Monitor * * Monitors lock expiry and provides automatic renewal prompts for moderators. * Prevents loss of work due to expired locks. */ import { useEffect } from 'react'; import type { ModerationState } from '../moderationStateMachine'; import type { ModerationAction } from '../moderationStateMachine'; import { hasActiveLock, needsLockRenewal } from '../moderationStateMachine'; import { toast } from '@/hooks/use-toast'; import { supabase } from '@/lib/supabaseClient'; import { handleNonCriticalError } from '../errorHandler'; /** * Hook to monitor lock status and warn about expiry * * @param state - Current moderation state * @param dispatch - State machine dispatch function * @param itemId - ID of the locked item (optional, for manual extension) * @returns Extension function to manually extend lock */ export function useLockMonitor( state: ModerationState, dispatch: React.Dispatch, itemId?: string ): { extendLock: () => Promise } { useEffect(() => { if (!hasActiveLock(state)) { return; } const checkInterval = setInterval(() => { if (needsLockRenewal(state)) { // Dispatch lock expiry warning dispatch({ type: 'LOCK_EXPIRED' }); // Show toast with extension option toast({ title: 'Lock Expiring Soon', description: 'Your lock on this submission will expire in less than 2 minutes. Click below to extend.', duration: Infinity, }); // Also call extension function automatically after showing toast if (itemId) { setTimeout(() => { handleExtendLock(itemId, dispatch); }, 100); } } }, 30000); // Check every 30 seconds return () => clearInterval(checkInterval); }, [state, dispatch, itemId]); const extendLock = async () => { if (itemId) { await handleExtendLock(itemId, dispatch); } }; return { extendLock }; } /** * Extend the lock on a submission * * @param submissionId - Submission ID * @param dispatch - State machine dispatch function */ export async function handleExtendLock( submissionId: string, dispatch: React.Dispatch ) { try { // Call Supabase to extend lock (assumes 15 minute extension) const { error } = await supabase .from('content_submissions') .update({ locked_until: new Date(Date.now() + 15 * 60 * 1000).toISOString(), }) .eq('id', submissionId); if (error) throw error; // Update state machine with new lock time dispatch({ type: 'LOCK_ACQUIRED', payload: { lockExpires: new Date(Date.now() + 15 * 60 * 1000).toISOString() }, }); toast({ title: 'Lock Extended', description: 'You have 15 more minutes to complete your review.', }); } catch (error: unknown) { handleNonCriticalError(error, { action: 'Extend Lock', metadata: { submissionId } }); toast({ title: 'Extension Failed', description: 'Could not extend lock. Please save your work and re-claim the item.', variant: 'destructive', }); } }