diff --git a/apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx b/apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx index a58f00c77e4..d505347d6e3 100644 --- a/apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx @@ -1,15 +1,17 @@ -import { Command, Search, Menu } from 'lucide-react' +import { memo, useState } from 'react' import dynamic from 'next/dynamic' import Image from 'next/image' import Link from 'next/link' -import type { FC } from 'react' -import { memo, useState } from 'react' +import { Command, Search, Menu } from 'lucide-react' -import { useIsLoggedIn, useIsUserLoading } from 'common' +import { useIsLoggedIn, useIsUserLoading, useUser } from 'common' import { Button, buttonVariants, cn } from 'ui' -import { CommandMenuTrigger } from 'ui-patterns/CommandMenu' - +import { AuthenticatedDropdownMenu, CommandMenuTrigger } from 'ui-patterns' import GlobalNavigationMenu from './GlobalNavigationMenu' +import useDropdownMenu from './useDropdownMenu' + +import type { FC } from 'react' + const GlobalMobileMenu = dynamic(() => import('./GlobalMobileMenu')) const TopNavDropdown = dynamic(() => import('./TopNavDropdown')) @@ -17,6 +19,8 @@ const TopNavBar: FC = () => { const isLoggedIn = useIsLoggedIn() const isUserLoading = useIsUserLoading() const [mobileMenuOpen, setMobileMenuOpen] = useState(false) + const user = useUser() + const menu = useDropdownMenu(user) return ( <> @@ -95,7 +99,11 @@ const TopNavBar: FC = () => { Dev-only secret sign-in )} - + {isLoggedIn ? ( + + ) : ( + + )} diff --git a/apps/docs/components/Navigation/NavigationMenu/TopNavDropdown.tsx b/apps/docs/components/Navigation/NavigationMenu/TopNavDropdown.tsx index 5b102255a38..7d7999c63c8 100644 --- a/apps/docs/components/Navigation/NavigationMenu/TopNavDropdown.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/TopNavDropdown.tsx @@ -19,7 +19,6 @@ import { cn, themes, } from 'ui' - import MenuIconPicker from './MenuIconPicker' const menu = [ @@ -70,7 +69,7 @@ const TopNavDropdown = () => { - + {menu.map((menuSection, sectionIdx) => ( {sectionIdx !== 0 && } diff --git a/apps/docs/components/Navigation/NavigationMenu/useDropdownMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/useDropdownMenu.tsx new file mode 100644 index 00000000000..ab0fb45827e --- /dev/null +++ b/apps/docs/components/Navigation/NavigationMenu/useDropdownMenu.tsx @@ -0,0 +1,80 @@ +'use client' + +import type { User } from '@supabase/supabase-js' +import { LogOut, Globe, LifeBuoy, Settings, UserIcon, Database } from 'lucide-react' +import { logOut } from 'common' + +import type { menuItem } from 'ui-patterns/AuthenticatedDropdownMenu' +import { IconGitHub } from './MenuIcons' + +const useDropdownMenu = (user: User | null) => { + const menu: menuItem[][] = [ + [ + { + label: user?.email ?? "You're logged in", + type: 'text', + icon: UserIcon, + }, + { + label: 'Account Preferences', + icon: Settings, + href: 'https://supabase.com/dashboard/account/me', + }, + { + label: 'All Projects', + icon: Database, + href: 'https://supabase.com/dashboard/projects', + }, + ], + [ + { + label: 'Supabase.com', + icon: Globe, + href: 'https://supabase.com', + otherProps: { + target: '_blank', + rel: 'noreferrer noopener', + }, + }, + { + label: 'GitHub', + icon: IconGitHub as any, + href: 'https://github.com/supabase/supabase', + otherProps: { + target: '_blank', + rel: 'noreferrer noopener', + }, + }, + { + label: 'Support', + icon: LifeBuoy, + href: 'https://supabase.com/support', + otherProps: { + target: '_blank', + rel: 'noreferrer noopener', + }, + }, + ], + [ + { + label: 'Theme', + type: 'theme', + }, + ], + [ + { + label: 'Logout', + type: 'button', + icon: LogOut, + onClick: async () => { + await logOut() + window.location.reload() + }, + }, + ], + ] + + return menu +} + +export default useDropdownMenu diff --git a/apps/docs/components/ProjectConfigVariables/ProjectConfigVariables.tsx b/apps/docs/components/ProjectConfigVariables/ProjectConfigVariables.tsx index 41250bcb1dc..565fca8b3f2 100644 --- a/apps/docs/components/ProjectConfigVariables/ProjectConfigVariables.tsx +++ b/apps/docs/components/ProjectConfigVariables/ProjectConfigVariables.tsx @@ -15,7 +15,7 @@ import CopyToClipboard from 'react-copy-to-clipboard' import { withErrorBoundary } from 'react-error-boundary' import { proxy, useSnapshot } from 'valtio' -import { useIsLoggedIn, useIsUserLoading } from 'common' +import { LOCAL_STORAGE_KEYS, useIsLoggedIn, useIsUserLoading } from 'common' import { Button_Shadcn_ as Button, Input_Shadcn_ as Input, cn } from 'ui' import { @@ -36,7 +36,7 @@ import { useOrganizationsQuery } from '~/lib/fetch/organizations' import { type SupavisorConfigData, useSupavisorConfigQuery } from '~/lib/fetch/pooler' import { useProjectApiQuery } from '~/lib/fetch/projectApi' import { isProjectPaused, type ProjectsData, useProjectsQuery } from '~/lib/fetch/projects' -import { LOCAL_STORAGE_KEYS, retrieve, storeOrRemoveNull } from '~/lib/storage' +import { retrieve, storeOrRemoveNull } from '~/lib/storage' import { useOnLogout } from '~/lib/userAuth' type ProjectOrgDataState = diff --git a/apps/docs/features/auth/auth.client.tsx b/apps/docs/features/auth/auth.client.tsx index 699ff559ce2..2884fc1d2f0 100644 --- a/apps/docs/features/auth/auth.client.tsx +++ b/apps/docs/features/auth/auth.client.tsx @@ -1,9 +1,9 @@ 'use client' import { useQueryClient } from '@tanstack/react-query' -import { AuthProvider } from 'common' +import { AuthProvider, LOCAL_STORAGE_KEYS } from 'common' import { type PropsWithChildren, useCallback } from 'react' -import { LOCAL_STORAGE_KEYS, remove } from '~/lib/storage' +import { remove } from '~/lib/storage' import { useOnLogout } from '~/lib/userAuth' /** diff --git a/apps/docs/lib/storage.ts b/apps/docs/lib/storage.ts index ea30f871d92..dcefeb2effe 100644 --- a/apps/docs/lib/storage.ts +++ b/apps/docs/lib/storage.ts @@ -1,11 +1,6 @@ -export const LOCAL_STORAGE_KEYS = { - SAVED_ORG: 'docs.ui.user.selected.org', - SAVED_PROJECT: 'docs.ui.user.selected.project', - SAVED_BRANCH: 'docs.ui.user.selected.branch', -} as const +import { LOCAL_STORAGE_KEYS } from 'common' type LocalStorageKey = (typeof LOCAL_STORAGE_KEYS)[keyof typeof LOCAL_STORAGE_KEYS] - type StorageType = 'local' | 'session' function getStorage(storageType: StorageType) { @@ -17,7 +12,7 @@ export function store(storageType: StorageType, key: LocalStorageKey, value: str const storage = getStorage(storageType) try { - storage.setItem(key, value) + storage.setItem(key as string, value) } catch { console.error(`Failed to set storage item with key "${key}"`) } @@ -26,13 +21,13 @@ export function store(storageType: StorageType, key: LocalStorageKey, value: str export function retrieve(storageType: StorageType, key: LocalStorageKey) { if (typeof window === 'undefined') return const storage = getStorage(storageType) - return storage.getItem(key) + return storage.getItem(key as string) } export function remove(storageType: StorageType, key: LocalStorageKey) { if (typeof window === 'undefined') return const storage = getStorage(storageType) - return storage.removeItem(key) + return storage.removeItem(key as string) } export function storeOrRemoveNull( diff --git a/apps/studio/components/grid/components/footer/pagination/Pagination.tsx b/apps/studio/components/grid/components/footer/pagination/Pagination.tsx index 01e6c182e45..b86e71b590c 100644 --- a/apps/studio/components/grid/components/footer/pagination/Pagination.tsx +++ b/apps/studio/components/grid/components/footer/pagination/Pagination.tsx @@ -2,12 +2,11 @@ import { ArrowLeft, ArrowRight, HelpCircle } from 'lucide-react' import { useEffect, useState } from 'react' import { useParams } from 'common' -import { formatFilterURLParams } from 'components/grid/SupabaseGrid.utils' +import { useTableFilter } from 'components/grid/hooks/useTableFilter' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import { useTableEditorQuery } from 'data/table-editor/table-editor-query' import { isTableLike } from 'data/table-editor/table-editor-types' import { THRESHOLD_COUNT, useTableRowsCountQuery } from 'data/table-rows/table-rows-count-query' -import { useUrlState } from 'hooks/ui/useUrlState' import { RoleImpersonationState } from 'lib/role-impersonation' import { useRoleImpersonationStateSnapshot } from 'state/role-impersonation-state' import { useTableEditorStateSnapshot } from 'state/table-editor' @@ -41,8 +40,7 @@ const Pagination = () => { // rowsCountEstimate is only applicable to table entities const rowsCountEstimate = isTableLike(selectedTable) ? selectedTable.live_rows_estimate : null - const [{ filter }] = useUrlState({ arrayKeys: ['filter'] }) - const filters = formatFilterURLParams(filter as string[]) + const { filters } = useTableFilter() const page = snap.page const roleImpersonationState = useRoleImpersonationStateSnapshot() @@ -61,7 +59,7 @@ const Pagination = () => { { projectRef: project?.ref, connectionString: project?.connectionString, - tableId: id, + tableId: snap.table.id, filters, enforceExactCount: snap.enforceExactCount, roleImpersonationState: roleImpersonationState as RoleImpersonationState, diff --git a/apps/studio/components/interfaces/Account/Preferences/DeleteAccountButton.tsx b/apps/studio/components/interfaces/Account/Preferences/DeleteAccountButton.tsx index 23caa8ab8ac..91306f2d191 100644 --- a/apps/studio/components/interfaces/Account/Preferences/DeleteAccountButton.tsx +++ b/apps/studio/components/interfaces/Account/Preferences/DeleteAccountButton.tsx @@ -1,6 +1,5 @@ import { zodResolver } from '@hookform/resolvers/zod' import { SupportCategories } from '@supabase/shared-types/out/constants' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import { useEffect, useState } from 'react' import { useForm } from 'react-hook-form' import { toast } from 'sonner' @@ -27,6 +26,7 @@ import { Input_Shadcn_, Separator, } from 'ui' +import { LOCAL_STORAGE_KEYS } from 'common' const setDeletionRequestFlag = () => { const expiryDate = new Date() diff --git a/apps/studio/components/interfaces/Account/Preferences/ThemeSettings.tsx b/apps/studio/components/interfaces/Account/Preferences/ThemeSettings.tsx index b05c8c5cf28..762a1b6c67d 100644 --- a/apps/studio/components/interfaces/Account/Preferences/ThemeSettings.tsx +++ b/apps/studio/components/interfaces/Account/Preferences/ThemeSettings.tsx @@ -5,7 +5,7 @@ import SVG from 'react-inlinesvg' import { DEFAULT_SIDEBAR_BEHAVIOR } from 'components/interfaces/Sidebar' import Panel from 'components/ui/Panel' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' -import { BASE_PATH, LOCAL_STORAGE_KEYS } from 'lib/constants' +import { BASE_PATH } from 'lib/constants' import { Label_Shadcn_, RadioGroup_Shadcn_, @@ -20,6 +20,7 @@ import { Theme, } from 'ui' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' +import { LOCAL_STORAGE_KEYS } from 'common' export const ThemeSettings = () => { const [mounted, setMounted] = useState(false) diff --git a/apps/studio/components/interfaces/App/AppBannerWrapperContext.tsx b/apps/studio/components/interfaces/App/AppBannerWrapperContext.tsx index c6d4e302322..9ccbbf6fbb0 100644 --- a/apps/studio/components/interfaces/App/AppBannerWrapperContext.tsx +++ b/apps/studio/components/interfaces/App/AppBannerWrapperContext.tsx @@ -1,8 +1,7 @@ +import { LOCAL_STORAGE_KEYS } from 'common' import { noop } from 'lodash' import { PropsWithChildren, createContext, useContext, useEffect, useState } from 'react' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' - const FLY_POSTGRES_DEPRECATION_WARNING_KEY = LOCAL_STORAGE_KEYS.FLY_POSTGRES_DEPRECATION_WARNING // [Joshen] This file is meant to be dynamic - update this as and when we need to use the NoticeBanner diff --git a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx index d4d4b770dfa..35c3c6eca40 100644 --- a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx +++ b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx @@ -1,8 +1,7 @@ import { noop } from 'lodash' - import { FeatureFlagContext } from 'common' import { useFlag } from 'hooks/ui/useFlag' -import { IS_PLATFORM, LOCAL_STORAGE_KEYS } from 'lib/constants' +import { IS_PLATFORM } from 'lib/constants' import { EMPTY_OBJ } from 'lib/void' import { PropsWithChildren, createContext, useContext, useEffect, useState } from 'react' import { APISidePanelPreview } from './APISidePanelPreview' @@ -11,6 +10,7 @@ import { InlineEditorPreview } from './InlineEditorPreview' import { LayoutUpdatePreview } from './LayoutUpdatePreview' import { SqlEditorTabsPreview } from './SqlEditorTabs' import { TableEditorTabsPreview } from './TableEditorTabs' +import { LOCAL_STORAGE_KEYS } from 'common' export const FEATURE_PREVIEWS = [ { diff --git a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx index 5f78dde3e6b..b77e797f3d0 100644 --- a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx +++ b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx @@ -2,10 +2,11 @@ import { ExternalLink, Eye, EyeOff, FlaskConical } from 'lucide-react' import Link from 'next/link' import { useEffect } from 'react' -import { useParams } from 'common' +import { LOCAL_STORAGE_KEYS, useParams } from 'common' import { useSendEventMutation } from 'data/telemetry/send-event-mutation' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' -import { IS_PLATFORM, LOCAL_STORAGE_KEYS } from 'lib/constants' +import { useFlag } from 'hooks/ui/useFlag' +import { IS_PLATFORM } from 'lib/constants' import { useAppStateSnapshot } from 'state/app-state' import { removeTabsByEditor } from 'state/tabs' import { Badge, Button, Modal, ScrollArea, cn } from 'ui' diff --git a/apps/studio/components/interfaces/App/FeaturePreview/LayoutUpdatePreview.tsx b/apps/studio/components/interfaces/App/FeaturePreview/LayoutUpdatePreview.tsx index be462dde866..643c3c0ab1c 100644 --- a/apps/studio/components/interfaces/App/FeaturePreview/LayoutUpdatePreview.tsx +++ b/apps/studio/components/interfaces/App/FeaturePreview/LayoutUpdatePreview.tsx @@ -1,10 +1,11 @@ import { ButtonTooltip } from 'components/ui/ButtonTooltip' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' -import { BASE_PATH, LOCAL_STORAGE_KEYS } from 'lib/constants' +import { BASE_PATH } from 'lib/constants' import { ExternalLink, X } from 'lucide-react' import Image from 'next/image' import { Alert_Shadcn_, AlertDescription_Shadcn_, AlertTitle_Shadcn_, Badge, Button } from 'ui' import { useIsNewLayoutEnabled } from './FeaturePreviewContext' +import { LOCAL_STORAGE_KEYS } from 'common' export const LayoutUpdatePreview = () => { return ( diff --git a/apps/studio/components/interfaces/App/RouteValidationWrapper.tsx b/apps/studio/components/interfaces/App/RouteValidationWrapper.tsx index ef14cacedce..00b47042edd 100644 --- a/apps/studio/components/interfaces/App/RouteValidationWrapper.tsx +++ b/apps/studio/components/interfaces/App/RouteValidationWrapper.tsx @@ -2,13 +2,13 @@ import { useRouter } from 'next/router' import { PropsWithChildren, useEffect } from 'react' import { toast } from 'sonner' -import { useIsLoggedIn, useParams } from 'common' +import { LOCAL_STORAGE_KEYS, useIsLoggedIn, useParams } from 'common' import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { useProjectsQuery } from 'data/projects/projects-query' import useLatest from 'hooks/misc/useLatest' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' -import { IS_PLATFORM, LOCAL_STORAGE_KEYS } from 'lib/constants' +import { IS_PLATFORM } from 'lib/constants' import { useAppStateSnapshot } from 'state/app-state' import { useIsNewLayoutEnabled } from './FeaturePreview/FeaturePreviewContext' diff --git a/apps/studio/components/interfaces/Auth/Policies/PolicyEditorModal/index.tsx b/apps/studio/components/interfaces/Auth/Policies/PolicyEditorModal/index.tsx index 2d639a4649b..cbb8fea06b1 100644 --- a/apps/studio/components/interfaces/Auth/Policies/PolicyEditorModal/index.tsx +++ b/apps/studio/components/interfaces/Auth/Policies/PolicyEditorModal/index.tsx @@ -2,7 +2,6 @@ import { isEmpty, noop } from 'lodash' import { useEffect, useState } from 'react' import { toast } from 'sonner' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import { useAppStateSnapshot } from 'state/app-state' import { Modal } from 'ui' import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' diff --git a/apps/studio/components/interfaces/Auth/Users/UsersV2.tsx b/apps/studio/components/interfaces/Auth/Users/UsersV2.tsx index be368dbd2bc..cf79c68398a 100644 --- a/apps/studio/components/interfaces/Auth/Users/UsersV2.tsx +++ b/apps/studio/components/interfaces/Auth/Users/UsersV2.tsx @@ -5,7 +5,7 @@ import { UIEvent, useEffect, useMemo, useRef, useState } from 'react' import DataGrid, { Column, DataGridHandle, Row } from 'react-data-grid' import { toast } from 'sonner' -import { useParams } from 'common' +import { LOCAL_STORAGE_KEYS, useParams } from 'common' import { useIsAPIDocsSidePanelEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import AlertError from 'components/ui/AlertError' @@ -18,7 +18,6 @@ import { useUserDeleteMutation } from 'data/auth/user-delete-mutation' import { useUsersCountQuery } from 'data/auth/users-count-query' import { User, useUsersInfiniteQuery } from 'data/auth/users-infinite-query' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import { Button, cn, diff --git a/apps/studio/components/interfaces/Database/Schemas/SchemaGraph.tsx b/apps/studio/components/interfaces/Database/Schemas/SchemaGraph.tsx index b32682ab37d..6a8dc557694 100644 --- a/apps/studio/components/interfaces/Database/Schemas/SchemaGraph.tsx +++ b/apps/studio/components/interfaces/Database/Schemas/SchemaGraph.tsx @@ -6,7 +6,7 @@ import { useEffect, useMemo, useState } from 'react' import ReactFlow, { Background, BackgroundVariant, MiniMap, useReactFlow } from 'reactflow' import 'reactflow/dist/style.css' -import { useParams } from 'common' +import { LOCAL_STORAGE_KEYS, useParams } from 'common' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState' import AlertError from 'components/ui/AlertError' @@ -16,7 +16,6 @@ import { useSchemasQuery } from 'data/database/schemas-query' import { useTablesQuery } from 'data/tables/tables-query' import { useLocalStorage } from 'hooks/misc/useLocalStorage' import { useQuerySchemaState } from 'hooks/misc/useSchemaQueryState' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import { toast } from 'sonner' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from 'ui' import { SchemaGraphLegend } from './SchemaGraphLegend' diff --git a/apps/studio/components/interfaces/Database/Schemas/Schemas.utils.ts b/apps/studio/components/interfaces/Database/Schemas/Schemas.utils.ts index 89d02f304ea..2a3aaaa2828 100644 --- a/apps/studio/components/interfaces/Database/Schemas/Schemas.utils.ts +++ b/apps/studio/components/interfaces/Database/Schemas/Schemas.utils.ts @@ -4,9 +4,9 @@ import { uniqBy } from 'lodash' import { Edge, Node, Position } from 'reactflow' import 'reactflow/dist/style.css' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import { tryParseJson } from 'lib/helpers' import { TABLE_NODE_ROW_HEIGHT, TABLE_NODE_WIDTH, TableNodeData } from './SchemaTableNode' +import { LOCAL_STORAGE_KEYS } from 'common' const NODE_SEP = 25 const RANK_SEP = 50 diff --git a/apps/studio/components/interfaces/GraphQL/GraphiQL.tsx b/apps/studio/components/interfaces/GraphQL/GraphiQL.tsx index 53a527e0d0c..82f5714e963 100644 --- a/apps/studio/components/interfaces/GraphQL/GraphiQL.tsx +++ b/apps/studio/components/interfaces/GraphQL/GraphiQL.tsx @@ -37,10 +37,10 @@ import { MouseEventHandler, useCallback, useEffect, useState } from 'react' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useLocalStorage } from 'hooks/misc/useLocalStorage' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, Button, cn } from 'ui' import { RoleImpersonationSelector } from '../RoleImpersonationSelector' import styles from './graphiql.module.css' +import { LOCAL_STORAGE_KEYS } from 'common' export interface GraphiQLProps { fetcher: Fetcher diff --git a/apps/studio/components/interfaces/Organization/GeneralSettings/DeleteOrganizationButton.tsx b/apps/studio/components/interfaces/Organization/GeneralSettings/DeleteOrganizationButton.tsx index 923e57e8a0e..75cc9e9c0a2 100644 --- a/apps/studio/components/interfaces/Organization/GeneralSettings/DeleteOrganizationButton.tsx +++ b/apps/studio/components/interfaces/Organization/GeneralSettings/DeleteOrganizationButton.tsx @@ -3,12 +3,12 @@ import { useRouter } from 'next/router' import { useState } from 'react' import { toast } from 'sonner' +import { LOCAL_STORAGE_KEYS } from 'common' import { useIsNewLayoutEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext' import { useOrganizationDeleteMutation } from 'data/organizations/organization-delete-mutation' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import { Button, Form, Input, Modal } from 'ui' const DeleteOrganizationButton = () => { diff --git a/apps/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx b/apps/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx index 4426b4af278..ce93229c347 100644 --- a/apps/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx +++ b/apps/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx @@ -3,7 +3,7 @@ import { useRouter } from 'next/router' import { useState } from 'react' import { toast } from 'sonner' -import { useParams } from 'common' +import { LOCAL_STORAGE_KEYS, useParams } from 'common' import { useIsNewLayoutEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext' import { ScaffoldActionsContainer, @@ -22,7 +22,6 @@ import { usePermissionsQuery } from 'data/permissions/permissions-query' import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import { useProfile } from 'lib/profile' import { Input } from 'ui-patterns/DataInputs/Input' import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' diff --git a/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.tsx b/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.tsx index 585b5b9d3b0..0dbe8fdd746 100644 --- a/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.tsx +++ b/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.tsx @@ -4,14 +4,14 @@ import { useRouter } from 'next/router' import { useEffect, useMemo, useState } from 'react' import { toast } from 'sonner' -import { useParams } from 'common' +import { LOCAL_STORAGE_KEYS, useParams } from 'common' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import { useReadReplicasQuery } from 'data/read-replicas/replicas-query' import { formatDatabaseID } from 'data/read-replicas/replicas.utils' import { executeSql } from 'data/sql/execute-sql-query' import { DbQueryHook } from 'hooks/analytics/useDbQuery' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' -import { IS_PLATFORM, LOCAL_STORAGE_KEYS } from 'lib/constants' +import { IS_PLATFORM } from 'lib/constants' import { useDatabaseSelectorStateSnapshot } from 'state/database-selector' import { Button, diff --git a/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceFilterBar.tsx b/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceFilterBar.tsx index f346b74cfc7..2e4b546c7be 100644 --- a/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceFilterBar.tsx +++ b/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceFilterBar.tsx @@ -2,14 +2,13 @@ import { ArrowDown, ArrowUp, RefreshCw } from 'lucide-react' import { useRouter } from 'next/router' import { useState } from 'react' -import { useParams } from 'common' +import { LOCAL_STORAGE_KEYS, useParams } from 'common' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import { DownloadResultsButton } from 'components/ui/DownloadResultsButton' import { FilterPopover } from 'components/ui/FilterPopover' import { useDatabaseRolesQuery } from 'data/database-roles/database-roles-query' import { DbQueryHook } from 'hooks/analytics/useDbQuery' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import { Button, DropdownMenu, diff --git a/apps/studio/components/interfaces/SQLEditor/MonacoEditor.tsx b/apps/studio/components/interfaces/SQLEditor/MonacoEditor.tsx index 538a75b6c2a..4306d01e034 100644 --- a/apps/studio/components/interfaces/SQLEditor/MonacoEditor.tsx +++ b/apps/studio/components/interfaces/SQLEditor/MonacoEditor.tsx @@ -3,10 +3,9 @@ import { debounce } from 'lodash' import { useRouter } from 'next/router' import { MutableRefObject, useEffect, useRef } from 'react' -import { useParams } from 'common' +import { LOCAL_STORAGE_KEYS, useParams } from 'common' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' import { useSelectedProject } from 'hooks/misc/useSelectedProject' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import { useProfile } from 'lib/profile' import { useAiAssistantStateSnapshot } from 'state/ai-assistant-state' import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2' diff --git a/apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx b/apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx index d4577ae1a8f..bcb691d950d 100644 --- a/apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx +++ b/apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx @@ -8,7 +8,7 @@ import { useRouter } from 'next/router' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { toast } from 'sonner' -import { useParams } from 'common' +import { LOCAL_STORAGE_KEYS, useParams } from 'common' import ResizableAIWidget from 'components/ui/AIEditor/ResizableAIWidget' import { GridFooter } from 'components/ui/GridFooter' import { useSqlTitleGenerateMutation } from 'data/ai/sql-title-mutation' @@ -24,7 +24,7 @@ import { useOrgOptedIntoAi } from 'hooks/misc/useOrgOptedIntoAi' import { useSchemasForAi } from 'hooks/misc/useSchemasForAi' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' import { useSelectedProject } from 'hooks/misc/useSelectedProject' -import { BASE_PATH, IS_PLATFORM, LOCAL_STORAGE_KEYS } from 'lib/constants' +import { BASE_PATH, IS_PLATFORM } from 'lib/constants' import { formatSql } from 'lib/formatSql' import { detectOS, uuidv4 } from 'lib/helpers' import { useProfile } from 'lib/profile' diff --git a/apps/studio/components/interfaces/SQLEditor/UtilityPanel/ChartConfig.tsx b/apps/studio/components/interfaces/SQLEditor/UtilityPanel/ChartConfig.tsx index 87d93bbd938..ff1a708ccb1 100644 --- a/apps/studio/components/interfaces/SQLEditor/UtilityPanel/ChartConfig.tsx +++ b/apps/studio/components/interfaces/SQLEditor/UtilityPanel/ChartConfig.tsx @@ -2,13 +2,12 @@ import dayjs from 'dayjs' import { ArrowUpDown, X } from 'lucide-react' import { useMemo } from 'react' -import { useParams } from 'common' +import { LOCAL_STORAGE_KEYS, useParams } from 'common' import { ButtonTooltip } from 'components/ui/ButtonTooltip' import BarChart from 'components/ui/Charts/BarChart' import NoDataPlaceholder from 'components/ui/Charts/NoDataPlaceholder' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' import { useFlag } from 'hooks/ui/useFlag' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import Link from 'next/link' import { Badge, diff --git a/apps/studio/components/interfaces/SQLEditor/UtilityPanel/UtilityActions.tsx b/apps/studio/components/interfaces/SQLEditor/UtilityPanel/UtilityActions.tsx index be65aa24e69..2767756c532 100644 --- a/apps/studio/components/interfaces/SQLEditor/UtilityPanel/UtilityActions.tsx +++ b/apps/studio/components/interfaces/SQLEditor/UtilityPanel/UtilityActions.tsx @@ -1,11 +1,11 @@ import { AlignLeft, Check, Heart, Keyboard, MoreVertical } from 'lucide-react' import { toast } from 'sonner' -import { useParams } from 'common' +import { LOCAL_STORAGE_KEYS, useParams } from 'common' import { RoleImpersonationPopover } from 'components/interfaces/RoleImpersonationSelector' import DatabaseSelector from 'components/ui/DatabaseSelector' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' -import { IS_PLATFORM, LOCAL_STORAGE_KEYS } from 'lib/constants' +import { IS_PLATFORM } from 'lib/constants' import { detectOS } from 'lib/helpers' import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2' import { diff --git a/apps/studio/components/interfaces/SQLEditor/useAddDefinitions.ts b/apps/studio/components/interfaces/SQLEditor/useAddDefinitions.ts index 28293fcd2fc..a3f770213eb 100644 --- a/apps/studio/components/interfaces/SQLEditor/useAddDefinitions.ts +++ b/apps/studio/components/interfaces/SQLEditor/useAddDefinitions.ts @@ -1,4 +1,5 @@ import { Monaco } from '@monaco-editor/react' +import { LOCAL_STORAGE_KEYS } from 'common' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import getPgsqlCompletionProvider from 'components/ui/CodeEditor/Providers/PgSQLCompletionProvider' import getPgsqlSignatureHelpProvider from 'components/ui/CodeEditor/Providers/PgSQLSignatureHelpProvider' @@ -7,7 +8,6 @@ import { useKeywordsQuery } from 'data/database/keywords-query' import { useSchemasQuery } from 'data/database/schemas-query' import { useTableColumnsQuery } from 'data/database/table-columns-query' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import { formatSql } from 'lib/formatSql' import { IDisposable } from 'monaco-editor' import { useEffect, useRef } from 'react' diff --git a/apps/studio/components/interfaces/Sidebar.tsx b/apps/studio/components/interfaces/Sidebar.tsx index 2e16dd04f4b..3a03f2d54b2 100644 --- a/apps/studio/components/interfaces/Sidebar.tsx +++ b/apps/studio/components/interfaces/Sidebar.tsx @@ -14,7 +14,7 @@ import Link from 'next/link' import { useRouter } from 'next/router' import { ComponentProps, ComponentPropsWithoutRef, FC, ReactNode, useEffect } from 'react' -import { useParams } from 'common' +import { LOCAL_STORAGE_KEYS, useParams } from 'common' import { generateOtherRoutes, generateProductRoutes, @@ -30,7 +30,6 @@ import { useLints } from 'hooks/misc/useLints' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' import { useFlag } from 'hooks/ui/useFlag' import { Home } from 'icons' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import { useAppStateSnapshot } from 'state/app-state' import { Button, diff --git a/apps/studio/components/layouts/AccountLayout/AccountLayout.tsx b/apps/studio/components/layouts/AccountLayout/AccountLayout.tsx index 29c32fd75b5..926eb4b256f 100644 --- a/apps/studio/components/layouts/AccountLayout/AccountLayout.tsx +++ b/apps/studio/components/layouts/AccountLayout/AccountLayout.tsx @@ -7,7 +7,8 @@ import { PropsWithChildren, useEffect } from 'react' import { useIsNewLayoutEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' import { withAuth } from 'hooks/misc/withAuth' -import { IS_PLATFORM, LOCAL_STORAGE_KEYS } from 'lib/constants' +import { IS_PLATFORM } from 'lib/constants' +import { LOCAL_STORAGE_KEYS } from 'common' import { useAppStateSnapshot } from 'state/app-state' import { cn, NavMenu, NavMenuItem } from 'ui' import { diff --git a/apps/studio/components/layouts/LogsLayout/LogsLayout.tsx b/apps/studio/components/layouts/LogsLayout/LogsLayout.tsx index 3a0d3769c6e..5c9c0d7ece7 100644 --- a/apps/studio/components/layouts/LogsLayout/LogsLayout.tsx +++ b/apps/studio/components/layouts/LogsLayout/LogsLayout.tsx @@ -6,9 +6,9 @@ import NoPermission from 'components/ui/NoPermission' import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' import { withAuth } from 'hooks/misc/withAuth' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import ProjectLayout from '../ProjectLayout/ProjectLayout' import { LogsSidebarMenuV2 } from './LogsSidebarMenuV2' +import { LOCAL_STORAGE_KEYS } from 'common' interface LogsLayoutProps { title?: string diff --git a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/HomeIcon.tsx b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/HomeIcon.tsx index 84e82a0990b..573f2a8ccdf 100644 --- a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/HomeIcon.tsx +++ b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/HomeIcon.tsx @@ -7,7 +7,8 @@ import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' import { useShowLayoutHeader } from 'hooks/misc/useShowLayoutHeader' -import { IS_PLATFORM, LOCAL_STORAGE_KEYS } from 'lib/constants' +import { IS_PLATFORM } from 'lib/constants' +import { LOCAL_STORAGE_KEYS } from 'common' export const HomeIcon = () => { const newLayoutPreview = useIsNewLayoutEnabled() diff --git a/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationIconLink.tsx b/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationIconLink.tsx index 960bdf7e5eb..4316d1eb983 100644 --- a/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationIconLink.tsx +++ b/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationIconLink.tsx @@ -11,7 +11,7 @@ import { cn, Tooltip, TooltipContent, TooltipTrigger } from 'ui' import type { Route } from 'components/ui/ui.types' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' +import { LOCAL_STORAGE_KEYS } from 'common' interface NavigationIconButtonProps extends AnchorHTMLAttributes { route: Route diff --git a/apps/studio/components/layouts/SQLEditorLayout/SQLEditorMenu.tsx b/apps/studio/components/layouts/SQLEditorLayout/SQLEditorMenu.tsx index 15b3be04104..dee5d5669c1 100644 --- a/apps/studio/components/layouts/SQLEditorLayout/SQLEditorMenu.tsx +++ b/apps/studio/components/layouts/SQLEditorLayout/SQLEditorMenu.tsx @@ -1,15 +1,14 @@ import { PermissionAction } from '@supabase/shared-types/out/constants' import { useDebounce } from '@uidotdev/usehooks' +import { LOCAL_STORAGE_KEYS, useParams } from 'common' import { FilePlus, FolderPlus, Plus, X } from 'lucide-react' import { useRouter } from 'next/router' import { useEffect, useState } from 'react' import { toast } from 'sonner' -import { useParams } from 'common' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useLocalStorage } from 'hooks/misc/useLocalStorage' import { useSelectedProject } from 'hooks/misc/useSelectedProject' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import { useProfile } from 'lib/profile' import { getAppStateSnapshot } from 'state/app-state' import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2' diff --git a/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SQLEditorNav.tsx b/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SQLEditorNav.tsx index cb26ba3cd0c..5f3f340550d 100644 --- a/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SQLEditorNav.tsx +++ b/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SQLEditorNav.tsx @@ -1,4 +1,4 @@ -import { useParams } from 'common' +import { LOCAL_STORAGE_KEYS, useParams } from 'common' import { useIsSQLEditorTabsEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext' import DownloadSnippetModal from 'components/interfaces/SQLEditor/DownloadSnippetModal' import { MoveQueryModal } from 'components/interfaces/SQLEditor/MoveQueryModal' @@ -17,7 +17,6 @@ import { Snippet, SnippetFolder, useSQLSnippetFoldersQuery } from 'data/content/ import { useSqlSnippetsQuery } from 'data/content/sql-snippets-query' import { useLocalStorage } from 'hooks/misc/useLocalStorage' import { useSelectedProject } from 'hooks/misc/useSelectedProject' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import { useProfile } from 'lib/profile' import uuidv4 from 'lib/uuid' import { Eye, EyeOffIcon, Heart, Unlock } from 'lucide-react' diff --git a/apps/studio/components/layouts/project/[ref]/settings/APIKeysLayout.tsx b/apps/studio/components/layouts/project/[ref]/settings/APIKeysLayout.tsx index 629b34f6fa9..f813d15b207 100644 --- a/apps/studio/components/layouts/project/[ref]/settings/APIKeysLayout.tsx +++ b/apps/studio/components/layouts/project/[ref]/settings/APIKeysLayout.tsx @@ -16,8 +16,7 @@ import { TabsTrigger_Shadcn_, } from 'ui' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' -import { useParams } from 'common' +import { LOCAL_STORAGE_KEYS, useParams } from 'common' const ApiKeysLayout = ({ children }: PropsWithChildren) => { const { ref: projectRef } = useParams() diff --git a/apps/studio/components/ui/GroupsTelemetry.tsx b/apps/studio/components/ui/GroupsTelemetry.tsx index f76ff65466e..1c05ec91cb5 100644 --- a/apps/studio/components/ui/GroupsTelemetry.tsx +++ b/apps/studio/components/ui/GroupsTelemetry.tsx @@ -3,12 +3,13 @@ import * as Sentry from '@sentry/nextjs' import { useRouter } from 'next/router' import { useEffect } from 'react' -import { useParams, useTelemetryCookie, useUser } from 'common' +import { LOCAL_STORAGE_KEYS, useParams, useTelemetryCookie, useUser } from 'common' import { useSendGroupsIdentifyMutation } from 'data/telemetry/send-groups-identify-mutation' import { useSendGroupsResetMutation } from 'data/telemetry/send-groups-reset-mutation' import { usePrevious } from 'hooks/deprecated' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' -import { IS_PLATFORM, LOCAL_STORAGE_KEYS } from 'lib/constants' +import { useAppStateSnapshot } from 'state/app-state' +import { IS_PLATFORM } from 'lib/constants' const getAnonId = async (id: string) => { const encoder = new TextEncoder() diff --git a/apps/studio/data/integrations/github-authorization-create-mutation.ts b/apps/studio/data/integrations/github-authorization-create-mutation.ts index 9c344ddbfe7..a9f69142bbc 100644 --- a/apps/studio/data/integrations/github-authorization-create-mutation.ts +++ b/apps/studio/data/integrations/github-authorization-create-mutation.ts @@ -3,7 +3,7 @@ import { toast } from 'sonner' import { handleError, post } from 'data/fetchers' import type { ResponseError } from 'types' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' +import { LOCAL_STORAGE_KEYS } from 'common' export type GitHubAuthorizationCreateVariables = { code: string diff --git a/apps/studio/hooks/misc/useLastSignIn.ts b/apps/studio/hooks/misc/useLastSignIn.ts index ad860ebe748..e71f7cb8262 100644 --- a/apps/studio/hooks/misc/useLastSignIn.ts +++ b/apps/studio/hooks/misc/useLastSignIn.ts @@ -1,4 +1,4 @@ -import { LOCAL_STORAGE_KEYS } from 'lib/constants' +import { LOCAL_STORAGE_KEYS } from 'common' import { useLocalStorage } from './useLocalStorage' export type LastSignInType = 'github' | 'email' | 'sso' | null diff --git a/apps/studio/hooks/misc/useSchemaQueryState.ts b/apps/studio/hooks/misc/useSchemaQueryState.ts index b0e183c6bd2..f981ac44951 100644 --- a/apps/studio/hooks/misc/useSchemaQueryState.ts +++ b/apps/studio/hooks/misc/useSchemaQueryState.ts @@ -1,8 +1,7 @@ import { parseAsString, useQueryState } from 'nuqs' import { useEffect, useMemo } from 'react' -import { useParams } from 'common' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' +import { LOCAL_STORAGE_KEYS, useParams } from 'common' /** * This hook wraps useQueryState because useQueryState imports app router for some reason which breaks the SSR in diff --git a/apps/studio/hooks/misc/useSchemasForAi.ts b/apps/studio/hooks/misc/useSchemasForAi.ts index ccdebb4e268..5f32ccea672 100644 --- a/apps/studio/hooks/misc/useSchemasForAi.ts +++ b/apps/studio/hooks/misc/useSchemasForAi.ts @@ -1,5 +1,5 @@ +import { LOCAL_STORAGE_KEYS } from 'common' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import { Dispatch, SetStateAction } from 'react' /** diff --git a/apps/studio/lib/auth.tsx b/apps/studio/lib/auth.tsx index e943b89c5ef..e2ef0cdca9f 100644 --- a/apps/studio/lib/auth.tsx +++ b/apps/studio/lib/auth.tsx @@ -1,10 +1,9 @@ import { useQueryClient } from '@tanstack/react-query' -import { AuthProvider as AuthProviderInternal, gotrueClient } from 'common' +import { AuthProvider as AuthProviderInternal, clearLocalStorage, gotrueClient } from 'common' import { PropsWithChildren, useCallback, useEffect } from 'react' import { toast } from 'sonner' import { GOTRUE_ERRORS, IS_PLATFORM } from './constants' -import { clearLocalStorage } from './local-storage' export const AuthProvider = ({ children }: PropsWithChildren<{}>) => { // Check for unverified GitHub users after a GitHub sign in diff --git a/apps/studio/lib/constants/index.ts b/apps/studio/lib/constants/index.ts index 942be406eb5..81e9970dc2c 100644 --- a/apps/studio/lib/constants/index.ts +++ b/apps/studio/lib/constants/index.ts @@ -38,65 +38,6 @@ export const STRIPE_PUBLIC_KEY = export const USAGE_APPROACHING_THRESHOLD = 0.75 -export const LOCAL_STORAGE_KEYS = { - AI_ASSISTANT_STATE: (projectRef: string | undefined) => - `supabase-ai-assistant-state-${projectRef}`, - SIDEBAR_BEHAVIOR: 'supabase-sidebar-behavior', - EDITOR_PANEL_STATE: 'supabase-editor-panel-state', - - UI_PREVIEW_API_SIDE_PANEL: 'supabase-ui-api-side-panel', - UI_PREVIEW_CLS: 'supabase-ui-cls', - UI_PREVIEW_INLINE_EDITOR: 'supabase-ui-preview-inline-editor', - UI_ONBOARDING_NEW_PAGE_SHOWN: 'supabase-ui-onboarding-new-page-shown', - UI_TABLE_EDITOR_TABS: 'supabase-ui-table-editor-tabs', - UI_SQL_EDITOR_TABS: 'supabase-ui-sql-editor-tabs', - UI_NEW_LAYOUT_PREVIEW: 'supabase-ui-new-layout-preview', - NEW_LAYOUT_NOTICE_ACKNOWLEDGED: 'new-layout-notice-acknowledge', - - DASHBOARD_HISTORY: (ref: string) => `dashboard-history-${ref}`, - STORAGE_PREFERENCE: (ref: string) => `storage-explorer-${ref}`, - - SQL_EDITOR_INTELLISENSE: 'supabase_sql-editor-intellisense-enabled', - SQL_EDITOR_SPLIT_SIZE: 'supabase_sql-editor-split-size', - // Key to track which schemas are ok to be sent to AI. The project ref is intentionally put at the end for easier search in the browser console. - SQL_EDITOR_AI_SCHEMA: (ref: string) => `supabase_sql-editor-ai-schema-enabled-${ref}`, - SQL_EDITOR_AI_OPEN: 'supabase_sql-editor-ai-open', - SQL_EDITOR_LAST_SELECTED_DB: (ref: string) => `sql-editor-last-selected-db-${ref}`, - SQL_EDITOR_SQL_BLOCK_ACKNOWLEDGED: (ref: string) => `sql-editor-sql-block-acknowledged-${ref}`, - SQL_EDITOR_SECTION_STATE: (ref: string) => `sql-editor-section-state-${ref}`, - SQL_EDITOR_SORT: (ref: string) => `sql-editor-sort-${ref}`, - - LOG_EXPLORER_SPLIT_SIZE: 'supabase_log-explorer-split-size', - GRAPHIQL_RLS_BYPASS_WARNING: 'graphiql-rls-bypass-warning-dismissed', - CLS_DIFF_WARNING: 'cls-diff-warning-dismissed', - CLS_SELECT_STAR_WARNING: 'cls-select-star-warning-dismissed', - QUERY_PERF_SHOW_BOTTOM_SECTION: 'supabase-query-perf-show-bottom-section', - // Key to track account deletion requests - ACCOUNT_DELETION_REQUEST: 'supabase-account-deletion-request', - // Used for storing a user id when sending reports to Sentry. The id is hashed for anonymity. - SENTRY_USER_ID: 'supabase-sentry-user-id', - // Used for storing the last sign in method used by the user - LAST_SIGN_IN_METHOD: 'supabase-last-sign-in-method', - // Key to track the last selected schema. The project ref is intentionally put at the end for easier search in the browser console. - LAST_SELECTED_SCHEMA: (ref: string) => `last-selected-schema-${ref}`, - // Track position of nodes for schema visualizer - SCHEMA_VISUALIZER_POSITIONS: (ref: string, schemaId: number) => - `schema-visualizer-positions-${ref}-${schemaId}`, - // Used for allowing the main nav panel to expand on hover - EXPAND_NAVIGATION_PANEL: 'supabase-expand-navigation-panel', - GITHUB_AUTHORIZATION_STATE: 'supabase-github-authorization-state', - // Notice banner keys - FLY_POSTGRES_DEPRECATION_WARNING: 'fly-postgres-deprecation-warning-dismissed', - AUTH_USERS_COLUMNS_CONFIGURATION: (ref: string) => `supabase-auth-users-columns-${ref}`, - - // api keys view switcher for new and legacy api keys - API_KEYS_VIEW: (ref: string) => `supabase-api-keys-view-${ref}`, - - // last visited logs page - LAST_VISITED_LOGS_PAGE: 'supabase-last-visited-logs-page', - LAST_VISITED_ORGANIZATION: 'last-visited-organization', -} - export const OPT_IN_TAGS = { AI_SQL: 'AI_SQL_GENERATOR_OPT_IN', } diff --git a/apps/studio/lib/github.tsx b/apps/studio/lib/github.tsx index 275ac6d743e..5d96032ef12 100644 --- a/apps/studio/lib/github.tsx +++ b/apps/studio/lib/github.tsx @@ -1,4 +1,4 @@ -import { LOCAL_STORAGE_KEYS } from './constants' +import { LOCAL_STORAGE_KEYS } from 'common' import { makeRandomString } from './helpers' const GITHUB_INTEGRATION_APP_NAME = diff --git a/apps/studio/lib/local-storage.ts b/apps/studio/lib/local-storage.ts deleted file mode 100644 index a4e3420d9b4..00000000000 --- a/apps/studio/lib/local-storage.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { LOCAL_STORAGE_KEYS as COMMON_LOCAL_STORAGE_KEYS } from 'common' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' - -const LOCAL_STORAGE_KEYS_ALLOWLIST = [ - 'graphiql:theme', - 'theme', - 'supabaseDarkMode', - 'supabase.dashboard.auth.debug', - 'supabase.dashboard.auth.navigatorLock.disabled', - COMMON_LOCAL_STORAGE_KEYS.TELEMETRY_CONSENT, - LOCAL_STORAGE_KEYS.UI_PREVIEW_API_SIDE_PANEL, - LOCAL_STORAGE_KEYS.UI_PREVIEW_INLINE_EDITOR, - LOCAL_STORAGE_KEYS.UI_TABLE_EDITOR_TABS, - LOCAL_STORAGE_KEYS.UI_SQL_EDITOR_TABS, - LOCAL_STORAGE_KEYS.UI_NEW_LAYOUT_PREVIEW, - LOCAL_STORAGE_KEYS.UI_PREVIEW_INLINE_EDITOR, - LOCAL_STORAGE_KEYS.UI_PREVIEW_CLS, - LOCAL_STORAGE_KEYS.LAST_SIGN_IN_METHOD, -] - -export function clearLocalStorage() { - for (const key in localStorage) { - if (!LOCAL_STORAGE_KEYS_ALLOWLIST.includes(key)) { - localStorage.removeItem(key) - } - } -} diff --git a/apps/studio/lib/posthog.ts b/apps/studio/lib/posthog.ts index 96735b0b77f..347040a6af9 100644 --- a/apps/studio/lib/posthog.ts +++ b/apps/studio/lib/posthog.ts @@ -1,7 +1,7 @@ import { components } from 'api-types' -import { hasConsented } from 'common' import { handleError, post } from 'data/fetchers' import { IS_PLATFORM } from './constants' +import { LOCAL_STORAGE_KEYS, hasConsented } from 'common' type TrackFeatureFlagVariables = components['schemas']['TelemetryFeatureFlagBodyDto'] diff --git a/apps/studio/localStores/storageExplorer/StorageExplorerStore.tsx b/apps/studio/localStores/storageExplorer/StorageExplorerStore.tsx index 63fb40418ea..82db7df68a5 100644 --- a/apps/studio/localStores/storageExplorer/StorageExplorerStore.tsx +++ b/apps/studio/localStores/storageExplorer/StorageExplorerStore.tsx @@ -42,11 +42,12 @@ import { downloadBucketObject } from 'data/storage/bucket-object-download-mutati import { StorageObject, listBucketObjects } from 'data/storage/bucket-objects-list-mutation' import { Bucket } from 'data/storage/buckets-query' import { moveStorageObject } from 'data/storage/object-move-mutation' -import { IS_PLATFORM, LOCAL_STORAGE_KEYS } from 'lib/constants' +import { IS_PLATFORM } from 'lib/constants' import { tryParseJson } from 'lib/helpers' import { lookupMime } from 'lib/mime' import Link from 'next/link' import { Button, SONNER_DEFAULT_DURATION, SonnerProgress } from 'ui' +import { LOCAL_STORAGE_KEYS } from 'common' type CachedFile = { id: string; fetchedAt: number; expiresIn: number; url: string } diff --git a/apps/studio/pages/project/[ref]/database/column-privileges.tsx b/apps/studio/pages/project/[ref]/database/column-privileges.tsx index 0409e6a391a..3a632285c63 100644 --- a/apps/studio/pages/project/[ref]/database/column-privileges.tsx +++ b/apps/studio/pages/project/[ref]/database/column-privileges.tsx @@ -1,4 +1,4 @@ -import { useParams } from 'common' +import { LOCAL_STORAGE_KEYS, useParams } from 'common' import { AlertCircle, XIcon } from 'lucide-react' import Link from 'next/link' import { useCallback, useMemo, useState } from 'react' @@ -27,7 +27,6 @@ import { useTablePrivilegesQuery } from 'data/privileges/table-privileges-query' import { useTablesQuery } from 'data/tables/tables-query' import { useLocalStorage } from 'hooks/misc/useLocalStorage' import { useQuerySchemaState } from 'hooks/misc/useSchemaQueryState' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import { PROTECTED_SCHEMAS } from 'lib/constants/schemas' import { useAppStateSnapshot } from 'state/app-state' import type { NextPageWithLayout } from 'types' diff --git a/apps/studio/pages/project/[ref]/logs/explorer/index.tsx b/apps/studio/pages/project/[ref]/logs/explorer/index.tsx index 2d9cd94cdca..c7c5a23f57b 100644 --- a/apps/studio/pages/project/[ref]/logs/explorer/index.tsx +++ b/apps/studio/pages/project/[ref]/logs/explorer/index.tsx @@ -6,7 +6,8 @@ import { useRouter } from 'next/router' import { useEffect, useRef, useState } from 'react' import { toast } from 'sonner' -import { IS_PLATFORM, useParams } from 'common' +import { IS_PLATFORM, LOCAL_STORAGE_KEYS, useParams } from 'common' + import { LOGS_LARGE_DATE_RANGE_DAYS_THRESHOLD, LOGS_TABLES, @@ -41,7 +42,6 @@ import useLogsQuery from 'hooks/analytics/useLogsQuery' import { useLogsUrlState } from 'hooks/analytics/useLogsUrlState' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' import { useUpgradePrompt } from 'hooks/misc/useUpgradePrompt' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import { uuidv4 } from 'lib/helpers' import { useProfile } from 'lib/profile' import type { LogSqlSnippets, NextPageWithLayout } from 'types' diff --git a/apps/studio/pages/project/[ref]/logs/index.tsx b/apps/studio/pages/project/[ref]/logs/index.tsx index cfb40aea392..1276db2782f 100644 --- a/apps/studio/pages/project/[ref]/logs/index.tsx +++ b/apps/studio/pages/project/[ref]/logs/index.tsx @@ -1,11 +1,10 @@ import { useRouter } from 'next/router' import { useEffect } from 'react' -import { useParams } from 'common' +import { LOCAL_STORAGE_KEYS, useParams } from 'common' import DefaultLayout from 'components/layouts/DefaultLayout' import LogsLayout from 'components/layouts/LogsLayout/LogsLayout' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import type { NextPageWithLayout } from 'types' export const LogPage: NextPageWithLayout = () => { diff --git a/apps/studio/pages/project/[ref]/settings/api-keys.tsx b/apps/studio/pages/project/[ref]/settings/api-keys.tsx index 4183084380e..8eb1be93ded 100644 --- a/apps/studio/pages/project/[ref]/settings/api-keys.tsx +++ b/apps/studio/pages/project/[ref]/settings/api-keys.tsx @@ -1,5 +1,5 @@ import { PermissionAction } from '@supabase/shared-types/out/constants' -import { useParams } from 'common' +import { LOCAL_STORAGE_KEYS, useParams } from 'common' import LegacyAPIKeys from 'components/interfaces/APIKeys/LegacyAPIKeys' import { PublishableAPIKeys } from 'components/interfaces/APIKeys/PublishableAPIKeys' @@ -9,7 +9,6 @@ import ApiKeysLayout from 'components/layouts/project/[ref]/settings/APIKeysLayo import SettingsLayout from 'components/layouts/ProjectSettingsLayout/SettingsLayout' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' import type { NextPageWithLayout } from 'types' import { Separator } from 'ui' diff --git a/apps/studio/pages/projects.tsx b/apps/studio/pages/projects.tsx index 1723e6bbaa4..d454494dbd6 100644 --- a/apps/studio/pages/projects.tsx +++ b/apps/studio/pages/projects.tsx @@ -13,9 +13,10 @@ import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { useAutoProjectsPrefetch } from 'data/projects/projects-query' import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' -import { IS_PLATFORM, LOCAL_STORAGE_KEYS, PROJECT_STATUS } from 'lib/constants' +import { IS_PLATFORM, PROJECT_STATUS } from 'lib/constants' import type { NextPageWithLayout } from 'types' import { cn } from 'ui' +import { LOCAL_STORAGE_KEYS } from 'common' const ProjectsPage: NextPageWithLayout = () => { const newLayoutPreview = useIsNewLayoutEnabled() diff --git a/apps/studio/state/ai-assistant-state.tsx b/apps/studio/state/ai-assistant-state.tsx index 8b5f7edcfb4..d4ddd0d661f 100644 --- a/apps/studio/state/ai-assistant-state.tsx +++ b/apps/studio/state/ai-assistant-state.tsx @@ -3,8 +3,7 @@ import type { Message as MessageType } from 'ai/react' import { createContext, PropsWithChildren, useContext, useEffect, useState } from 'react' import { proxy, snapshot, subscribe, useSnapshot } from 'valtio' import { debounce } from 'lodash' - -import { LOCAL_STORAGE_KEYS } from 'lib/constants' +import { LOCAL_STORAGE_KEYS } from 'common' type SuggestionsType = { title: string diff --git a/apps/studio/state/app-state.ts b/apps/studio/state/app-state.ts index 186f4078c61..22012f85f9f 100644 --- a/apps/studio/state/app-state.ts +++ b/apps/studio/state/app-state.ts @@ -1,7 +1,7 @@ import { proxy, snapshot, subscribe, useSnapshot } from 'valtio' +import { LOCAL_STORAGE_KEYS as COMMON_LOCAL_STORAGE_KEYS, LOCAL_STORAGE_KEYS } from 'common' import { SQL_TEMPLATES } from 'components/interfaces/SQLEditor/SQLEditor.queries' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' export type Template = { name: string diff --git a/apps/studio/state/storage-explorer.tsx b/apps/studio/state/storage-explorer.tsx index 886e8eeae19..a4dfab69d5a 100644 --- a/apps/studio/state/storage-explorer.tsx +++ b/apps/studio/state/storage-explorer.tsx @@ -8,7 +8,7 @@ import { STORAGE_SORT_BY_ORDER, STORAGE_VIEWS, } from 'components/to-be-cleaned/Storage/Storage.constants' -import { LOCAL_STORAGE_KEYS } from 'lib/constants' +import { LOCAL_STORAGE_KEYS } from 'common' import { tryParseJson } from 'lib/helpers' const DEFAULT_PREFERENCES = { diff --git a/apps/www/components/LaunchWeek/11/Countdown.tsx b/apps/www/components/LaunchWeek/11/Countdown.tsx deleted file mode 100644 index d0a61eb6c30..00000000000 --- a/apps/www/components/LaunchWeek/11/Countdown.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react' -import Countdown from 'react-countdown' -import { CountdownWidget } from 'ui-patterns/CountdownWidget' - -const CountdownComponent = ({ - date, - showCard = true, - className, - size = 'small', -}: { - date: string | number | Date - showCard?: boolean - className?: string - size?: 'small' | 'large' -}) => { - if (!date) return null - - const renderer = ({ days, hours, minutes, seconds, completed }: any) => { - if (completed) { - // Render a completed state - return null - } else { - // Render countdown - return ( - - ) - } - } - - return -} - -export default CountdownComponent diff --git a/apps/www/components/LaunchWeek/11/HackathonCallout.tsx b/apps/www/components/LaunchWeek/11/HackathonCallout.tsx index 93177617c1d..9eede3f04cb 100644 --- a/apps/www/components/LaunchWeek/11/HackathonCallout.tsx +++ b/apps/www/components/LaunchWeek/11/HackathonCallout.tsx @@ -1,11 +1,7 @@ import React from 'react' import { TextLink, cn } from 'ui' -import CountdownComponent from './Countdown' -import { LW11_LAUNCH_DATE_END } from '~/lib/constants' const HackathonCallout = ({ className }: { className?: string }) => { - const isActive = new Date() < new Date(LW11_LAUNCH_DATE_END) - return (
{ )} >
- {isActive ? ( - <> - Hackathon ends in - - ) : ( - 'Open Source Hackathon 2024' - )} + Open Source Hackathon 2024
{ - const canvasRef = React.useRef(null) - const [size, setSize] = useState({ w: 1200, h: 800 }) - const [config] = useState(defaultConfig) - const [hasInitialized, setHasInitialized] = useState(false) - const Window = useWindowSize() - - const DOT_AREA = config.dotGrid - let c = canvasRef.current?.getContext('2d') - let dotsArray: any[] = [] - - function init() { - if (!c) return - - setHasInitialized(true) - c = canvasRef.current?.getContext('2d')! - c.clearRect(0, 0, size.w, size.h) - dotsArray = [] - const GRID_COLS = Math.floor(size.w / DOT_AREA) - const GRID_ROWS = Math.floor(size.h / DOT_AREA) - - for (let i = 0; i < GRID_COLS; i++) { - for (let j = 0; j < GRID_ROWS; j++) { - const isLarge = Math.random() > config.percentageLarge - const isAnimated = isLarge || Math.random() > config.percentageAnimated - const direction = isAnimated ? (Math.random() > 0.5 ? 'vertical' : 'horizontal') : undefined - const speed = isAnimated ? anime.random(config.minSpeed, config.maxSpeed) : undefined - const opacity = isLarge ? 1 : anime.random(0.4, 1) - const isReverse = isAnimated ? Math.random() > 0.5 : undefined - const oscillation = isAnimated - ? anime.random(config.minOscillation, config.maxOscillation).toFixed() - : undefined - const dotSize = isLarge - ? Math.random() * config.randomizeLargeDots - : Math.random() * config.randomizeSmallDots - const endPos = { - x: anime - .random( - DOT_AREA * 1 - DOT_AREA / 2 + dotSize / 2, - DOT_AREA * 10 - DOT_AREA / 2 + dotSize / 2 - ) - .toFixed(), - y: anime - .random( - DOT_AREA * 1 - DOT_AREA / 2 + dotSize / 2, - DOT_AREA * 10 - DOT_AREA / 2 + dotSize / 2 - ) - .toFixed(), - } - const delay = anime.random(config.minDelay, config.maxDelay) - const duration = anime.random(config.minDuration, config.maxDuration) - const animationConfig: any = isAnimated - ? { - direction, - speed, - isReverse, - oscillation, - endPos, - delay, - duration, - } - : undefined - const x = (size.w / GRID_COLS) * i + DOT_AREA / 2 - dotSize / 2 - const y = (size.h / GRID_ROWS) * j + DOT_AREA / 2 - dotSize / 2 - const w = dotSize - const h = dotSize - - dotsArray.push(new Dot(x, y, w, h, opacity, animationConfig)) - } - } - - animate(0) - } - - function animate(clock?: number) { - if (!isBrowser) return - - for (let i = 0; i < dotsArray.length; i++) { - dotsArray[i].update(c, clock) - } - - const tl = anime - .timeline({ - targets: dotsArray.filter((dot) => dot.anim), - loop: true, - direction: 'alternate', - autoplay: true, - update: renderParticule, - }) - .add( - { - x: (p: any) => - !p.isVert ? `${p.anim?.isReverse ? '+' : '-'}=${DOT_AREA * p.anim.oscillation}` : p.x, - y: (p: any) => - p.isVert ? `${p.anim?.isReverse ? '+' : '-'}=${DOT_AREA * p.anim.oscillation}` : p.y, - duration: (p: any) => p.anim?.duration, - delay: (p: any) => p.anim?.delay - 1000, - easing: 'easeInOutExpo', - }, - '-=1000' - ) - - tl.play() - } - - function renderParticule(anim: any) { - if (!isBrowser) return - - c?.clearRect(0, 0, size.w, size.h) - for (let i = 0; i < dotsArray.length; i++) { - dotsArray[i].update(c, 0) - } - for (var i = 0; i < anim.animatables.length; i++) { - anim.animatables[i].target.update(c, 0) - } - } - - function resize() { - setSize({ w: Window.width, h: Window.height }) - init() - } - - useEffect(() => { - if (!isBrowser) return - resize() - }, [Window]) - - useEffect(() => { - resize() - init() - }, []) - - !hasInitialized && init() - - return ( - - ) -} - -export default LW11Background diff --git a/apps/www/components/LaunchWeek/11/Releases/LW11Header.tsx b/apps/www/components/LaunchWeek/11/Releases/LW11Header.tsx index 0113dd2a219..5dd989514aa 100644 --- a/apps/www/components/LaunchWeek/11/Releases/LW11Header.tsx +++ b/apps/www/components/LaunchWeek/11/Releases/LW11Header.tsx @@ -1,7 +1,6 @@ import React from 'react' import { Button, cn } from 'ui' import SectionContainer from '~/components/Layouts/SectionContainer' -import LW11Background from '../LW11Background' import Image from 'next/image' import Link from 'next/link' @@ -39,12 +38,6 @@ const LW11Header = ({ className }: { className?: string }) => { Read full announcement -
-
- -
-
-
) } diff --git a/apps/www/components/LaunchWeek/11/Releases/components/DaySection.tsx b/apps/www/components/LaunchWeek/11/Releases/components/DaySection.tsx index 34875bdab63..2281f462687 100644 --- a/apps/www/components/LaunchWeek/11/Releases/components/DaySection.tsx +++ b/apps/www/components/LaunchWeek/11/Releases/components/DaySection.tsx @@ -7,7 +7,6 @@ import { Edit } from 'lucide-react' import { useBreakpoint } from 'common' import { WeekDayProps } from '../data' -import CountdownComponent from '../../Countdown' import { DayLink } from '.' const DaySection = ({ day, className }: { day: WeekDayProps; className?: string }) => { @@ -130,7 +129,6 @@ const DaySection = ({ day, className }: { day: WeekDayProps; className?: string /> - {day.hasCountdown && }
)} diff --git a/apps/www/components/LaunchWeek/11/TicketsGrid.tsx b/apps/www/components/LaunchWeek/11/TicketsGrid.tsx deleted file mode 100644 index 78c5df2204f..00000000000 --- a/apps/www/components/LaunchWeek/11/TicketsGrid.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { motion } from 'framer-motion' -import Image from 'next/image' -import Link from 'next/link' -import { useBreakpoint } from 'common/hooks/useBreakpoint' -import { UserData } from '../hooks/use-conf-data' - -interface Props { - loadedUsers: UserData[] - isLoading: boolean - pageCount: number - offset: number -} - -export default function TicketsGrid({ loadedUsers, isLoading, pageCount, offset }: Props) { - const STORAGE_URL = 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lwx' - const getTicketImageUrl = (username: string, isGold: boolean) => - `${STORAGE_URL}/tickets/${isGold ? 'platinum' : 'regular'}/${username}.png` - const isMobile = useBreakpoint(1024) - const isTablet = useBreakpoint(1280) - const horizontalOffset = isMobile ? 8 : 20 - - return ( -
- {loadedUsers?.map((user, i) => { - const rowTickets = isMobile ? 2 : isTablet ? 3 : 4 - const divider = Math.floor(i / rowTickets) - const isOddRow = divider % 2 === 0 - // Delay should be no more than pageCount for new loaded users - const recalculatedDelay = i >= pageCount * 2 ? (i - pageCount * (offset - 1)) / 15 : i / 15 - const imgUrl = - getTicketImageUrl(user.username!, !!user.golden) ?? - '/images/launchweek/lwx/tickets/placeholder.png' - - return ( - window.scrollTo(0, 0)} - legacyBehavior - passHref - > - -
- {user.username -
-
- - ) - })} - {isLoading && - Array.from({ length: pageCount }, (_, i) => ( - -
-
-
- - ))} -
- ) -} diff --git a/apps/www/components/LaunchWeek/12/Releases/components/DaySection.tsx b/apps/www/components/LaunchWeek/12/Releases/components/DaySection.tsx index ded00e9ea9e..6d744664703 100644 --- a/apps/www/components/LaunchWeek/12/Releases/components/DaySection.tsx +++ b/apps/www/components/LaunchWeek/12/Releases/components/DaySection.tsx @@ -7,7 +7,6 @@ import { Edit } from 'lucide-react' import { useBreakpoint } from 'common' import { WeekDayProps } from '../data' -// import CountdownComponent from '../../Countdown' import { DayLink } from '.' const DaySection = ({ day, className }: { day: WeekDayProps; className?: string }) => { diff --git a/apps/www/components/LaunchWeek/13/Countdown.tsx b/apps/www/components/LaunchWeek/13/Countdown.tsx deleted file mode 100644 index d0a61eb6c30..00000000000 --- a/apps/www/components/LaunchWeek/13/Countdown.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react' -import Countdown from 'react-countdown' -import { CountdownWidget } from 'ui-patterns/CountdownWidget' - -const CountdownComponent = ({ - date, - showCard = true, - className, - size = 'small', -}: { - date: string | number | Date - showCard?: boolean - className?: string - size?: 'small' | 'large' -}) => { - if (!date) return null - - const renderer = ({ days, hours, minutes, seconds, completed }: any) => { - if (completed) { - // Render a completed state - return null - } else { - // Render countdown - return ( - - ) - } - } - - return -} - -export default CountdownComponent diff --git a/apps/www/components/LaunchWeek/13/Multiplayer/CanvasPartyMode.tsx b/apps/www/components/LaunchWeek/13/Multiplayer/CanvasPartyMode.tsx deleted file mode 100644 index 5847dcda9d7..00000000000 --- a/apps/www/components/LaunchWeek/13/Multiplayer/CanvasPartyMode.tsx +++ /dev/null @@ -1,373 +0,0 @@ -'use client' - -import { useEffect, useRef, useState, useCallback, ReactElement } from 'react' -import { - REALTIME_LISTEN_TYPES, - REALTIME_PRESENCE_LISTEN_EVENTS, - REALTIME_SUBSCRIBE_STATES, - RealtimeChannel, -} from '@supabase/supabase-js' -import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' -import { v4 as uuidv4 } from 'uuid' -import { useTheme } from 'next-themes' -import { getColor } from './randomColor' -import { Coordinates, Payload, User } from './types' -import { cloneDeep, throttle } from 'lodash' -import Cursor from './Cursor' - -export const GRID_SIZE = 100 -export const CELL_SIZE = 40 -export const CANVAS_WIDTH = 1800 -export const CANVAS_HEIGHT = 1600 -export const HOVER_DURATION = 100 -export const FADE_DURATION = 300 - -const MAX_ROOM_USERS = 50 -const MAX_EVENTS_PER_SECOND = 10 -export const X_THRESHOLD = 25 -export const Y_THRESHOLD = 65 - -interface CellState { - isHovered: boolean - fadeStartTime: number | null - color: string -} - -export const INTERACTIVE_GRID_COLORS = (isDark: boolean) => ({ - GRID_STROKE: isDark ? '#242424' : '#EDEDED', - CURRENT_USER_HOVER: isDark ? '#00311D' : '#72E3AD', -}) - -// Generate a random user id -const userId = uuidv4() - -export default function InteractiveGrid() { - const { supabase, userData } = useConfData() - const { resolvedTheme } = useTheme() - const isDarkTheme = resolvedTheme?.includes('dark')! - const canvasRef = useRef(null) - const [hoveredCells, setHoveredCells] = useState>(new Map()) - const animationFrameRef = useRef() - - // These states will be managed via ref as they're mutated within event listeners - const usersRef = useRef<{ [key: string]: User }>({}) - const mousePositionRef = useRef() - - const joinTimestampRef = useRef() - const [mousePosition, _setMousePosition] = useState() - - const [isInitialStateSynced, setIsInitialStateSynced] = useState(false) - const [roomId, setRoomId] = useState(undefined) - const [users, setUsers] = useState<{ [key: string]: User }>({}) - - const setMousePosition = (coordinates: Coordinates) => { - // if (!mousePositionRef.current) return - mousePositionRef.current = coordinates - _setMousePosition(coordinates) - } - - const mapInitialUsers = (userChannel: RealtimeChannel, roomId: string) => { - const state = userChannel.presenceState() - const _users = state[roomId] - - if (!_users) return - - if (_users) { - setUsers((existingUsers) => { - const updatedUsers = _users.reduce( - (acc: { [key: string]: User }, { user_id: userId }: any, index: number) => { - acc[userId] = existingUsers[userId] || { - x: 0, - y: 0, - } - return acc - }, - {} - ) - usersRef.current = updatedUsers - return updatedUsers - }) - } - } - - useEffect(() => { - let roomChannel: RealtimeChannel - - if (!roomId) { - joinTimestampRef.current = performance.now() - - /* - Client is joining 'rooms' channel to examine existing rooms and their users - and then the channel is removed once a room is selected - */ - roomChannel = supabase?.channel('lw13_rooms')! - - roomChannel - .on(REALTIME_LISTEN_TYPES.PRESENCE, { event: REALTIME_PRESENCE_LISTEN_EVENTS.SYNC }, () => { - let newRoomId - const state = roomChannel.presenceState() - - // User will be assigned an existing room with the fewest users - if (!newRoomId) { - const [mostVacantRoomId, users] = - Object.entries(state).sort(([, a], [, b]) => a.length - b.length)[0] ?? [] - - if (users && users.length < MAX_ROOM_USERS) { - newRoomId = mostVacantRoomId - } - } - - // Generate an id if no existing rooms are available - setRoomId(newRoomId ?? uuidv4()) - }) - .subscribe() - } else { - // When user has been placed in a room - roomChannel = supabase?.channel('lw13_rooms', { config: { presence: { key: roomId } } })! - roomChannel.on( - REALTIME_LISTEN_TYPES.PRESENCE, - { event: REALTIME_PRESENCE_LISTEN_EVENTS.SYNC }, - () => { - setIsInitialStateSynced(true) - mapInitialUsers(roomChannel, roomId) - } - ) - roomChannel.subscribe(async (status: `${REALTIME_SUBSCRIBE_STATES}`) => { - if (status === REALTIME_SUBSCRIBE_STATES.SUBSCRIBED) - await roomChannel.track({ user_id: userId }) - }) - } - - // Must properly remove subscribed channel - return () => { - roomChannel && supabase?.removeChannel(roomChannel) - } - }, [supabase, roomId]) - - useEffect(() => { - // if (!roomId || !isInitialStateSynced) return - - let messageChannel: RealtimeChannel - let setMouseEvent: (e: MouseEvent) => void = () => {} - - messageChannel = supabase?.channel(`chat_messages:${roomId}`)! - - // Listen for cursor positions from other users in the room - messageChannel.on( - REALTIME_LISTEN_TYPES.BROADCAST, - { event: 'POS' }, - (payload: Payload<{ user_id: string } & Coordinates>) => { - setUsers((users) => { - const userId = payload!.payload!.user_id - const existingUser = users[userId] - - if (existingUser) { - const x = - (payload?.payload?.x ?? 0) - X_THRESHOLD > window.innerWidth - ? window.innerWidth - X_THRESHOLD - : payload?.payload?.x - const y = - (payload?.payload?.y ?? 0 - Y_THRESHOLD) > window.innerHeight - ? window.innerHeight - Y_THRESHOLD - : payload?.payload?.y - - users[userId] = { ...existingUser, ...{ x, y } } - users = cloneDeep(users) - } - - return users - }) - } - ) - - messageChannel?.subscribe((status: `${REALTIME_SUBSCRIBE_STATES}`) => { - // Lodash throttle will be removed once realtime-js client throttles on the channel level - const sendMouseBroadcast = throttle(({ x, y }) => { - messageChannel - .send({ - type: 'broadcast', - event: 'POS', - payload: { user_id: userId, x, y }, - }) - .catch(() => {}) - }, 1000 / MAX_EVENTS_PER_SECOND) - - setMouseEvent = (e: MouseEvent) => { - const top = window.pageYOffset || document.documentElement.scrollTop - const [x, y] = [e.clientX, e.clientY - Y_THRESHOLD + top] - if (status === REALTIME_SUBSCRIBE_STATES.SUBSCRIBED) { - sendMouseBroadcast({ x, y }) - } - setMousePosition({ x, y }) - } - - window.addEventListener('mousemove', setMouseEvent) - }) - - return () => { - window.removeEventListener('mousemove', setMouseEvent) - messageChannel && supabase?.removeChannel(messageChannel) - } - }, [roomId, isInitialStateSynced]) - - const setCellHovered = useCallback((key: string, isHovered: boolean, color: string) => { - setHoveredCells((prev) => { - const newState = new Map(prev) - if (isHovered) { - newState.set(key, { isHovered: true, fadeStartTime: null, color }) - } else { - const existingCell = newState.get(key) - if (existingCell && existingCell.isHovered) { - newState.set(key, { - isHovered: false, - fadeStartTime: Date.now() + HOVER_DURATION, - color, - }) - } - } - - return newState - }) - }, []) - - const drawGrid = useCallback( - (ctx: CanvasRenderingContext2D, currentTime: number) => { - ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT) - ctx.lineWidth = 0.15 - ctx.strokeStyle = INTERACTIVE_GRID_COLORS(isDarkTheme).GRID_STROKE - - for (let x = 0; x < GRID_SIZE; x++) { - for (let y = 0; y < GRID_SIZE; y++) { - const cellX = x * CELL_SIZE - const cellY = y * CELL_SIZE - - ctx.strokeRect(cellX, cellY, CELL_SIZE, CELL_SIZE) - - const cellKey = `${x},${y}` - const cellState = hoveredCells.get(cellKey) - - if (cellState) { - let opacity = 0.5 - if (!cellState.isHovered && cellState.fadeStartTime) { - const fadeElapsed = currentTime - cellState.fadeStartTime - if (fadeElapsed >= 0) { - opacity = Math.max(0, 0.5 * (1 - fadeElapsed / FADE_DURATION)) - if (opacity === 0) { - setHoveredCells((prev) => { - const newState = new Map(prev) - newState.delete(cellKey) - return newState - }) - } - } - } - ctx.fillStyle = `rgba(${parseInt(cellState.color.slice(1, 3), 16)}, ${parseInt(cellState.color.slice(3, 5), 16)}, ${parseInt(cellState.color.slice(5, 7), 16)}, ${opacity})` - ctx.fillRect(cellX, cellY, CELL_SIZE, CELL_SIZE) - } - } - } - }, - [hoveredCells, isDarkTheme] - ) - - const animate = useCallback(() => { - const canvas = canvasRef.current - if (!canvas) return - - const ctx = canvas.getContext('2d') - if (!ctx) return - - const currentTime = Date.now() - drawGrid(ctx, currentTime) - - animationFrameRef.current = requestAnimationFrame(animate) - }, [drawGrid]) - - useEffect(() => { - animate() - return () => { - if (animationFrameRef.current) { - cancelAnimationFrame(animationFrameRef.current) - } - } - }, [animate]) - - const handleMouseMove = useCallback( - (e: React.MouseEvent) => { - handleUserTrail(e.nativeEvent.offsetX, e.nativeEvent.offsetY) - }, - [hoveredCells, setCellHovered, isDarkTheme] - ) - - const handleUserTrail = ( - xPos: number, - yPos: number, - color = INTERACTIVE_GRID_COLORS(isDarkTheme).CURRENT_USER_HOVER - ) => { - const x = Math.floor(xPos / CELL_SIZE) - const y = Math.floor(yPos / CELL_SIZE) - - const cellKey = `${x},${y}` - - if (x >= 0 && x < GRID_SIZE && y >= 0 && y < GRID_SIZE) { - setCellHovered(cellKey, true, color) - - hoveredCells.forEach((_, key) => { - if (key !== cellKey) { - setCellHovered(key, false, color) - } - }) - } - } - - const handleMouseLeave = useCallback(() => { - hoveredCells.forEach((_, key) => { - setCellHovered(key, false, INTERACTIVE_GRID_COLORS(isDarkTheme).CURRENT_USER_HOVER) - }) - }, [hoveredCells, setCellHovered, userData]) - - return ( -
- - - {/* Current user cursor */} - {/* */} - - {/* Online users cursors */} - {Object.entries(users).reduce((acc, [userId, data]) => { - const { x, y, message, isTyping } = data - if (x && y) { - acc.push( - - ) - } - return acc - }, [] as ReactElement[])} -
- ) -} diff --git a/apps/www/components/LaunchWeek/13/Multiplayer/CanvasSingleMode.tsx b/apps/www/components/LaunchWeek/13/Multiplayer/CanvasSingleMode.tsx deleted file mode 100644 index 0383fa446d8..00000000000 --- a/apps/www/components/LaunchWeek/13/Multiplayer/CanvasSingleMode.tsx +++ /dev/null @@ -1,219 +0,0 @@ -'use client' - -import { useEffect, useRef, useState, useCallback } from 'react' -import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' -import { v4 as uuidv4 } from 'uuid' -import { useTheme } from 'next-themes' -import Cursor from './Cursor' -import { getColor } from './randomColor' -import { Coordinates } from './types' -import { - HOVER_DURATION, - CANVAS_WIDTH, - CANVAS_HEIGHT, - GRID_SIZE, - CELL_SIZE, - FADE_DURATION, - Y_THRESHOLD, -} from './CanvasPartyMode' - -interface CellState { - isHovered: boolean - fadeStartTime: number | null - color: string -} - -export default function InteractiveGridSingle() { - const { supabase, userData } = useConfData() - const { resolvedTheme } = useTheme() - const isDarkTheme = resolvedTheme?.includes('dark')! - const canvasRef = useRef(null) - const [hoveredCells, setHoveredCells] = useState>(new Map()) - const [userColors, setUserColors] = useState>({}) - const animationFrameRef = useRef() - const [mousePosition, _setMousePosition] = useState() - const mousePositionRef = useRef() - - const currentUserIdRef = useRef(userData?.id || uuidv4()) - const CURRENT_USER_ID = currentUserIdRef.current - - const setMousePosition = (coordinates: Coordinates) => { - mousePositionRef.current = coordinates - _setMousePosition(coordinates) - } - - const INTERACTIVE_GRID_COLORS = (isDark: boolean) => ({ - GRID_STROKE: isDark ? '#242424' : '#EDEDED', - CURRENT_USER_HOVER: isDark ? '#242424' : '#d3d3d3', - }) - - const getUserColor = useCallback( - (userId: string | undefined) => { - if (!userId) { - return INTERACTIVE_GRID_COLORS(isDarkTheme).CURRENT_USER_HOVER // fallback color - } - - if (userColors[userId]) { - return userColors[userId] - } - - const colors = [INTERACTIVE_GRID_COLORS(isDarkTheme).CURRENT_USER_HOVER] - const color = colors[userId.charCodeAt(0) % colors.length] - - setUserColors((prev) => ({ ...prev, [userId]: color })) - return color - }, - [userColors] - ) - - const setCellHovered = useCallback((key: string, isHovered: boolean, color: string) => { - setHoveredCells((prev) => { - const newState = new Map(prev) - if (isHovered) { - newState.set(key, { isHovered: true, fadeStartTime: null, color }) - } else { - const existingCell = newState.get(key) - if (existingCell && existingCell.isHovered) { - newState.set(key, { - isHovered: false, - fadeStartTime: Date.now() + HOVER_DURATION, - color, - }) - } - } - - return newState - }) - }, []) - - const drawGrid = useCallback( - (ctx: CanvasRenderingContext2D, currentTime: number) => { - ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT) - ctx.lineWidth = 0.15 - ctx.strokeStyle = INTERACTIVE_GRID_COLORS(isDarkTheme).GRID_STROKE - - for (let x = 0; x < GRID_SIZE; x++) { - for (let y = 0; y < GRID_SIZE; y++) { - const cellX = x * CELL_SIZE - const cellY = y * CELL_SIZE - - ctx.strokeRect(cellX, cellY, CELL_SIZE, CELL_SIZE) - - const cellKey = `${x},${y}` - const cellState = hoveredCells.get(cellKey) - if (cellState) { - let opacity = 0.5 - if (!cellState.isHovered && cellState.fadeStartTime) { - const fadeElapsed = currentTime - cellState.fadeStartTime - if (fadeElapsed >= 0) { - opacity = Math.max(0, 0.5 * (1 - fadeElapsed / FADE_DURATION)) - if (opacity === 0) { - setHoveredCells((prev) => { - const newState = new Map(prev) - newState.delete(cellKey) - return newState - }) - } - } - } - ctx.fillStyle = `rgba(${parseInt(cellState.color.slice(1, 3), 16)}, ${parseInt(cellState.color.slice(3, 5), 16)}, ${parseInt(cellState.color.slice(5, 7), 16)}, ${opacity})` - ctx.fillRect(cellX, cellY, CELL_SIZE, CELL_SIZE) - } - } - } - }, - [hoveredCells, getUserColor, isDarkTheme] - ) - - const animate = useCallback(() => { - const canvas = canvasRef.current - if (!canvas) return - - const ctx = canvas.getContext('2d') - if (!ctx) return - - const currentTime = Date.now() - drawGrid(ctx, currentTime) - - animationFrameRef.current = requestAnimationFrame(animate) - }, [drawGrid]) - - useEffect(() => { - animate() - return () => { - if (animationFrameRef.current) { - cancelAnimationFrame(animationFrameRef.current) - } - } - }, [animate]) - - const handleMouseMove = useCallback( - (e: React.MouseEvent) => { - const x = Math.floor(e.nativeEvent.offsetX / CELL_SIZE) - const y = Math.floor(e.nativeEvent.offsetY / CELL_SIZE) - const cellKey = `${x},${y}` - const userColor = getUserColor(CURRENT_USER_ID) - - if (x >= 0 && x < GRID_SIZE && y >= 0 && y < GRID_SIZE) { - setCellHovered(cellKey, true, userColor) - - hoveredCells.forEach((_, key) => { - if (key !== cellKey) { - setCellHovered(key, false, userColor) - } - }) - } - }, - [hoveredCells, setCellHovered, getUserColor] - ) - - const handleMouseLeave = useCallback(() => { - hoveredCells.forEach((_, key) => { - setCellHovered(key, false, getUserColor(CURRENT_USER_ID)) - }) - }, [hoveredCells, setCellHovered, getUserColor, userData]) - - useEffect(() => { - let setMouseEvent: (e: MouseEvent) => void = () => {} - - setMouseEvent = (e: MouseEvent) => { - const top = window.pageYOffset || document.documentElement.scrollTop - const [x, y] = [e.clientX, e.clientY - Y_THRESHOLD + top] - setMousePosition({ x, y }) - } - - window.addEventListener('mousemove', setMouseEvent) - - return () => { - window.removeEventListener('mousemove', setMouseEvent) - } - }, [supabase]) - - return ( -
- - - {/* Current user cursor */} - {/* */} -
- ) -} - -function onlyUnique(value: any, index: number, array: any[]) { - return array.indexOf(value) === index -} diff --git a/apps/www/components/LaunchWeek/13/Multiplayer/Chatbox.tsx b/apps/www/components/LaunchWeek/13/Multiplayer/Chatbox.tsx deleted file mode 100644 index 3c04542699c..00000000000 --- a/apps/www/components/LaunchWeek/13/Multiplayer/Chatbox.tsx +++ /dev/null @@ -1,47 +0,0 @@ -// import { IconLoader } from '@supabase/ui' -import { FC, RefObject } from 'react' -import { Message } from './types' - -interface Props { - messages: Message[] - chatboxRef: RefObject - messagesInTransit: string[] - areMessagesFetched: boolean -} - -const Chatbox: FC = ({ messages, chatboxRef, messagesInTransit, areMessagesFetched }) => { - return ( -
-
- {!areMessagesFetched ? ( -
- {/* */} -

Loading messages

-
- ) : messages.length === 0 && messagesInTransit.length === 0 ? ( -
- Type anything to start chatting 🥳 -
- ) : ( -
- )} - {messages.map((message) => ( -

- {message.message} -

- ))} - {messagesInTransit.map((message, idx: number) => ( -

- {message} -

- ))} - {/*
*/} -
-
- ) -} - -export default Chatbox diff --git a/apps/www/components/LaunchWeek/13/Multiplayer/Cursor.tsx b/apps/www/components/LaunchWeek/13/Multiplayer/Cursor.tsx deleted file mode 100644 index 6bc80797437..00000000000 --- a/apps/www/components/LaunchWeek/13/Multiplayer/Cursor.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { useBreakpoint } from 'common' -import { FC, useEffect, useRef, useState } from 'react' -import { cn } from 'ui' - -interface Props { - x?: number - y?: number - color: string - hue: string - message: string - isCurrentUser: boolean - isTyping: boolean - isCancelled?: boolean - isLocalClient?: boolean - onUpdateMessage?: (message: string) => void -} - -const MAX_DURATION = 4000 - -const Cursor: FC = ({ - x, - y, - color, - hue, - message, - isCurrentUser, - isTyping, - isCancelled, - isLocalClient, - onUpdateMessage = () => {}, -}) => { - const _isLocalClient = !x || !y - const inputRef = useRef() as any - const timeoutRef = useRef() as any - const chatBubbleRef = useRef() as any - const isMobile = useBreakpoint('sm') - - const [isTouchDevice, setIsTouchDevice] = useState(false) - const [hideInput, setHideInput] = useState(false) - const [showMessageBubble, setShowMessageBubble] = useState(false) - - useEffect(() => { - if (isTyping) { - setShowMessageBubble(true) - if (timeoutRef.current) clearTimeout(timeoutRef.current) - - if (isLocalClient) { - if (inputRef.current) inputRef.current.focus() - setHideInput(false) - } - } else { - if (!message || isCancelled) { - setShowMessageBubble(false) - } else { - if (timeoutRef.current) clearTimeout(timeoutRef.current) - if (isLocalClient) setHideInput(true) - const timeoutId = setTimeout(() => { - setShowMessageBubble(false) - }, MAX_DURATION) - timeoutRef.current = timeoutId - } - } - }, [isLocalClient, isTyping, isCancelled, message, inputRef]) - - useEffect(() => { - // touchscreen - if (window.matchMedia('(pointer: coarse)').matches && isMobile) { - setIsTouchDevice(true) - } - }, [isMobile]) - - if (isCurrentUser && isTouchDevice) return null - - return ( - <> - {!_isLocalClient && ( - - - - )} -
- - ) -} - -export default Cursor diff --git a/apps/www/components/LaunchWeek/13/Multiplayer/randomColor.ts b/apps/www/components/LaunchWeek/13/Multiplayer/randomColor.ts deleted file mode 100644 index c650cbcb2f7..00000000000 --- a/apps/www/components/LaunchWeek/13/Multiplayer/randomColor.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { sampleSize } from 'lodash' - -const colors = { - brand: { - bg: 'hsl(var(--brand-default))', - hue: 'hsl(var(--brand-500))', - }, - gray: { - bg: 'hsl(var(--foreground-muted))', - hue: 'hsl(var(--background-surface-100))', - }, -} - -export const getRandomUniqueColor = (currentColors: string[]) => { - const colorNames = Object.values(colors).map((col) => col.bg) - const uniqueColors = colorNames.filter((color: string) => !currentColors.includes(color)) - const uniqueColor = uniqueColors[Math.floor(Math.random() * uniqueColors.length)] - const uniqueColorSet = Object.values(colors).find((color) => color.bg === uniqueColor) - return uniqueColorSet || getRandomColor() -} - -export const getRandomColors = (qty: number) => { - return sampleSize(Object.values(colors), qty) -} - -export const getRandomColor = () => { - return Object.values(colors)[Math.floor(Math.random() * Object.values(colors).length)] -} - -export const getColor = (color: 'brand' | 'gray') => { - return colors[color] -} diff --git a/apps/www/components/LaunchWeek/13/Multiplayer/types.ts b/apps/www/components/LaunchWeek/13/Multiplayer/types.ts deleted file mode 100644 index d992d7a4ab0..00000000000 --- a/apps/www/components/LaunchWeek/13/Multiplayer/types.ts +++ /dev/null @@ -1,23 +0,0 @@ -export interface Coordinates { - x: number | undefined - y: number | undefined -} - -export interface Message { - id: number - user_id: string - message: string -} - -export interface Payload { - type: string - event: string - payload?: T -} - -export interface User extends Coordinates { - color: string - hue: string - isTyping?: boolean - message?: string -} diff --git a/apps/www/components/LaunchWeek/13/Releases/LWHeader.tsx b/apps/www/components/LaunchWeek/13/Releases/LWHeader.tsx index d55fc2b6c59..8f65741fd31 100644 --- a/apps/www/components/LaunchWeek/13/Releases/LWHeader.tsx +++ b/apps/www/components/LaunchWeek/13/Releases/LWHeader.tsx @@ -1,8 +1,6 @@ import React from 'react' import { cn } from 'ui' import SectionContainer from '~/components/Layouts/SectionContainer' -import CanvasSingleMode from '../Multiplayer/CanvasSingleMode' -// import LW12Background from '../LW12Background' const LWHeader = ({ className }: { className?: string }) => { return ( @@ -12,12 +10,6 @@ const LWHeader = ({ className }: { className?: string }) => { className )} > -
-
- -
-
-

Launch Week 13 diff --git a/apps/www/components/LaunchWeek/13/Releases/components/DaySection.tsx b/apps/www/components/LaunchWeek/13/Releases/components/DaySection.tsx index 25315c704c2..768437b79cb 100644 --- a/apps/www/components/LaunchWeek/13/Releases/components/DaySection.tsx +++ b/apps/www/components/LaunchWeek/13/Releases/components/DaySection.tsx @@ -7,7 +7,6 @@ import { Edit } from 'lucide-react' import { useBreakpoint } from 'common' import { WeekDayProps } from '../data' -import CountdownComponent from '../../Countdown' import { DayLink } from '.' const DaySection = ({ day, className }: { day: WeekDayProps; className?: string }) => { @@ -147,7 +146,6 @@ const DaySection = ({ day, className }: { day: WeekDayProps; className?: string /> - {day.hasCountdown && }

diff --git a/apps/www/components/LaunchWeek/13/ThreeTicketCanvas.tsx b/apps/www/components/LaunchWeek/13/ThreeTicketCanvas.tsx deleted file mode 100644 index 033e3853062..00000000000 --- a/apps/www/components/LaunchWeek/13/ThreeTicketCanvas.tsx +++ /dev/null @@ -1,544 +0,0 @@ -'use client' - -import React, { useEffect, useRef } from 'react' -import { useTheme } from 'next-themes' -import { cn, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from 'ui' -import * as THREE from 'three' -import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js' -import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js' -import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry.js' -import { useKey } from 'react-use' -import useConfData from '../hooks/use-conf-data' -import useLwGame, { VALID_KEYS } from '../hooks/useLwGame' - -const ThreeTicketCanvas: React.FC<{ - username: string - className?: string - ticketType?: 'regular' | 'platinum' | 'secret' - ticketPosition?: 'left' | 'right' - sharePage?: boolean -}> = ({ - username = 'Francesco Sansalvadore', - className, - ticketType = 'regular', - ticketPosition = 'right', - sharePage = false, -}) => { - const { userData } = useConfData() - const { resolvedTheme } = useTheme() - const isDarkTheme = resolvedTheme?.includes('dark')! - const inputRef = useRef(null) - const canvasRef = useRef(null) - const ticketRef = useRef(null) - const animationFrameRef = useRef() - const targetRotation = useRef<{ x: number; y: number }>({ x: 0, y: 0 }) - const targetScale = useRef(0) - const groupYRotation = useRef(0) - const isFlipped = useRef(false) - const dragStartX = useRef(null) - const dragDelta = useRef(0) - const isDragging = useRef(false) - const currentValue = useRef('') - const flipDirection = useRef(0) - const { - isGameMode, - setIsGameMode, - winningPhrase, - handleIndexCount, - setGameState, - value, - phraseLength, - REGEXP_ONLY_CHARS, - hasWon, - } = useLwGame(inputRef) - - const CANVAS_HEIGHT = 600 - const FLIP_DELTA = 20 // Swipe px threshold for flipping - const LINE_HEIGHT = 1.6 - const SCALE_VARIATION_ON_INTERACTION = 0.025 - const TICKET_FONT_PADDING_LEFT = -6.4 - const DISPLAY_NAME = username?.split(' ').reverse() || [] - const ALIGN_RIGHT = ticketPosition === 'right' - const IS_PLATINUM = ticketType === 'platinum' - const IS_SECRET = ticketType === 'secret' - const TEXT_Z_POSITION = 0 - const FOOTER_CONTENT = [ - { - text: 'LAUNCH WEEK 13', - position: { x: TICKET_FONT_PADDING_LEFT, y: -6.8, z: TEXT_Z_POSITION }, - size: 0.63, - }, - { - text: '2-6 DEC / 7AM PT', - position: { x: TICKET_FONT_PADDING_LEFT, y: -7.7, z: TEXT_Z_POSITION }, - size: 0.55, - }, - ] - const CONFIG = { - regular: { - ticketColor: isDarkTheme ? 0xf3f3f3 : 0x121212, - ticketForeground: isDarkTheme ? 0x171717 : 0xffffff, - }, - platinum: { - ticketColor: isDarkTheme ? 0x9ea1a1 : 0x9ea1a1, - ticketForeground: 0x0f0f0f, - }, - secret: { - ticketColor: isDarkTheme ? 0xe7a938 : 0xf2c66d, - ticketForeground: 0x0f0f0f, - }, - } - - const isDesktop = (width: number) => width > 1024 - const getTicketScale = (width: number) => (isDesktop(width) ? 0.35 : 0.3) - const getTicketXPosition = (width: number, isRight: boolean) => - isDesktop(width) ? (isRight ? 5 : -5) : 0 - - useKey('Escape', () => { - resetRotation() - }) - - // Reset handler with smooth transition - const resetRotation = () => { - targetRotation.current = { x: 0, y: 0 } - isFlipped.current = false - setIsGameMode(false) - } - - useEffect(() => { - if (!canvasRef.current) return - - let scale = getTicketScale(window.innerWidth) - targetScale.current = scale - const calculateDesktopWidth = () => window.innerWidth - const initialCanvasWidth = calculateDesktopWidth() - const initialCanvasHeight = CANVAS_HEIGHT - const ticketYIdleRotation = 0 - - // Initialize scene, camera, and renderer - const scene = new THREE.Scene() - const camera = new THREE.PerspectiveCamera( - 25, - initialCanvasWidth / initialCanvasHeight, - 0.1, - 1000 - ) - const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }) - renderer.setSize(initialCanvasWidth, initialCanvasHeight) - renderer.setPixelRatio(window.devicePixelRatio) - renderer.shadowMap.enabled = true - canvasRef.current.appendChild(renderer.domElement) - - // Camera setup - const cameraDistance = 30 - camera.position.z = cameraDistance - - // Rest of your existing setup code (loader, materials, etc.) - const gltfLoader = new GLTFLoader() - - // Texture credits to Freepik: https://www.freepik.com/free-photo/golden-wall-background_1213228.htm - const metalTexture = new THREE.TextureLoader().load( - '/images/launchweek/13/ticket/metal-texture.jpg' - ) - const goldTexture = new THREE.TextureLoader().load( - '/images/launchweek/13/ticket/gold-texture.jpg' - ) - - const metalMaterial = new THREE.MeshStandardMaterial({ - color: CONFIG[ticketType].ticketColor, - map: IS_SECRET ? goldTexture : IS_PLATINUM ? metalTexture : undefined, - bumpMap: IS_SECRET ? goldTexture : IS_PLATINUM ? metalTexture : undefined, - metalnessMap: IS_SECRET ? goldTexture : metalTexture, - // roughnessMap: IS_SECRET ? goldTexture : metalTexture, - metalness: IS_SECRET ? 0.8 : IS_PLATINUM ? 1.0 : isDarkTheme ? 0.2 : 0.9, - roughness: IS_SECRET ? 0.12 : IS_PLATINUM ? 0.22 : isDarkTheme ? 0.2 : 0.5, - bumpScale: IS_SECRET ? 0.02 : IS_PLATINUM ? 0.0045 : undefined, - }) - - // Load Ticket model, fonts and textures - let ticket3DImport: THREE.Mesh - const ticketGroup = new THREE.Group() - const ticketScale = getTicketScale(window.innerWidth) - ticketGroup.scale.set(ticketScale, ticketScale, ticketScale) - ticketGroup.position.x = getTicketXPosition(window.innerWidth, ALIGN_RIGHT) - - gltfLoader.load('/images/launchweek/13/ticket/3D-ticket.glb', (gltf) => { - ticket3DImport = gltf.scene.children[0] as THREE.Mesh - ticket3DImport.rotation.x = Math.PI * 0.5 - ticket3DImport.traverse((child) => { - if (child) { - // @ts-ignore - child.material = metalMaterial - child.castShadow = true - child.receiveShadow = true - } - }) - - ticketGroup.add(ticket3DImport) - scene.add(ticketGroup) - ticketRef.current = ticket3DImport - camera.lookAt(ticket3DImport.position) - }) - - const textMaterial = new THREE.MeshStandardMaterial({ - color: CONFIG[ticketType].ticketForeground, - metalness: 0.2, - roughness: 0.35, - }) - - // Load font and add text geometry - const fontLoader = new FontLoader() - - // Load Inter font - fontLoader.load('/images/launchweek/13/ticket/Murecho_Regular.json', (font) => { - DISPLAY_NAME.map((text, index) => { - // Front - const textGeometry = new TextGeometry(text, { - font, - size: 1.2, - height: 0.2, - }) - const textMesh = new THREE.Mesh(textGeometry, textMaterial) - textMesh.updateMatrix() - textMesh.position.set( - TICKET_FONT_PADDING_LEFT, - -5 + LINE_HEIGHT * (index + 1), - TEXT_Z_POSITION - ) - textMesh.castShadow = true - ticketGroup.add(textMesh) - }) - - // Back - if (!sharePage) { - const backTextGeometry = new TextGeometry('Type the secret code', { - font, - size: 0.6, - height: 0.2, - }) - const backTextMesh = new THREE.Mesh(backTextGeometry, textMaterial) - backTextMesh.updateMatrix() - backTextMesh.position.set(4.0, 2, -TEXT_Z_POSITION) - backTextMesh.rotation.y = Math.PI - - backTextMesh.castShadow = true - ticketGroup.add(backTextMesh) - } - }) - - // Load mono font - fontLoader.load('/images/launchweek/13/ticket/SourceCodePro_Regular.json', (font) => { - // Front - FOOTER_CONTENT.map((line) => { - const textGeometry = new TextGeometry(line.text, { - font, - size: line.size, - height: 0.2, - }) - const textMesh = new THREE.Mesh(textGeometry, textMaterial) - textMesh.updateMatrix() - textMesh.position.set(line.position.x, line.position.y, line.position.z) - textMesh.castShadow = true - ticketGroup.add(textMesh) - }) - - // Back - if (!sharePage) { - winningPhrase.flat().map((letter, index) => { - const letterGeometry = new TextGeometry(letter.toUpperCase(), { - font, - size: 2.2, - height: 0.2, - }) - const letterMaterial = new THREE.MeshStandardMaterial({ - color: CONFIG[ticketType].ticketForeground, - metalness: 0.2, - roughness: 0.35, - }) - const backTextMesh = new THREE.Mesh(letterGeometry, letterMaterial) - backTextMesh.updateMatrix() - backTextMesh.position.set(3.9 - index * 1.8, -2, -TEXT_Z_POSITION) - backTextMesh.rotation.y = Math.PI - backTextMesh.name = `letter-${index}` - backTextMesh.material.transparent = true - backTextMesh.material.opacity = 0 - - ticketGroup.add(backTextMesh) - }) - } - }) - - ticketGroup.updateMatrix() - - // Environment Map - const envMapLoader = new THREE.TextureLoader() - envMapLoader.load('/images/launchweek/13/ticket/env-map.jpg', (texture) => { - texture.mapping = THREE.EquirectangularReflectionMapping - scene.environment = texture - metalMaterial.envMap = texture - metalMaterial.envMapIntensity = IS_SECRET - ? isDarkTheme - ? 1.4 - : 0.9 - : isDarkTheme - ? 2.8 - : 0.75 - }) - - // Lights - const ambientLight = new THREE.AmbientLight( - 0xffffff, - IS_SECRET ? (isDarkTheme ? 1.3 : 1.2) : IS_PLATINUM ? (isDarkTheme ? 1.7 : 3.2) : 2 - ) - scene.add(ambientLight) - - const spotLight = new THREE.SpotLight(0xffffff, isDarkTheme ? 0.8 : 0.2) - spotLight.position.z = ticketGroup.position.z + 8 - spotLight.position.x = ticketGroup.position.x + 10 - spotLight.angle = (Math.PI / 2) * 0.5 - spotLight.castShadow = true - spotLight.lookAt(ticketGroup.position) - scene.add(spotLight) - - const getTicketScreenPosition = () => { - if (!ticketRef.current) return null - - const vector = new THREE.Vector3() - ticketRef.current.getWorldPosition(vector) - vector.project(camera) - - const x = (vector.x * 0.5 + 0.5) * initialCanvasWidth - const y = -(vector.y * 0.5 - 0.5) * initialCanvasHeight - - return { x, y } - } - - const handleKeyDown = () => { - currentValue.current.split('').map((_letter, index) => { - const letterToShow = ticketGroup.getObjectByName(`letter-${index}`) - letterToShow?.traverse((child) => { - if (child) { - // @ts-ignore - child.material.opacity = 1 - } - }) - }) - } - - const animate = () => { - // Smooth rotation - if (!isDragging.current) { - ticketGroup.rotation.x += (targetRotation.current.x - ticketGroup.rotation.x) * 0.18 - ticketGroup.rotation.y += - ticketYIdleRotation + (targetRotation.current.y - ticketGroup.rotation.y) * 0.18 - } - if (ticketGroup.scale.x < targetScale.current) { - scale += 0.0025 - } else if (ticketGroup.scale.x > targetScale.current) { - scale -= 0.0025 - } else { - scale = targetScale.current - } - - ticketGroup.scale.set(scale, scale, scale) - groupYRotation.current = ticketGroup.rotation.y - - handleKeyDown() - - renderer.render(scene, camera) - animationFrameRef.current = requestAnimationFrame(animate) - } - - // Start animation - animate() - - const handleMouseMove = (e: MouseEvent) => handlePointerMove(e.clientX, e.clientY) - const handleTouchMove = (e: TouchEvent) => { - const touch = e.touches[0] - handlePointerMove(touch.clientX, touch.clientY) - } - // Tilt and drag logic - const handlePointerMove = (clientX: number, clientY: number) => { - if (!canvasRef.current) return - if (isDragging.current) { - // Update drag delta - if (dragStartX.current !== null) { - dragDelta.current = clientX - dragStartX.current - ticketGroup.rotation.y = targetRotation.current.y + dragDelta.current * 0.01 - } - } else { - // Get canvas bounds - const canvasRect = canvasRef.current.getBoundingClientRect() - - // Calculate center of the canvas - const centerX = canvasRect.left + canvasRect.width / 2 - const centerY = canvasRect.top + canvasRect.height / 2 - const ticketPosition = getTicketScreenPosition() || { x: centerX, y: centerY } - - // Calculate distance from cursor to center of ticket - const deltaX = clientX - (canvasRect.left + ticketPosition.x) - const deltaY = clientY - (canvasRect.top + ticketPosition.y) - const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY) - - // Maximum distance for sensitivity calculation (diagonal of the canvas) - const maxDistance = Math.sqrt( - Math.pow(canvasRect.width / 2, 2) + Math.pow(canvasRect.height / 2, 2) - ) - - // Calculate sensitivity based on distance (inverse relationship) - const sensitivity = 0.002 * (1 - Math.min(distance / maxDistance, 1)) - - // Update target rotation with distance-based sensitivity - targetRotation.current.y = - deltaX * sensitivity + - (isFlipped.current ? (flipDirection.current < 0 ? -Math.PI : Math.PI) : 0) - targetRotation.current.x = deltaY * sensitivity * 0.3 - } - } - - const handleMouseDown = (e: MouseEvent) => handlePointerDown(e.clientX) - const handleTouchStart = (e: TouchEvent) => { - const touch = e.touches[0] - handlePointerDown(touch.clientX) - } - const handlePointerDown = (clientX: number) => { - isDragging.current = true - dragStartX.current = clientX - targetScale.current += SCALE_VARIATION_ON_INTERACTION - } - - const handleMouseUp = () => handlePointerUp() - const handleTouchEnd = () => handlePointerUp() - - const handlePointerUp = () => { - if (!isDragging.current) return - isDragging.current = false - targetScale.current -= SCALE_VARIATION_ON_INTERACTION - - // Detect swipe gesture - if (Math.abs(dragDelta.current) > FLIP_DELTA) { - // Flip the ticket - isFlipped.current = !isFlipped.current - if (!userData.secret) setIsGameMode(isFlipped.current) - const sign = Math.sign(dragDelta.current) - flipDirection.current = sign - targetRotation.current.y += sign * Math.PI // Add full flip - } else { - // Reset rotation - ticketGroup.rotation.y = targetRotation.current.y - } - - // Reset drag state - dragStartX.current = null - dragDelta.current = 0 - } - - // Handle window resize - const handleResize = () => { - const newWidth = calculateDesktopWidth() - ticketGroup.position.x = getTicketXPosition(window.innerWidth, ALIGN_RIGHT) - const tickeScale = getTicketScale(window.innerWidth) - ticketGroup.scale.set(tickeScale, tickeScale, tickeScale) - camera.aspect = newWidth / CANVAS_HEIGHT - camera.updateProjectionMatrix() - renderer.setSize(newWidth, CANVAS_HEIGHT) - } - - function onKeyDown(event: KeyboardEvent) { - if (isGameMode) return - const newKey = event.key.toLocaleLowerCase() - - if (!(event.metaKey || event.ctrlKey) && VALID_KEYS.includes(newKey)) { - targetScale.current += SCALE_VARIATION_ON_INTERACTION - } - - setTimeout(() => { - targetScale.current = getTicketScale(window.innerWidth) - }, 100) - } - - // Event listeners - window.addEventListener('resize', handleResize) - window.addEventListener('mousedown', handleMouseDown) - window.addEventListener('mouseup', handleMouseUp) - window.addEventListener('mousemove', handleMouseMove) - // window.addEventListener('mouseout', resetRotation) - window.addEventListener('touchmove', handleTouchMove) - window.addEventListener('touchstart', handleTouchStart) - window.addEventListener('touchend', handleTouchEnd) - window.addEventListener('touchcancel', resetRotation) - window.addEventListener('keydown', onKeyDown) - - // Cleanup - return () => { - window.removeEventListener('resize', handleResize) - window.removeEventListener('mousedown', handleMouseDown) - window.removeEventListener('mouseup', handleMouseUp) - window.removeEventListener('mousemove', handleMouseMove) - // window.removeEventListener('mouseout', resetRotation) - window.removeEventListener('touchmove', handleTouchMove) - window.removeEventListener('touchstart', handleTouchStart) - window.removeEventListener('touchend', handleTouchEnd) - window.removeEventListener('touchcancel', resetRotation) - window.removeEventListener('keydown', onKeyDown) - - if (animationFrameRef.current) { - cancelAnimationFrame(animationFrameRef.current) - } - canvasRef.current?.removeChild(renderer.domElement) - renderer.dispose() - } - }, [username, isDarkTheme, ticketType]) - - useEffect(() => { - currentValue.current = value - }, [value]) - - return ( -
-
- {isGameMode && !hasWon && !sharePage && ( - setGameState('winner')} - > - {winningPhrase.map((word, w_idx) => ( - <> - - {word.map((_, c_idx) => { - // index is sum of every letter of every previous word + index of current wo - const currentIndex = handleIndexCount(w_idx, c_idx) - return ( - - ) - })} - - {w_idx !== winningPhrase.length - 1 && } - - ))} - - )} -
- ) -} - -export default ThreeTicketCanvas diff --git a/apps/www/components/LaunchWeek/13/Ticket/TicketActions.tsx b/apps/www/components/LaunchWeek/13/Ticket/TicketActions.tsx deleted file mode 100644 index d7dd3281404..00000000000 --- a/apps/www/components/LaunchWeek/13/Ticket/TicketActions.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import { useBreakpoint } from 'common' -import dayjs from 'dayjs' -import { Check } from 'lucide-react' -import { useTheme } from 'next-themes' -import Link from 'next/link' -import { useEffect, useRef, useState } from 'react' -import { Button, cn } from 'ui' -import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' -import { LW_URL, TWEET_TEXT, TWEET_TEXT_PLATINUM, TWEET_TEXT_SECRET } from '~/lib/constants' -import useLWPartyMode from '../useLWPartyMode' - -export default function TicketActions() { - const { resolvedTheme } = useTheme() - const { userData, supabase } = useConfData() - const { platinum, username, metadata, secret: hasSecretTicket } = userData - const [_imgReady, setImgReady] = useState(false) - const [_loading, setLoading] = useState(false) - const isLessThanMd = useBreakpoint() - const downloadLink = useRef() - const link = `${LW_URL}/tickets/${username}?lw=13${ - hasSecretTicket ? '&secret=true' : platinum ? `&platinum=true` : '' - }&t=${dayjs(new Date()).format('DHHmmss')}` - const permalink = encodeURIComponent(link) - const text = hasSecretTicket ? TWEET_TEXT_SECRET : platinum ? TWEET_TEXT_PLATINUM : TWEET_TEXT - const encodedText = encodeURIComponent(text) - const tweetUrl = `https://twitter.com/intent/tweet?url=${permalink}&text=${encodedText}` - const linkedInUrl = `https://www.linkedin.com/sharing/share-offsite/?url=${permalink}&text=${encodedText}` - const downloadUrl = `/api-v2/ticket-og?username=${encodeURIComponent(username ?? '')}` - const TICKETS_TABLE = 'tickets' - - useEffect(() => { - setImgReady(false) - - const img = new Image() - - img.src = downloadUrl - img.onload = () => { - setImgReady(true) - setLoading(false) - if (downloadLink.current) { - downloadLink.current.click() - downloadLink.current = undefined - } - } - }, [downloadUrl]) - - const handleShare = async (social: 'twitter' | 'linkedin') => { - if (!supabase) return - - setTimeout(async () => { - if (social === 'twitter') { - await supabase - .from(TICKETS_TABLE) - .update({ - shared_on_twitter: 'now', - metadata: { ...metadata, theme: resolvedTheme, hasSharedSecret: hasSecretTicket }, - }) - .eq('launch_week', 'lw13') - .eq('username', username) - } else if (social === 'linkedin') { - await supabase - .from(TICKETS_TABLE) - .update({ - shared_on_linkedin: 'now', - metadata: { ...metadata, theme: resolvedTheme, hasSharedSecret: hasSecretTicket }, - }) - .eq('launch_week', 'lw13') - .eq('username', username) - } - - if (userData.shared_on_linkedin && userData.shared_on_twitter) { - await fetch(`/api-v2/ticket-og?username=${username}&platinum=true`) - } - }) - } - - return ( -
- - -
- ) -} diff --git a/apps/www/components/LaunchWeek/13/Ticket/TicketCopy.tsx b/apps/www/components/LaunchWeek/13/Ticket/TicketCopy.tsx deleted file mode 100644 index 2534e349879..00000000000 --- a/apps/www/components/LaunchWeek/13/Ticket/TicketCopy.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { useState, useRef } from 'react' -import { LW_URL } from '~/lib/constants' -import { Check, Copy } from 'lucide-react' -import useConfData from '../../hooks/use-conf-data' - -export default function TicketCopy() { - const { userData } = useConfData() - const { username, platinum, secret } = userData - const [copied, setCopied] = useState(false) - const buttonRef = useRef(null) - const hasSecretTicket = secret - const displayUrl = `.../launch-week/tickets/${username}?lw=13${ - hasSecretTicket ? '&secret=true' : platinum ? `&platinum=true` : '' - }` - const url = `${LW_URL}/tickets/${username}?lw=13${ - hasSecretTicket ? '&secret=true' : platinum ? `&platinum=true` : '' - }` - - return ( - - ) -} diff --git a/apps/www/components/LaunchWeek/13/Ticket/TicketForm.tsx b/apps/www/components/LaunchWeek/13/Ticket/TicketForm.tsx deleted file mode 100644 index 0b111c67ed0..00000000000 --- a/apps/www/components/LaunchWeek/13/Ticket/TicketForm.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import { useState, useEffect } from 'react' -import { SupabaseClient } from '@supabase/supabase-js' -import { useRouter } from 'next/router' -import { LW_URL } from '~/lib/constants' -import { Button } from 'ui' -import { useKey } from 'react-use' - -import { useCommandMenuOpen } from 'ui-patterns' -import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' -import { useTheme } from 'next-themes' - -type FormState = 'default' | 'loading' | 'error' - -export default function TicketForm() { - const { resolvedTheme } = useTheme() - const [formState, setFormState] = useState('default') - const [realtimeChannel, setRealtimeChannel] = useState | null>(null) - const [errorMsg] = useState('') - const { supabase, session, setUserData, setTicketState, userData } = useConfData() - const router = useRouter() - - // Triggered on session - async function fetchOrCreateUser() { - if (supabase && session?.user && !userData.id) { - const username = session.user.user_metadata.user_name - const name = session.user.user_metadata.full_name - const email = session.user.email - const userId = session.user.id - - if (!userData.id) { - await supabase - .from('tickets') - .insert({ - user_id: userId, - launch_week: 'lw13', - email, - name, - username, - metadata: { theme: resolvedTheme }, - referred_by: router.query?.referral ?? null, - }) - .select() - .single() - .then(({ data, error }: any) => { - console.log(data, error, username, userData) - fetchUser({ error, username }) - }) - } - } - } - - const fetchUser = async ({ error, username }: any) => { - if (!supabase) return - - // If error because of duplicate email, ignore and proceed, otherwise sign out. - if (error && error?.code !== '23505') { - setFormState('error') - return supabase.auth.signOut() - } - - const { data } = await supabase - .from('tickets_view') - .select('*') - .eq('launch_week', 'lw13') - .eq('username', username) - .single() - .throwOnError() - - if (data) setUserData(data) - - setFormState('default') - - // Prefetch GitHub avatar - new Image().src = `https://github.com/${username}.png` - - // Prefetch the twitter share URL to eagerly generate the page - // fetch(`/launch-week/tickets/${username}?came_from_signup=true`).catch((_) => {}) - await fetch(`/api-v2/ticket-og?username=${username}`) - - if (!realtimeChannel) { - const channel = supabase - .channel('changes') - .on( - 'postgres_changes', - { - event: 'UPDATE', - schema: 'public', - table: 'tickets', - filter: `username=eq.${username}`, - }, - (payload: any) => { - const platinum = !!payload.new.shared_on_twitter && !!payload.new.shared_on_linkedin - const secret = !!payload.new.game_won_at - setUserData({ - ...payload.new, - platinum, - secret, - }) - } - ) - .subscribe() - setRealtimeChannel(channel) - } - } - - useEffect(() => { - fetchOrCreateUser() - - return () => { - // Cleanup realtime subscription on unmount - realtimeChannel?.unsubscribe() - } - }, [session]) - - const isCommandMenuOpen = useCommandMenuOpen() - useKey('t', () => !isCommandMenuOpen && handleGithubSignIn(), {}, [isCommandMenuOpen]) - - async function handleGithubSignIn() { - if (formState !== 'default') { - setFormState('default') - return - } - - setFormState('loading') - setTicketState('loading') - - const redirectTo = `${LW_URL}${userData.username ? '?referral=' + userData.username : ''}` - - supabase?.auth.signInWithOAuth({ - provider: 'github', - options: { - redirectTo, - }, - }) - } - - return formState === 'error' ? ( -
-
{errorMsg}
- -
- ) : ( -
- -
- ) -} diff --git a/apps/www/components/LaunchWeek/13/Ticket/TicketPresence.tsx b/apps/www/components/LaunchWeek/13/Ticket/TicketPresence.tsx deleted file mode 100644 index 07faf88418e..00000000000 --- a/apps/www/components/LaunchWeek/13/Ticket/TicketPresence.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React, { useEffect, useState } from 'react' -import { SupabaseClient } from '@supabase/supabase-js' -import { cn } from 'ui' -import { Dot } from 'lucide-react' -import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' - -const TicketPresence = (props: { className?: string }) => { - const { supabase, ticketState } = useConfData() - const hasTicket = ticketState === 'ticket' - const [realtimeChannel, setRealtimeChannel] = useState | null>(null) - const [onlineUsers, setOnlineUsers] = useState([]) - const isSingular = onlineUsers.length === 1 - const lowTraffic = onlineUsers.length < 5 - - useEffect(() => { - // Listen to realtime presence - if (!realtimeChannel && supabase) { - const lw13Room = supabase?.channel('lw13_rooms', { - config: { broadcast: { self: true, ack: true } }, - }) - - setRealtimeChannel(lw13Room) - const userStatus = {} - - lw13Room - ?.on('presence', { event: 'sync' }, () => { - const newState = lw13Room.presenceState() - const users = [...Object.entries(newState).map(([_, value]) => value[0])] - const uniqueUsrs = users.filter(onlyUnique) - setOnlineUsers(uniqueUsrs) - }) - .subscribe(async (status) => { - if (status !== 'SUBSCRIBED') { - return null - } - await lw13Room.track(userStatus) - }) - } - - return () => { - realtimeChannel?.unsubscribe() - } - }, []) - - return ( -
- - {onlineUsers.length} {isSingular ? 'person is' : 'people are'} online -
- ) -} - -function onlyUnique(value: any, index: number, array: any[]) { - return array.indexOf(value) === index -} - -export default TicketPresence diff --git a/apps/www/components/LaunchWeek/13/Ticket/TicketSwagCtaBox.tsx b/apps/www/components/LaunchWeek/13/Ticket/TicketSwagCtaBox.tsx deleted file mode 100644 index 386c47de2da..00000000000 --- a/apps/www/components/LaunchWeek/13/Ticket/TicketSwagCtaBox.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { cn } from 'ui' -import TicketActions from './TicketActions' -import TicketCopy from './TicketCopy' - -interface Props { - className?: string -} - -export default function TicketSwagCtaBox({ className }: Props) { - return ( -
- - -
- ) -} diff --git a/apps/www/components/LaunchWeek/13/Ticket/TicketingFlow.tsx b/apps/www/components/LaunchWeek/13/Ticket/TicketingFlow.tsx deleted file mode 100644 index 31f2cf907c1..00000000000 --- a/apps/www/components/LaunchWeek/13/Ticket/TicketingFlow.tsx +++ /dev/null @@ -1,168 +0,0 @@ -import { AnimatePresence, LazyMotion, domAnimation, m } from 'framer-motion' -import { cn } from 'ui' -import { DEFAULT_TRANSITION, INITIAL_BOTTOM, getAnimation } from '~/lib/animations' -import { LW13_DATE } from '~/lib/constants' - -import CanvasSingleMode from '~/components/LaunchWeek/13/Multiplayer/CanvasSingleMode' -import ThreeTicketCanvas from '~/components/LaunchWeek/13/ThreeTicketCanvas' -import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' -import useWinningChances from '~/components/LaunchWeek/hooks/useWinningChances' -import SectionContainer from '~/components/Layouts/SectionContainer' - -import TicketForm from '~/components/LaunchWeek/13/Ticket/TicketForm' -import TicketSwagCtaBox from '~/components/LaunchWeek/13/Ticket/TicketSwagCtaBox' -import TicketPresence from './TicketPresence' - -const TicketingFlow = () => { - const { ticketState, userData } = useConfData() - - const isLoading = ticketState === 'loading' - const isRegistering = ticketState === 'registration' - const hasTicket = ticketState === 'ticket' - const hasPlatinumTicket = userData.platinum - const hasSecretTicket = userData.secret - const ticketType = hasSecretTicket ? 'secret' : hasPlatinumTicket ? 'platinum' : 'regular' - - const transition = DEFAULT_TRANSITION - const initial = INITIAL_BOTTOM - const animate = getAnimation({ duration: 1 }) - const exit = { opacity: 0, transition: { ...transition, duration: 0.2 } } - - const winningChances = useWinningChances() - - const DISPLAY_NAME = userData?.name || userData?.username - const FIRST_NAME = DISPLAY_NAME?.split(' ')[0] - - return ( - <> - -
-

Supabase Launch Week 13 | {LW13_DATE}

- - - {isLoading && ( - -
- -
- - - -
- )} - {isRegistering && ( - -
-
-
-

- Launch Week 13 -

-

- {LW13_DATE} -

-
-
- You can still claim a ticket thoughout the week. -
-
- -
-
-
-
- -
-
- )} - {hasTicket && ( - -
-
- {/* Header */} -
- {hasPlatinumTicket ? ( - hasSecretTicket ? ( -

Got the gold ticket, {FIRST_NAME}!

- ) : ( -

- Good to see you, {FIRST_NAME}! -

- ) - ) : winningChances !== 2 ? ( - <> - {hasSecretTicket && ( -

- {FIRST_NAME}, you're gold! -

- )} - {!hasSecretTicket && ( -

- Good to see you, {FIRST_NAME}! -

- )} - - ) : ( -

- Good to see you, {FIRST_NAME}! -

- )} -
-
-
- - -
-
-
- )} -
-
-
-
- - {hasTicket && ( - - )} - - ) -} - -export default TicketingFlow diff --git a/apps/www/components/LaunchWeek/13/Ticket/ticketThemes.tsx b/apps/www/components/LaunchWeek/13/Ticket/ticketThemes.tsx deleted file mode 100644 index a4b6b9f1fce..00000000000 --- a/apps/www/components/LaunchWeek/13/Ticket/ticketThemes.tsx +++ /dev/null @@ -1,11 +0,0 @@ -export const themes = (isDark?: boolean) => ({ - regular: { - TICKET_FOREGROUND: isDark ? '#1F1F1F' : '#FAFAFA', - }, - platinum: { - TICKET_FOREGROUND: '#1F1F1F', - }, - secret: { - TICKET_FOREGROUND: '#1F1F1F', - }, -}) diff --git a/apps/www/components/LaunchWeek/13/useLWPartyMode.tsx b/apps/www/components/LaunchWeek/13/useLWPartyMode.tsx deleted file mode 100644 index c61ac4f2ef5..00000000000 --- a/apps/www/components/LaunchWeek/13/useLWPartyMode.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { useEffect, useState } from 'react' -import { useKey } from 'react-use' -import { useCommandMenuOpen } from 'ui-patterns' -import useLwGame from '../hooks/useLwGame' -import useConfData from '../hooks/use-conf-data' - -const useLWPartyMode = (disabled?: boolean) => { - const [isPartyMode, setIsPartyMode] = useState(true) - const { ticketState } = useConfData() - - const isCommandMenuOpen = useCommandMenuOpen() - const { isGameMode } = useLwGame() - const enableTrigger = !disabled && !isCommandMenuOpen && !isGameMode - - useKey('p', () => enableTrigger && setIsPartyMode(!isPartyMode), {}, [ - isPartyMode, - isCommandMenuOpen, - isGameMode, - ]) - - useKey('Escape', () => !isGameMode && setIsPartyMode(false), {}, [isPartyMode, isGameMode]) - - useEffect(() => { - setIsPartyMode(isPartyMode) - }, [ticketState]) - - return { isPartyMode, setIsPartyMode } -} - -export default useLWPartyMode diff --git a/apps/www/components/LaunchWeek/7/LaunchSection/TicketBrick.tsx b/apps/www/components/LaunchWeek/7/LaunchSection/TicketBrick.tsx deleted file mode 100644 index 769195f29f2..00000000000 --- a/apps/www/components/LaunchWeek/7/LaunchSection/TicketBrick.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React, { useState } from 'react' -// import TicketHeader from '../Ticket/TicketHeader' -// import TicketNumber from '../Ticket/TicketNumber' -import { UserData } from '~/components/LaunchWeek/hooks/use-conf-data' -import Image from 'next/image' - -interface Props { - user: UserData -} - -export function TicketBrick({ user }: Props) { - const [isLoading, setLoading] = useState(true) - - const golden = user.shared_on_linkedin && user.shared_on_twitter - - // reg_bg_57.png - const baseImagePath = `https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw7/tickets_bg/` - - const randomNumber = Math.floor(Math.random() * 200) + 1 - const goldRandomNumber = Math.floor(Math.random() * 56) + 1 - - return ( - <> -
- setLoading(false)} - alt="" - /> -
- {/* */} -
- {user.username} -
-

{user.name ? user.name : user.username}

- {user.name &&

@{user.username}

} -
-
- {/* */} -
-
- - ) -} diff --git a/apps/www/components/LaunchWeek/7/LaunchSection/TicketBrickWall.tsx b/apps/www/components/LaunchWeek/7/LaunchSection/TicketBrickWall.tsx deleted file mode 100644 index e5d2e6836f2..00000000000 --- a/apps/www/components/LaunchWeek/7/LaunchSection/TicketBrickWall.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import Link from 'next/link' -import React from 'react' -import { Button } from 'ui' -import { useBreakpoint } from 'common/hooks/useBreakpoint' -import { UserData } from '~/components/LaunchWeek/hooks/use-conf-data' -import { TicketBrickWallSlider } from './TicketBrickWallSlider' - -type user = UserData - -interface Props { - users: user[] -} - -export default function TicketBrickWall({ users }: Props) { - const isMobile = useBreakpoint(768) - return ( -
-
-
-

- Explore the unique tickets generated by our community -

-
-
- - - {isMobile && } -
-
- -
-
-
- ) -} diff --git a/apps/www/components/LaunchWeek/7/LaunchSection/TicketBrickWallSlider.tsx b/apps/www/components/LaunchWeek/7/LaunchSection/TicketBrickWallSlider.tsx deleted file mode 100644 index 5f8cb1331db..00000000000 --- a/apps/www/components/LaunchWeek/7/LaunchSection/TicketBrickWallSlider.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { useEffect, useRef, useState } from 'react' -import Image from 'next/image' -import Link from 'next/link' -import { UserData } from '~/components/LaunchWeek/hooks/use-conf-data' -import { useWindowSize } from 'react-use' - -import { Swiper, SwiperSlide } from 'swiper/react' -import 'swiper/css' -import SwiperCore from 'swiper' -import { Autoplay } from 'swiper/modules' - -SwiperCore.use([Autoplay]) - -interface Props { - users: UserData[] - reverse?: boolean - speed?: number -} - -export function TicketBrickWallSlider({ users, reverse, speed = 10000 }: Props) { - const ref = useRef(null) - const [swiperInstance, setSwiperInstance] = useState(null) - const { width } = useWindowSize() - const STORAGE_URL = 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw7' - const BUCKET_FOLDER_VERSION = 'v3' - const getOgUrl = (username: string, isGold: boolean) => - `${STORAGE_URL}/tickets/gallery/${ - isGold ? 'golden' : 'regular' - }/${BUCKET_FOLDER_VERSION}/${username}.png` - - useEffect(() => { - // trigger autoplay if viewport resize - if (swiperInstance) swiperInstance?.autoplay?.run() - }, [width]) - - return ( -
-
- setSwiperInstance(swiper)} - centeredSlides={true} - spaceBetween={10} - slidesPerView={5} - speed={speed} - loop={true} - watchOverflow - threshold={2} - updateOnWindowResize - allowTouchMove={false} - autoplay={{ - delay: 0, - disableOnInteraction: true, - reverseDirection: reverse, - }} - breakpoints={{ - 320: { - slidesPerView: 2.5, - }, - 720: { - slidesPerView: 3.5, - }, - 1440: { - slidesPerView: 5, - }, - }} - > - {users.map((user, i) => ( - - -
-
- {user.username -
-
- -
- ))} -
-
-
- ) -} diff --git a/apps/www/components/LaunchWeek/7/LaunchSection/ticket-brick-wall-slider.module.css b/apps/www/components/LaunchWeek/7/LaunchSection/ticket-brick-wall-slider.module.css deleted file mode 100644 index 164e683b3a2..00000000000 --- a/apps/www/components/LaunchWeek/7/LaunchSection/ticket-brick-wall-slider.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.swiper-wrapper .swiper-slide { - width: 300px !important; -} diff --git a/apps/www/components/LaunchWeek/7/ReferralIndicator.tsx b/apps/www/components/LaunchWeek/7/ReferralIndicator.tsx deleted file mode 100644 index 22ae524aa6e..00000000000 --- a/apps/www/components/LaunchWeek/7/ReferralIndicator.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react' -import useWinningChances from '../hooks/useWinningChances' - -export default function ReferralIndicator() { - const winningChances = useWinningChances() - - return ( -
-
- {winningChances}x chance -
-
- ) -} diff --git a/apps/www/components/LaunchWeek/7/TicketsGrid.tsx b/apps/www/components/LaunchWeek/7/TicketsGrid.tsx deleted file mode 100644 index 2fafb62bf7f..00000000000 --- a/apps/www/components/LaunchWeek/7/TicketsGrid.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { motion } from 'framer-motion' -import Image from 'next/image' -import Link from 'next/link' -import { useBreakpoint } from 'common/hooks/useBreakpoint' -import { UserData } from '../hooks/use-conf-data' - -interface Props { - loadedUsers: UserData[] - isLoading: boolean - pageCount: number - offset: number -} - -export default function TicketsGrid({ loadedUsers, isLoading, pageCount, offset }: Props) { - const STORAGE_URL = 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw7' - const BUCKET_FOLDER_VERSION = 'v3' - const getOgUrl = (username: string, isGold: boolean) => - `${STORAGE_URL}/tickets/gallery/${ - isGold ? 'golden' : 'regular' - }/${BUCKET_FOLDER_VERSION}/${username}.png` - const isMobile = useBreakpoint(768) - const isTablet = useBreakpoint(1024) - - return ( -
- {loadedUsers?.map((user, i) => { - const rowTickets = isMobile ? 2 : isTablet ? 3 : 5 - const divider = Math.floor(i / rowTickets) - const isOddRow = divider % 2 === 0 - // Delay should be no more than pageCount for new loaded users - const recalculatedDelay = i >= pageCount * 2 ? (i - pageCount * (offset - 1)) / 15 : i / 15 - - return ( - window.scrollTo(0, 0)} - legacyBehavior - passHref - > - -
- {user.username -
-
- - ) - })} - {isLoading && - Array.from({ length: pageCount }, (_, i) => ( - -
-
-
- - ))} -
- ) -} diff --git a/apps/www/components/LaunchWeek/8/Countdown.tsx b/apps/www/components/LaunchWeek/8/Countdown.tsx deleted file mode 100644 index 265740fb11b..00000000000 --- a/apps/www/components/LaunchWeek/8/Countdown.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react' -import Countdown from 'react-countdown' -import { CountdownWidget } from 'ui-patterns/CountdownWidget' - -const CountdownComponent = ({ - date, - showCard = true, -}: { - date: string | number | Date - showCard?: boolean -}) => { - if (!date) return null - - const renderer = ({ days, hours, minutes, seconds, completed }: any) => { - if (completed) { - // Render a completed state - return null - } else { - // Render countdown - return ( - - ) - } - } - - return -} - -export default CountdownComponent diff --git a/apps/www/components/LaunchWeek/8/LW8Meetups.tsx b/apps/www/components/LaunchWeek/8/LW8Meetups.tsx deleted file mode 100644 index a2188ecf8c0..00000000000 --- a/apps/www/components/LaunchWeek/8/LW8Meetups.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import Link from 'next/link' -import React, { useEffect, useState } from 'react' -import { Badge } from 'ui' -import useConfData from '../hooks/use-conf-data' -import { SupabaseClient } from '@supabase/supabase-js' - -export interface Meetup { - id?: any - title: string - isLive: boolean - link: string - display_info: string - start_at: string -} - -const LW8Meetups = ({ meetups }: { meetups?: Meetup[] }) => { - const { supabase } = useConfData() - const [meets, setMeets] = useState(meetups ?? []) - const [realtimeChannel, setRealtimeChannel] = useState | null>(null) - - useEffect(() => { - // Listen to realtime changes - if (supabase && !realtimeChannel) { - const channel = supabase - .channel('changes') - .on( - 'postgres_changes', - { - event: 'UPDATE', - schema: 'public', - table: 'lw8_meetups', - }, - async () => { - const { data: newMeets } = await supabase.from('lw8_meetups').select('*') - setMeets(newMeets!) - } - ) - .subscribe() - setRealtimeChannel(channel) - } - - return () => { - // Cleanup realtime subscription on unmount - realtimeChannel?.unsubscribe() - } - }, []) - - return ( -
-
-

Community meetups

-

- We celebrated LW8 with our first-ever live community meetups across various locations. -

-
-
- {meets && - meets - ?.sort((a, b) => (new Date(a.start_at) > new Date(b.start_at) ? 1 : -1)) - .map(({ display_info, link, isLive, title }: Meetup) => ( - -
- {title} - {isLive && Live now} - - - - - -
- {display_info && {display_info}} - - ))} -
-
- ) -} - -export default LW8Meetups diff --git a/apps/www/components/LaunchWeek/8/TicketBrickWall.tsx b/apps/www/components/LaunchWeek/8/TicketBrickWall.tsx deleted file mode 100644 index 1df8ef8964d..00000000000 --- a/apps/www/components/LaunchWeek/8/TicketBrickWall.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import Link from 'next/link' -import React, { useEffect, useRef, useState } from 'react' -import { Button } from 'ui' -import { useBreakpoint } from 'common/hooks/useBreakpoint' -import { UserData } from '~/components/LaunchWeek/hooks/use-conf-data' -import { TicketBrickWallSlider } from './TicketBrickWallSlider' - -type user = UserData - -interface Props { - users: user[] -} - -export default function TicketBrickWall({ users }: Props) { - const isMobile = useBreakpoint(768) - - return ( -
-
-
-

Tickets from the community

-
-
- - - {isMobile && } -
-
- -
-
-
- ) -} diff --git a/apps/www/components/LaunchWeek/8/TicketBrickWallSlider.tsx b/apps/www/components/LaunchWeek/8/TicketBrickWallSlider.tsx deleted file mode 100644 index 54c12264d85..00000000000 --- a/apps/www/components/LaunchWeek/8/TicketBrickWallSlider.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import Image from 'next/image' -import Link from 'next/link' -import { cn } from 'ui' -import { UserData } from '~/components/LaunchWeek/hooks/use-conf-data' - -import 'swiper/css' -import { Swiper, SwiperSlide } from 'swiper/react' - -interface Props { - users: UserData[] - reverse?: boolean - speed?: number - animate?: boolean -} - -export function TicketBrickWallSlider({ users, reverse, speed = 50000, animate }: Props) { - const STORAGE_URL = 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw8' - const BUCKET_FOLDER_VERSION = 'v1' - const getOgUrl = (username: string, isGold: boolean) => - `${STORAGE_URL}/tickets/${ - isGold ? 'golden' : 'regular' - }/${BUCKET_FOLDER_VERSION}/${username}.png` - - return ( -
-
- - {users.map((user, i) => ( - - -
-
- {user.username -
-
- -
- ))} -
-
-
- ) -} diff --git a/apps/www/components/LaunchWeek/8/TicketsGrid.tsx b/apps/www/components/LaunchWeek/8/TicketsGrid.tsx deleted file mode 100644 index 0c672a17f0c..00000000000 --- a/apps/www/components/LaunchWeek/8/TicketsGrid.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { motion } from 'framer-motion' -import Image from 'next/image' -import Link from 'next/link' -import { useBreakpoint } from 'common/hooks/useBreakpoint' -import { UserData } from '../hooks/use-conf-data' - -interface Props { - loadedUsers: UserData[] - isLoading: boolean - pageCount: number - offset: number -} - -export default function TicketsGrid({ loadedUsers, isLoading, pageCount, offset }: Props) { - const STORAGE_URL = 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lwx' - const BUCKET_FOLDER_VERSION = 'v1' - const getTicketImageUrl = (username: string, isGold: boolean) => - `${STORAGE_URL}/tickets/${ - isGold ? 'golden' : 'regular' - }/${BUCKET_FOLDER_VERSION}/${username}.png` - const isMobile = useBreakpoint(1024) - const isTablet = useBreakpoint(1280) - const horizontalOffset = isMobile ? 8 : 20 - - return ( -
- {loadedUsers?.map((user, i) => { - const rowTickets = isMobile ? 2 : isTablet ? 3 : 4 - const divider = Math.floor(i / rowTickets) - const isOddRow = divider % 2 === 0 - // Delay should be no more than pageCount for new loaded users - const recalculatedDelay = i >= pageCount * 2 ? (i - pageCount * (offset - 1)) / 15 : i / 15 - const imgUrl = - getTicketImageUrl(user.username!, !!user.golden) ?? - '/images/launchweek/8/lw8-ticket-empty.jpg' - - return ( - window.scrollTo(0, 0)} - legacyBehavior - passHref - > - -
- {user.username -
-
- - ) - })} - {isLoading && - Array.from({ length: pageCount }, (_, i) => ( - -
-
-
- - ))} -
- ) -} diff --git a/apps/www/components/LaunchWeek/8/ticket-brick-wall-slider.module.css b/apps/www/components/LaunchWeek/8/ticket-brick-wall-slider.module.css deleted file mode 100644 index 164e683b3a2..00000000000 --- a/apps/www/components/LaunchWeek/8/ticket-brick-wall-slider.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.swiper-wrapper .swiper-slide { - width: 300px !important; -} diff --git a/apps/www/components/LaunchWeek/X/Countdown.tsx b/apps/www/components/LaunchWeek/X/Countdown.tsx deleted file mode 100644 index 265740fb11b..00000000000 --- a/apps/www/components/LaunchWeek/X/Countdown.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react' -import Countdown from 'react-countdown' -import { CountdownWidget } from 'ui-patterns/CountdownWidget' - -const CountdownComponent = ({ - date, - showCard = true, -}: { - date: string | number | Date - showCard?: boolean -}) => { - if (!date) return null - - const renderer = ({ days, hours, minutes, seconds, completed }: any) => { - if (completed) { - // Render a completed state - return null - } else { - // Render countdown - return ( - - ) - } - } - - return -} - -export default CountdownComponent diff --git a/apps/www/components/LaunchWeek/X/LWXBackground.tsx b/apps/www/components/LaunchWeek/X/LWXBackground.tsx deleted file mode 100644 index f3d6e03e896..00000000000 --- a/apps/www/components/LaunchWeek/X/LWXBackground.tsx +++ /dev/null @@ -1,183 +0,0 @@ -import React, { useEffect, useRef, useState } from 'react' -import Image from 'next/image' -import { cn } from 'ui' -import useConfData from '../hooks/use-conf-data' -import { useBreakpoint } from 'common' - -interface Props { - className?: string - isGameMode?: boolean -} - -const LWXBackground = ({ className, isGameMode = false }: Props) => { - const { ticketState } = useConfData() - const containerRef = useRef(null) - const isMobile = useBreakpoint() - const ref = useRef(null) - const [isActive, setIsActive] = useState(false) - const [gradientPos, setGradientPos] = useState({ x: 0, y: 0 }) - - const hasTicket = ticketState === 'ticket' - - const handleGlow = (event: any) => { - if (isMobile) return setGradientPos({ x: 70, y: 40 }) - if (!ref.current || !containerRef.current || hasTicket || isGameMode) return null - - const containerRefElement = containerRef.current as HTMLDivElement - - const { - x: contX, - y: contY, - width: containerWidth, - height: containerHeight, - } = containerRefElement.getBoundingClientRect() - const xCont = event.clientX - contX - const yCont = event.clientY - contY - - const isContainerHovered = - xCont > -3 && xCont < containerWidth + 3 && yCont > -3 && yCont < containerHeight + 3 - setIsActive(isContainerHovered) - - if (!isContainerHovered) return - - const svgElement = ref.current as SVGElement - const { x: svgX, y: svgY } = svgElement.getBoundingClientRect() - const x = event.clientX - svgX - 50 - const y = event.clientY - svgY - 50 - - setGradientPos({ x, y }) - } - - useEffect(() => { - if (typeof window === 'undefined') return - - window.addEventListener('mousemove', handleGlow) - return () => { - window.removeEventListener('mousemove', handleGlow) - } - }, [ticketState, isMobile]) - - const gradientTransformBg = `translate(${gradientPos?.x} ${gradientPos?.y}) rotate(67.9381) scale(182.424)` - const gradientTransformStroke = `translate(${gradientPos?.x} ${gradientPos?.y}) rotate(71.6455) scale(105.635)` - - return ( -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ) -} - -export default LWXBackground diff --git a/apps/www/components/LaunchWeek/X/Releases/components/DaySection.tsx b/apps/www/components/LaunchWeek/X/Releases/components/DaySection.tsx index 9fcd86afec7..f90276139de 100644 --- a/apps/www/components/LaunchWeek/X/Releases/components/DaySection.tsx +++ b/apps/www/components/LaunchWeek/X/Releases/components/DaySection.tsx @@ -5,7 +5,6 @@ import Link from 'next/link' import { cn } from 'ui' import { DayLink } from '.' -import CountdownComponent from '../../Countdown' import { WeekDayProps } from '../data' import { Edit2 } from 'lucide-react' @@ -117,7 +116,6 @@ const DaySection = ({ day }: { day: WeekDayProps }) => { /> - {day.hasCountdown && }
)} diff --git a/apps/www/components/LaunchWeek/X/TicketsGrid.tsx b/apps/www/components/LaunchWeek/X/TicketsGrid.tsx deleted file mode 100644 index ffea3c5bbb0..00000000000 --- a/apps/www/components/LaunchWeek/X/TicketsGrid.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { motion } from 'framer-motion' -import Image from 'next/image' -import Link from 'next/link' -import { useBreakpoint } from 'common/hooks/useBreakpoint' -import { UserData } from '../hooks/use-conf-data' - -interface Props { - loadedUsers: UserData[] - isLoading: boolean - pageCount: number - offset: number -} - -export default function TicketsGrid({ loadedUsers, isLoading, pageCount, offset }: Props) { - const STORAGE_URL = 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lwx' - const getTicketImageUrl = (username: string, isGold: boolean) => - `${STORAGE_URL}/tickets/${isGold ? 'platinum' : 'regular'}/${username}.png` - const isMobile = useBreakpoint(1024) - const isTablet = useBreakpoint(1280) - const horizontalOffset = isMobile ? 8 : 20 - - return ( -
- {loadedUsers?.map((user, i) => { - const rowTickets = isMobile ? 2 : isTablet ? 3 : 4 - const divider = Math.floor(i / rowTickets) - const isOddRow = divider % 2 === 0 - // Delay should be no more than pageCount for new loaded users - const recalculatedDelay = i >= pageCount * 2 ? (i - pageCount * (offset - 1)) / 15 : i / 15 - const imgUrl = - getTicketImageUrl(user.username!, !!user.golden) ?? - '/images/launchweek/lwx/tickets/placeholder.png' - - return ( - window.scrollTo(0, 0)} - legacyBehavior - passHref - > - -
- {user.username -
-
- - ) - })} - {isLoading && - Array.from({ length: pageCount }, (_, i) => ( - -
-
-
- - ))} -
- ) -} diff --git a/apps/www/components/Nav/index.tsx b/apps/www/components/Nav/index.tsx index bced494601a..a1f59fb7928 100644 --- a/apps/www/components/Nav/index.tsx +++ b/apps/www/components/Nav/index.tsx @@ -4,7 +4,7 @@ import { useRouter } from 'next/router' import React, { useState } from 'react' import { useWindowSize } from 'react-use' -import { useIsLoggedIn } from 'common' +import { useIsLoggedIn, useUser } from 'common' import { Button, buttonVariants, cn } from 'ui' import { NavigationMenu, @@ -23,6 +23,8 @@ import MenuItem from './MenuItem' import MobileMenu from './MobileMenu' import RightClickBrandLogo from './RightClickBrandLogo' import { useSendTelemetryEvent } from '~/lib/telemetry' +import useDropdownMenu from './useDropdownMenu' +import { AuthenticatedDropdownMenu } from 'ui-patterns' interface Props { hideNavbar: boolean @@ -37,6 +39,8 @@ const Nav = ({ hideNavbar, stickyNavbar = true }: Props) => { const isLoggedIn = useIsLoggedIn() const menu = getMenu() const sendTelemetryEvent = useSendTelemetryEvent() + const user = useUser() + const userMenu = useDropdownMenu(user) const isHomePage = router.pathname === '/' const isLaunchWeekPage = router.pathname.includes('/launch-week') @@ -130,9 +134,12 @@ const Nav = ({ hideNavbar, stickyNavbar = true }: Props) => { {isLoggedIn ? ( - + <> + + + ) : ( <>
- {users && }
) } - -export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { - // fetch users for the TicketBrickWall - const { data: users } = await supabaseAdmin! - .from('lw7_tickets_golden') - .select('*') - .order('createdAt', { ascending: false }) - .limit(17) - - return { - props: { - users, - }, - } -} diff --git a/apps/www/pages/launch-week/8/index.tsx b/apps/www/pages/launch-week/8/index.tsx index 5c95b0b948a..8ec8a2c338a 100644 --- a/apps/www/pages/launch-week/8/index.tsx +++ b/apps/www/pages/launch-week/8/index.tsx @@ -1,7 +1,6 @@ import 'swiper/css' import { useState, useEffect } from 'react' -import { GetServerSideProps } from 'next' import dynamic from 'next/dynamic' import Head from 'next/head' import Image from 'next/image' @@ -14,30 +13,22 @@ import DefaultLayout from '~/components/Layouts/Default' import { TicketState, ConfDataContext, UserData } from '~/components/LaunchWeek/hooks/use-conf-data' import SectionContainer from '~/components/Layouts/SectionContainer' import { LaunchWeekLogoHeader } from '~/components/LaunchWeek/8/LaunchWeekLogoHeader' -import { Meetup } from '~/components/LaunchWeek/8/LW8Meetups' import LW8CalloutsSection from '~/components/LaunchWeek/8/LW8CalloutsSection' const LW8Releases = dynamic(() => import('~/components/LaunchWeek/8/Releases')) -const LW8Meetups = dynamic(() => import('~/components/LaunchWeek/8/LW8Meetups')) const LWArchive = dynamic(() => import('~/components/LaunchWeek/8/LWArchive')) const LaunchWeekPrizeSection = dynamic( () => import('~/components/LaunchWeek/8/LaunchWeekPrizeSection') ) -const TicketBrickWall = dynamic(() => import('~/components/LaunchWeek/8/TicketBrickWall')) const CTABanner = dynamic(() => import('~/components/CTABanner')) -interface Props { - users?: UserData[] - meetups?: Meetup[] -} - const supabaseAdmin = createClient( process.env.NEXT_PUBLIC_MISC_USE_URL ?? 'http://localhost:54321', // ANON KEY process.env.NEXT_PUBLIC_MISC_USE_ANON_KEY! ) -export default function TicketHome({ users, meetups }: Props) { +export default function TicketHome() { const { query } = useRouter() const TITLE = 'Supabase Launch Week 8' @@ -171,10 +162,6 @@ export default function TicketHome({ users, meetups }: Props) { - - - - @@ -182,7 +169,6 @@ export default function TicketHome({ users, meetups }: Props) { - {users && }
@@ -190,20 +176,3 @@ export default function TicketHome({ users, meetups }: Props) { ) } - -export const getServerSideProps: GetServerSideProps = async () => { - // fetch users for the TicketBrickWall - const { data: users } = await supabaseAdmin! - .from('lw8_tickets_golden') - .select('username, golden') - .limit(17) - - const { data: meetups } = await supabaseAdmin!.from('lw8_meetups').select('*') - - return { - props: { - users, - meetups, - }, - } -} diff --git a/packages/common/auth.tsx b/packages/common/auth.tsx index 9064c365448..642925856d4 100644 --- a/packages/common/auth.tsx +++ b/packages/common/auth.tsx @@ -11,6 +11,7 @@ import { useState, } from 'react' import { gotrueClient, type User } from './gotrue' +import { clearLocalStorage } from './constants/local-storage' export type { User } @@ -115,6 +116,13 @@ export const useIsLoggedIn = () => { return user !== null } +export const signOut = async () => await gotrueClient.signOut() + +export const logOut = async () => { + await signOut() + clearLocalStorage() +} + let currentSession: Session | null = null gotrueClient.onAuthStateChange((event, session) => { diff --git a/packages/common/constants/local-storage.ts b/packages/common/constants/local-storage.ts index f65187df517..8ccb89eeee2 100644 --- a/packages/common/constants/local-storage.ts +++ b/packages/common/constants/local-storage.ts @@ -1,7 +1,111 @@ export const LOCAL_STORAGE_KEYS = { + /** + * STUDIO + */ + AI_ASSISTANT_STATE: (projectRef: string | undefined) => + `supabase-ai-assistant-state-${projectRef}`, + SIDEBAR_BEHAVIOR: 'supabase-sidebar-behavior', + EDITOR_PANEL_STATE: 'supabase-editor-panel-state', + + UI_PREVIEW_API_SIDE_PANEL: 'supabase-ui-api-side-panel', + UI_PREVIEW_CLS: 'supabase-ui-cls', + UI_PREVIEW_INLINE_EDITOR: 'supabase-ui-preview-inline-editor', + UI_ONBOARDING_NEW_PAGE_SHOWN: 'supabase-ui-onboarding-new-page-shown', + UI_TABLE_EDITOR_TABS: 'supabase-ui-table-editor-tabs', + UI_SQL_EDITOR_TABS: 'supabase-ui-sql-editor-tabs', + UI_NEW_LAYOUT_PREVIEW: 'supabase-ui-new-layout-preview', + NEW_LAYOUT_NOTICE_ACKNOWLEDGED: 'new-layout-notice-acknowledge', + + DASHBOARD_HISTORY: (ref: string) => `dashboard-history-${ref}`, + STORAGE_PREFERENCE: (ref: string) => `storage-explorer-${ref}`, + + SQL_EDITOR_INTELLISENSE: 'supabase_sql-editor-intellisense-enabled', + SQL_EDITOR_SPLIT_SIZE: 'supabase_sql-editor-split-size', + // Key to track which schemas are ok to be sent to AI. The project ref is intentionally put at the end for easier search in the browser console. + SQL_EDITOR_AI_SCHEMA: (ref: string) => `supabase_sql-editor-ai-schema-enabled-${ref}`, + SQL_EDITOR_AI_OPEN: 'supabase_sql-editor-ai-open', + SQL_EDITOR_LAST_SELECTED_DB: (ref: string) => `sql-editor-last-selected-db-${ref}`, + SQL_EDITOR_SQL_BLOCK_ACKNOWLEDGED: (ref: string) => `sql-editor-sql-block-acknowledged-${ref}`, + SQL_EDITOR_SECTION_STATE: (ref: string) => `sql-editor-section-state-${ref}`, + SQL_EDITOR_SORT: (ref: string) => `sql-editor-sort-${ref}`, + + LOG_EXPLORER_SPLIT_SIZE: 'supabase_log-explorer-split-size', + GRAPHIQL_RLS_BYPASS_WARNING: 'graphiql-rls-bypass-warning-dismissed', + CLS_DIFF_WARNING: 'cls-diff-warning-dismissed', + CLS_SELECT_STAR_WARNING: 'cls-select-star-warning-dismissed', + QUERY_PERF_SHOW_BOTTOM_SECTION: 'supabase-query-perf-show-bottom-section', + // Key to track account deletion requests + ACCOUNT_DELETION_REQUEST: 'supabase-account-deletion-request', + // Used for storing a user id when sending reports to Sentry. The id is hashed for anonymity. + SENTRY_USER_ID: 'supabase-sentry-user-id', + // Used for storing the last sign in method used by the user + LAST_SIGN_IN_METHOD: 'supabase-last-sign-in-method', + // Key to track the last selected schema. The project ref is intentionally put at the end for easier search in the browser console. + LAST_SELECTED_SCHEMA: (ref: string) => `last-selected-schema-${ref}`, + // Track position of nodes for schema visualizer + SCHEMA_VISUALIZER_POSITIONS: (ref: string, schemaId: number) => + `schema-visualizer-positions-${ref}-${schemaId}`, + // Used for allowing the main nav panel to expand on hover + EXPAND_NAVIGATION_PANEL: 'supabase-expand-navigation-panel', + GITHUB_AUTHORIZATION_STATE: 'supabase-github-authorization-state', + // Notice banner keys + FLY_POSTGRES_DEPRECATION_WARNING: 'fly-postgres-deprecation-warning-dismissed', + AUTH_USERS_COLUMNS_CONFIGURATION: (ref: string) => `supabase-auth-users-columns-${ref}`, + + // api keys view switcher for new and legacy api keys + API_KEYS_VIEW: (ref: string) => `supabase-api-keys-view-${ref}`, + + // last visited logs page + LAST_VISITED_LOGS_PAGE: 'supabase-last-visited-logs-page', + LAST_VISITED_ORGANIZATION: 'last-visited-organization', + + /** + * COMMON + */ /** @deprecated – we're using usercentrics instead to handle telemetry consent */ TELEMETRY_CONSENT: 'supabase-consent-ph', TELEMETRY_DATA: 'supabase-telemetry-data', + + /** + * DOCS + */ + SAVED_ORG: 'docs.ui.user.selected.org', + SAVED_PROJECT: 'docs.ui.user.selected.project', + SAVED_BRANCH: 'docs.ui.user.selected.branch', + HIDE_PROMO_TOAST: 'supabase-hide-promo-toast-lw13-d1', + + /** + * WWW + */ BLOG_VIEW: 'supabase-blog-view', +} as const + +export type LocalStorageKey = (typeof LOCAL_STORAGE_KEYS)[keyof typeof LOCAL_STORAGE_KEYS] + +const LOCAL_STORAGE_KEYS_ALLOWLIST = [ + 'graphiql:theme', + 'theme', + 'supabaseDarkMode', + 'supabase.dashboard.auth.debug', + 'supabase.dashboard.auth.navigatorLock.disabled', + LOCAL_STORAGE_KEYS.TELEMETRY_CONSENT, + LOCAL_STORAGE_KEYS.UI_PREVIEW_API_SIDE_PANEL, + LOCAL_STORAGE_KEYS.UI_PREVIEW_INLINE_EDITOR, + LOCAL_STORAGE_KEYS.UI_TABLE_EDITOR_TABS, + LOCAL_STORAGE_KEYS.UI_SQL_EDITOR_TABS, + LOCAL_STORAGE_KEYS.UI_NEW_LAYOUT_PREVIEW, + LOCAL_STORAGE_KEYS.UI_PREVIEW_INLINE_EDITOR, + LOCAL_STORAGE_KEYS.UI_PREVIEW_CLS, + LOCAL_STORAGE_KEYS.LAST_SIGN_IN_METHOD, + LOCAL_STORAGE_KEYS.HIDE_PROMO_TOAST, + LOCAL_STORAGE_KEYS.BLOG_VIEW, +] + +export function clearLocalStorage() { + for (const key in localStorage) { + if (!LOCAL_STORAGE_KEYS_ALLOWLIST.includes(key)) { + localStorage.removeItem(key) + } + } } diff --git a/packages/ui-patterns/AuthenticatedDropdownMenu/index.tsx b/packages/ui-patterns/AuthenticatedDropdownMenu/index.tsx new file mode 100644 index 00000000000..7b5ed8c4e52 --- /dev/null +++ b/packages/ui-patterns/AuthenticatedDropdownMenu/index.tsx @@ -0,0 +1,169 @@ +'use client' + +import { Fragment } from 'react' +import Image from 'next/image' +import Link from 'next/link' +import { useTheme } from 'next-themes' +import { UserIcon } from 'lucide-react' +import { + cn, + buttonVariants, + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger, +} from 'ui' +import { themes } from 'ui/src/components/ThemeProvider/themes' + +import type { User } from '@supabase/supabase-js' +import type { LucideIcon } from 'icons/src/createSupabaseIcon' +import type { Theme } from 'ui/src/components/ThemeProvider/themes' + +interface Props { + menu: menuItem[][] + user: User | null + hasThemeSelector?: boolean + site?: 'docs' | 'www' | 'studio' +} + +export interface menuItem { + label: string + type?: 'link' | 'button' | 'text' | 'theme' + icon?: LucideIcon + href?: string + shortcut?: string + onClick?: VoidFunction + otherProps?: { + target?: '_blank' + rel?: 'noreferrer noopener' + } +} + +export const AuthenticatedDropdownMenu = ({ user, menu, site }: Props) => { + const { theme, setTheme } = useTheme() + const userAvatar = user && user.user_metadata?.avatar_url + + return ( + + + + + + {menu.map((menuSection, sectionIdx) => ( + + {sectionIdx !== 0 && ( + + )} + {menuSection.map((sectionItem, itemIdx) => { + switch (sectionItem.type) { + case 'text': + return ( +
+ +
+ ) + case 'button': + return ( + + + + ) + case 'theme': + return ( + <> + + {sectionItem.label} + + { + setTheme(value) + }} + > + {themes + .filter( + (x) => x.value === 'light' || x.value === 'dark' || x.value === 'system' + ) + .map((theme: Theme) => ( + + {theme.name} + + ))} + + + ) + case 'link': + default: + return ( + + {}} + > + + + + ) + } + })} +
+ ))} +
+
+ ) +} + +const DropdownItemContent = ({ icon: Icon, ...sectionItem }: menuItem) => ( + <> + {Icon && } + {sectionItem.label} + {sectionItem.shortcut && {sectionItem.shortcut}} + +) + +export default AuthenticatedDropdownMenu diff --git a/packages/ui-patterns/index.tsx b/packages/ui-patterns/index.tsx index 5ef7583b73c..47f08a27e03 100644 --- a/packages/ui-patterns/index.tsx +++ b/packages/ui-patterns/index.tsx @@ -2,26 +2,28 @@ * The components are listed here so that VsCode can find out about them and list them as import suggestions. Don't * import directly from here. */ +export * from './admonition' +export * from './AssistantChat/AssistantChatForm' +export * from './AssistantChat/AssistantCommandsPopover' +export * from './AuthenticatedDropdownMenu' export * from './CommandMenu' +export * from './ComputeBadge' +export * from './ConsentToast' export * from './consent' export * from './CountdownWidget' +export * from './Dialogs/ConfirmDialog' +export * from './Dialogs/ConfirmationModal' export * from './ExpandableVideo' export * from './GlassPanel' export * from './IconPanel' +export * from './InnerSideMenu' +export * from './LogsBarChart' export * from './MobileSheetNav' export * from './PrivacySettings' +export * from './PromoToast' export * from './ThemeToggle' export * from './TweetCard' -export * from './InnerSideMenu' -export * from './ShimmeringLoader' -export * from './Dialogs/ConfirmDialog' -export * from './Dialogs/ConfirmationModal' -export * from './AssistantChat/AssistantChatForm' -export * from './AssistantChat/AssistantCommandsPopover' -export * from './PromoToast' -export * from './admonition' -export * from './ComputeBadge' export * from './TimestampInfo' export * from './FilterBar' export * from './Toc' -export * from './LogsBarChart' +export * from './ShimmeringLoader' diff --git a/packages/ui-patterns/package.json b/packages/ui-patterns/package.json index 87724d91eeb..f440a4179c5 100644 --- a/packages/ui-patterns/package.json +++ b/packages/ui-patterns/package.json @@ -14,6 +14,7 @@ "@hookform/resolvers": "^3.1.1", "@monaco-editor/react": "^4.6.0", "@supabase/sql-to-rest": "^0.1.6", + "@supabase/supabase-js": "catalog:", "class-variance-authority": "^0.6.0", "clsx": "^1.2.1", "cmdk": "^1.0.0", @@ -22,6 +23,7 @@ "dayjs": "^1.11.13", "framer-motion": "^11.1.9", "github-slugger": "^2.0.0", + "icons": "workspace:*", "lodash": "^4.17.21", "lucide-react": "*", "mdast": "^3.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 813f80371fd..5ade37c473c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2021,6 +2021,9 @@ importers: '@supabase/sql-to-rest': specifier: ^0.1.6 version: 0.1.6(encoding@0.1.13)(supports-color@8.1.1) + '@supabase/supabase-js': + specifier: 'catalog:' + version: 2.49.3 class-variance-authority: specifier: ^0.6.0 version: 0.6.1 @@ -2045,6 +2048,9 @@ importers: github-slugger: specifier: ^2.0.0 version: 2.0.0 + icons: + specifier: workspace:* + version: link:../icons lodash: specifier: ^4.17.21 version: 4.17.21 diff --git a/supabase/functions/common/database-types.ts b/supabase/functions/common/database-types.ts index 80190d89f83..e69de29bb2d 100644 --- a/supabase/functions/common/database-types.ts +++ b/supabase/functions/common/database-types.ts @@ -1,436 +0,0 @@ -export type Json = - | string - | number - | boolean - | null - | { [key: string]: Json | undefined } - | Json[] - -export interface Database { - graphql_public: { - Tables: { - [_ in never]: never - } - Views: { - [_ in never]: never - } - Functions: { - graphql: { - Args: { - operationName?: string - query?: string - variables?: Json - extensions?: Json - } - Returns: Json - } - } - Enums: { - [_ in never]: never - } - CompositeTypes: { - [_ in never]: never - } - } - public: { - Tables: { - page: { - Row: { - checksum: string | null - id: number - last_refresh: string | null - meta: Json | null - parent_page_id: number | null - path: string - source: string | null - type: string | null - version: string | null - } - Insert: { - checksum?: string | null - id?: number - last_refresh?: string | null - meta?: Json | null - parent_page_id?: number | null - path: string - source?: string | null - type?: string | null - version?: string | null - } - Update: { - checksum?: string | null - id?: number - last_refresh?: string | null - meta?: Json | null - parent_page_id?: number | null - path?: string - source?: string | null - type?: string | null - version?: string | null - } - Relationships: [ - { - foreignKeyName: "page_parent_page_id_fkey" - columns: ["parent_page_id"] - referencedRelation: "page" - referencedColumns: ["id"] - } - ] - } - page_section: { - Row: { - content: string | null - embedding: string | null - fts_tokens: unknown | null - heading: string | null - id: number - page_id: number - slug: string | null - token_count: number | null - } - Insert: { - content?: string | null - embedding?: string | null - fts_tokens?: unknown | null - heading?: string | null - id?: number - page_id: number - slug?: string | null - token_count?: number | null - } - Update: { - content?: string | null - embedding?: string | null - fts_tokens?: unknown | null - heading?: string | null - id?: number - page_id?: number - slug?: string | null - token_count?: number | null - } - Relationships: [ - { - foreignKeyName: "page_section_page_id_fkey" - columns: ["page_id"] - referencedRelation: "page" - referencedColumns: ["id"] - } - ] - } - } - Views: { - [_ in never]: never - } - Functions: { - docs_search_embeddings: { - Args: { - embedding: string - match_threshold: number - } - Returns: { - id: number - path: string - type: string - title: string - subtitle: string - description: string - headings: string[] - slugs: string[] - }[] - } - docs_search_fts: { - Args: { - query: string - } - Returns: { - id: number - path: string - type: string - title: string - subtitle: string - description: string - headings: string[] - slugs: string[] - }[] - } - get_page_parents: { - Args: { - page_id: number - } - Returns: { - id: number - parent_page_id: number - path: string - meta: Json - }[] - } - hnswhandler: { - Args: { - "": unknown - } - Returns: unknown - } - ivfflathandler: { - Args: { - "": unknown - } - Returns: unknown - } - match_page_sections: { - Args: { - embedding: string - match_threshold: number - match_count: number - min_content_length: number - } - Returns: { - id: number - page_id: number - slug: string - heading: string - content: string - similarity: number - }[] - } - match_page_sections_v2: { - Args: { - embedding: string - match_threshold: number - min_content_length: number - } - Returns: { - content: string | null - embedding: string | null - fts_tokens: unknown | null - heading: string | null - id: number - page_id: number - slug: string | null - token_count: number | null - }[] - } - vector_avg: { - Args: { - "": number[] - } - Returns: string - } - vector_dims: { - Args: { - "": string - } - Returns: number - } - vector_norm: { - Args: { - "": string - } - Returns: number - } - vector_out: { - Args: { - "": string - } - Returns: unknown - } - vector_send: { - Args: { - "": string - } - Returns: string - } - vector_typmod_in: { - Args: { - "": unknown[] - } - Returns: number - } - } - Enums: { - [_ in never]: never - } - CompositeTypes: { - [_ in never]: never - } - } - storage: { - Tables: { - buckets: { - Row: { - allowed_mime_types: string[] | null - avif_autodetection: boolean | null - created_at: string | null - file_size_limit: number | null - id: string - name: string - owner: string | null - public: boolean | null - updated_at: string | null - } - Insert: { - allowed_mime_types?: string[] | null - avif_autodetection?: boolean | null - created_at?: string | null - file_size_limit?: number | null - id: string - name: string - owner?: string | null - public?: boolean | null - updated_at?: string | null - } - Update: { - allowed_mime_types?: string[] | null - avif_autodetection?: boolean | null - created_at?: string | null - file_size_limit?: number | null - id?: string - name?: string - owner?: string | null - public?: boolean | null - updated_at?: string | null - } - Relationships: [ - { - foreignKeyName: "buckets_owner_fkey" - columns: ["owner"] - referencedRelation: "users" - referencedColumns: ["id"] - } - ] - } - migrations: { - Row: { - executed_at: string | null - hash: string - id: number - name: string - } - Insert: { - executed_at?: string | null - hash: string - id: number - name: string - } - Update: { - executed_at?: string | null - hash?: string - id?: number - name?: string - } - Relationships: [] - } - objects: { - Row: { - bucket_id: string | null - created_at: string | null - id: string - last_accessed_at: string | null - metadata: Json | null - name: string | null - owner: string | null - path_tokens: string[] | null - updated_at: string | null - version: string | null - } - Insert: { - bucket_id?: string | null - created_at?: string | null - id?: string - last_accessed_at?: string | null - metadata?: Json | null - name?: string | null - owner?: string | null - path_tokens?: string[] | null - updated_at?: string | null - version?: string | null - } - Update: { - bucket_id?: string | null - created_at?: string | null - id?: string - last_accessed_at?: string | null - metadata?: Json | null - name?: string | null - owner?: string | null - path_tokens?: string[] | null - updated_at?: string | null - version?: string | null - } - Relationships: [ - { - foreignKeyName: "objects_bucketId_fkey" - columns: ["bucket_id"] - referencedRelation: "buckets" - referencedColumns: ["id"] - } - ] - } - } - Views: { - [_ in never]: never - } - Functions: { - can_insert_object: { - Args: { - bucketid: string - name: string - owner: string - metadata: Json - } - Returns: undefined - } - extension: { - Args: { - name: string - } - Returns: string - } - filename: { - Args: { - name: string - } - Returns: string - } - foldername: { - Args: { - name: string - } - Returns: unknown - } - get_size_by_bucket: { - Args: Record - Returns: { - size: number - bucket_id: string - }[] - } - search: { - Args: { - prefix: string - bucketname: string - limits?: number - levels?: number - offsets?: number - search?: string - sortcolumn?: string - sortorder?: string - } - Returns: { - name: string - id: string - updated_at: string - created_at: string - last_accessed_at: string - metadata: Json - }[] - } - } - Enums: { - [_ in never]: never - } - CompositeTypes: { - [_ in never]: never - } - } -} -