diff --git a/apps/studio/components/interfaces/App/RouteValidationWrapper.tsx b/apps/studio/components/interfaces/App/RouteValidationWrapper.tsx
index 55aa8393983..c0ee4634e34 100644
--- a/apps/studio/components/interfaces/App/RouteValidationWrapper.tsx
+++ b/apps/studio/components/interfaces/App/RouteValidationWrapper.tsx
@@ -5,28 +5,22 @@ import { toast } from 'sonner'
import { LOCAL_STORAGE_KEYS, useIsLoggedIn, useIsMFAEnabled, useParams } from 'common'
import { useOrganizationsQuery } from 'data/organizations/organizations-query'
import { useProjectsQuery } from 'data/projects/projects-query'
+import { useDashboardHistory } from 'hooks/misc/useDashboardHistory'
import useLatest from 'hooks/misc/useLatest'
import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { IS_PLATFORM } from 'lib/constants'
-import { useAppStateSnapshot } from 'state/app-state'
// Ideally these could all be within a _middleware when we use Next 12
const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => {
const router = useRouter()
const { ref, slug, id } = useParams()
-
- const isLoggedIn = useIsLoggedIn()
- const snap = useAppStateSnapshot()
- const isUserMFAEnabled = useIsMFAEnabled()
-
const { data: organization } = useSelectedOrganizationQuery()
- const [dashboardHistory, _, { isSuccess: isSuccessStorage }] = useLocalStorageQuery(
- LOCAL_STORAGE_KEYS.DASHBOARD_HISTORY(ref ?? ''),
- { editor: undefined, sql: undefined }
- )
+ const isLoggedIn = useIsLoggedIn()
+ const isUserMFAEnabled = useIsMFAEnabled()
+ const { setLastVisitedSnippet, setLastVisitedTable } = useDashboardHistory()
const [__, setLastVisitedOrganization] = useLocalStorageQuery(
LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
''
@@ -106,22 +100,13 @@ const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => {
useEffect(() => {
if (ref !== undefined && id !== undefined) {
if (router.pathname.endsWith('/sql/[id]') && id !== 'new') {
- snap.setDashboardHistory(ref, 'sql', id)
+ setLastVisitedSnippet(id)
+ } else if (router.pathname.endsWith('/editor/[id]')) {
+ setLastVisitedTable(id)
}
- if (router.pathname.endsWith('/editor/[id]')) {
- snap.setDashboardHistory(ref, 'editor', id)
- }
- }
- }, [ref, id])
-
- useEffect(() => {
- // Load dashboard history into app state
- if (isSuccessStorage && ref) {
- snap.setDashboardHistory(ref, 'editor', dashboardHistory.editor)
- snap.setDashboardHistory(ref, 'sql', dashboardHistory.sql)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [isSuccessStorage, ref])
+ }, [ref, id])
useEffect(() => {
if (organization) {
diff --git a/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx b/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx
index fdeef8867e4..5a31e04f07c 100644
--- a/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx
+++ b/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx
@@ -13,9 +13,9 @@ import {
isView,
} from 'data/table-editor/table-editor-types'
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
+import { useDashboardHistory } from 'hooks/misc/useDashboardHistory'
import { useUrlState } from 'hooks/ui/useUrlState'
import { useIsProtectedSchema } from 'hooks/useProtectedSchemas'
-import { useAppStateSnapshot } from 'state/app-state'
import { TableEditorTableStateContextProvider } from 'state/table-editor-table'
import { createTabId, useTabsStateSnapshot } from 'state/tabs'
import { Button } from 'ui'
@@ -34,8 +34,8 @@ export const TableGridEditor = ({
selectedTable,
}: TableGridEditorProps) => {
const router = useRouter()
- const appSnap = useAppStateSnapshot()
const { ref: projectRef, id } = useParams()
+ const { setLastVisitedTable } = useDashboardHistory()
const tabs = useTabsStateSnapshot()
@@ -58,10 +58,6 @@ export const TableGridEditor = ({
const tabId = !!id ? tabs.openTabs.find((x) => x.endsWith(id)) : undefined
const openTabs = tabs.openTabs.filter((x) => !x.startsWith('sql'))
- const onClearDashboardHistory = useCallback(() => {
- if (projectRef) appSnap.setDashboardHistory(projectRef, 'editor', undefined)
- }, [appSnap, projectRef])
-
const onTableCreated = useCallback(
(table: { id: number }) => {
router.push(`/project/${projectRef}/editor/${table.id}`)
@@ -74,9 +70,14 @@ export const TableGridEditor = ({
if (selectedTable) {
// Close tab
const tabId = createTabId(selectedTable.entity_type, { id: selectedTable.id })
- tabs.handleTabClose({ id: tabId, router, editor: 'table', onClearDashboardHistory })
+ tabs.handleTabClose({
+ id: tabId,
+ router,
+ editor: 'table',
+ onClearDashboardHistory: () => setLastVisitedTable(undefined),
+ })
}
- }, [onClearDashboardHistory, router, selectedTable, tabs])
+ }, [router, selectedTable, tabs])
const { isSchemaLocked } = useIsProtectedSchema({ schema: selectedTable?.schema ?? '' })
@@ -111,7 +112,7 @@ export const TableGridEditor = ({
id: tabId,
router,
editor: 'table',
- onClearDashboardHistory,
+ onClearDashboardHistory: () => setLastVisitedTable(undefined),
})
}}
>
@@ -122,7 +123,7 @@ export const TableGridEditor = ({
asChild
type="default"
className="mt-2"
- onClick={() => appSnap.setDashboardHistory(projectRef, 'editor', undefined)}
+ onClick={() => setLastVisitedTable(undefined)}
>
Close tab
@@ -133,7 +134,7 @@ export const TableGridEditor = ({
asChild
type="default"
className="mt-2"
- onClick={() => appSnap.setDashboardHistory(projectRef, 'editor', undefined)}
+ onClick={() => setLastVisitedTable(undefined)}
>
Head back
diff --git a/apps/studio/components/layouts/Tabs/Tabs.tsx b/apps/studio/components/layouts/Tabs/Tabs.tsx
index c8d1b017aae..0816bd0a3f9 100644
--- a/apps/studio/components/layouts/Tabs/Tabs.tsx
+++ b/apps/studio/components/layouts/Tabs/Tabs.tsx
@@ -12,7 +12,7 @@ import { Plus, X } from 'lucide-react'
import { useRouter } from 'next/router'
import { useParams } from 'common'
-import { useAppStateSnapshot } from 'state/app-state'
+import { useDashboardHistory } from 'hooks/misc/useDashboardHistory'
import { editorEntityTypes, useTabsStateSnapshot, type Tab } from 'state/tabs'
import {
cn,
@@ -32,7 +32,7 @@ import { TabPreview } from './TabPreview'
export const EditorTabs = () => {
const { ref, id } = useParams()
const router = useRouter()
- const appSnap = useAppStateSnapshot()
+ const { setLastVisitedSnippet, setLastVisitedTable } = useDashboardHistory()
const editor = useEditorType()
const tabs = useTabsStateSnapshot()
@@ -68,8 +68,10 @@ export const EditorTabs = () => {
}
const onClearDashboardHistory = () => {
- if (ref && editor) {
- appSnap.setDashboardHistory(ref, editor === 'table' ? 'editor' : editor, undefined)
+ if (editor === 'table') {
+ setLastVisitedTable(undefined)
+ } else if (editor === 'sql') {
+ setLastVisitedSnippet(undefined)
}
}
diff --git a/apps/studio/hooks/misc/useDashboardHistory.ts b/apps/studio/hooks/misc/useDashboardHistory.ts
new file mode 100644
index 00000000000..11da087ace1
--- /dev/null
+++ b/apps/studio/hooks/misc/useDashboardHistory.ts
@@ -0,0 +1,30 @@
+import { LOCAL_STORAGE_KEYS, useParams } from 'common'
+import { useLocalStorageQuery } from './useLocalStorage'
+
+type DashboardHistory = { editor?: string; sql?: string }
+const DEFAULT_HISTORY = { editor: undefined, sql: 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(
+ LOCAL_STORAGE_KEYS.DASHBOARD_HISTORY(ref ?? ''),
+ DEFAULT_HISTORY
+ )
+
+ const setLastVisitedTable = (id?: string) => {
+ setHistory({ ...history, editor: id })
+ }
+
+ const setLastVisitedSnippet = (id?: string) => {
+ setHistory({ ...history, sql: id })
+ }
+
+ return {
+ history,
+ setLastVisitedTable,
+ setLastVisitedSnippet,
+ isHistoryLoaded: isSuccess,
+ }
+}
diff --git a/apps/studio/pages/project/[ref]/editor/index.tsx b/apps/studio/pages/project/[ref]/editor/index.tsx
index 6ba1073515f..6133db86b66 100644
--- a/apps/studio/pages/project/[ref]/editor/index.tsx
+++ b/apps/studio/pages/project/[ref]/editor/index.tsx
@@ -8,7 +8,7 @@ import { EditorBaseLayout } from 'components/layouts/editors/EditorBaseLayout'
import TableEditorLayout from 'components/layouts/TableEditorLayout/TableEditorLayout'
import { TableEditorMenu } from 'components/layouts/TableEditorLayout/TableEditorMenu'
import { NewTab } from 'components/layouts/Tabs/NewTab'
-import { useAppStateSnapshot } from 'state/app-state'
+import { useDashboardHistory } from 'hooks/misc/useDashboardHistory'
import { editorEntityTypes, useTabsStateSnapshot } from 'state/tabs'
import type { NextPageWithLayout } from 'types'
@@ -16,26 +16,28 @@ const TableEditorPage: NextPageWithLayout = () => {
const router = useRouter()
const { ref: projectRef } = useParams()
const tabStore = useTabsStateSnapshot()
- const appSnap = useAppStateSnapshot()
+ const { history, isHistoryLoaded } = useDashboardHistory()
const onTableCreated = (table: { id: number }) => {
router.push(`/project/${projectRef}/editor/${table.id}`)
}
useEffect(() => {
- const lastOpenedTab = appSnap.dashboardHistory.editor
+ if (isHistoryLoaded) {
+ const lastOpenedTable = history.editor
+ const lastTabId = tabStore.openTabs.find((id) =>
+ editorEntityTypes.table.includes(tabStore.tabsMap[id]?.type)
+ )
- const lastTabId = tabStore.openTabs.find((id) =>
- editorEntityTypes.table.includes(tabStore.tabsMap[id]?.type)
- )
- if (lastOpenedTab !== undefined) {
- router.push(`/project/${projectRef}/editor/${appSnap.dashboardHistory.editor}`)
- } else if (lastTabId) {
// Handle redirect to last opened table tab, or last table tab
- const lastTab = tabStore.tabsMap[lastTabId]
- if (lastTab) router.push(`/project/${projectRef}/editor/${lastTab.metadata?.tableId}`)
+ if (lastOpenedTable !== undefined) {
+ router.push(`/project/${projectRef}/editor/${history.editor}`)
+ } else if (lastTabId) {
+ const lastTab = tabStore.tabsMap[lastTabId]
+ if (lastTab) router.push(`/project/${projectRef}/editor/${lastTab.metadata?.tableId}`)
+ }
}
- }, [])
+ }, [isHistoryLoaded])
return (
<>
diff --git a/apps/studio/pages/project/[ref]/index.tsx b/apps/studio/pages/project/[ref]/index.tsx
index b786f450fe3..ec00ec1dd47 100644
--- a/apps/studio/pages/project/[ref]/index.tsx
+++ b/apps/studio/pages/project/[ref]/index.tsx
@@ -7,6 +7,7 @@ import type { NextPageWithLayout } from 'types'
const HomePage: NextPageWithLayout = () => {
const isHomeNew = useFlag('homeNew')
+
if (isHomeNew) {
return
}
diff --git a/apps/studio/pages/project/[ref]/sql/[id].tsx b/apps/studio/pages/project/[ref]/sql/[id].tsx
index 36acdb1ef89..558cfb8a90e 100644
--- a/apps/studio/pages/project/[ref]/sql/[id].tsx
+++ b/apps/studio/pages/project/[ref]/sql/[id].tsx
@@ -1,3 +1,4 @@
+import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect } from 'react'
@@ -9,8 +10,8 @@ import { useEditorType } from 'components/layouts/editors/EditorsLayout.hooks'
import SQLEditorLayout from 'components/layouts/SQLEditorLayout/SQLEditorLayout'
import { SQLEditorMenu } from 'components/layouts/SQLEditorLayout/SQLEditorMenu'
import { useContentIdQuery } from 'data/content/content-id-query'
-import Link from 'next/link'
-import { useAppStateSnapshot } from 'state/app-state'
+import { useDashboardHistory } from 'hooks/misc/useDashboardHistory'
+import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { SnippetWithContent, useSnippets, useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2'
import { createTabId, useTabsStateSnapshot } from 'state/tabs'
import type { NextPageWithLayout } from 'types'
@@ -20,11 +21,12 @@ import { Admonition } from 'ui-patterns'
const SqlEditor: NextPageWithLayout = () => {
const router = useRouter()
const { id, ref, content, skip } = useParams()
+ const { data: project } = useSelectedProjectQuery()
const editor = useEditorType()
const tabs = useTabsStateSnapshot()
- const appSnap = useAppStateSnapshot()
const snapV2 = useSqlEditorV2StateSnapshot()
+ const { history, setLastVisitedSnippet } = useDashboardHistory()
const allSnippets = useSnippets(ref!)
const snippet = allSnippets.find((x) => x.id === id)
@@ -49,21 +51,27 @@ const SqlEditor: NextPageWithLayout = () => {
const invalidId = isError && error.code === 400 && error.message.includes('Invalid uuid')
useEffect(() => {
- if (ref && data) {
- snapV2.setSnippet(ref, data as unknown as SnippetWithContent)
+ if (ref && data && project) {
+ // [Joshen] Check if snippet belongs to the current project
+ if (data.project_id === project.id) {
+ snapV2.setSnippet(ref, data as unknown as SnippetWithContent)
+ } else {
+ setLastVisitedSnippet(undefined)
+ router.push(`/project/${ref}/sql/new`)
+ }
}
- }, [ref, data])
+ }, [ref, data, project])
// Load the last visited snippet when landing on /new
useEffect(() => {
if (
id === 'new' &&
skip !== 'true' && // [Joshen] Skip flag implies to skip loading the last visited snippet
- appSnap.dashboardHistory.sql !== undefined &&
+ history.sql !== undefined &&
content === undefined
) {
- const snippet = allSnippets.find((snippet) => snippet.id === appSnap.dashboardHistory.sql)
- if (snippet !== undefined) router.push(`/project/${ref}/sql/${appSnap.dashboardHistory.sql}`)
+ const snippet = allSnippets.find((snippet) => snippet.id === history.sql)
+ if (snippet !== undefined) router.push(`/project/${ref}/sql/${history.sql}`)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [id, allSnippets, content])
@@ -104,9 +112,7 @@ const SqlEditor: NextPageWithLayout = () => {
id: tabId,
router,
editor,
- onClearDashboardHistory: () => {
- if (ref) appSnap.setDashboardHistory(ref, 'sql', undefined)
- },
+ onClearDashboardHistory: () => setLastVisitedSnippet(undefined),
})
}}
>
diff --git a/apps/studio/pages/project/[ref]/sql/index.tsx b/apps/studio/pages/project/[ref]/sql/index.tsx
index 893eee8c440..79aff70fbf1 100644
--- a/apps/studio/pages/project/[ref]/sql/index.tsx
+++ b/apps/studio/pages/project/[ref]/sql/index.tsx
@@ -6,35 +6,39 @@ import DefaultLayout from 'components/layouts/DefaultLayout'
import { EditorBaseLayout } from 'components/layouts/editors/EditorBaseLayout'
import SQLEditorLayout from 'components/layouts/SQLEditorLayout/SQLEditorLayout'
import { SQLEditorMenu } from 'components/layouts/SQLEditorLayout/SQLEditorMenu'
-import { useAppStateSnapshot } from 'state/app-state'
+import { useDashboardHistory } from 'hooks/misc/useDashboardHistory'
import { useTabsStateSnapshot } from 'state/tabs'
import type { NextPageWithLayout } from 'types'
-const TableEditorPage: NextPageWithLayout = () => {
+const SQLEditorIndexPage: NextPageWithLayout = () => {
const router = useRouter()
const { ref: projectRef } = useParams()
const store = useTabsStateSnapshot()
- const appSnap = useAppStateSnapshot()
+
+ const { history, isHistoryLoaded } = useDashboardHistory()
useEffect(() => {
- // Handle redirect to last opened snippet tab, or last snippet tab
- const lastOpenedTab = appSnap.dashboardHistory.sql
- const lastTabId = store.openTabs.find((id) => store.tabsMap[id]?.type === 'sql')
- if (lastOpenedTab !== undefined) {
- router.push(`/project/${projectRef}/sql/${appSnap.dashboardHistory.sql}`)
- } else if (lastTabId) {
- const lastTab = store.tabsMap[lastTabId]
- if (lastTab) router.push(`/project/${projectRef}/sql/${lastTab.id.replace('sql-', '')}`)
- } else {
- router.push(`/project/${projectRef}/sql/new`)
+ if (isHistoryLoaded) {
+ // Handle redirect to last opened snippet tab, or last snippet tab
+ const lastOpenedTab = history.sql
+ const lastTabId = store.openTabs.find((id) => store.tabsMap[id]?.type === 'sql')
+
+ if (lastOpenedTab !== undefined) {
+ router.push(`/project/${projectRef}/sql/${history.sql}`)
+ } else if (lastTabId) {
+ const lastTab = store.tabsMap[lastTabId]
+ if (lastTab) router.push(`/project/${projectRef}/sql/${lastTab.id.replace('sql-', '')}`)
+ } else {
+ router.push(`/project/${projectRef}/sql/new`)
+ }
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [])
+ }, [isHistoryLoaded])
return null
}
-TableEditorPage.getLayout = (page) => (
+SQLEditorIndexPage.getLayout = (page) => (
} product="SQL Editor">
{page}
@@ -42,4 +46,4 @@ TableEditorPage.getLayout = (page) => (
)
-export default TableEditorPage
+export default SQLEditorIndexPage
diff --git a/apps/studio/state/app-state.ts b/apps/studio/state/app-state.ts
index 133c53edd2e..d4533c5462e 100644
--- a/apps/studio/state/app-state.ts
+++ b/apps/studio/state/app-state.ts
@@ -1,19 +1,9 @@
import { proxy, snapshot, useSnapshot } from 'valtio'
-import { LOCAL_STORAGE_KEYS as COMMON_LOCAL_STORAGE_KEYS, LOCAL_STORAGE_KEYS } from 'common'
-type DashboardHistoryType = {
- sql?: string
- editor?: string
-}
-
-const EMPTY_DASHBOARD_HISTORY: DashboardHistoryType = {
- sql: undefined,
- editor: undefined,
-}
+import { LOCAL_STORAGE_KEYS as COMMON_LOCAL_STORAGE_KEYS } from 'common'
const getInitialState = () => {
return {
- dashboardHistory: EMPTY_DASHBOARD_HISTORY,
activeDocsSection: ['introduction'],
docsLanguage: 'js',
showProjectApiDocs: false,
@@ -31,16 +21,6 @@ const getInitialState = () => {
export const appState = proxy({
...getInitialState(),
- setDashboardHistory: (ref: string, key: 'sql' | 'editor', id: string | undefined) => {
- if (appState.dashboardHistory[key] !== id) {
- appState.dashboardHistory[key] = id
- localStorage.setItem(
- LOCAL_STORAGE_KEYS.DASHBOARD_HISTORY(ref),
- JSON.stringify(appState.dashboardHistory)
- )
- }
- },
-
activeDocsSection: ['introduction'],
docsLanguage: 'js' as 'js' | 'bash',
showProjectApiDocs: false,