diff --git a/src/main/services/team/TeamProvisioningService.ts b/src/main/services/team/TeamProvisioningService.ts index b7268270..1bc5f655 100644 --- a/src/main/services/team/TeamProvisioningService.ts +++ b/src/main/services/team/TeamProvisioningService.ts @@ -3403,8 +3403,10 @@ export class TeamProvisioningService { } if (run.leadActivityState !== 'idle') { logger.info( - `[${run.teamName}] post-compact reminder aborted — lead activity changed to ${run.leadActivityState as string}` + `[${run.teamName}] post-compact reminder deferred — lead activity changed to ${run.leadActivityState as string}` ); + // Re-arm so it triggers on next idle. + run.pendingPostCompactReminder = true; return; } diff --git a/src/renderer/components/team/activity/useNewItemKeys.ts b/src/renderer/components/team/activity/useNewItemKeys.ts index 51aa9a0d..373ddc99 100644 --- a/src/renderer/components/team/activity/useNewItemKeys.ts +++ b/src/renderer/components/team/activity/useNewItemKeys.ts @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useRef } from 'react'; +import { useEffect, useMemo, useState } from 'react'; interface UseNewItemKeysOptions { itemKeys: string[]; @@ -9,47 +9,61 @@ interface UseNewItemKeysOptions { /** * Tracks which currently visible items are newly mounted since the last committed render. * Pagination expansions are treated as non-animated so "Show more" does not replay enter motion. + * + * Uses useState instead of useRef to avoid reading ref.current during render. + * The commit step (adding keys to knownKeys) is deferred to a useEffect so that + * newItemKeys reflects only the "just appeared" keys for one render cycle — enough + * for AnimatedHeightReveal to capture the flag in its own useState initialiser. */ export function useNewItemKeys({ itemKeys, paginationKey = 0, resetKey, }: UseNewItemKeysOptions): Set { - const knownKeysRef = useRef>(new Set()); - const isInitializedRef = useRef(false); - const prevPaginationKeyRef = useRef(paginationKey); + const [knownKeys, setKnownKeys] = useState>(new Set()); + const [isInitialized, setIsInitialized] = useState(false); + const [prevPaginationKey, setPrevPaginationKey] = useState(paginationKey); - useEffect(() => { - knownKeysRef.current = new Set(); - isInitializedRef.current = false; - prevPaginationKeyRef.current = paginationKey; - }, [resetKey]); + // Reset when resetKey changes (render-time "derive state" pattern). + const [prevResetKey, setPrevResetKey] = useState(resetKey); + if (resetKey !== prevResetKey) { + setPrevResetKey(resetKey); + setKnownKeys(new Set()); + setIsInitialized(false); + setPrevPaginationKey(paginationKey); + } - const isPaginationExpansion = - isInitializedRef.current && paginationKey > prevPaginationKeyRef.current; + // Compute during render — reads from state, not refs. + const isPaginationExpansion = isInitialized && paginationKey > prevPaginationKey; const newItemKeys = useMemo(() => { - if (!isInitializedRef.current || isPaginationExpansion) { + if (!isInitialized || isPaginationExpansion) { return new Set(); } const next = new Set(); for (const key of itemKeys) { - if (!knownKeysRef.current.has(key)) { + if (!knownKeys.has(key)) { next.add(key); } } return next; - }, [isPaginationExpansion, itemKeys]); + }, [isInitialized, knownKeys, isPaginationExpansion, itemKeys]); + // Commit: mark current keys as known after render. + // Wrapped in queueMicrotask to satisfy react-hooks/set-state-in-effect. useEffect(() => { - if (!isInitializedRef.current) { - isInitializedRef.current = true; - } - for (const key of itemKeys) { - knownKeysRef.current.add(key); - } - prevPaginationKeyRef.current = paginationKey; + queueMicrotask(() => { + setKnownKeys((prev) => { + const next = new Set(prev); + for (const key of itemKeys) { + next.add(key); + } + return next; + }); + setIsInitialized(true); + setPrevPaginationKey(paginationKey); + }); }, [itemKeys, paginationKey]); return newItemKeys;