diff --git a/apps/studio/state/tabs.test.ts b/apps/studio/state/tabs.test.ts new file mode 100644 index 00000000000..21915e58d46 --- /dev/null +++ b/apps/studio/state/tabs.test.ts @@ -0,0 +1,62 @@ +import { beforeEach, describe, expect, it } from 'vitest' + +import { createTabsState } from './tabs' +import { ENTITY_TYPE } from '@/data/entity-types/entity-type-constants' + +describe('tabs recent items', () => { + beforeEach(() => { + localStorage.clear() + }) + + it('refreshes an existing recent item label when the tab is re-added', () => { + const store = createTabsState('default') + + store.addRecentItem({ + id: 'r-1', + type: ENTITY_TYPE.TABLE, + label: 'tasks', + metadata: { + schema: 'public', + name: 'tasks', + tableId: 1, + }, + }) + + store.addRecentItem({ + id: 'r-1', + type: ENTITY_TYPE.TABLE, + label: 'routines', + metadata: { + schema: 'public', + name: 'routines', + tableId: 1, + }, + }) + + expect(store.recentItems).toHaveLength(1) + expect(store.recentItems[0].label).toBe('routines') + expect(store.recentItems[0].metadata?.name).toBe('routines') + }) + + it('keeps recent items aligned when an open tab label changes', () => { + const store = createTabsState('default') + + store.addTab({ + id: 'r-1', + type: ENTITY_TYPE.TABLE, + label: 'tasks', + metadata: { + schema: 'public', + name: 'tasks', + tableId: 1, + }, + isPreview: false, + }) + + store.updateTab('r-1', { label: 'routines' }) + + expect(store.tabsMap['r-1'].label).toBe('routines') + expect(store.recentItems[0].label).toBe('routines') + expect(store.recentItems[0].metadata?.name).toBe('routines') + }) +}) diff --git a/apps/studio/state/tabs.tsx b/apps/studio/state/tabs.tsx index 2ded8c04b08..d757d1c61ae 100644 --- a/apps/studio/state/tabs.tsx +++ b/apps/studio/state/tabs.tsx @@ -110,7 +110,21 @@ function getSavedTabs(ref: string) { } } -function createTabsState(projectRef: string) { +const getRecentItemLabel = (tab: Pick) => + tab.label || tab.metadata?.name || 'Untitled' + +const syncRecentItemWithTab = (item: RecentItem, tab: Pick) => { + const nextLabel = getRecentItemLabel(tab) + + item.label = nextLabel + item.metadata = { + ...item.metadata, + ...tab.metadata, + name: nextLabel, + } +} + +export function createTabsState(projectRef: string) { const recentItems = getSavedRecentItems(projectRef) const { openTabs, activeTab, tabsMap, previewTabId } = getSavedTabs(projectRef) @@ -125,6 +139,7 @@ function createTabsState(projectRef: string) { if (existingItem) { // If it exists, update its timestamp existingItem.timestamp = Date.now() + syncRecentItemWithTab(existingItem, tab) return // Exit the function } @@ -132,7 +147,7 @@ function createTabsState(projectRef: string) { const recentItem: RecentItem = { id: tab.id, // Set the ID type: tab.type, // Set the type - label: tab.label || 'Untitled', // Set the label or default to 'Untitled' + label: getRecentItemLabel(tab), // Set the label or default to 'Untitled' timestamp: Date.now(), // Set the current timestamp metadata: tab.metadata, // Set the metadata } @@ -211,6 +226,8 @@ function createTabsState(projectRef: string) { if (!!store.tabsMap[id]) { if ('label' in updates) { store.tabsMap[id].label = updates.label + const recentItem = store.recentItems.find((item) => item.id === id) + if (recentItem) syncRecentItemWithTab(recentItem, store.tabsMap[id]) } if ('scrollTop' in updates && store.tabsMap[id].metadata) { store.tabsMap[id].metadata.scrollTop = updates.scrollTop