diff --git a/src/components/moderation/ModerationQueue.tsx b/src/components/moderation/ModerationQueue.tsx index abd1ec6e..74b04a5d 100644 --- a/src/components/moderation/ModerationQueue.tsx +++ b/src/components/moderation/ModerationQueue.tsx @@ -80,6 +80,7 @@ export const ModerationQueue = forwardRef((props, ref) => { const [interactingWith, setInteractingWith] = useState>(new Set()); const [newItemsCount, setNewItemsCount] = useState(0); const [isRefreshing, setIsRefreshing] = useState(false); + const [profileCache, setProfileCache] = useState>(new Map()); const { toast } = useToast(); const { isAdmin, isSuperuser } = useUserRole(); const { user } = useAuth(); @@ -109,8 +110,8 @@ export const ModerationQueue = forwardRef((props, ref) => { return; } - // Prevent duplicate requests - if (isRefreshing && silent) { + // Prevent ANY refresh if one is in progress + if (isRefreshing) { console.log('⏭️ Skipping refresh - already in progress'); return; } @@ -377,7 +378,20 @@ export const ModerationQueue = forwardRef((props, ref) => { .select('user_id, username, display_name, avatar_url') .in('user_id', userIds); - const profileMap = new Map(profiles?.map(p => [p.user_id, p]) || []); + // Update profile cache with stable references + setProfileCache(prevCache => { + const newCache = new Map(prevCache); + profiles?.forEach(p => { + const existing = newCache.get(p.user_id); + // Only update if data actually changed + if (!existing || JSON.stringify(existing) !== JSON.stringify(p)) { + newCache.set(p.user_id, p); + } + }); + return newCache; + }); + + const profileMap = profileCache; // Combine and format items const formattedItems: ModerationItem[] = [ @@ -448,7 +462,7 @@ export const ModerationQueue = forwardRef((props, ref) => { addToTop: true, }); - // If there are changes + // Only update state if there are actual changes if (mergeResult.hasChanges) { const actuallyNewItems = mergeResult.changes.added.length; @@ -458,30 +472,31 @@ export const ModerationQueue = forwardRef((props, ref) => { updated: mergeResult.changes.updated.length, removed: mergeResult.changes.removed.length, totalItems: mergeResult.items.length, - protectedIds: Array.from(preserveInteraction ? interactingWith : new Set()), - strategy: refreshStrategy, - preserveInteraction }); - // Filter out items user is interacting with - const protectedIds = preserveInteraction ? interactingWith : new Set(); - const mergedWithProtection = mergeResult.items.map(item => { - if (protectedIds.has(item.id)) { - // Find and preserve the current version of this item - const currentItem = items.find(i => i.id === item.id); - return currentItem || item; - } - return item; - }); + // Only apply protection map if needed + let finalItems = mergeResult.items; + if (preserveInteraction && interactingWith.size > 0) { + finalItems = mergeResult.items.map(item => { + if (interactingWith.has(item.id)) { + const currentItem = items.find(i => i.id === item.id); + return currentItem || item; + } + return item; + }); + } - setItems(mergedWithProtection); + // Only call setItems if reference has actually changed + if (finalItems !== items) { + setItems(finalItems); + } // Only set new items count if there are genuinely new items if (actuallyNewItems > 0) { setNewItemsCount(actuallyNewItems); } } else { - // No changes detected - keep current state completely unchanged + // No changes detected - skip all state updates console.log('✅ No changes detected, keeping current state'); } } else {