mirror of
https://github.com/supabase/supabase.git
synced 2026-10-09 11:25:06 +03:00
## What Final PR of the SQL editor state re-layering stack. Removes the compatibility shims left in place during the migration: - Migrates all **23** consumers of the `@/state/sql-editor-v2` facade to import directly from `@/state/sql-editor/sql-editor-state`, where `useSqlEditorV2StateSnapshot`, `getSqlEditorV2StateSnapshot`, `useSnippets`, and `useSnippetFolders` actually live. - Deletes `state/sql-editor-v2.ts` (the facade) and `state/sql-editor/index.ts` (the barrel). Both re-exported the same symbols; nothing imports them after the migration. This collapses the two-layer re-export (`sql-editor-v2` → `index` → source) into direct source imports, matching the repo convention to avoid barrel re-export files. ## Notes - Pure import-path migration — no behavior change. All 23 consumers imported only value symbols that resolve to `sql-editor-state.ts`; none imported the `StateSnippet`/`StateSnippetFolder` types via the facade. - Symbol names keep their `V2` suffix for now — renaming `useSqlEditorV2StateSnapshot` etc. is a separate, larger churn best done on its own. - 25 files: 23 one-line import changes + 2 deletions (23 insertions / 39 deletions). ## Validation - `pnpm --filter studio typecheck` ✅ (confirms no dangling facade/barrel imports anywhere) - `pnpm exec vitest --run state/sql-editor/` ✅ (113 passed) - lint ✅ (0 errors; no ratcheted-rule regressions — a path swap can't add `any`/deps/nested-component violations, and no import-order rule is enforced) - grep confirms zero remaining `sql-editor-v2` references --------- Co-authored-by: supabase-autofix-bot <noreply@supabase.com>
82 lines
3.6 KiB
TypeScript
82 lines
3.6 KiB
TypeScript
import { useParams } from 'common'
|
|
import { useRouter } from 'next/router'
|
|
import { useEffect, useEffectEvent } from 'react'
|
|
|
|
import { createSqlSnippetSkeletonV2 } from './SQLEditor.utils'
|
|
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
|
import { useProfile } from '@/lib/profile'
|
|
import { wasNeverPersisted } from '@/state/sql-editor/sql-editor-lifecycle'
|
|
import { canEditSnippet } from '@/state/sql-editor/sql-editor-rules'
|
|
import { useSqlEditorV2StateSnapshot } from '@/state/sql-editor/sql-editor-state'
|
|
import { useTabsStateSnapshot } from '@/state/tabs'
|
|
|
|
/**
|
|
* Owns the editing lifecycle of a single SQL snippet: creating the snippet in
|
|
* the store on first edit (and routing to its URL), writing changes back to the
|
|
* store via `setSql`, and seeding the editor from a `?content=` deep link.
|
|
*
|
|
* Edits are written to the store synchronously on every change; debouncing the
|
|
* actual persistence is the save mechanism's job (see `createSaveMechanism`), so
|
|
* the store — and therefore the snippet's dirty status — always reflects the
|
|
* latest edit immediately.
|
|
*
|
|
* Extracted from `MonacoEditor` so that component stays a thin editor shell.
|
|
*/
|
|
export function useSnippetEditor({ id, snippetName }: { id: string; snippetName: string }) {
|
|
const router = useRouter()
|
|
const { profile } = useProfile()
|
|
const { ref, content } = useParams()
|
|
const { data: project } = useSelectedProjectQuery()
|
|
|
|
const snapV2 = useSqlEditorV2StateSnapshot()
|
|
const tabsSnap = useTabsStateSnapshot()
|
|
|
|
const snippet = snapV2.snippets[id]
|
|
const disableEdit = !!snippet && !canEditSnippet(snippet.snippet, profile?.id)
|
|
|
|
function handleEditorChange(value: string | undefined) {
|
|
tabsSnap.makeActiveTabPermanent()
|
|
if (!id || !value) return
|
|
|
|
if (!snippet && ref && profile !== undefined && project !== undefined) {
|
|
const newSnippet = createSqlSnippetSkeletonV2({
|
|
idOverride: id,
|
|
name: snippetName,
|
|
sql: value,
|
|
owner_id: profile?.id,
|
|
project_id: project?.id,
|
|
})
|
|
snapV2.addSnippet({ projectRef: ref, snippet: newSnippet })
|
|
// When the editor was seeded from a `content` deep-link, replace rather
|
|
// than push. The caller navigated to `/sql/new?content=...` (a long,
|
|
// one-shot URL); replacing collapses it out of history so Back returns to
|
|
// the originating page instead of a wasted step that re-seeds the snippet.
|
|
if (router.query.content !== undefined) {
|
|
router.replace(`/project/${ref}/sql/${newSnippet.id}`, undefined, { shallow: true })
|
|
} else {
|
|
router.push(`/project/${ref}/sql/${newSnippet.id}`, undefined, { shallow: true })
|
|
}
|
|
}
|
|
|
|
// A snippet that has never been persisted needs the snippet/folder lists
|
|
// invalidated so it shows up in the sidebar. If `snippet` was undefined at
|
|
// render we just created one above (status 'new'), which always qualifies.
|
|
const shouldInvalidate = snippet ? wasNeverPersisted(snippet.snippet.status) : true
|
|
snapV2.setSql({ id, sql: value, shouldInvalidate })
|
|
}
|
|
|
|
// if an SQL query is passed by the content parameter, set the editor value to its content. This
|
|
// is usually used for sending the user to SQL editor from other pages with SQL.
|
|
const seedFromContentParam = useEffectEvent(() => {
|
|
if (content && content.length > 0) handleEditorChange(content)
|
|
})
|
|
useEffect(() => {
|
|
seedFromContentParam()
|
|
// The useEffectEvent return is stable and must not be a dependency; this
|
|
// disable can go once our eslint version understands useEffectEvent.
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [])
|
|
|
|
return { snippet, disableEdit, handleEditorChange }
|
|
}
|