From 58d6b45c0e77c7205b42176f8193f6639bf31463 Mon Sep 17 00:00:00 2001 From: devswha <25837994+devswha@users.noreply.github.com> Date: Mon, 17 Aug 2026 02:04:58 +0900 Subject: [PATCH] fix(ui): refresh active project metadata after rename --- src/hooks/useProjectsState.test.ts | 32 +++++++++++++++++++++++++++++- src/hooks/useProjectsState.ts | 25 +++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/hooks/useProjectsState.test.ts b/src/hooks/useProjectsState.test.ts index 3aa77b4..1cdba05 100644 --- a/src/hooks/useProjectsState.test.ts +++ b/src/hooks/useProjectsState.test.ts @@ -3,7 +3,7 @@ import test from 'node:test'; import type { Project } from '../types/app'; -import { projectsHaveChanges, readProjectsResponse } from './useProjectsState'; +import { projectsHaveChanges, readProjectsResponse, reconcileSelectedProject } from './useProjectsState'; const autoProject: Project = { projectId: 'project-1', @@ -53,3 +53,33 @@ test('readProjectsResponse rejects ok responses whose body is not an array', asy assert.equal(await readProjectsResponse(objectBody, 'refreshing'), null); assert.equal(await readProjectsResponse(invalidJson, 'refreshing'), null); }); + +test('reconcileSelectedProject applies refreshed display metadata to the active project', () => { + const renamedProject = { ...autoProject, displayName: 'Renamed project' }; + + const reconciled = reconcileSelectedProject(autoProject, [renamedProject]); + + assert.equal(reconciled?.displayName, 'Renamed project'); +}); + +test('reconcileSelectedProject preserves expanded session pages during metadata refresh', () => { + const loadedProject: Project = { + ...autoProject, + sessions: [ + { id: 'session-1', summary: 'First' }, + { id: 'session-2', summary: 'Second' }, + ], + sessionMeta: { hasMore: false, total: 2 }, + }; + const renamedProject: Project = { + ...autoProject, + displayName: 'Renamed project', + sessions: [{ id: 'session-1', summary: 'First' }], + sessionMeta: { hasMore: true, total: 2 }, + }; + + const reconciled = reconcileSelectedProject(loadedProject, [renamedProject]); + + assert.equal(reconciled?.displayName, 'Renamed project'); + assert.deepEqual(reconciled?.sessions?.map((session) => session.id), ['session-1', 'session-2']); +}); diff --git a/src/hooks/useProjectsState.ts b/src/hooks/useProjectsState.ts index 36c4625..369f810 100644 --- a/src/hooks/useProjectsState.ts +++ b/src/hooks/useProjectsState.ts @@ -182,6 +182,27 @@ const mergeExpandedSessionPages = (previousProjects: Project[], incomingProjects }); }; +export const reconcileSelectedProject = ( + previousProject: Project | null, + freshProjects: Project[], +): Project | null => { + if (!previousProject) { + return null; + } + + const freshProject = freshProjects.find( + (project) => project.projectId === previousProject.projectId, + ); + if (!freshProject) { + return previousProject; + } + + const [mergedProject] = mergeExpandedSessionPages([previousProject], [freshProject]); + return projectsHaveChanges([previousProject], [mergedProject]) + ? mergedProject + : previousProject; +}; + const mergeProjectSessionPage = ( existingProject: Project, sessionsPage: ProjectSessionPage, @@ -455,6 +476,10 @@ export function useProjectsState({ return; } + setSelectedProject((previousProject) => + reconcileSelectedProject(previousProject, projectData), + ); + setProjects((prevProjects) => { const mergedProjects = mergeExpandedSessionPages(prevProjects, projectData);