mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 13:31:12 -05:00
feat: Implement emergency hotfixes
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user