Files
supabase/apps/studio/hooks/misc/useDashboardHistory.ts
Joshen Lim ad30c04e0e Persist last visited explorer tab (#50557)
## Context

Saves the last visited explorer tab via `useDashboardHistory`, such that
landing back on `/explorer` will open the last visited page. Similar
behaviour to Table Editor and SQL Editor

Would also be useful when going between the SQL Editor and Explorer to
bring snippets over

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Explorer now remembers and restores the last visited query, chat, or
notebook tab.
* Automatically returns to the Explorer home screen when a saved tab is
unavailable.
* Displays a loading state while the last visited tab is being restored.

* **Bug Fixes**
  * Closing deleted chat tabs now clears their saved history.

* **Tests**
* Added coverage for Explorer tab restoration, loading states, and stale
history cleanup.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-21 16:34:50 +08:00

64 lines
1.8 KiB
TypeScript

import { LOCAL_STORAGE_KEYS, useParams } from 'common'
import { useCallback } from 'react'
import { useLocalStorageQuery } from './useLocalStorage'
type ExplorerHistoryEntry = { type: 'notebook' | 'query' | 'chat'; id: string }
type DashboardHistory = { editor?: string; sql?: string; explorer?: ExplorerHistoryEntry }
const DEFAULT_HISTORY = { editor: undefined, sql: undefined, explorer: undefined }
export const useDashboardHistory = () => {
// [Joshen] History should always refer to the project that the user is currently on
const { ref } = useParams()
const [history, setHistory, { isSuccess }] = useLocalStorageQuery<DashboardHistory>(
LOCAL_STORAGE_KEYS.DASHBOARD_HISTORY(ref ?? ''),
DEFAULT_HISTORY
)
const setLastVisitedTable = useCallback(
(id?: string) => {
setHistory((current) => ({ ...current, editor: id }))
},
[setHistory]
)
const setLastVisitedSnippet = useCallback(
(id?: string) => {
setHistory((current) => ({ ...current, sql: id }))
},
[setHistory]
)
const setLastVisitedExplorerTab = useCallback(
(entry?: ExplorerHistoryEntry) => {
setHistory((current) => ({ ...current, explorer: entry }))
},
[setHistory]
)
/**
* Purge the last-visited snippet when it's one of the deleted snippets, so that
* navigating back to the SQL editor doesn't resurrect a deleted snippet.
*/
const clearSnippetsFromHistory = useCallback(
(ids: string[]) => {
setHistory((current) =>
current.sql !== undefined && ids.includes(current.sql)
? { ...current, sql: undefined }
: current
)
},
[setHistory]
)
return {
history,
setLastVisitedTable,
setLastVisitedSnippet,
setLastVisitedExplorerTab,
clearSnippetsFromHistory,
isHistoryLoaded: isSuccess,
}
}