From 10950a4034be689c2ba09aad6edd50354055f814 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sat, 11 Oct 2025 17:20:35 +0000 Subject: [PATCH] Fix moderation queue tab switch reload --- src/components/moderation/ModerationQueue.tsx | 32 ++++++++++++++----- src/hooks/useAdminSettings.ts | 7 ++++ ...4_7a9a6b30-b457-490c-aad8-d5a5549cb876.sql | 11 +++++++ 3 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 supabase/migrations/20251011171914_7a9a6b30-b457-490c-aad8-d5a5549cb876.sql diff --git a/src/components/moderation/ModerationQueue.tsx b/src/components/moderation/ModerationQueue.tsx index f1394a04..9dbdd9b6 100644 --- a/src/components/moderation/ModerationQueue.tsx +++ b/src/components/moderation/ModerationQueue.tsx @@ -157,13 +157,15 @@ export const ModerationQueue = forwardRef((props, ref) => { getAdminPanelPollInterval, getAutoRefreshStrategy, getPreserveInteractionState, - getUseRealtimeQueue + getUseRealtimeQueue, + getRefreshOnTabVisible } = useAdminSettings(); const refreshMode = getAdminPanelRefreshMode(); const pollInterval = getAdminPanelPollInterval(); const refreshStrategy = getAutoRefreshStrategy(); const preserveInteraction = getPreserveInteractionState(); const useRealtimeQueue = getUseRealtimeQueue(); + const refreshOnTabVisible = getRefreshOnTabVisible(); // Track recently removed items to prevent realtime override of optimistic updates const recentlyRemovedRef = useRef>(new Set()); @@ -172,6 +174,8 @@ export const ModerationQueue = forwardRef((props, ref) => { // Store admin settings and stable refs to avoid triggering fetchItems recreation const refreshStrategyRef = useRef(refreshStrategy); const preserveInteractionRef = useRef(preserveInteraction); + const refreshOnTabVisibleRef = useRef(refreshOnTabVisible); + const activeTabRef = useRef(activeTab); const userRef = useRef(user); const toastRef = useRef(toast); const isAdminRef = useRef(isAdmin); @@ -180,11 +184,17 @@ export const ModerationQueue = forwardRef((props, ref) => { useEffect(() => { refreshStrategyRef.current = refreshStrategy; preserveInteractionRef.current = preserveInteraction; + refreshOnTabVisibleRef.current = refreshOnTabVisible; userRef.current = user; toastRef.current = toast; isAdminRef.current = isAdmin; isSuperuserRef.current = isSuperuser; - }, [refreshStrategy, preserveInteraction, user, toast, isAdmin, isSuperuser]); + }, [refreshStrategy, preserveInteraction, refreshOnTabVisible, user, toast, isAdmin, isSuperuser]); + + // Sync activeTab with ref + useEffect(() => { + activeTabRef.current = activeTab; + }, [activeTab]); // Persist sort configuration useEffect(() => { @@ -1165,7 +1175,7 @@ export const ModerationQueue = forwardRef((props, ref) => { }; }, [user, useRealtimeQueue, debouncedRealtimeUpdate]); - // Visibility change handler - pause queue updates when tab is hidden + // Visibility change handler - smart tab switching behavior useEffect(() => { const handleVisibilityChange = () => { if (document.hidden) { @@ -1174,17 +1184,23 @@ export const ModerationQueue = forwardRef((props, ref) => { } else { console.log('📱 Tab visible - resuming queue updates'); pauseFetchingRef.current = false; - // Optional: trigger single refresh when tab becomes visible - if (initialFetchCompleteRef.current && !isMountingRef.current) { - console.log('🔄 Tab became visible - triggering refresh'); - fetchItems(filtersRef.current.entityFilter, filtersRef.current.statusFilter, true, activeTab); + + // Check admin setting for auto-refresh behavior + const shouldRefresh = refreshOnTabVisibleRef.current; + + if (shouldRefresh && initialFetchCompleteRef.current && !isMountingRef.current) { + console.log('🔄 Tab became visible - triggering refresh (admin setting enabled)'); + fetchItems(filtersRef.current.entityFilter, filtersRef.current.statusFilter, true, activeTabRef.current); + } else { + console.log('✅ Tab became visible - resuming without refresh'); + // Realtime subscriptions will handle updates naturally } } }; document.addEventListener('visibilitychange', handleVisibilityChange); return () => document.removeEventListener('visibilitychange', handleVisibilityChange); - }, [fetchItems, activeTab]); + }, []); // Empty deps - all values come from refs const handleResetToPending = async (item: ModerationItem) => { setActionLoading(item.id); diff --git a/src/hooks/useAdminSettings.ts b/src/hooks/useAdminSettings.ts index 9da234de..d54b12ad 100644 --- a/src/hooks/useAdminSettings.ts +++ b/src/hooks/useAdminSettings.ts @@ -157,6 +157,12 @@ export function useAdminSettings() { return cleanValue === 'true' || cleanValue === true; }; + const getRefreshOnTabVisible = (): boolean => { + const value = getSettingValue('system.refresh_on_tab_visible', 'false'); + const cleanValue = typeof value === 'string' ? value.replace(/"/g, '') : value; + return cleanValue === 'true' || cleanValue === true; + }; + return { settings, isLoading, @@ -180,5 +186,6 @@ export function useAdminSettings() { getAutoRefreshStrategy, getPreserveInteractionState, getUseRealtimeQueue, + getRefreshOnTabVisible, }; } \ No newline at end of file diff --git a/supabase/migrations/20251011171914_7a9a6b30-b457-490c-aad8-d5a5549cb876.sql b/supabase/migrations/20251011171914_7a9a6b30-b457-490c-aad8-d5a5549cb876.sql new file mode 100644 index 00000000..43206db4 --- /dev/null +++ b/supabase/migrations/20251011171914_7a9a6b30-b457-490c-aad8-d5a5549cb876.sql @@ -0,0 +1,11 @@ +-- Add admin setting to control auto-refresh on tab visibility change +INSERT INTO admin_settings (setting_key, setting_value, category, description, created_at, updated_at) +VALUES ( + 'system.refresh_on_tab_visible', + 'false', + 'system', + 'Auto-refresh moderation queue when returning to tab (default: false, realtime handles updates)', + now(), + now() +) +ON CONFLICT (setting_key) DO NOTHING; \ No newline at end of file