import { createContext, useCallback, useContext, useState, useMemo, ReactNode, } from 'react' import { postJSON } from '../../../infrastructure/fetch-json' import useIsMounted from '../../../shared/hooks/use-is-mounted' import { set, cloneDeep } from 'lodash' import getMeta from '../../../utils/meta' import type { OAuthProviders, OAuthProvider, } from '../../../../../types/oauth-providers' import type { ThirdPartyIds } from '../../../../../types/third-party-ids' export type SSOSubscription = { providerId: string provider: OAuthProvider linked: boolean } type SSOContextValue = { subscriptions: Record unlink: (id: string, signal?: AbortSignal) => Promise } export const SSOContext = createContext(undefined) type SSOProviderProps = { children: ReactNode } export function SSOProvider({ children }: SSOProviderProps) { const isMountedRef = useIsMounted() const oauthProviders = getMeta('ol-oauthProviders', {}) as OAuthProviders const thirdPartyIds = getMeta('ol-thirdPartyIds') as ThirdPartyIds const [subscriptions, setSubscriptions] = useState< Record >(() => { const initialSubscriptions: Record = {} for (const [id, provider] of Object.entries(oauthProviders)) { const linked = !!thirdPartyIds[id] if (!provider.hideWhenNotLinked || linked) { initialSubscriptions[id] = { providerId: id, provider, linked, } } } return initialSubscriptions }) const unlink = useCallback( (providerId: string, signal?: AbortSignal) => { if (!subscriptions[providerId].linked) { return Promise.resolve() } const body = { link: false, providerId, } return postJSON('/user/oauth-unlink', { body, signal }).then(() => { if (isMountedRef.current) { setSubscriptions(subs => set(cloneDeep(subs), `${providerId}.linked`, false) ) } }) }, [isMountedRef, subscriptions] ) const value = useMemo( () => ({ subscriptions, unlink, }), [subscriptions, unlink] ) return {children} } export function useSSOContext() { const context = useContext(SSOContext) if (!context) { throw new Error('SSOContext is only available inside SSOProvider') } return context }