[APP-1776] Refactor Live Now config, reorg files (#9871)
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import {createContext, useContext} from 'react'
|
||||
import {QueryClient, useQuery} from '@tanstack/react-query'
|
||||
|
||||
import {APP_CONFIG_URL} from '#/env'
|
||||
|
||||
const qc = new QueryClient()
|
||||
const appConfigQueryKey = ['app-config']
|
||||
|
||||
/**
|
||||
* Matches the types defined in our `app-config` worker
|
||||
*/
|
||||
type AppConfigResponse = {
|
||||
liveNow: {
|
||||
allow: string[]
|
||||
exceptions: {
|
||||
did: string
|
||||
allow: string[]
|
||||
}[]
|
||||
}
|
||||
}
|
||||
|
||||
export const DEFAULT_APP_CONFIG_RESPONSE: AppConfigResponse = {
|
||||
liveNow: {
|
||||
allow: [],
|
||||
exceptions: [],
|
||||
},
|
||||
}
|
||||
|
||||
let fetchAppConfigPromise: Promise<AppConfigResponse> | undefined
|
||||
|
||||
async function fetchAppConfig(): Promise<AppConfigResponse | null> {
|
||||
try {
|
||||
if (!fetchAppConfigPromise) {
|
||||
fetchAppConfigPromise = (async () => {
|
||||
const r = await fetch(`${APP_CONFIG_URL}/config`)
|
||||
if (!r.ok) throw new Error(await r.text())
|
||||
const data = await r.json()
|
||||
return data
|
||||
})()
|
||||
}
|
||||
return await fetchAppConfigPromise
|
||||
} catch (e) {
|
||||
fetchAppConfigPromise = undefined
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
const Context = createContext<AppConfigResponse>(DEFAULT_APP_CONFIG_RESPONSE)
|
||||
|
||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
const {data} = useQuery<AppConfigResponse | null>(
|
||||
{
|
||||
staleTime: Infinity,
|
||||
queryKey: appConfigQueryKey,
|
||||
refetchInterval: query => {
|
||||
// refetch regularly if fetch failed, otherwise never refetch
|
||||
return query.state.status === 'error' ? 60e3 : Infinity
|
||||
},
|
||||
async queryFn() {
|
||||
return fetchAppConfig()
|
||||
},
|
||||
},
|
||||
qc,
|
||||
)
|
||||
return (
|
||||
<Context.Provider value={data ?? DEFAULT_APP_CONFIG_RESPONSE}>
|
||||
{children}
|
||||
</Context.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export async function prefetchAppConfig() {
|
||||
try {
|
||||
const data = await fetchAppConfig()
|
||||
if (data) {
|
||||
qc.setQueryData(appConfigQueryKey, data)
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export function useAppConfig() {
|
||||
const ctx = useContext(Context)
|
||||
if (!ctx) {
|
||||
throw new Error('useAppConfig must be used within a Provider')
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
BSKY_SERVICE_DID,
|
||||
PUBLIC_BSKY_SERVICE,
|
||||
} from '#/lib/constants'
|
||||
import {useDebouncedValue} from '#/lib/hooks/useDebouncedValue'
|
||||
import {createFullHandle} from '#/lib/strings/handles'
|
||||
import {useDebouncedValue} from '#/components/live/utils'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {Agent} from '../session/agent'
|
||||
|
||||
@@ -2,28 +2,17 @@ import {createContext, useContext, useMemo} from 'react'
|
||||
|
||||
import {useLanguagePrefs} from '#/state/preferences/languages'
|
||||
import {useServiceConfigQuery} from '#/state/queries/service-config'
|
||||
import {useSession} from '#/state/session'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_DEV} from '#/env'
|
||||
import {device} from '#/storage'
|
||||
|
||||
type TrendingContext = {
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
type LiveNowContext = {
|
||||
did: string
|
||||
domains: string[]
|
||||
}[]
|
||||
|
||||
const TrendingContext = createContext<TrendingContext>({
|
||||
enabled: false,
|
||||
})
|
||||
TrendingContext.displayName = 'TrendingContext'
|
||||
|
||||
const LiveNowContext = createContext<LiveNowContext>([])
|
||||
LiveNowContext.displayName = 'LiveNowContext'
|
||||
|
||||
const CheckEmailConfirmedContext = createContext<boolean | null>(null)
|
||||
|
||||
export function Provider({children}: {children: React.ReactNode}) {
|
||||
@@ -60,19 +49,15 @@ export function Provider({children}: {children: React.ReactNode}) {
|
||||
return {enabled}
|
||||
}, [isInitialLoad, config, langPrefs.contentLanguages])
|
||||
|
||||
const liveNow = useMemo<LiveNowContext>(() => config?.liveNow ?? [], [config])
|
||||
|
||||
// probably true, so default to true when loading
|
||||
// if the call fails, the query will set it to false for us
|
||||
const checkEmailConfirmed = config?.checkEmailConfirmed ?? true
|
||||
|
||||
return (
|
||||
<TrendingContext.Provider value={trending}>
|
||||
<LiveNowContext.Provider value={liveNow}>
|
||||
<CheckEmailConfirmedContext.Provider value={checkEmailConfirmed}>
|
||||
{children}
|
||||
</CheckEmailConfirmedContext.Provider>
|
||||
</LiveNowContext.Provider>
|
||||
<CheckEmailConfirmedContext.Provider value={checkEmailConfirmed}>
|
||||
{children}
|
||||
</CheckEmailConfirmedContext.Provider>
|
||||
</TrendingContext.Provider>
|
||||
)
|
||||
}
|
||||
@@ -81,55 +66,6 @@ export function useTrendingConfig() {
|
||||
return useContext(TrendingContext)
|
||||
}
|
||||
|
||||
const DEFAULT_LIVE_ALLOWED_DOMAINS = [
|
||||
'twitch.tv',
|
||||
'www.twitch.tv',
|
||||
'stream.place',
|
||||
'bluecast.app',
|
||||
'www.bluecast.app',
|
||||
]
|
||||
export type LiveNowConfig = {
|
||||
currentAccountAllowedHosts: Set<string>
|
||||
defaultAllowedHosts: Set<string>
|
||||
allowedHostsExceptionsByDid: Map<string, Set<string>>
|
||||
}
|
||||
export function useLiveNowConfig(): LiveNowConfig {
|
||||
const ctx = useContext(LiveNowContext)
|
||||
const canGoLive = useCanGoLive()
|
||||
const {currentAccount} = useSession()
|
||||
return useMemo(() => {
|
||||
const defaultAllowedHosts = new Set(DEFAULT_LIVE_ALLOWED_DOMAINS)
|
||||
const allowedHostsExceptionsByDid = new Map<string, Set<string>>()
|
||||
for (const live of ctx) {
|
||||
allowedHostsExceptionsByDid.set(
|
||||
live.did,
|
||||
new Set(DEFAULT_LIVE_ALLOWED_DOMAINS.concat(live.domains)),
|
||||
)
|
||||
}
|
||||
if (!currentAccount?.did || !canGoLive)
|
||||
return {
|
||||
currentAccountAllowedHosts: new Set(),
|
||||
defaultAllowedHosts,
|
||||
allowedHostsExceptionsByDid,
|
||||
}
|
||||
const vip = ctx.find(live => live.did === currentAccount.did)
|
||||
return {
|
||||
currentAccountAllowedHosts: new Set(
|
||||
DEFAULT_LIVE_ALLOWED_DOMAINS.concat(vip ? vip.domains : []),
|
||||
),
|
||||
defaultAllowedHosts,
|
||||
allowedHostsExceptionsByDid,
|
||||
}
|
||||
}, [ctx, currentAccount, canGoLive])
|
||||
}
|
||||
|
||||
export function useCanGoLive() {
|
||||
const ax = useAnalytics()
|
||||
const {hasSession} = useSession()
|
||||
if (!hasSession) return false
|
||||
return IS_DEV ? true : !ax.features.enabled(ax.features.LiveNowBetaDisable)
|
||||
}
|
||||
|
||||
export function useCheckEmailConfirmed() {
|
||||
const ctx = useContext(CheckEmailConfirmedContext)
|
||||
if (ctx === null) {
|
||||
|
||||
Reference in New Issue
Block a user