story-kit: merge 137_bug_lozengeflycontext_animation_queue_race_condition_on_rapid_updates
This commit is contained in:
@@ -101,6 +101,11 @@ export function LozengeFlyProvider({
|
||||
const pendingFlyInActionsRef = useRef<PendingFlyIn[]>([]);
|
||||
const pendingFlyOutActionsRef = useRef<PendingFlyOut[]>([]);
|
||||
|
||||
// Track the active animation ID per story/agent so stale timeouts
|
||||
// from superseded animations don't prematurely clear state.
|
||||
const activeFlyInPerStory = useRef<Map<string, string>>(new Map());
|
||||
const activeFlyOutPerAgent = useRef<Map<string, string>>(new Map());
|
||||
|
||||
const [pendingFlyIns, setPendingFlyIns] = useState<ReadonlySet<string>>(
|
||||
new Set(),
|
||||
);
|
||||
@@ -258,6 +263,7 @@ export function LozengeFlyProvider({
|
||||
|
||||
const rosterRect = rosterEl.getBoundingClientRect();
|
||||
const id = `fly-in-${action.agentName}-${action.storyId}-${Date.now()}`;
|
||||
activeFlyInPerStory.current.set(action.storyId, id);
|
||||
|
||||
setFlyingLozenges((prev) => [
|
||||
...prev,
|
||||
@@ -282,14 +288,19 @@ export function LozengeFlyProvider({
|
||||
});
|
||||
});
|
||||
|
||||
// After the transition completes, remove clone and reveal slot lozenge
|
||||
// After the transition completes, remove clone and reveal slot lozenge.
|
||||
// Only clear pendingFlyIns if this is still the active animation for
|
||||
// this story — a newer animation may have superseded this one.
|
||||
setTimeout(() => {
|
||||
setFlyingLozenges((prev) => prev.filter((l) => l.id !== id));
|
||||
setPendingFlyIns((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.delete(action.storyId);
|
||||
return next;
|
||||
});
|
||||
if (activeFlyInPerStory.current.get(action.storyId) === id) {
|
||||
activeFlyInPerStory.current.delete(action.storyId);
|
||||
setPendingFlyIns((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.delete(action.storyId);
|
||||
return next;
|
||||
});
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
@@ -307,6 +318,7 @@ export function LozengeFlyProvider({
|
||||
|
||||
const rosterRect = rosterEl?.getBoundingClientRect();
|
||||
const id = `fly-out-${action.agentName}-${action.storyId}-${Date.now()}`;
|
||||
activeFlyOutPerAgent.current.set(action.agentName, id);
|
||||
|
||||
setFlyingLozenges((prev) => [
|
||||
...prev,
|
||||
@@ -330,14 +342,18 @@ export function LozengeFlyProvider({
|
||||
});
|
||||
});
|
||||
|
||||
// Only reveal the roster badge if this is still the active fly-out
|
||||
// for this agent — a newer fly-out may have superseded this one.
|
||||
setTimeout(() => {
|
||||
setFlyingLozenges((prev) => prev.filter((l) => l.id !== id));
|
||||
// Reveal the roster badge now that the clone has landed.
|
||||
setFlyingOutAgents((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.delete(action.agentName);
|
||||
return next;
|
||||
});
|
||||
if (activeFlyOutPerAgent.current.get(action.agentName) === id) {
|
||||
activeFlyOutPerAgent.current.delete(action.agentName);
|
||||
setFlyingOutAgents((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.delete(action.agentName);
|
||||
return next;
|
||||
});
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
}, [pipeline]);
|
||||
|
||||
Reference in New Issue
Block a user