Skip to content

Commit

Permalink
Single responsibility for supabase-context
Browse files Browse the repository at this point in the history
  • Loading branch information
tonyxiao committed Apr 29, 2023
1 parent 8900654 commit 42b4d5f
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 54 deletions.
67 changes: 38 additions & 29 deletions apps/web/app/(admin)/AdminLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,59 @@
'use client'

import {createBrowserSupabaseClient} from '@supabase/auth-helpers-nextjs'
import {QueryClientProvider, useQueryClient} from '@tanstack/react-query'
// import {TRPCProvider} from '@usevenice/engine-frontend'
import type {Viewer} from '@usevenice/cdk-core'
import {QueryClientProvider} from '@tanstack/react-query'
import {zViewerFromUnverifiedJwtToken} from '@usevenice/cdk-core'
import {TRPCProvider} from '@usevenice/engine-frontend'
import React from 'react'
import {
SupabaseViewerProvider,
SupabaseProvider,
useSupabaseContext,
} from '../../contexts/SupabaseViewerProvider'
} from '../../contexts/supabase-context'
import {ViewerContext} from '../../contexts/viewer-context'
import {createQueryClient} from '../../lib/query-client'
import type {Database} from '../../supabase/supabase.gen'
import {useViewerContext} from '../../contexts/viewer-context'
import type {Database as DB} from '../../supabase/supabase.gen'

export function AdminLayout({
children,
initialViewer,
}: {
export function AdminLayout(props: {
children: React.ReactNode
initialViewer: Viewer & {accessToken?: string | null}
/** Viewer will be inferred from this... */
initialAccessToken: string | null | undefined
}) {
console.log('[AdminLayout] rendering', initialViewer)
const {current: queryClient} = React.useRef(createQueryClient())
const {current: supabase} = React.useRef(
createBrowserSupabaseClient<Database>({}),
)
console.log('[AdminLayout] rendering', props.initialAccessToken)
const {current: supabase} = React.useRef(createBrowserSupabaseClient<DB>({}))
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
;(globalThis as any).supabase = supabase
;(globalThis as any).queryClient = queryClient

return (
<QueryClientProvider client={queryClient}>
<SupabaseViewerProvider supabase={supabase} initialViewer={initialViewer}>
<_TRPCProvider>{children}</_TRPCProvider>
</SupabaseViewerProvider>
</QueryClientProvider>
<SupabaseProvider supabase={supabase}>
<AdminLayoutInner {...props} />
</SupabaseProvider>
)
}

function _TRPCProvider({children}: {children: React.ReactNode}) {
const queryClient = useQueryClient()
const {accessToken} = useViewerContext()
/** Separate Inner component is needed in order to useSupabaseContext */
function AdminLayoutInner({
children,
initialAccessToken,
}: React.ComponentProps<typeof AdminLayout>) {
const {session, status, error} = useSupabaseContext()
const accessToken =
status === 'initial' || status === 'loading'
? initialAccessToken
: session?.access_token
const viewer = zViewerFromUnverifiedJwtToken.parse(accessToken)

// NOTE: Should change queryClient when authenticated identity changes to reset all trpc cache
const {current: queryClient} = React.useRef(createQueryClient())
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
;(globalThis as any).queryClient = queryClient

return (
<TRPCProvider queryClient={queryClient} accessToken={accessToken}>
{children}
</TRPCProvider>
<QueryClientProvider client={queryClient}>
<TRPCProvider queryClient={queryClient} accessToken={accessToken}>
<ViewerContext.Provider value={{error, status, viewer, accessToken}}>
{children}
</ViewerContext.Provider>
</TRPCProvider>
</QueryClientProvider>
)
}
4 changes: 2 additions & 2 deletions apps/web/app/(admin)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default async function AdminLayoutServer({
console.log('[AdminLayoutServer] rendering')
// We only get the viewer from cookies to be consistent with how it works
// createBrowserSupabaseClient which only uses cookie and does not use header etc.
const viewer = await serverGetViewer({cookies})
const {accessToken} = await serverGetViewer({cookies})

return <AdminLayout initialViewer={viewer}>{children}</AdminLayout>
return <AdminLayout initialAccessToken={accessToken}>{children}</AdminLayout>
}
2 changes: 1 addition & 1 deletion apps/web/app/(admin)/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import {Auth, ThemeSupa} from '@supabase/auth-ui-react'

import {useMutation} from '@tanstack/react-query'
import {useSupabaseContext} from '../../../contexts/SupabaseViewerProvider'
import {useSupabaseContext} from '../../../contexts/supabase-context'
import {useViewerContext} from '../../../contexts/viewer-context'
import {VeniceTheme} from '../../../themes'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,28 @@
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
import type {Session, SupabaseClient} from '@supabase/supabase-js'

import type {Viewer} from '@usevenice/cdk-core'
import {zViewerFromUnverifiedJwtToken} from '@usevenice/cdk-core'
import React from 'react'
import {browserAnalytics} from '../lib/browser-analytics'
import type {Database} from '../supabase/supabase.gen'
import type {AsyncStatus} from './viewer-context'
import {ViewerContext} from './viewer-context'

export const SupabaseContext = React.createContext<
| {
session: Session | null | undefined
supabase: SupabaseClient
session: Session | null | undefined
status: AsyncStatus
error: unknown
}
| undefined
>(undefined)

export interface SupabaseProviderProps {
supabase: SupabaseClient<Database>
initialViewer?: Viewer & {accessToken?: string | null}
children: React.ReactNode
}

// TODO: Maybe separate out to SupabaseProvider and ViewerProvider?
export function SupabaseViewerProvider({
supabase,
initialViewer = {role: 'anon'},
children,
}: SupabaseProviderProps) {
export function SupabaseProvider({supabase, children}: SupabaseProviderProps) {
const [{session, error, status}, setState] = React.useState<{
session: Session | null
error: unknown
Expand Down Expand Up @@ -76,19 +70,9 @@ export function SupabaseViewerProvider({
}
}, [email, phone, userId])

const accessToken =
status === 'initial' || status === 'loading'
? initialViewer.accessToken
: session?.access_token
const viewer =
status === 'initial' || status === 'loading'
? initialViewer
: zViewerFromUnverifiedJwtToken.parse(accessToken)
return (
<SupabaseContext.Provider value={{session, supabase}}>
<ViewerContext.Provider value={{error, status, viewer, accessToken}}>
{children}
</ViewerContext.Provider>
<SupabaseContext.Provider value={{session, supabase, error, status}}>
{children}
</SupabaseContext.Provider>
)
}
Expand Down

0 comments on commit 42b4d5f

Please sign in to comment.