From 97ff8d190d192aab353ff7d197591d1583f44e4f Mon Sep 17 00:00:00 2001 From: Arunavo Ray Date: Sat, 24 May 2025 21:06:53 +0530 Subject: [PATCH] refactor: update ActivityList and ActivityLog components to improve loading state management and add live active indicator --- src/components/activity/ActivityList.tsx | 64 ++++++++++++++++++++---- src/components/activity/ActivityLog.tsx | 48 +++++++++++------- 2 files changed, 82 insertions(+), 30 deletions(-) diff --git a/src/components/activity/ActivityList.tsx b/src/components/activity/ActivityList.tsx index 788ea35..f433436 100644 --- a/src/components/activity/ActivityList.tsx +++ b/src/components/activity/ActivityList.tsx @@ -14,6 +14,7 @@ type MirrorJobWithKey = MirrorJob & { _rowKey: string }; interface ActivityListProps { activities: MirrorJobWithKey[]; isLoading: boolean; + isLiveActive?: boolean; filter: FilterParams; setFilter: (filter: FilterParams) => void; } @@ -21,6 +22,7 @@ interface ActivityListProps { export default function ActivityList({ activities, isLoading, + isLiveActive = false, filter, setFilter, }: ActivityListProps) { @@ -120,18 +122,19 @@ export default function ActivityList({ } return ( - -
+ - {virtualizer.getVirtualItems().map((vRow) => { +
+ {virtualizer.getVirtualItems().map((vRow) => { const activity = filteredActivities[vRow.index]; const isExpanded = expandedItems.has(activity._rowKey); @@ -213,5 +216,44 @@ export default function ActivityList({ })}
+ + {/* Status Bar */} +
+
+
+ + {filteredActivities.length} {filteredActivities.length === 1 ? 'activity' : 'activities'} total + +
+ + {/* Center - Live active indicator */} + {isLiveActive && ( +
+
+ + Live active + +
+
+ )} + + {(filter.searchTerm || filter.status || filter.type || filter.name) && ( + + Filters applied + + )} +
+
); } diff --git a/src/components/activity/ActivityLog.tsx b/src/components/activity/ActivityLog.tsx index fe7abef..246c1ad 100644 --- a/src/components/activity/ActivityLog.tsx +++ b/src/components/activity/ActivityLog.tsx @@ -67,12 +67,12 @@ function deepClone(obj: T): T { export function ActivityLog() { const { user } = useAuth(); - const { registerRefreshCallback } = useLiveRefresh(); + const { registerRefreshCallback, isLiveEnabled } = useLiveRefresh(); const { isFullyConfigured } = useConfigStatus(); const { navigationKey } = useNavigation(); const [activities, setActivities] = useState([]); - const [isLoading, setIsLoading] = useState(false); + const [isInitialLoading, setIsInitialLoading] = useState(false); const [showCleanupDialog, setShowCleanupDialog] = useState(false); // Ref to track if component is mounted to prevent state updates after unmount @@ -138,11 +138,14 @@ export function ActivityLog() { /* ------------------------- initial fetch --------------------------- */ - const fetchActivities = useCallback(async () => { + const fetchActivities = useCallback(async (isLiveRefresh = false) => { if (!user?.id) return false; try { - setIsLoading(true); + // Set appropriate loading state based on refresh type + if (!isLiveRefresh) { + setIsInitialLoading(true); + } const res = await apiRequest( `/activities?userId=${user.id}`, @@ -150,7 +153,10 @@ export function ActivityLog() { ); if (!res.success) { - toast.error(res.message ?? 'Failed to fetch activities.'); + // Only show error toast for manual refreshes to avoid spam during live updates + if (!isLiveRefresh) { + toast.error(res.message ?? 'Failed to fetch activities.'); + } return false; } @@ -176,22 +182,25 @@ export function ActivityLog() { return true; } catch (err) { if (isMountedRef.current) { - toast.error( - err instanceof Error ? err.message : 'Failed to fetch activities.', - ); + // Only show error toast for manual refreshes to avoid spam during live updates + if (!isLiveRefresh) { + toast.error( + err instanceof Error ? err.message : 'Failed to fetch activities.', + ); + } } return false; } finally { - if (isMountedRef.current) { - setIsLoading(false); + if (isMountedRef.current && !isLiveRefresh) { + setIsInitialLoading(false); } } }, [user?.id]); // Only depend on user.id, not entire user object useEffect(() => { // Reset loading state when component becomes active - setIsLoading(true); - fetchActivities(); + setIsInitialLoading(true); + fetchActivities(false); // Manual refresh, not live }, [fetchActivities, navigationKey]); // Include navigationKey to trigger on navigation // Register with global live refresh system @@ -203,7 +212,7 @@ export function ActivityLog() { } const unregister = registerRefreshCallback(() => { - fetchActivities(); + fetchActivities(true); // Live refresh }); return unregister; @@ -301,7 +310,7 @@ export function ActivityLog() { if (!user?.id) return; try { - setIsLoading(true); + setIsInitialLoading(true); setShowCleanupDialog(false); // Use fetch directly to avoid potential axios issues @@ -329,7 +338,7 @@ export function ActivityLog() { console.error('Error cleaning up activities:', error); toast.error(error instanceof Error ? error.message : 'Failed to cleanup activities.'); } finally { - setIsLoading(false); + setIsInitialLoading(false); } }; @@ -430,7 +439,7 @@ export function ActivityLog() {