fix: Implement merging for activity updates to preserve component state
This commit is contained in:
@@ -34,7 +34,8 @@ export function ActivityFeed({typeFilter, dateRangeDays = 30, contactId}: Activi
|
|||||||
setIsLoadingMore(true);
|
setIsLoadingMore(true);
|
||||||
} else {
|
} else {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setActivities([]);
|
// Don't clear activities immediately - we'll do a smart merge
|
||||||
|
// This preserves React component instances and their local state
|
||||||
setNextCursor(undefined);
|
setNextCursor(undefined);
|
||||||
setHasMore(true);
|
setHasMore(true);
|
||||||
}
|
}
|
||||||
@@ -58,11 +59,25 @@ export function ActivityFeed({typeFilter, dateRangeDays = 30, contactId}: Activi
|
|||||||
const result = await network.fetch<CursorPaginatedResponse<Activity>>('GET', `/activity?${params.toString()}`);
|
const result = await network.fetch<CursorPaginatedResponse<Activity>>('GET', `/activity?${params.toString()}`);
|
||||||
|
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
// Append to existing activities
|
// Append to existing activities (pagination)
|
||||||
setActivities(prev => [...prev, ...result.data]);
|
setActivities(prev => [...prev, ...result.data]);
|
||||||
} else {
|
} else {
|
||||||
// Replace activities
|
// Smart merge: preserve existing activity objects by ID to maintain component state
|
||||||
setActivities(result.data);
|
setActivities(prev => {
|
||||||
|
// If no previous activities, just use new data
|
||||||
|
if (prev.length === 0) {
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a map of existing activities by ID for fast lookup
|
||||||
|
const existingMap = new Map(prev.map(activity => [activity.id, activity]));
|
||||||
|
|
||||||
|
// For each new activity, reuse existing object if ID matches (preserves React component instances)
|
||||||
|
// Otherwise use new object. This maintains correct ordering while preserving component state.
|
||||||
|
return result.data.map(newActivity =>
|
||||||
|
existingMap.get(newActivity.id) ?? newActivity
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setNextCursor(result.cursor);
|
setNextCursor(result.cursor);
|
||||||
@@ -95,7 +110,21 @@ export function ActivityFeed({typeFilter, dateRangeDays = 30, contactId}: Activi
|
|||||||
|
|
||||||
const result = await network.fetch<{activities: Activity[]}>('GET', `/activity/upcoming?${params.toString()}`);
|
const result = await network.fetch<{activities: Activity[]}>('GET', `/activity/upcoming?${params.toString()}`);
|
||||||
|
|
||||||
setUpcomingActivities(result.activities);
|
// Smart merge: preserve existing activity objects by ID to maintain component state
|
||||||
|
setUpcomingActivities(prev => {
|
||||||
|
// If no previous activities, just use new data
|
||||||
|
if (prev.length === 0) {
|
||||||
|
return result.activities;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a map of existing activities by ID for fast lookup
|
||||||
|
const existingMap = new Map(prev.map(activity => [activity.id, activity]));
|
||||||
|
|
||||||
|
// For each new activity, reuse existing object if ID matches
|
||||||
|
return result.activities.map(newActivity =>
|
||||||
|
existingMap.get(newActivity.id) ?? newActivity
|
||||||
|
);
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error fetching upcoming activities:', err);
|
console.error('Error fetching upcoming activities:', err);
|
||||||
// Don't set error state for upcoming - just fail silently
|
// Don't set error state for upcoming - just fail silently
|
||||||
|
|||||||
Reference in New Issue
Block a user