import { Session, StytchClient, User } from '@stytch/stytch-js'; import React, { ComponentType, createContext, ReactNode, useContext, useEffect, useState } from 'react'; import { useAsyncState } from 'utils/async'; import { lazyError, noProviderError } from 'utils/errors'; import { invariant } from 'utils/invariant'; type StytchMountedContextType = { isMounted: boolean; isLazy: boolean; }; const StytchMountedContext = createContext({ isLazy: false, isMounted: false, }); const StytchUserContext = createContext(null); const StytchSessionContext = createContext(null); const StytchContext = createContext(null); export const useIsMounted__INTERNAL = (): boolean => useContext(StytchMountedContext).isMounted; export const useIsLazy__INTERNAL = (): boolean => useContext(StytchMountedContext).isLazy; export const useStytchRaw__INTERNAL = (): StytchClient | null => useContext(StytchContext); export const useStytchUser = (): User | null => { invariant(useIsMounted__INTERNAL(), noProviderError('useStytchUser')); return useContext(StytchUserContext); }; export const useStytchSession = (): Session | null => { invariant(useIsMounted__INTERNAL(), noProviderError('useStytchSession')); return useContext(StytchSessionContext); }; export const useStytch = (): StytchClient => { invariant(useIsMounted__INTERNAL(), noProviderError('useStytch')); invariant(!useIsLazy__INTERNAL(), lazyError('useStytch')); return useContext(StytchContext); }; export const useStytchLazy = (): StytchClient => { invariant(useIsMounted__INTERNAL(), noProviderError('useStytchLazy')); return useContext(StytchContext); }; export const withStytch = ( Component: ComponentType, ): ComponentType => { const WithStytch: ComponentType = (props) => { invariant(useIsMounted__INTERNAL(), noProviderError('withStytch')); invariant(!useIsLazy__INTERNAL(), lazyError('withStytch')); return ; }; WithStytch.displayName = `withStytch(${Component.displayName || Component.name || 'Component'})`; return WithStytch; }; export const withStytchLazy = ( Component: ComponentType, ): ComponentType => { const WithStytchLazy: ComponentType = (props) => { invariant(useIsMounted__INTERNAL(), noProviderError('withStytchLazy')); return ; }; WithStytchLazy.displayName = `withStytchLazy(${Component.displayName || Component.name || 'Component'})`; return WithStytchLazy; }; export const withStytchUser = ( Component: ComponentType, ): ComponentType => { const WithStytchUser: ComponentType = (props) => { invariant(useIsMounted__INTERNAL(), noProviderError('withStytchUser')); return ; }; WithStytchUser.displayName = `withStytchUser(${Component.displayName || Component.name || 'Component'})`; return WithStytchUser; }; export const withStytchSession = ( Component: ComponentType, ): ComponentType => { const WithStytchSession: ComponentType = (props) => { invariant(useIsMounted__INTERNAL(), noProviderError('withStytchSession')); return ; }; WithStytchSession.displayName = `withStytchSession(${Component.displayName || Component.name || 'Component'})`; return WithStytchSession; }; export type StytchProviderProps = { stytch: StytchClient | null | Promise; children?: ReactNode; }; export const StytchProvider = ({ stytch, children }: StytchProviderProps): JSX.Element => { invariant(!useIsMounted__INTERNAL(), 'You cannot render a inside another .'); const [stytchClient, setStytchClient] = useState( stytch instanceof Promise || !stytch ? null : stytch, ); const [mountedContext] = useState({ isLazy: !stytchClient, isMounted: true, }); const [user, setUser] = useAsyncState(stytchClient && stytchClient.user.getSync()); const [session, setSession] = useAsyncState(stytchClient && stytchClient.session.getSync()); useEffect(() => { if (stytch instanceof Promise) { stytch.then(setStytchClient); } }, [stytch]); useEffect(() => { if (stytchClient) { if (mountedContext.isLazy) { setUser(stytchClient.user.getSync()); setSession(stytchClient.session.getSync()); } const unsubscribeUser = stytchClient.user.onChange(setUser); const unsubscribeSession = stytchClient.session.onChange(setSession); return () => { unsubscribeUser(); unsubscribeSession(); }; } }, [mountedContext, stytchClient, setUser, setSession]); return ( {children} ); };