From 7f20a5efa2f056e5df96e7097d2d35af67bcfccc Mon Sep 17 00:00:00 2001 From: hugo Date: Fri, 20 Feb 2026 22:19:59 +0100 Subject: [PATCH] fix; tab bars didn't update on post creation without restart of app --- src/renderer/components/TabBar/TabBar.tsx | 35 +++++++++++++++++-- tests/renderer/components/TabBar.test.tsx | 41 ++++++++++++++++++++++- 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/src/renderer/components/TabBar/TabBar.tsx b/src/renderer/components/TabBar/TabBar.tsx index 574780e..62d55dc 100644 --- a/src/renderer/components/TabBar/TabBar.tsx +++ b/src/renderer/components/TabBar/TabBar.tsx @@ -179,6 +179,7 @@ export const TabBar: React.FC = () => { const { tabs, activeTabId, + posts, media, activeProject, dirtyPosts, @@ -199,6 +200,34 @@ export const TabBar: React.FC = () => { // Fetch post titles from database for post tabs useEffect(() => { const postTabs = tabs.filter(t => t.type === 'post'); + const postTabIds = new Set(postTabs.map(t => t.id)); + + setPostTitles((previous) => { + const next = new Map(previous); + let changed = false; + + for (const id of Array.from(next.keys())) { + if (!postTabIds.has(id)) { + next.delete(id); + changed = true; + } + } + + for (const post of posts) { + if (!postTabIds.has(post.id)) { + continue; + } + + const title = post.title || 'Untitled'; + if (next.get(post.id) !== title) { + next.set(post.id, title); + changed = true; + } + } + + return changed ? next : previous; + }); + if (postTabs.length === 0) return; const fetchTitles = async () => { @@ -206,7 +235,9 @@ export const TabBar: React.FC = () => { let changed = false; for (const tab of postTabs) { - if (!postTitles.has(tab.id)) { + const postInStore = posts.find((post) => post.id === tab.id); + + if (!postInStore) { try { const post = await window.electronAPI?.posts.get(tab.id); if (post) { @@ -225,7 +256,7 @@ export const TabBar: React.FC = () => { }; fetchTitles(); - }, [tabs]); // Note: intentionally not including postTitles to avoid infinite loops + }, [tabs, posts]); // Note: intentionally not including postTitles to avoid infinite loops // Listen for post updates to refresh titles useEffect(() => { diff --git a/tests/renderer/components/TabBar.test.tsx b/tests/renderer/components/TabBar.test.tsx index 3915286..81820c5 100644 --- a/tests/renderer/components/TabBar.test.tsx +++ b/tests/renderer/components/TabBar.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { render, screen } from '@testing-library/react'; +import { act, render, screen } from '@testing-library/react'; import { TabBar } from '../../../src/renderer/components/TabBar/TabBar'; import { useAppStore } from '../../../src/renderer/store'; @@ -97,4 +97,43 @@ describe('TabBar', () => { expect(await screen.findByText('Style')).toBeInTheDocument(); }); + + it('updates post tab title when post title changes in store', async () => { + useAppStore.setState({ + tabs: [{ type: 'post', id: 'post-1', isTransient: false }], + activeTabId: 'post-1', + posts: [{ + id: 'post-1', + title: '', + slug: 'post-1', + content: '', + excerpt: null, + author: null, + status: 'draft', + publishedAt: null, + scheduledAt: null, + tags: [], + categories: [], + metadata: {}, + projectId: 'project-1', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }], + }); + + (window as any).electronAPI.posts.get = vi.fn().mockResolvedValue({ + id: 'post-1', + title: '', + }); + + render(); + + expect(await screen.findByText('Untitled')).toBeInTheDocument(); + + act(() => { + useAppStore.getState().updatePost('post-1', { title: 'Updated Title' }); + }); + + expect(await screen.findByText('Updated Title')).toBeInTheDocument(); + }); });