2022-04-08 07:02:17 -04:00
|
|
|
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'
|
2022-04-22 09:49:26 -04:00
|
|
|
import getMeta from '../../../utils/meta'
|
|
|
|
import type {
|
|
|
|
OAuthProviders,
|
|
|
|
OAuthProvider,
|
|
|
|
} from '../../../../../types/oauth-providers'
|
|
|
|
import type { ThirdPartyIds } from '../../../../../types/third-party-ids'
|
2022-04-08 07:02:17 -04:00
|
|
|
|
2022-04-22 09:49:26 -04:00
|
|
|
export type SSOSubscription = {
|
|
|
|
providerId: string
|
|
|
|
provider: OAuthProvider
|
|
|
|
linked: boolean
|
2022-04-08 07:02:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
type SSOContextValue = {
|
|
|
|
subscriptions: Record<string, SSOSubscription>
|
|
|
|
unlink: (id: string, signal?: AbortSignal) => Promise<void>
|
|
|
|
}
|
|
|
|
|
|
|
|
export const SSOContext = createContext<SSOContextValue | undefined>(undefined)
|
|
|
|
|
|
|
|
type SSOProviderProps = {
|
|
|
|
children: ReactNode
|
|
|
|
}
|
|
|
|
|
|
|
|
export function SSOProvider({ children }: SSOProviderProps) {
|
|
|
|
const isMountedRef = useIsMounted()
|
2022-04-27 12:13:42 -04:00
|
|
|
const oauthProviders = getMeta('ol-oauthProviders', {}) as OAuthProviders
|
2022-04-22 09:49:26 -04:00
|
|
|
const thirdPartyIds = getMeta('ol-thirdPartyIds') as ThirdPartyIds
|
2022-04-08 07:02:17 -04:00
|
|
|
|
2022-04-22 09:49:26 -04:00
|
|
|
const [subscriptions, setSubscriptions] = useState<
|
|
|
|
Record<string, SSOSubscription>
|
|
|
|
>(() => {
|
2022-04-08 07:02:17 -04:00
|
|
|
const initialSubscriptions: Record<string, SSOSubscription> = {}
|
2022-04-22 09:49:26 -04:00
|
|
|
for (const [id, provider] of Object.entries(oauthProviders)) {
|
|
|
|
const linked = !!thirdPartyIds[id]
|
|
|
|
if (!provider.hideWhenNotLinked || linked) {
|
|
|
|
initialSubscriptions[id] = {
|
|
|
|
providerId: id,
|
|
|
|
provider,
|
|
|
|
linked,
|
|
|
|
}
|
2022-04-08 07:02:17 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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<SSOContextValue>(
|
|
|
|
() => ({
|
|
|
|
subscriptions,
|
|
|
|
unlink,
|
|
|
|
}),
|
|
|
|
[subscriptions, unlink]
|
|
|
|
)
|
|
|
|
|
|
|
|
return <SSOContext.Provider value={value}>{children}</SSOContext.Provider>
|
|
|
|
}
|
|
|
|
|
|
|
|
export function useSSOContext() {
|
|
|
|
const context = useContext(SSOContext)
|
|
|
|
if (!context) {
|
|
|
|
throw new Error('SSOContext is only available inside SSOProvider')
|
|
|
|
}
|
|
|
|
return context
|
|
|
|
}
|