mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 17:31:13 -05:00
feat: Implement Sprint 3 Performance Optimizations
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { useState, useImperativeHandle, forwardRef, useMemo, useCallback } from 'react';
|
||||
import { useState, useImperativeHandle, forwardRef, useMemo, useCallback, useRef } from 'react';
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { TooltipProvider } from '@/components/ui/tooltip';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
@@ -94,6 +95,16 @@ export const ModerationQueue = forwardRef<ModerationQueueRef, ModerationQueuePro
|
||||
// Keyboard shortcuts help dialog
|
||||
const [showShortcutsHelp, setShowShortcutsHelp] = useState(false);
|
||||
|
||||
// Virtual scrolling setup
|
||||
const parentRef = useRef<HTMLDivElement>(null);
|
||||
const virtualizer = useVirtualizer({
|
||||
count: queueManager.items.length,
|
||||
getScrollElement: () => parentRef.current,
|
||||
estimateSize: () => 420, // Estimated average height of QueueItem (card + spacing)
|
||||
overscan: 3, // Render 3 items above/below viewport for smoother scrolling
|
||||
enabled: queueManager.items.length > 10, // Only enable virtual scrolling for 10+ items
|
||||
});
|
||||
|
||||
// UI-specific handlers
|
||||
const handleNoteChange = (id: string, value: string) => {
|
||||
setNotes(prev => ({ ...prev, [id]: value }));
|
||||
@@ -258,37 +269,103 @@ export const ModerationQueue = forwardRef<ModerationQueueRef, ModerationQueuePro
|
||||
/>
|
||||
) : (
|
||||
<TooltipProvider>
|
||||
<div className="space-y-6">
|
||||
{queueManager.items.map((item, index) => (
|
||||
<ModerationErrorBoundary key={item.id} submissionId={item.id}>
|
||||
<QueueItem
|
||||
key={item.id}
|
||||
item={item}
|
||||
isMobile={isMobile}
|
||||
actionLoading={queueManager.actionLoading}
|
||||
isLockedByMe={queueManager.queue.isLockedByMe(item.id, item.assigned_to, item.locked_until)}
|
||||
isLockedByOther={queueManager.queue.isLockedByOther(item.id, item.assigned_to, item.locked_until)}
|
||||
lockStatus={getLockStatus({ assigned_to: item.assigned_to, locked_until: item.locked_until }, user?.id || '')}
|
||||
currentLockSubmissionId={queueManager.queue.currentLock?.submissionId}
|
||||
notes={notes}
|
||||
isAdmin={isAdmin()}
|
||||
isSuperuser={isSuperuser()}
|
||||
queueIsLoading={queueManager.queue.isLoading}
|
||||
onNoteChange={handleNoteChange}
|
||||
onApprove={queueManager.performAction}
|
||||
onResetToPending={queueManager.resetToPending}
|
||||
onRetryFailed={queueManager.retryFailedItems}
|
||||
onOpenPhotos={handleOpenPhotos}
|
||||
onOpenReviewManager={handleOpenReviewManager}
|
||||
onOpenItemEditor={handleOpenItemEditor}
|
||||
onClaimSubmission={queueManager.queue.claimSubmission}
|
||||
onDeleteSubmission={handleDeleteSubmission}
|
||||
onInteractionFocus={(id) => queueManager.markInteracting(id, true)}
|
||||
onInteractionBlur={(id) => queueManager.markInteracting(id, false)}
|
||||
/>
|
||||
</ModerationErrorBoundary>
|
||||
))}
|
||||
</div>
|
||||
{queueManager.items.length <= 10 ? (
|
||||
// Standard rendering for small lists (no virtual scrolling overhead)
|
||||
<div className="space-y-6">
|
||||
{queueManager.items.map((item) => (
|
||||
<ModerationErrorBoundary key={item.id} submissionId={item.id}>
|
||||
<QueueItem
|
||||
item={item}
|
||||
isMobile={isMobile}
|
||||
actionLoading={queueManager.actionLoading}
|
||||
isLockedByMe={queueManager.queue.isLockedByMe(item.id, item.assigned_to, item.locked_until)}
|
||||
isLockedByOther={queueManager.queue.isLockedByOther(item.id, item.assigned_to, item.locked_until)}
|
||||
lockStatus={getLockStatus({ assigned_to: item.assigned_to, locked_until: item.locked_until }, user?.id || '')}
|
||||
currentLockSubmissionId={queueManager.queue.currentLock?.submissionId}
|
||||
notes={notes}
|
||||
isAdmin={isAdmin()}
|
||||
isSuperuser={isSuperuser()}
|
||||
queueIsLoading={queueManager.queue.isLoading}
|
||||
onNoteChange={handleNoteChange}
|
||||
onApprove={queueManager.performAction}
|
||||
onResetToPending={queueManager.resetToPending}
|
||||
onRetryFailed={queueManager.retryFailedItems}
|
||||
onOpenPhotos={handleOpenPhotos}
|
||||
onOpenReviewManager={handleOpenReviewManager}
|
||||
onOpenItemEditor={handleOpenItemEditor}
|
||||
onClaimSubmission={queueManager.queue.claimSubmission}
|
||||
onDeleteSubmission={handleDeleteSubmission}
|
||||
onInteractionFocus={(id) => queueManager.markInteracting(id, true)}
|
||||
onInteractionBlur={(id) => queueManager.markInteracting(id, false)}
|
||||
/>
|
||||
</ModerationErrorBoundary>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
// Virtual scrolling for large lists (10+ items)
|
||||
<div
|
||||
ref={parentRef}
|
||||
className="overflow-auto"
|
||||
style={{
|
||||
height: '70vh',
|
||||
contain: 'strict',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
height: `${virtualizer.getTotalSize()}px`,
|
||||
width: '100%',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
{virtualizer.getVirtualItems().map((virtualItem) => {
|
||||
const item = queueManager.items[virtualItem.index];
|
||||
return (
|
||||
<div
|
||||
key={item.id}
|
||||
data-index={virtualItem.index}
|
||||
ref={virtualizer.measureElement}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
transform: `translateY(${virtualItem.start}px)`,
|
||||
}}
|
||||
className="pb-6"
|
||||
>
|
||||
<ModerationErrorBoundary submissionId={item.id}>
|
||||
<QueueItem
|
||||
item={item}
|
||||
isMobile={isMobile}
|
||||
actionLoading={queueManager.actionLoading}
|
||||
isLockedByMe={queueManager.queue.isLockedByMe(item.id, item.assigned_to, item.locked_until)}
|
||||
isLockedByOther={queueManager.queue.isLockedByOther(item.id, item.assigned_to, item.locked_until)}
|
||||
lockStatus={getLockStatus({ assigned_to: item.assigned_to, locked_until: item.locked_until }, user?.id || '')}
|
||||
currentLockSubmissionId={queueManager.queue.currentLock?.submissionId}
|
||||
notes={notes}
|
||||
isAdmin={isAdmin()}
|
||||
isSuperuser={isSuperuser()}
|
||||
queueIsLoading={queueManager.queue.isLoading}
|
||||
onNoteChange={handleNoteChange}
|
||||
onApprove={queueManager.performAction}
|
||||
onResetToPending={queueManager.resetToPending}
|
||||
onRetryFailed={queueManager.retryFailedItems}
|
||||
onOpenPhotos={handleOpenPhotos}
|
||||
onOpenReviewManager={handleOpenReviewManager}
|
||||
onOpenItemEditor={handleOpenItemEditor}
|
||||
onClaimSubmission={queueManager.queue.claimSubmission}
|
||||
onDeleteSubmission={handleDeleteSubmission}
|
||||
onInteractionFocus={(id) => queueManager.markInteracting(id, true)}
|
||||
onInteractionBlur={(id) => queueManager.markInteracting(id, false)}
|
||||
/>
|
||||
</ModerationErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</TooltipProvider>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user