feat: Implement emergency hotfixes

This commit is contained in:
gpt-engineer-app[bot]
2025-10-13 20:39:39 +00:00
parent 1d3c907c8a
commit 27c7f36ca4
5 changed files with 242 additions and 60 deletions

View File

@@ -127,25 +127,34 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
// Refs for tracking
const recentlyRemovedRef = useRef<Set<string>>(new Set());
const fetchInProgressRef = useRef(false);
const itemsRef = useRef<ModerationItem[]>([]);
const lastFetchTimeRef = useRef<number>(0);
const pauseFetchingRef = useRef(false);
const initialFetchCompleteRef = useRef(false);
const isMountingRef = useRef(true);
const fetchItemsRef = useRef<((silent?: boolean) => Promise<void>) | null>(null);
const FETCH_COOLDOWN_MS = 1000;
// Store settings in refs to avoid re-creating fetchItems
// Store settings, filters, sort, and pagination in refs to stabilize fetchItems
const settingsRef = useRef(settings);
const filtersRef = useRef(filters);
const sortRef = useRef(sort.debouncedConfig);
const paginationRef = useRef(pagination);
// Sync refs with state
useEffect(() => {
settingsRef.current = settings;
}, [settings]);
// Sync items with ref
useEffect(() => {
itemsRef.current = items;
}, [items]);
filtersRef.current = filters;
}, [filters]);
useEffect(() => {
sortRef.current = sort.debouncedConfig;
}, [sort.debouncedConfig]);
useEffect(() => {
paginationRef.current = pagination;
}, [pagination]);
/**
* Fetch queue items from database
@@ -160,20 +169,13 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
console.log("🔄 [FETCH ITEMS] Called", {
silent,
pauseFetchingRef: pauseFetchingRef.current,
documentHidden: document.hidden,
caller: callerLine,
sortField: sort.debouncedConfig.field,
sortDirection: sort.debouncedConfig.direction,
sortField: sortRef.current.field,
sortDirection: sortRef.current.direction,
timestamp: new Date().toISOString(),
});
// Check if fetching is paused (controlled by visibility handler if enabled)
if (pauseFetchingRef.current) {
console.log("⏸️ Fetch paused by pauseFetchingRef");
return;
}
// Prevent concurrent calls
if (fetchInProgressRef.current) {
console.log("⚠️ Fetch already in progress, skipping");
@@ -192,10 +194,10 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
lastFetchTimeRef.current = now;
console.log("🔍 fetchItems called:", {
entityFilter: filters.debouncedEntityFilter,
statusFilter: filters.debouncedStatusFilter,
sortField: sort.debouncedConfig.field,
sortDirection: sort.debouncedConfig.direction,
entityFilter: filtersRef.current.debouncedEntityFilter,
statusFilter: filtersRef.current.debouncedStatusFilter,
sortField: sortRef.current.field,
sortDirection: sortRef.current.direction,
silent,
timestamp: new Date().toISOString(),
});
@@ -237,28 +239,28 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
// CRITICAL: Multi-level ordering
console.log('📊 [SORT QUERY] Applying multi-level sort:', {
level1: 'escalated DESC',
level2: `${sort.debouncedConfig.field} ${sort.debouncedConfig.direction.toUpperCase()}`,
level3: sort.debouncedConfig.field !== 'created_at' ? 'created_at ASC' : 'none'
level2: `${sortRef.current.field} ${sortRef.current.direction.toUpperCase()}`,
level3: sortRef.current.field !== 'created_at' ? 'created_at ASC' : 'none'
});
// Level 1: Always sort by escalated first (descending)
submissionsQuery = submissionsQuery.order('escalated', { ascending: false });
// Level 2: Apply user-selected sort (use debounced config)
// Level 2: Apply user-selected sort (use ref)
submissionsQuery = submissionsQuery.order(
sort.debouncedConfig.field,
{ ascending: sort.debouncedConfig.direction === 'asc' }
sortRef.current.field,
{ ascending: sortRef.current.direction === 'asc' }
);
// Level 3: Tertiary sort by created_at (if not already primary)
if (sort.debouncedConfig.field !== 'created_at') {
if (sortRef.current.field !== 'created_at') {
submissionsQuery = submissionsQuery.order('created_at', { ascending: true });
}
// Apply tab-based status filtering
const tab = filters.activeTab;
const statusFilter = filters.debouncedStatusFilter;
const entityFilter = filters.debouncedEntityFilter;
// Apply tab-based status filtering (use refs)
const tab = filtersRef.current.activeTab;
const statusFilter = filtersRef.current.debouncedStatusFilter;
const entityFilter = filtersRef.current.debouncedEntityFilter;
if (tab === "mainQueue") {
if (statusFilter === "all") {
@@ -328,11 +330,11 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
const { count } = await countQuery;
pagination.setTotalCount(count || 0);
paginationRef.current.setTotalCount(count || 0);
// Apply pagination
const startIndex = pagination.startIndex;
const endIndex = pagination.endIndex;
// Apply pagination (use refs)
const startIndex = paginationRef.current.startIndex;
const endIndex = paginationRef.current.endIndex;
submissionsQuery = submissionsQuery.range(startIndex, endIndex);
const { data: submissions, error: submissionsError } = await submissionsQuery;
@@ -341,7 +343,7 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
// Log the actual data returned to verify sort order
if (submissions && submissions.length > 0) {
const sortField = sort.debouncedConfig.field;
const sortField = sortRef.current.field;
const preview = submissions.slice(0, 3).map(s => ({
id: s.id.substring(0, 8),
[sortField]: s[sortField],
@@ -426,7 +428,7 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
if (silent) {
// Background polling: detect new submissions
const currentDisplayedIds = new Set(itemsRef.current.map((item) => item.id));
const currentDisplayedIds = new Set(items.map((item) => item.id));
const newSubmissions = moderationItems.filter((item) => !currentDisplayedIds.has(item.id));
if (newSubmissions.length > 0) {
@@ -452,7 +454,7 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
case "merge":
if (newSubmissions.length > 0) {
const currentIds = new Set(itemsRef.current.map((item) => item.id));
const currentIds = new Set(items.map((item) => item.id));
const trulyNewSubmissions = newSubmissions.filter((item) => !currentIds.has(item.id));
if (trulyNewSubmissions.length > 0) {
@@ -463,7 +465,7 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
break;
case "replace":
const mergeResult = smartMergeArray(itemsRef.current, moderationItems, {
const mergeResult = smartMergeArray(items, moderationItems, {
compareFields: [
"status",
"content",
@@ -527,11 +529,6 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
],
);
// Store fetchItems in ref to avoid re-creating visibility listener
useEffect(() => {
fetchItemsRef.current = fetchItems;
}, [fetchItems]);
/**
* Show pending new items by merging them into the queue
*/
@@ -865,21 +862,23 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
});
pagination.reset();
fetchItemsRef.current?.(false);
fetchItems(false);
}, [
filters.debouncedEntityFilter,
filters.debouncedStatusFilter,
sort.debouncedConfig.field,
sort.debouncedConfig.direction,
user
user,
fetchItems,
pagination
]);
// Pagination changes trigger refetch
useEffect(() => {
if (!user || !initialFetchCompleteRef.current || pagination.currentPage === 1) return;
fetchItemsRef.current?.(true);
}, [pagination.currentPage, pagination.pageSize]);
fetchItems(true);
}, [pagination.currentPage, pagination.pageSize, user, fetchItems]);
// Polling effect (when realtime disabled)
useEffect(() => {
@@ -890,14 +889,14 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
console.log("⚠️ Polling ENABLED - interval:", settings.pollInterval);
const interval = setInterval(() => {
console.log("🔄 Polling refresh triggered");
fetchItemsRef.current?.(true);
fetchItems(true);
}, settings.pollInterval);
return () => {
clearInterval(interval);
console.log("🛑 Polling stopped");
};
}, [user, settings.refreshMode, settings.pollInterval, loadingState, settings.useRealtimeQueue]);
}, [user, settings.refreshMode, settings.pollInterval, loadingState, settings.useRealtimeQueue, fetchItems]);
// Initialize realtime subscriptions
useRealtimeSubscriptions({
@@ -941,7 +940,6 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
profileCache,
recentlyRemovedIds: recentlyRemovedRef.current,
interactingWithIds: interactingWith,
currentItemsRef: itemsRef,
});
return {