diff --git a/apps/studio/components/interfaces/Auth/Policies/AIPolicyEditorPanel/LockedQuerySection.tsx b/apps/studio/components/interfaces/Auth/Policies/AIPolicyEditorPanel/LockedQuerySection.tsx
index 02dd1daac6b..224a8499203 100644
--- a/apps/studio/components/interfaces/Auth/Policies/AIPolicyEditorPanel/LockedQuerySection.tsx
+++ b/apps/studio/components/interfaces/Auth/Policies/AIPolicyEditorPanel/LockedQuerySection.tsx
@@ -3,11 +3,11 @@ import { Lock } from 'lucide-react'
import { useRouter } from 'next/router'
import { useParams } from 'common'
-import { useTableEditorStateSnapshot } from 'state/table-editor'
import { Button } from 'ui'
import { generateAlterPolicyQuery, generateCreatePolicyQuery } from './AIPolicyEditorPanel.utils'
interface LockedCreateQuerySection {
+ schema: string
selectedPolicy?: PostgresPolicy
formFields: { name: string; table: string; behavior: string; command: string; roles: string }
editorOneRef: any
@@ -15,6 +15,7 @@ interface LockedCreateQuerySection {
}
export const LockedCreateQuerySection = ({
+ schema,
selectedPolicy,
formFields,
editorOneRef,
@@ -22,7 +23,6 @@ export const LockedCreateQuerySection = ({
}: LockedCreateQuerySection) => {
const router = useRouter()
const { ref } = useParams()
- const state = useTableEditorStateSnapshot()
const isEditing = selectedPolicy !== undefined
const { name, table, behavior, command, roles } = formFields
@@ -44,7 +44,7 @@ export const LockedCreateQuerySection = ({
const query = isEditing
? generateCreatePolicyQuery({
name,
- schema: state.selectedSchemaName,
+ schema,
table,
behavior,
command,
@@ -58,7 +58,7 @@ export const LockedCreateQuerySection = ({
: generateAlterPolicyQuery({
name: '',
newName: name,
- schema: state.selectedSchemaName,
+ schema,
table,
command,
roles: roles.length === 0 ? 'public' : roles,
@@ -84,7 +84,7 @@ export const LockedCreateQuerySection = ({
2
- on "{state.selectedSchemaName}"."
+ on "{schema}"."
{table}"
@@ -124,16 +124,16 @@ export const LockedCreateQuerySection = ({
export const LockedRenameQuerySection = ({
oldName,
newName,
+ schema,
table,
lineNumber,
}: {
oldName: string
newName: string
+ schema: string
table: string
lineNumber: number
}) => {
- const state = useTableEditorStateSnapshot()
-
return (
@@ -153,7 +153,7 @@ export const LockedRenameQuerySection = ({
- on "{state.selectedSchemaName}"."{table}"
+ on "{schema}"."{table}"
diff --git a/apps/studio/components/interfaces/Auth/Policies/AIPolicyEditorPanel/PolicyDetailsV2.tsx b/apps/studio/components/interfaces/Auth/Policies/AIPolicyEditorPanel/PolicyDetailsV2.tsx
index 07fb57e485f..a7d1e848a92 100644
--- a/apps/studio/components/interfaces/Auth/Policies/AIPolicyEditorPanel/PolicyDetailsV2.tsx
+++ b/apps/studio/components/interfaces/Auth/Policies/AIPolicyEditorPanel/PolicyDetailsV2.tsx
@@ -1,10 +1,8 @@
import { useEffect } from 'react'
-import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { useDatabaseRolesQuery } from 'data/database-roles/database-roles-query'
import { useTablesQuery } from 'data/tables/tables-query'
-import { useTableEditorStateSnapshot } from 'state/table-editor'
import {
FormControl_Shadcn_,
FormField_Shadcn_,
@@ -24,6 +22,7 @@ import {
import { MultiSelectV2 } from 'ui-patterns/MultiSelect/MultiSelectV2'
interface PolicyDetailsV2Props {
+ schema: string
searchString?: string
isEditing: boolean
form: any
@@ -31,18 +30,18 @@ interface PolicyDetailsV2Props {
}
export const PolicyDetailsV2 = ({
+ schema,
searchString,
isEditing,
form,
onUpdateCommand,
}: PolicyDetailsV2Props) => {
const { project } = useProjectContext()
- const snap = useTableEditorStateSnapshot()
const { data: tables, isSuccess: isSuccessTables } = useTablesQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
- schema: snap.selectedSchemaName,
+ schema: schema,
sortByProperty: 'name',
includeColumns: true,
})
@@ -66,7 +65,7 @@ export const PolicyDetailsV2 = ({
if (!isEditing) {
const table = tables?.find(
(table) =>
- table.schema === snap.selectedSchemaName &&
+ table.schema === schema &&
(table.id.toString() === searchString || table.name === searchString)
)
if (table) {
@@ -117,7 +116,7 @@ export const PolicyDetailsV2 = ({
onValueChange={(value) => form.setValue('table', value)}
>
- {snap.selectedSchemaName}.{field.value}
+ {schema}.{field.value}
diff --git a/apps/studio/components/interfaces/Auth/Policies/AIPolicyEditorPanel/PolicyTemplates.tsx b/apps/studio/components/interfaces/Auth/Policies/AIPolicyEditorPanel/PolicyTemplates.tsx
index 520534f9a50..73fefa60219 100644
--- a/apps/studio/components/interfaces/Auth/Policies/AIPolicyEditorPanel/PolicyTemplates.tsx
+++ b/apps/studio/components/interfaces/Auth/Policies/AIPolicyEditorPanel/PolicyTemplates.tsx
@@ -9,9 +9,9 @@ import CardButton from 'components/ui/CardButton'
import CopyButton from 'components/ui/CopyButton'
import NoSearchResults from 'components/ui/NoSearchResults'
import { getGeneralPolicyTemplates } from '../PolicyEditorModal/PolicyEditorModal.constants'
-import { useTableEditorStateSnapshot } from 'state/table-editor'
interface PolicyTemplatesProps {
+ schema: string
table: string
selectedPolicy?: PostgresPolicy
selectedTemplate?: string
@@ -19,17 +19,14 @@ interface PolicyTemplatesProps {
}
export const PolicyTemplates = ({
+ schema,
table,
selectedPolicy,
selectedTemplate,
onSelectTemplate,
}: PolicyTemplatesProps) => {
const [search, setSearch] = useState('')
- const state = useTableEditorStateSnapshot()
- const templates = getGeneralPolicyTemplates(
- state.selectedSchemaName,
- table.length > 0 ? table : 'table_name'
- )
+ const templates = getGeneralPolicyTemplates(schema, table.length > 0 ? table : 'table_name')
const baseTemplates =
selectedPolicy !== undefined
diff --git a/apps/studio/components/interfaces/Auth/Policies/AIPolicyEditorPanel/index.tsx b/apps/studio/components/interfaces/Auth/Policies/AIPolicyEditorPanel/index.tsx
index fc78e0cbeba..730e5ce1e5e 100644
--- a/apps/studio/components/interfaces/Auth/Policies/AIPolicyEditorPanel/index.tsx
+++ b/apps/studio/components/interfaces/Auth/Policies/AIPolicyEditorPanel/index.tsx
@@ -15,7 +15,6 @@ import {
Checkbox_Shadcn_,
Form_Shadcn_,
Label_Shadcn_,
- Modal,
ScrollArea,
Sheet,
SheetContent,
@@ -45,7 +44,6 @@ import { useSelectedOrganization, useSelectedProject } from 'hooks'
import { BASE_PATH, OPT_IN_TAGS } from 'lib/constants'
import { uuidv4 } from 'lib/helpers'
import Telemetry from 'lib/telemetry'
-import { useTableEditorStateSnapshot } from 'state/table-editor'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { AIPolicyChat } from './AIPolicyChat'
import {
@@ -70,6 +68,7 @@ const DiffEditor = dynamic(
interface AIPolicyEditorPanelProps {
visible: boolean
+ schema: string
searchString?: string
selectedPolicy?: PostgresPolicy
onSelectCancel: () => void
@@ -80,6 +79,7 @@ interface AIPolicyEditorPanelProps {
*/
export const AIPolicyEditorPanel = memo(function ({
visible,
+ schema,
searchString,
selectedPolicy,
onSelectCancel,
@@ -91,7 +91,6 @@ export const AIPolicyEditorPanel = memo(function ({
const selectedOrganization = useSelectedOrganization()
const telemetryProps = useTelemetryProps()
- const state = useTableEditorStateSnapshot()
const isAiAssistantEnabled = useIsRLSAIAssistantEnabled()
// [Joshen] Hyrid form fields, just spit balling to get a decent POC out
@@ -330,7 +329,7 @@ export const AIPolicyEditorPanel = memo(function ({
if (selectedPolicy === undefined) {
const sql = generateCreatePolicyQuery({
name: name,
- schema: state.selectedSchemaName,
+ schema,
table,
behavior,
command,
@@ -534,6 +533,7 @@ export const AIPolicyEditorPanel = memo(function ({
) : (
<>
({})
const [selectedPolicyToDelete, setSelectedPolicyToDelete] = useState({})
@@ -137,7 +137,7 @@ const Policies = ({
return (
<>
- {isLocked &&
}
+ {isLocked &&
}
{tables.length > 0 ? (
tables.map((table: any) => (
diff --git a/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx b/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx
index 0bd784d291c..14204939c98 100644
--- a/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx
+++ b/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx
@@ -162,7 +162,7 @@ const GridHeaderActions = ({ table, isViewSelected, isTableSelected }: GridHeade
>
Add RLS policy
@@ -202,7 +202,10 @@ const GridHeaderActions = ({ table, isViewSelected, isTableSelected }: GridHeade
)
}
>
-
+
Auth {policies.length > 1 ? 'policies' : 'policy'}
diff --git a/apps/studio/hooks/ui/useUrlState.ts b/apps/studio/hooks/ui/useUrlState.ts
index ef8bf5c0657..d08217df167 100644
--- a/apps/studio/hooks/ui/useUrlState.ts
+++ b/apps/studio/hooks/ui/useUrlState.ts
@@ -1,11 +1,11 @@
-import { useCallback } from 'react'
import { useRouter } from 'next/router'
+import { Dispatch, SetStateAction, useCallback, useMemo } from 'react'
export type UrlStateParams = {
[k: string]: string | string[] | undefined
}
-export function useUrlState({
+export function useUrlState({
replace = true,
arrayKeys = [],
}: {
@@ -14,22 +14,26 @@ export function useUrlState({
*/
replace?: boolean
arrayKeys?: string[]
-} = {}) {
- const arrayKeysSet = new Set(arrayKeys)
+} = {}): [ValueParams, Dispatch>] {
+ const stringifiedArrayKeys = JSON.stringify(arrayKeys)
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ const arrayKeysSet = useMemo(() => new Set(arrayKeys), [stringifiedArrayKeys])
const router = useRouter()
- const params: UrlStateParams = Object.fromEntries(
- Object.entries(router.query).map(([key, value]) => {
- if (arrayKeysSet.has(key)) {
- return Array.isArray(value) ? [key, value] : [key, [value]]
- }
+ const params: ValueParams = useMemo(() => {
+ return Object.fromEntries(
+ Object.entries(router.query).map(([key, value]) => {
+ if (arrayKeysSet.has(key)) {
+ return Array.isArray(value) ? [key, value] : [key, [value]]
+ }
- return [key, value]
- })
- )
+ return [key, value]
+ })
+ )
+ }, [arrayKeysSet, router.query])
- const setParams = useCallback(
- (newParams: UrlStateParams | ((previousParams: UrlStateParams) => UrlStateParams)) => {
+ const setParams: Dispatch> = useCallback(
+ (newParams) => {
const nextParams = typeof newParams === 'function' ? newParams(params) : newParams
let newQuery = Object.fromEntries(
Object.entries({ ...params, ...nextParams }).filter(([, value]) => Boolean(value))
diff --git a/apps/studio/pages/project/[ref]/auth/policies.tsx b/apps/studio/pages/project/[ref]/auth/policies.tsx
index 46b944bdb1a..48b0060ca3f 100644
--- a/apps/studio/pages/project/[ref]/auth/policies.tsx
+++ b/apps/studio/pages/project/[ref]/auth/policies.tsx
@@ -1,10 +1,7 @@
import type { PostgresPolicy, PostgresTable } from '@supabase/postgres-meta'
import { PermissionAction } from '@supabase/shared-types/out/constants'
-import { partition } from 'lodash'
-import { useEffect, useState } from 'react'
+import { useState } from 'react'
-import { useParams } from 'common'
-import { useIsRLSAIAssistantEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext'
import { Policies } from 'components/interfaces/Auth/Policies'
import { AIPolicyEditorPanel } from 'components/interfaces/Auth/Policies/AIPolicyEditorPanel'
import { AuthLayout } from 'components/layouts'
@@ -16,9 +13,9 @@ import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
import { useDatabasePoliciesQuery } from 'data/database-policies/database-policies-query'
import { useSchemasQuery } from 'data/database/schemas-query'
import { useTablesQuery } from 'data/tables/tables-query'
-import { useCheckPermissions, usePermissionsLoaded } from 'hooks'
+import { useCheckPermissions, usePermissionsLoaded, useUrlState } from 'hooks'
import { EXCLUDED_SCHEMAS } from 'lib/constants/schemas'
-import { useTableEditorStateSnapshot } from 'state/table-editor'
+import { partition } from 'lodash'
import type { NextPageWithLayout } from 'types'
import {
Button,
@@ -66,22 +63,16 @@ const onFilterTables = (
}
const AuthPoliciesPage: NextPageWithLayout = () => {
- const { search, schema } = useParams()
+ const [params, setParams] = useUrlState<{
+ schema?: string
+ search?: string
+ }>()
+ const { schema = 'public', search: searchString = '' } = params
const { project } = useProjectContext()
- const snap = useTableEditorStateSnapshot()
- const [searchString, setSearchString] = useState('')
const [showPolicyAiEditor, setShowPolicyAiEditor] = useState(false)
const [selectedPolicyToEdit, setSelectedPolicyToEdit] = useState()
- useEffect(() => {
- if (search) setSearchString(search)
- }, [search])
-
- useEffect(() => {
- if (schema) snap.setSelectedSchemaName(schema)
- }, [schema])
-
const { data: schemas } = useSchemasQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
@@ -89,7 +80,7 @@ const AuthPoliciesPage: NextPageWithLayout = () => {
const [protectedSchemas] = partition(schemas, (schema) =>
EXCLUDED_SCHEMAS.includes(schema?.name ?? '')
)
- const selectedSchema = schemas?.find((schema) => schema.name === snap.selectedSchemaName)
+ const selectedSchema = schemas?.find((s) => s.name === schema)
const isLocked = protectedSchemas.some((s) => s.id === selectedSchema?.id)
const { data: policies } = useDatabasePoliciesQuery({
@@ -106,7 +97,7 @@ const AuthPoliciesPage: NextPageWithLayout = () => {
} = useTablesQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
- schema: snap.selectedSchemaName,
+ schema: schema,
})
const filteredTables = onFilterTables(tables ?? [], policies ?? [], searchString)
@@ -128,18 +119,20 @@ const AuthPoliciesPage: NextPageWithLayout = () => {
className="w-[260px]"
size="small"
showError={false}
- selectedSchemaName={snap.selectedSchemaName}
- onSelectSchema={(schema: string) => {
- snap.setSelectedSchemaName(schema)
- setSearchString('')
+ selectedSchemaName={schema}
+ onSelectSchema={(schema) => {
+ setParams({ ...params, search: undefined, schema })
}}
/>
setSearchString(e.target.value)}
+ value={searchString || ''}
+ onChange={(e) => {
+ const str = e.target.value
+ setParams({ ...params, search: str === '' ? undefined : str })
+ }}
icon={}
/>
@@ -169,7 +162,7 @@ const AuthPoliciesPage: NextPageWithLayout = () => {
{!canCreatePolicies
? 'You need additional permissions to create RLS policies'
: schemaHasNoTables
- ? `No table in schema ${snap.selectedSchemaName} to create policies on`
+ ? `No table in schema ${schema} to create policies on`
: null}
)}
@@ -184,6 +177,7 @@ const AuthPoliciesPage: NextPageWithLayout = () => {
{isSuccess && (
0}
isLocked={isLocked}
@@ -196,6 +190,7 @@ const AuthPoliciesPage: NextPageWithLayout = () => {
{
diff --git a/apps/www/components/Layouts/Default.tsx b/apps/www/components/Layouts/Default.tsx
index 375cb1fd2d0..e20d3563378 100644
--- a/apps/www/components/Layouts/Default.tsx
+++ b/apps/www/components/Layouts/Default.tsx
@@ -1,6 +1,7 @@
import Nav from 'components/Nav/index'
import Footer from 'components/Footer/index'
import { cn } from 'ui'
+import { useForceDeepDark } from '~/lib/theme.utils'
type Props = {
hideHeader?: boolean
@@ -19,6 +20,8 @@ const DefaultLayout = (props: Props) => {
children,
} = props
+ useForceDeepDark()
+
return (
<>
diff --git a/apps/www/lib/theme.utils.ts b/apps/www/lib/theme.utils.ts
new file mode 100644
index 00000000000..d724e802406
--- /dev/null
+++ b/apps/www/lib/theme.utils.ts
@@ -0,0 +1,43 @@
+import { useEffect } from 'react'
+import { useTheme } from 'next-themes'
+import { useRouter } from 'next/router'
+
+export function useForceDeepDark() {
+ const router = useRouter()
+ const { resolvedTheme, theme } = useTheme()
+
+ const isDarkTheme = resolvedTheme?.includes('dark')
+ const isGaSection = router.pathname.includes('/ga-week') || router.pathname === '/ga'
+
+ useEffect(() => {
+ const handleDocumentLoad = () => {
+ // Update the HTML element attributes
+ const theme = isDarkTheme ? (isGaSection ? 'deep-dark' : 'dark') : 'light'
+
+ document.documentElement.setAttribute('data-theme', theme)
+ document.documentElement.style.colorScheme = theme
+
+ // wait before setting the theme
+ setTimeout(() => {
+ document.documentElement.setAttribute('data-theme', theme)
+ document.documentElement.style.colorScheme = theme
+ }, 200)
+
+ // Clean up the event listener
+ window.removeEventListener('load', handleDocumentLoad)
+ }
+
+ // Check if document is already loaded
+ if (document.readyState === 'complete') {
+ handleDocumentLoad()
+ } else {
+ // Add a global load event listener
+ window.addEventListener('load', handleDocumentLoad)
+ }
+
+ // Clean up the event listener on component unmount
+ return () => {
+ window.removeEventListener('load', handleDocumentLoad)
+ }
+ }, [resolvedTheme, theme, isDarkTheme, router])
+}
diff --git a/apps/www/pages/ga-week/index.tsx b/apps/www/pages/ga-week/index.tsx
index 706f20c4fec..5cb1a9c4d5e 100644
--- a/apps/www/pages/ga-week/index.tsx
+++ b/apps/www/pages/ga-week/index.tsx
@@ -14,7 +14,6 @@ import { Meetup } from '~/components/LaunchWeek/11/LW11Meetups'
import LW11StickyNav from '~/components/LaunchWeek/11/Releases/LW11StickyNav'
import LW11Header from '~/components/LaunchWeek/11/Releases/LW11Header'
import MainStage from '~/components/LaunchWeek/11/Releases/MainStage'
-import { useTheme } from 'next-themes'
const BuildStage = dynamic(() => import('~/components/LaunchWeek/11/Releases/BuildStage'))
const LW11Meetups = dynamic(() => import('~/components/LaunchWeek/11/LW11Meetups'))
@@ -29,7 +28,6 @@ interface Props {
export default function GAWeekIndex({ meetups }: Props) {
const { query } = useRouter()
- const { resolvedTheme, setTheme } = useTheme()
const TITLE = 'Supabase GA Week | 15-19 April 2024'
const DESCRIPTION = 'Join us for a week of announcing new features, every day at 7 AM PT.'
@@ -65,19 +63,6 @@ export default function GAWeekIndex({ meetups }: Props) {
}
}, [supabase])
- const isDark = resolvedTheme?.includes('dark')
- const isDarkTheme = resolvedTheme === 'dark'
-
- useEffect(() => {
- isDarkTheme && setTheme('deep-dark')
- }, [isDarkTheme])
-
- useEffect(() => {
- return () => {
- isDark && setTheme('dark')
- }
- }, [])
-
useEffect(() => {
if (session?.user) {
if (userData?.id) {
diff --git a/apps/www/pages/ga.tsx b/apps/www/pages/ga.tsx
index 48d367ff2eb..9624ae133bc 100644
--- a/apps/www/pages/ga.tsx
+++ b/apps/www/pages/ga.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect } from 'react'
+import React from 'react'
import dynamic from 'next/dynamic'
import Link from 'next/link'
import { useRouter } from 'next/router'
@@ -19,7 +19,7 @@ const CTABanner = dynamic(() => import('~/components/CTABanner'))
export default function IndexPage() {
const router = useRouter()
- const { resolvedTheme, setTheme } = useTheme()
+ const { resolvedTheme } = useTheme()
const isDark = resolvedTheme?.includes('dark')
const data = pageData(isDark!)
@@ -27,18 +27,6 @@ export default function IndexPage() {
const meta_description = 'Supabase is officially launching into General Availability.'
const meta_image = '/images/ga/ga-og-alt.png'
- const isDarkTheme = resolvedTheme === 'dark'
-
- useEffect(() => {
- isDarkTheme && setTheme('deep-dark')
- }, [isDarkTheme])
-
- useEffect(() => {
- return () => {
- isDark && setTheme('dark')
- }
- }, [])
-
return (