| author | |
| committer | |
| log | eecb53498ad182f51ad1743280e8d22e5e1d116c |
| tree | 0fa7a3d09e120043257b60deb27d9e99f8d9788e |
| parent | 5cf140b0ee2d7320d101b082217a90afb7f230ce |
- Dispatch `MARK_THREAD_VISITED` when `activeThreadId` changes in root route
- Add reducer handling to update a thread's `lastVisitedAt`
- Add reducer test coverage for selecting a thread updates visit time3 files changed, 39 insertions(+), 0 deletions(-)
apps/web/src/routes/__root.tsx+9| ... | ... | @@ -67,6 +67,15 @@ function EventRouter() { |
| 67 | 67 | }); |
| 68 | 68 | }, [activeThreadId, api, dispatch, queryClient]); |
| 69 | 69 | |
| 70 | useEffect(() => { | |
| 71 | if (!activeThreadId) return; | |
| 72 | dispatch({ | |
| 73 | type: "MARK_THREAD_VISITED", | |
| 74 | threadId: activeThreadId, | |
| 75 | visitedAt: new Date().toISOString(), | |
| 76 | }); | |
| 77 | }, [activeThreadId, dispatch]); | |
| 78 | ||
| 70 | 79 | useEffect(() => { |
| 71 | 80 | if (!api) return; |
| 72 | 81 | return api.terminal.onEvent((event) => { |
apps/web/src/store.test.ts+18| ... | ... | @@ -815,6 +815,24 @@ describe("store reducer thread continuity", () => { |
| 815 | 815 | expect(next.threads[0]?.lastVisitedAt).toBe(completedAt); |
| 816 | 816 | }); |
| 817 | 817 | |
| 818 | it("marks a thread as visited when selected", () => { | |
| 819 | const state = makeState( | |
| 820 | makeThread({ | |
| 821 | latestTurnCompletedAt: "2026-02-08T10:00:10.000Z", | |
| 822 | lastVisitedAt: "2026-02-08T10:00:00.000Z", | |
| 823 | }), | |
| 824 | ); | |
| 825 | ||
| 826 | const nextVisitedAt = "2026-02-08T10:00:20.000Z"; | |
| 827 | const next = reducer(state, { | |
| 828 | type: "MARK_THREAD_VISITED", | |
| 829 | threadId: "thread-local-1", | |
| 830 | visitedAt: nextVisitedAt, | |
| 831 | }); | |
| 832 | ||
| 833 | expect(next.threads[0]?.lastVisitedAt).toBe(nextVisitedAt); | |
| 834 | }); | |
| 835 | ||
| 818 | 836 | it("reverts thread state to a checkpoint snapshot", () => { |
| 819 | 837 | const state = makeState( |
| 820 | 838 | makeThread({ |
apps/web/src/store.ts+12| ... | ... | @@ -105,6 +105,7 @@ type Action = |
| 105 | 105 | branch: string | null; |
| 106 | 106 | worktreePath: string | null; |
| 107 | 107 | } |
| 108 | | { type: "MARK_THREAD_VISITED"; threadId: string; visitedAt?: string } | |
| 108 | 109 | | { type: "SET_RUNTIME_MODE"; mode: RuntimeMode } |
| 109 | 110 | | { type: "DELETE_THREAD"; threadId: string }; |
| 110 | 111 | |
| ... | ... | @@ -1141,6 +1142,17 @@ export function reducer(state: AppState, action: Action): AppState { |
| 1141 | 1142 | }; |
| 1142 | 1143 | } |
| 1143 | 1144 | |
| 1145 | case "MARK_THREAD_VISITED": { | |
| 1146 | const visitedAt = action.visitedAt ?? new Date().toISOString(); | |
| 1147 | return { | |
| 1148 | ...state, | |
| 1149 | threads: updateThread(state.threads, action.threadId, (t) => ({ | |
| 1150 | ...t, | |
| 1151 | lastVisitedAt: visitedAt, | |
| 1152 | })), | |
| 1153 | }; | |
| 1154 | } | |
| 1155 | ||
| 1144 | 1156 | case "SET_RUNTIME_MODE": |
| 1145 | 1157 | return { |
| 1146 | 1158 | ...state, |