From 2dad02de62e1dc8052df99c79093711ac74b4515 Mon Sep 17 00:00:00 2001 From: Ivan Vasilov Date: Wed, 17 Apr 2024 12:05:01 +0200 Subject: [PATCH 1/3] feat: Use URL query params in Auth Policies page (#22786) * Make the useUrlState typesafe. * Remove the dependency on the valtio state for the Ai Policy Panel. * Remove the valtio state and use the URL query state for keeping selected schema and filter string. * Update GridHeaderActions component to include schema parameter in policy search URL --------- Co-authored-by: Alaister Young --- .../LockedQuerySection.tsx | 16 +++---- .../AIPolicyEditorPanel/PolicyDetailsV2.tsx | 11 ++--- .../AIPolicyEditorPanel/PolicyTemplates.tsx | 9 ++-- .../Policies/AIPolicyEditorPanel/index.tsx | 11 +++-- .../interfaces/Auth/Policies/Policies.tsx | 6 +-- .../TableGridEditor/GridHeaderActions.tsx | 7 ++- apps/studio/hooks/ui/useUrlState.ts | 32 +++++++------ .../pages/project/[ref]/auth/policies.tsx | 47 +++++++++---------- 8 files changed, 70 insertions(+), 69 deletions(-) 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 = () => { { From 919427231b6c07af00d146aefb53770484bb0b99 Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Wed, 17 Apr 2024 18:39:15 +0800 Subject: [PATCH 2/3] no longer forcing theme in next/theme (#22817) * init * Update index.tsx * Update apps/www/pages/ga.tsx Co-authored-by: Jonathan Summers-Muir * Update apps/www/pages/ga.tsx Co-authored-by: Jonathan Summers-Muir * Update apps/www/pages/ga-week/index.tsx Co-authored-by: Jonathan Summers-Muir * Update apps/www/pages/ga-week/index.tsx Co-authored-by: Jonathan Summers-Muir * Update index.tsx * Update ga.tsx * hack * Update index.tsx * use hook and check router * update import --------- Co-authored-by: Francesco Sansalvadore Co-authored-by: Francesco Sansalvadore --- apps/www/components/Layouts/Default.tsx | 3 ++ apps/www/lib/theme.utils.ts | 43 +++++++++++++++++++++++++ apps/www/pages/ga-week/index.tsx | 15 --------- apps/www/pages/ga.tsx | 16 ++------- 4 files changed, 48 insertions(+), 29 deletions(-) create mode 100644 apps/www/lib/theme.utils.ts 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 ( <>