feat: Implement Phase 5 optimization and best practices

This commit is contained in:
gpt-engineer-app[bot]
2025-10-13 22:56:47 +00:00
parent 68a2572c23
commit 3e520e1520
14 changed files with 341 additions and 165 deletions

View File

@@ -1,6 +1,8 @@
import { useState, useCallback, useRef, useEffect, useMemo } from "react";
import { supabase } from "@/integrations/supabase/client";
import { useToast } from "@/hooks/use-toast";
import { logger } from "@/lib/logger";
import { MODERATION_CONSTANTS } from "@/lib/moderation/constants";
import type { User } from "@supabase/supabase-js";
import {
useEntityCache,
@@ -71,7 +73,7 @@ export interface ModerationQueueManager {
* Consolidates all queue-related logic into a single hook
*/
export function useModerationQueueManager(config: ModerationQueueManagerConfig): ModerationQueueManager {
console.log('🚀 [QUEUE MANAGER] Hook mounting/rendering', {
logger.log('🚀 [QUEUE MANAGER] Hook mounting/rendering', {
hasUser: !!config.user,
isAdmin: config.isAdmin,
timestamp: new Date().toISOString()
@@ -169,7 +171,7 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
useEffect(() => {
if (queueQuery.items) {
setItems(queueQuery.items);
console.log('✅ Queue items updated from TanStack Query:', queueQuery.items.length);
logger.log('✅ Queue items updated from TanStack Query:', queueQuery.items.length);
}
}, [queueQuery.items]);
@@ -187,7 +189,7 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
// Show error toast when query fails
useEffect(() => {
if (queueQuery.error) {
console.error('❌ Queue query error:', queueQuery.error);
logger.error('❌ Queue query error:', queueQuery.error);
toast({
variant: 'destructive',
title: 'Failed to Load Queue',
@@ -205,7 +207,7 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
useEffect(() => {
if (!queueQuery.isLoading && !initialFetchCompleteRef.current) {
initialFetchCompleteRef.current = true;
console.log('✅ Initial queue fetch complete');
logger.log('✅ Initial queue fetch complete');
}
}, [queueQuery.isLoading]);
@@ -213,7 +215,7 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
* Manual refresh function
*/
const refresh = useCallback(async () => {
console.log('🔄 Manual refresh triggered');
logger.log('🔄 Manual refresh triggered');
await queueQuery.refetch();
}, [queueQuery]);
@@ -221,7 +223,7 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
* Show pending new items by invalidating query
*/
const showNewItems = useCallback(async () => {
console.log('✅ Showing new items via query invalidation');
logger.log('✅ Showing new items via query invalidation');
await queueQuery.invalidate();
setPendingNewItems([]);
setNewItemsCount(0);
@@ -521,16 +523,25 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
[filters.statusFilter, toast],
);
// Extract stable callbacks for dependencies
const invalidateQuery = useCallback(() => {
queueQuery.invalidate();
}, [queueQuery.invalidate]);
const resetPagination = useCallback(() => {
pagination.reset();
}, [pagination.reset]);
// Mark initial fetch as complete when query loads
useEffect(() => {
if (!queueQuery.isLoading && !initialFetchCompleteRef.current) {
initialFetchCompleteRef.current = true;
isMountingRef.current = false;
console.log('✅ Initial queue fetch complete');
logger.log('✅ Initial queue fetch complete');
}
}, [queueQuery.isLoading]);
// Invalidate query when filters or sort changes
// Invalidate query when filters or sort changes (OPTIMIZED)
useEffect(() => {
if (
!user ||
@@ -538,36 +549,41 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
isMountingRef.current
) return;
console.log('🔄 Filters/sort changed, invalidating query');
pagination.reset();
queueQuery.invalidate();
logger.log('🔄 Filters/sort changed, invalidating query');
resetPagination();
invalidateQuery();
}, [
filters.debouncedEntityFilter,
filters.debouncedStatusFilter,
filters.debouncedSortConfig.field,
filters.debouncedSortConfig.direction,
user,
queueQuery,
pagination
invalidateQuery,
resetPagination
]);
// Polling effect (when realtime disabled)
// Polling effect (when realtime disabled) - MUTUALLY EXCLUSIVE
useEffect(() => {
if (!user || settings.refreshMode !== "auto" || loadingState === "initial" || settings.useRealtimeQueue) {
const shouldPoll = settings.refreshMode === 'auto'
&& !settings.useRealtimeQueue
&& loadingState !== 'initial'
&& !!user;
if (!shouldPoll) {
return;
}
console.log("⚠️ Polling ENABLED - interval:", settings.pollInterval);
logger.log("⚠️ Polling ENABLED - interval:", settings.pollInterval);
const interval = setInterval(() => {
console.log("🔄 Polling refresh triggered");
logger.log("🔄 Polling refresh triggered");
queueQuery.refetch();
}, settings.pollInterval);
return () => {
clearInterval(interval);
console.log("🛑 Polling stopped");
logger.log("🛑 Polling stopped");
};
}, [user, settings.refreshMode, settings.pollInterval, loadingState, settings.useRealtimeQueue, queueQuery]);
}, [user, settings.refreshMode, settings.pollInterval, loadingState, settings.useRealtimeQueue, queueQuery.refetch]);
// Initialize realtime subscriptions
useRealtimeSubscriptions({
@@ -591,21 +607,18 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
if (recentlyRemovedRef.current.has(item.id)) return;
if (interactingWith.has(item.id)) return;
if (shouldRemove) {
setItems((prev) => prev.filter((i) => i.id !== item.id));
} else {
setItems((prev) => {
const exists = prev.some((i) => i.id === item.id);
if (exists) {
return prev.map((i) => (i.id === item.id ? item : i));
} else {
return [item, ...prev];
}
});
// Only track removals for optimistic update protection
if (shouldRemove && !recentlyRemovedRef.current.has(item.id)) {
recentlyRemovedRef.current.add(item.id);
setTimeout(() => recentlyRemovedRef.current.delete(item.id), MODERATION_CONSTANTS.REALTIME_OPTIMISTIC_REMOVAL_TIMEOUT);
}
// TanStack Query handles actual state updates via invalidation
},
onItemRemoved: (itemId: string) => {
setItems((prev) => prev.filter((i) => i.id !== itemId));
// Track for optimistic update protection
recentlyRemovedRef.current.add(itemId);
setTimeout(() => recentlyRemovedRef.current.delete(itemId), MODERATION_CONSTANTS.REALTIME_OPTIMISTIC_REMOVAL_TIMEOUT);
// TanStack Query handles removal via invalidation
},
entityCache,
profileCache,