diff --git a/apps/studio/components/grid/SupabaseGrid.utils.ts b/apps/studio/components/grid/SupabaseGrid.utils.ts
index 5f6ae0cf5fa..cea90123024 100644
--- a/apps/studio/components/grid/SupabaseGrid.utils.ts
+++ b/apps/studio/components/grid/SupabaseGrid.utils.ts
@@ -1,6 +1,6 @@
import AwesomeDebouncePromise from 'awesome-debounce-promise'
-import { compact } from 'lodash'
-import { useEffect } from 'react'
+import { compact, filter } from 'lodash'
+import { useEffect, useState } from 'react'
import { CalculatedColumn, CellKeyboardEvent } from 'react-data-grid'
import type { Filter, SavedState } from 'components/grid/types'
@@ -11,6 +11,9 @@ import { FilterOperatorOptions } from './components/header/filter/Filter.constan
import { STORAGE_KEY_PREFIX } from './constants'
import type { Sort, SupaColumn, SupaTable } from './types'
import { formatClipboardValue } from './utils/common'
+import { parseAsArrayOf, parseAsBoolean, parseAsString, useQueryStates } from 'nuqs'
+
+export const LOAD_TAB_FROM_CACHE_PARAM = 'loadFromCache'
export function formatSortURLParams(tableName: string, sort?: string[]): Sort[] {
if (Array.isArray(sort)) {
@@ -176,41 +179,60 @@ export const saveTableEditorStateToLocalStorageDebounced = AwesomeDebouncePromis
500
)
-export function useLoadTableEditorStateFromLocalStorageIntoUrl({
+function getLatestParams() {
+ const queryParams = new URLSearchParams(window.location.search)
+ const sort = queryParams.getAll('sort')
+ const filter = queryParams.getAll('filter')
+ const loadFromCache = !!queryParams.get(LOAD_TAB_FROM_CACHE_PARAM)
+ return { sort, filter, loadFromCache }
+}
+
+export function useSyncTableEditorStateFromLocalStorageWithUrl({
projectRef,
table,
}: {
projectRef: string | undefined
table: Entity | undefined
}) {
- const [_, setParams] = useUrlState({
- arrayKeys: ['sort', 'filter'],
- })
+ const [urlParams, updateUrlParams] = useQueryStates(
+ {
+ sort: parseAsArrayOf(parseAsString).withDefault([]),
+ filter: parseAsArrayOf(parseAsString).withDefault([]),
+ [LOAD_TAB_FROM_CACHE_PARAM]: parseAsBoolean.withDefault(false),
+ },
+ {
+ history: 'replace',
+ }
+ )
+
useEffect(() => {
if (!projectRef || !table) {
return
}
- const searchParams = new URLSearchParams(window.location.search)
+ // `urlParams` from `useQueryStates` can be stale so always get the latest from the URL
+ const latestUrlParams = getLatestParams()
- const savedState = loadTableEditorStateFromLocalStorage(projectRef, table.name, table.schema)
-
- // If no sort params are set, use saved state
-
- let params: { sort?: string[]; filter?: string[] } | undefined
-
- if (searchParams.getAll('sort').length <= 0 && savedState?.sorts) {
- params = { ...params, sort: savedState.sorts }
+ if (latestUrlParams.loadFromCache) {
+ const savedState = loadTableEditorStateFromLocalStorage(projectRef, table.name, table.schema)
+ updateUrlParams(
+ {
+ sort: savedState?.sorts ?? [],
+ filter: savedState?.filters ?? [],
+ loadFromCache: false,
+ },
+ { clearOnDefault: true }
+ )
+ } else {
+ saveTableEditorStateToLocalStorage({
+ projectRef,
+ tableName: table.name,
+ schema: table.schema,
+ sorts: latestUrlParams.sort,
+ filters: latestUrlParams.filter,
+ })
}
-
- if (searchParams.getAll('filter').length <= 0 && savedState?.filters) {
- params = { ...params, filter: savedState.filters }
- }
-
- if (params) {
- setParams((prevParams) => ({ ...prevParams, ...params }))
- }
- }, [projectRef, table])
+ }, [urlParams, table, projectRef])
}
export const handleCopyCell = (
diff --git a/apps/studio/components/grid/hooks/useSaveTableEditorState.ts b/apps/studio/components/grid/hooks/useSaveTableEditorState.ts
deleted file mode 100644
index cab483a243b..00000000000
--- a/apps/studio/components/grid/hooks/useSaveTableEditorState.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-import { useCallback } from 'react'
-
-import { saveTableEditorStateToLocalStorage } from 'components/grid/SupabaseGrid.utils'
-import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
-import { useTableEditorTableStateSnapshot } from 'state/table-editor-table'
-
-/**
- * Hook for saving state and triggering side effects.
- */
-export function useSaveTableEditorState() {
- const { data: project } = useSelectedProjectQuery()
- const snap = useTableEditorTableStateSnapshot()
-
- const saveDataAndTriggerSideEffects = useCallback(
- (dataToSave: { filters?: string[]; sorts?: string[] }) => {
- const projectRef = project?.ref
-
- if (!projectRef) {
- return console.warn(
- '[useSaveTableEditorState] ProjectRef missing, cannot save or trigger side effects.'
- )
- }
-
- try {
- snap.setPage(1)
- snap.setEnforceExactCount(false)
-
- const tableName = snap.table?.name
- const schema = snap.table?.schema
-
- if (tableName) {
- saveTableEditorStateToLocalStorage({
- projectRef,
- tableName,
- schema,
- ...dataToSave,
- })
- } else {
- console.warn('[useSaveTableEditorState] Table name missing, skipping localStorage save.')
- }
- } catch (error) {
- console.error('[useSaveTableEditorState] Error during interaction with snapshot:', error)
- }
- },
- [snap, project]
- )
-
- const saveFiltersAndTriggerSideEffects = useCallback(
- (urlFilters: string[]) => saveDataAndTriggerSideEffects({ filters: urlFilters }),
- [saveDataAndTriggerSideEffects]
- )
- const saveSortsAndTriggerSideEffects = useCallback(
- (urlSorts: string[]) => saveDataAndTriggerSideEffects({ sorts: urlSorts }),
- [saveDataAndTriggerSideEffects]
- )
-
- return { saveFiltersAndTriggerSideEffects, saveSortsAndTriggerSideEffects }
-}
diff --git a/apps/studio/components/grid/hooks/useTableFilter.ts b/apps/studio/components/grid/hooks/useTableFilter.ts
index 0181bbce9ca..a93df6386bb 100644
--- a/apps/studio/components/grid/hooks/useTableFilter.ts
+++ b/apps/studio/components/grid/hooks/useTableFilter.ts
@@ -3,7 +3,6 @@ import { useCallback } from 'react'
import { filtersToUrlParams, formatFilterURLParams } from 'components/grid/SupabaseGrid.utils'
import type { Filter } from 'components/grid/types'
import { useTableEditorFiltersSort } from 'hooks/misc/useTableEditorFiltersSort'
-import { useSaveTableEditorState } from './useSaveTableEditorState'
/**
* Hook for managing table filter URL parameters and saving.
@@ -11,7 +10,6 @@ import { useSaveTableEditorState } from './useSaveTableEditorState'
*/
export function useTableFilter() {
const { filters: urlFilters, setParams } = useTableEditorFiltersSort()
- const { saveFiltersAndTriggerSideEffects } = useSaveTableEditorState()
const filters = formatFilterURLParams(urlFilters)
@@ -19,9 +17,8 @@ export function useTableFilter() {
(appliedFilters: Filter[]) => {
const newUrlFilters = filtersToUrlParams(appliedFilters)
setParams((prevParams) => ({ ...prevParams, filter: newUrlFilters }))
- saveFiltersAndTriggerSideEffects(newUrlFilters)
},
- [setParams, saveFiltersAndTriggerSideEffects]
+ [setParams]
)
return {
diff --git a/apps/studio/components/grid/hooks/useTableSort.ts b/apps/studio/components/grid/hooks/useTableSort.ts
index f114d9f1753..deb26d373f9 100644
--- a/apps/studio/components/grid/hooks/useTableSort.ts
+++ b/apps/studio/components/grid/hooks/useTableSort.ts
@@ -4,19 +4,16 @@ import { formatSortURLParams, sortsToUrlParams } from 'components/grid/SupabaseG
import type { Sort } from 'components/grid/types'
import { useTableEditorFiltersSort } from 'hooks/misc/useTableEditorFiltersSort'
import { useTableEditorTableStateSnapshot } from 'state/table-editor-table'
-import { useSaveTableEditorState } from './useSaveTableEditorState'
/**
* Hook for managing table sort URL parameters and saving.
* Uses snapshot ONLY to get table name for formatting/mapping.
- * Uses useSaveTableEditorState for saving and side effects.
* Does NOT format initial sorts (needs table name externally).
* Does NOT interact with snapshot directly.
*/
export function useTableSort() {
const { sorts: urlSorts, setParams } = useTableEditorFiltersSort()
const snap = useTableEditorTableStateSnapshot()
- const { saveSortsAndTriggerSideEffects } = useSaveTableEditorState()
const tableName = useMemo(() => snap.table?.name || '', [snap])
@@ -36,10 +33,8 @@ export function useTableSort() {
const newUrlSorts = sortsToUrlParams(sortsWithTable)
setParams((prevParams) => ({ ...prevParams, sort: newUrlSorts }))
-
- saveSortsAndTriggerSideEffects(newUrlSorts)
},
- [snap, setParams, saveSortsAndTriggerSideEffects]
+ [snap, setParams]
)
/**
diff --git a/apps/studio/components/interfaces/Database/Schemas/SchemaTableNode.tsx b/apps/studio/components/interfaces/Database/Schemas/SchemaTableNode.tsx
index c3800ff4cc5..5c354a6d784 100644
--- a/apps/studio/components/interfaces/Database/Schemas/SchemaTableNode.tsx
+++ b/apps/studio/components/interfaces/Database/Schemas/SchemaTableNode.tsx
@@ -1,3 +1,4 @@
+import { LOAD_TAB_FROM_CACHE_PARAM } from 'components/grid/SupabaseGrid.utils'
import { DiamondIcon, ExternalLink, Fingerprint, Hash, Key, Table2 } from 'lucide-react'
import Link from 'next/link'
import { Handle, NodeProps } from 'reactflow'
@@ -67,7 +68,9 @@ const TableNode = ({
{data.id && !placeholder && (
diff --git a/apps/studio/components/interfaces/Database/Tables/TableList.tsx b/apps/studio/components/interfaces/Database/Tables/TableList.tsx
index a54046da943..f88f13ebe16 100644
--- a/apps/studio/components/interfaces/Database/Tables/TableList.tsx
+++ b/apps/studio/components/interfaces/Database/Tables/TableList.tsx
@@ -63,6 +63,7 @@ import {
} from 'ui'
import { ProtectedSchemaWarning } from '../ProtectedSchemaWarning'
import { formatAllEntities } from './Tables.utils'
+import { LOAD_TAB_FROM_CACHE_PARAM } from 'components/grid/SupabaseGrid.utils'
interface TableListProps {
onAddTable: () => void
@@ -488,7 +489,9 @@ export const TableList = ({
- router.push(`/project/${project?.ref}/editor/${x.id}`)
+ router.push(
+ `/project/${project?.ref}/editor/${x.id}?${LOAD_TAB_FROM_CACHE_PARAM}=true`
+ )
}
onMouseEnter={() =>
prefetchEditorTablePage({
diff --git a/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx b/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx
index 4f7ac11ccf0..b9c6d19ce1c 100644
--- a/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx
+++ b/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx
@@ -5,7 +5,7 @@ import { useCallback } from 'react'
import { useParams } from 'common'
import { SupabaseGrid } from 'components/grid/SupabaseGrid'
-import { useLoadTableEditorStateFromLocalStorageIntoUrl } from 'components/grid/SupabaseGrid.utils'
+import { useSyncTableEditorStateFromLocalStorageWithUrl } from 'components/grid/SupabaseGrid.utils'
import {
Entity,
isForeignTable,
@@ -41,13 +41,12 @@ export const TableGridEditor = ({
const tabs = useTabsStateSnapshot()
- useLoadTableEditorStateFromLocalStorageIntoUrl({
+ useSyncTableEditorStateFromLocalStorageWithUrl({
projectRef,
table: selectedTable,
})
const [{ view: selectedView = 'data' }] = useUrlState()
-
const { can: canEditTables } = useAsyncCheckPermissions(
PermissionAction.TENANT_SQL_ADMIN_WRITE,
'tables'
diff --git a/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx b/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx
index 84dff45cae3..e1291923de4 100644
--- a/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx
+++ b/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx
@@ -9,7 +9,7 @@ import {
MAX_EXPORT_ROW_COUNT,
MAX_EXPORT_ROW_COUNT_MESSAGE,
} from 'components/grid/components/header/Header'
-import { parseSupaTable } from 'components/grid/SupabaseGrid.utils'
+import { LOAD_TAB_FROM_CACHE_PARAM, parseSupaTable } from 'components/grid/SupabaseGrid.utils'
import {
formatTableRowsToSQL,
getEntityLintDetails,
@@ -237,7 +237,7 @@ const EntityListItem: ItemRenderer = ({
{
// Handle redirect to last opened table tab, or last table tab
if (lastOpenedTable !== undefined) {
- router.push(`/project/${projectRef}/editor/${history.editor}`)
+ router.push(
+ `/project/${projectRef}/editor/${history.editor}?${LOAD_TAB_FROM_CACHE_PARAM}=true`
+ )
} else if (lastTabId) {
const lastTab = tabStore.tabsMap[lastTabId]
- if (lastTab) router.push(`/project/${projectRef}/editor/${lastTab.metadata?.tableId}`)
+ if (lastTab)
+ router.push(
+ `/project/${projectRef}/editor/${lastTab.metadata?.tableId}?${LOAD_TAB_FROM_CACHE_PARAM}=true`
+ )
}
}
}, [isHistoryLoaded])
diff --git a/apps/studio/state/tabs.tsx b/apps/studio/state/tabs.tsx
index 348528335f3..f7ddd272e5d 100644
--- a/apps/studio/state/tabs.tsx
+++ b/apps/studio/state/tabs.tsx
@@ -1,3 +1,4 @@
+import { LOAD_TAB_FROM_CACHE_PARAM } from 'components/grid/SupabaseGrid.utils'
import { ENTITY_TYPE } from 'data/entity-types/entity-type-constants'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { partition } from 'lodash'
@@ -286,7 +287,7 @@ function createTabsState(projectRef: string) {
case 'f':
case 'p':
router.push(
- `/project/${router.query.ref}/editor/${tab.metadata?.tableId}?schema=${tab.metadata?.schema}`
+ `/project/${router.query.ref}/editor/${tab.metadata?.tableId}?schema=${tab.metadata?.schema}&${LOAD_TAB_FROM_CACHE_PARAM}=true`
)
break
}