···4import {msg, Trans} from '@lingui/macro'
5import {useLingui} from '@lingui/react'
67-import {useActorStatus} from '#/lib/actor-status'
8import {isJwtExpired} from '#/lib/jwt'
9import {sanitizeDisplayName} from '#/lib/strings/display-names'
10import {sanitizeHandle} from '#/lib/strings/handles'
···19import {Text} from '#/components/Typography'
20import {useSimpleVerificationState} from '#/components/verification'
21import {VerificationCheck} from '#/components/verification/VerificationCheck'
02223export function AccountList({
24 onSelectAccount,
···4import {msg, Trans} from '@lingui/macro'
5import {useLingui} from '@lingui/react'
607import {isJwtExpired} from '#/lib/jwt'
8import {sanitizeDisplayName} from '#/lib/strings/display-names'
9import {sanitizeHandle} from '#/lib/strings/handles'
···18import {Text} from '#/components/Typography'
19import {useSimpleVerificationState} from '#/components/verification'
20import {VerificationCheck} from '#/components/verification/VerificationCheck'
21+import {useActorStatus} from '#/features/liveNow'
2223export function AccountList({
24 onSelectAccount,
+1-1
src/components/ProfileCard.tsx
···14import {msg} from '@lingui/macro'
15import {useLingui} from '@lingui/react'
1617-import {useActorStatus} from '#/lib/actor-status'
18import {getModerationCauseKey} from '#/lib/moderation'
19import {forceLTR} from '#/lib/strings/bidi'
20import {NON_BREAKING_SPACE} from '#/lib/strings/constants'
···47import {useSimpleVerificationState} from '#/components/verification'
48import {VerificationCheck} from '#/components/verification/VerificationCheck'
49import {type Metrics} from '#/analytics'
050import type * as bsky from '#/types/bsky'
5152export function Default({
···14import {msg} from '@lingui/macro'
15import {useLingui} from '@lingui/react'
16017import {getModerationCauseKey} from '#/lib/moderation'
18import {forceLTR} from '#/lib/strings/bidi'
19import {NON_BREAKING_SPACE} from '#/lib/strings/constants'
···46import {useSimpleVerificationState} from '#/components/verification'
47import {VerificationCheck} from '#/components/verification/VerificationCheck'
48import {type Metrics} from '#/analytics'
49+import {useActorStatus} from '#/features/liveNow'
50import type * as bsky from '#/types/bsky'
5152export function Default({
+2-2
src/components/ProfileHoverCard/index.web.tsx
···10import {useLingui} from '@lingui/react'
11import {useNavigation} from '@react-navigation/native'
1213-import {useActorStatus} from '#/lib/actor-status'
14import {getModerationCauseKey} from '#/lib/moderation'
15import {makeProfileLink} from '#/lib/routes/links'
16import {type NavigationProp} from '#/lib/routes/types'
···34 shouldShowKnownFollowers,
35} from '#/components/KnownFollowers'
36import {InlineLinkText, Link} from '#/components/Link'
37-import {LiveStatus} from '#/components/live/LiveStatusDialog'
38import {Loader} from '#/components/Loader'
39import * as Pills from '#/components/Pills'
40import {Portal} from '#/components/Portal'
···43import {useSimpleVerificationState} from '#/components/verification'
44import {VerificationCheck} from '#/components/verification/VerificationCheck'
45import {IS_WEB_TOUCH_DEVICE} from '#/env'
0046import {type ProfileHoverCardProps} from './types'
4748const floatingMiddlewares = [
···10import {useLingui} from '@lingui/react'
11import {useNavigation} from '@react-navigation/native'
12013import {getModerationCauseKey} from '#/lib/moderation'
14import {makeProfileLink} from '#/lib/routes/links'
15import {type NavigationProp} from '#/lib/routes/types'
···33 shouldShowKnownFollowers,
34} from '#/components/KnownFollowers'
35import {InlineLinkText, Link} from '#/components/Link'
036import {Loader} from '#/components/Loader'
37import * as Pills from '#/components/Pills'
38import {Portal} from '#/components/Portal'
···41import {useSimpleVerificationState} from '#/components/verification'
42import {VerificationCheck} from '#/components/verification/VerificationCheck'
43import {IS_WEB_TOUCH_DEVICE} from '#/env'
44+import {useActorStatus} from '#/features/liveNow'
45+import {LiveStatus} from '#/features/liveNow/components/LiveStatusDialog'
46import {type ProfileHoverCardProps} from './types'
4748const floatingMiddlewares = [
···9import {useLingui} from '@lingui/react'
10import {differenceInMinutes} from 'date-fns'
11012import {cleanError} from '#/lib/strings/errors'
13import {definitelyUrl} from '#/lib/strings/url-helpers'
14import {useTickEveryMinute} from '#/state/shell'
···20import {Clock_Stroke2_Corner0_Rounded as ClockIcon} from '#/components/icons/Clock'
21import {Loader} from '#/components/Loader'
22import {Text} from '#/components/Typography'
23-import {LinkPreview} from './LinkPreview'
24import {
025 useLiveLinkMetaQuery,
26 useRemoveLiveStatusMutation,
27 useUpsertLiveStatusMutation,
28-} from './queries'
29-import {displayDuration, useDebouncedValue} from './utils'
3031export function EditLiveDialog({
32 control,
···9import {useLingui} from '@lingui/react'
10import {differenceInMinutes} from 'date-fns'
1112+import {useDebouncedValue} from '#/lib/hooks/useDebouncedValue'
13import {cleanError} from '#/lib/strings/errors'
14import {definitelyUrl} from '#/lib/strings/url-helpers'
15import {useTickEveryMinute} from '#/state/shell'
···21import {Clock_Stroke2_Corner0_Rounded as ClockIcon} from '#/components/icons/Clock'
22import {Loader} from '#/components/Loader'
23import {Text} from '#/components/Typography'
024import {
25+ displayDuration,
26 useLiveLinkMetaQuery,
27 useRemoveLiveStatusMutation,
28 useUpsertLiveStatusMutation,
29+} from '#/features/liveNow'
30+import {LinkPreview} from '#/features/liveNow/components/LinkPreview'
3132export function EditLiveDialog({
33 control,
···3import {msg, Trans} from '@lingui/macro'
4import {useLingui} from '@lingui/react'
506import {cleanError} from '#/lib/strings/errors'
7import {definitelyUrl} from '#/lib/strings/url-helpers'
8import {useModerationOpts} from '#/state/preferences/moderation-opts'
9-import {useLiveNowConfig} from '#/state/service-config'
10import {useTickEveryMinute} from '#/state/shell'
11import {atoms as a, ios, native, platform, useTheme, web} from '#/alf'
12import {Admonition} from '#/components/Admonition'
13import {Button, ButtonIcon, ButtonText} from '#/components/Button'
14import * as Dialog from '#/components/Dialog'
15import * as TextField from '#/components/forms/TextField'
16-import {
17- displayDuration,
18- getLiveServiceNames,
19- useDebouncedValue,
20-} from '#/components/live/utils'
21import {Loader} from '#/components/Loader'
22import * as ProfileCard from '#/components/ProfileCard'
23import * as Select from '#/components/Select'
24import {Text} from '#/components/Typography'
000000025import type * as bsky from '#/types/bsky'
26import {LinkPreview} from './LinkPreview'
27-import {useLiveLinkMetaQuery, useUpsertLiveStatusMutation} from './queries'
2829export function GoLiveDialog({
30 control,
···3import {msg, Trans} from '@lingui/macro'
4import {useLingui} from '@lingui/react'
56+import {useDebouncedValue} from '#/lib/hooks/useDebouncedValue'
7import {cleanError} from '#/lib/strings/errors'
8import {definitelyUrl} from '#/lib/strings/url-helpers'
9import {useModerationOpts} from '#/state/preferences/moderation-opts'
010import {useTickEveryMinute} from '#/state/shell'
11import {atoms as a, ios, native, platform, useTheme, web} from '#/alf'
12import {Admonition} from '#/components/Admonition'
13import {Button, ButtonIcon, ButtonText} from '#/components/Button'
14import * as Dialog from '#/components/Dialog'
15import * as TextField from '#/components/forms/TextField'
0000016import {Loader} from '#/components/Loader'
17import * as ProfileCard from '#/components/ProfileCard'
18import * as Select from '#/components/Select'
19import {Text} from '#/components/Typography'
20+import {
21+ displayDuration,
22+ getLiveServiceNames,
23+ useLiveLinkMetaQuery,
24+ useLiveNowConfig,
25+ useUpsertLiveStatusMutation,
26+} from '#/features/liveNow'
27import type * as bsky from '#/types/bsky'
28import {LinkPreview} from './LinkPreview'
02930export function GoLiveDialog({
31 control,
···17import {Button, ButtonIcon, ButtonText} from '#/components/Button'
18import * as Dialog from '#/components/Dialog'
19import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfoIcon} from '#/components/icons/CircleInfo'
0020import {createStaticClick, SimpleInlineLinkText} from '#/components/Link'
21import {useGlobalReportDialogControl} from '#/components/moderation/ReportDialog'
22import * as ProfileCard from '#/components/ProfileCard'
23import {Text} from '#/components/Typography'
24import {useAnalytics} from '#/analytics'
025import type * as bsky from '#/types/bsky'
26-import {Globe_Stroke2_Corner0_Rounded} from '../icons/Globe'
27-import {SquareArrowTopRight_Stroke2_Corner0_Rounded as SquareArrowTopRightIcon} from '../icons/SquareArrowTopRight'
28-import {LiveIndicator} from './LiveIndicator'
2930export function LiveStatusDialog({
31 control,
···17import {Button, ButtonIcon, ButtonText} from '#/components/Button'
18import * as Dialog from '#/components/Dialog'
19import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfoIcon} from '#/components/icons/CircleInfo'
20+import {Globe_Stroke2_Corner0_Rounded} from '#/components/icons/Globe'
21+import {SquareArrowTopRight_Stroke2_Corner0_Rounded as SquareArrowTopRightIcon} from '#/components/icons/SquareArrowTopRight'
22import {createStaticClick, SimpleInlineLinkText} from '#/components/Link'
23import {useGlobalReportDialogControl} from '#/components/moderation/ReportDialog'
24import * as ProfileCard from '#/components/ProfileCard'
25import {Text} from '#/components/Typography'
26import {useAnalytics} from '#/analytics'
27+import {LiveIndicator} from '#/features/liveNow/components/LiveIndicator'
28import type * as bsky from '#/types/bsky'
0002930export function LiveStatusDialog({
31 control,
···1+import {useEffect, useState} from 'react'
2+3+/**
4+ * Returns a debounced version of the input value that only updates after the
5+ * specified delay has passed without any changes to the input value.
6+ */
7+export function useDebouncedValue<T>(val: T, delayMs: number): T {
8+ const [prev, setPrev] = useState(val)
9+10+ useEffect(() => {
11+ const timeout = setTimeout(() => setPrev(val), delayMs)
12+ return () => clearTimeout(timeout)
13+ }, [val, delayMs])
14+15+ return prev
16+}
···10import {msg, Plural, Trans} from '@lingui/macro'
11import {useLingui} from '@lingui/react'
1213-import {useActorStatus} from '#/lib/actor-status'
14import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
15import {useTranslate} from '#/lib/hooks/useTranslate'
16import {makeProfileLink} from '#/lib/routes/links'
···60import {VerificationCheckButton} from '#/components/verification/VerificationCheckButton'
61import {WhoCanReply} from '#/components/WhoCanReply'
62import {useAnalytics} from '#/analytics'
063import * as bsky from '#/types/bsky'
6465export function ThreadItemAnchor({
···10import {msg, Plural, Trans} from '@lingui/macro'
11import {useLingui} from '@lingui/react'
12013import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
14import {useTranslate} from '#/lib/hooks/useTranslate'
15import {makeProfileLink} from '#/lib/routes/links'
···59import {VerificationCheckButton} from '#/components/verification/VerificationCheckButton'
60import {WhoCanReply} from '#/components/WhoCanReply'
61import {useAnalytics} from '#/analytics'
62+import {useActorStatus} from '#/features/liveNow'
63import * as bsky from '#/types/bsky'
6465export function ThreadItemAnchor({
···8} from '@atproto/api'
9import {Trans} from '@lingui/macro'
1011-import {useActorStatus} from '#/lib/actor-status'
12import {MAX_POST_LINES} from '#/lib/constants'
13import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
14import {makeProfileLink} from '#/lib/routes/links'
···44import * as Skele from '#/components/Skeleton'
45import {SubtleHover} from '#/components/SubtleHover'
46import {Text} from '#/components/Typography'
04748export type ThreadItemPostProps = {
49 item: Extract<ThreadItem, {type: 'threadPost'}>
···8} from '@atproto/api'
9import {Trans} from '@lingui/macro'
10011import {MAX_POST_LINES} from '#/lib/constants'
12import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
13import {makeProfileLink} from '#/lib/routes/links'
···43import * as Skele from '#/components/Skeleton'
44import {SubtleHover} from '#/components/SubtleHover'
45import {Text} from '#/components/Typography'
46+import {useActorStatus} from '#/features/liveNow'
4748export type ThreadItemPostProps = {
49 item: Extract<ThreadItem, {type: 'threadPost'}>
···10import {msg, Trans} from '@lingui/macro'
11import {useLingui} from '@lingui/react'
1213-import {useActorStatus} from '#/lib/actor-status'
14import {useHaptics} from '#/lib/haptics'
15import {sanitizeDisplayName} from '#/lib/strings/display-names'
16import {sanitizeHandle} from '#/lib/strings/handles'
···39import {Text} from '#/components/Typography'
40import {VerificationCheckButton} from '#/components/verification/VerificationCheckButton'
41import {IS_IOS} from '#/env'
042import {GermButton} from '../components/GermButton'
43import {EditProfileDialog} from './EditProfileDialog'
44import {ProfileHeaderHandle} from './Handle'
···10import {msg, Trans} from '@lingui/macro'
11import {useLingui} from '@lingui/react'
12013import {useHaptics} from '#/lib/haptics'
14import {sanitizeDisplayName} from '#/lib/strings/display-names'
15import {sanitizeHandle} from '#/lib/strings/handles'
···38import {Text} from '#/components/Typography'
39import {VerificationCheckButton} from '#/components/verification/VerificationCheckButton'
40import {IS_IOS} from '#/env'
41+import {useActorStatus} from '#/features/liveNow'
42import {GermButton} from '../components/GermButton'
43import {EditProfileDialog} from './EditProfileDialog'
44import {ProfileHeaderHandle} from './Handle'
+4-4
src/screens/Profile/Header/Shell.tsx
···14import {useLingui} from '@lingui/react'
15import {useNavigation} from '@react-navigation/native'
1617-import {useActorStatus} from '#/lib/actor-status'
18import {BACK_HITSLOP} from '#/lib/constants'
19import {useHaptics} from '#/lib/haptics'
20import {type NavigationProp} from '#/lib/routes/types'
···28import {Button} from '#/components/Button'
29import {useDialogControl} from '#/components/Dialog'
30import {ArrowLeft_Stroke2_Corner0_Rounded as ArrowLeftIcon} from '#/components/icons/Arrow'
31-import {EditLiveDialog} from '#/components/live/EditLiveDialog'
32-import {LiveIndicator} from '#/components/live/LiveIndicator'
33-import {LiveStatusDialog} from '#/components/live/LiveStatusDialog'
34import {LabelsOnMe} from '#/components/moderation/LabelsOnMe'
35import {ProfileHeaderAlerts} from '#/components/moderation/ProfileHeaderAlerts'
36import {useAnalytics} from '#/analytics'
37import {IS_IOS} from '#/env'
000038import {GrowableAvatar} from './GrowableAvatar'
39import {GrowableBanner} from './GrowableBanner'
40import {StatusBarShadow} from './StatusBarShadow'
···14import {useLingui} from '@lingui/react'
15import {useNavigation} from '@react-navigation/native'
16017import {BACK_HITSLOP} from '#/lib/constants'
18import {useHaptics} from '#/lib/haptics'
19import {type NavigationProp} from '#/lib/routes/types'
···27import {Button} from '#/components/Button'
28import {useDialogControl} from '#/components/Dialog'
29import {ArrowLeft_Stroke2_Corner0_Rounded as ArrowLeftIcon} from '#/components/icons/Arrow'
00030import {LabelsOnMe} from '#/components/moderation/LabelsOnMe'
31import {ProfileHeaderAlerts} from '#/components/moderation/ProfileHeaderAlerts'
32import {useAnalytics} from '#/analytics'
33import {IS_IOS} from '#/env'
34+import {useActorStatus} from '#/features/liveNow'
35+import {EditLiveDialog} from '#/features/liveNow/components/EditLiveDialog'
36+import {LiveIndicator} from '#/features/liveNow/components/LiveIndicator'
37+import {LiveStatusDialog} from '#/features/liveNow/components/LiveStatusDialog'
38import {GrowableAvatar} from './GrowableAvatar'
39import {GrowableBanner} from './GrowableBanner'
40import {StatusBarShadow} from './StatusBarShadow'
+1-1
src/screens/Settings/Settings.tsx
···7import {useNavigation} from '@react-navigation/native'
8import {type NativeStackScreenProps} from '@react-navigation/native-stack'
910-import {useActorStatus} from '#/lib/actor-status'
11import {HELP_DESK_URL} from '#/lib/constants'
12import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher'
13import {useApplyPullRequestOTAUpdate} from '#/lib/hooks/useOTAUpdates'
···70} from '#/components/verification/VerificationCheckButton'
71import {useAnalytics} from '#/analytics'
72import {IS_INTERNAL, IS_IOS, IS_NATIVE} from '#/env'
073import {device, useStorage} from '#/storage'
74import {useActivitySubscriptionsNudged} from '#/storage/hooks/activity-subscriptions-nudged'
75
···7import {useNavigation} from '@react-navigation/native'
8import {type NativeStackScreenProps} from '@react-navigation/native-stack'
9010import {HELP_DESK_URL} from '#/lib/constants'
11import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher'
12import {useApplyPullRequestOTAUpdate} from '#/lib/hooks/useOTAUpdates'
···69} from '#/components/verification/VerificationCheckButton'
70import {useAnalytics} from '#/analytics'
71import {IS_INTERNAL, IS_IOS, IS_NATIVE} from '#/env'
72+import {useActorStatus} from '#/features/liveNow'
73import {device, useStorage} from '#/storage'
74import {useActivitySubscriptionsNudged} from '#/storage/hooks/activity-subscriptions-nudged'
75
···1+import {createContext, useContext} from 'react'
2+import {QueryClient, useQuery} from '@tanstack/react-query'
3+4+import {APP_CONFIG_URL} from '#/env'
5+6+const qc = new QueryClient()
7+const appConfigQueryKey = ['app-config']
8+9+/**
10+ * Matches the types defined in our `app-config` worker
11+ */
12+type AppConfigResponse = {
13+ liveNow: {
14+ allow: string[]
15+ exceptions: {
16+ did: string
17+ allow: string[]
18+ }[]
19+ }
20+}
21+22+export const DEFAULT_APP_CONFIG_RESPONSE: AppConfigResponse = {
23+ liveNow: {
24+ allow: [],
25+ exceptions: [],
26+ },
27+}
28+29+let fetchAppConfigPromise: Promise<AppConfigResponse> | undefined
30+31+async function fetchAppConfig(): Promise<AppConfigResponse | null> {
32+ try {
33+ if (!fetchAppConfigPromise) {
34+ fetchAppConfigPromise = (async () => {
35+ const r = await fetch(`${APP_CONFIG_URL}/config`)
36+ if (!r.ok) throw new Error(await r.text())
37+ const data = await r.json()
38+ return data
39+ })()
40+ }
41+ return await fetchAppConfigPromise
42+ } catch (e) {
43+ fetchAppConfigPromise = undefined
44+ throw e
45+ }
46+}
47+48+const Context = createContext<AppConfigResponse>(DEFAULT_APP_CONFIG_RESPONSE)
49+50+export function Provider({children}: React.PropsWithChildren<{}>) {
51+ const {data} = useQuery<AppConfigResponse | null>(
52+ {
53+ staleTime: Infinity,
54+ queryKey: appConfigQueryKey,
55+ refetchInterval: query => {
56+ // refetch regularly if fetch failed, otherwise never refetch
57+ return query.state.status === 'error' ? 60e3 : Infinity
58+ },
59+ async queryFn() {
60+ return fetchAppConfig()
61+ },
62+ },
63+ qc,
64+ )
65+ return (
66+ <Context.Provider value={data ?? DEFAULT_APP_CONFIG_RESPONSE}>
67+ {children}
68+ </Context.Provider>
69+ )
70+}
71+72+export async function prefetchAppConfig() {
73+ try {
74+ const data = await fetchAppConfig()
75+ if (data) {
76+ qc.setQueryData(appConfigQueryKey, data)
77+ }
78+ } catch {}
79+}
80+81+export function useAppConfig() {
82+ const ctx = useContext(Context)
83+ if (!ctx) {
84+ throw new Error('useAppConfig must be used within a Provider')
85+ }
86+ return ctx
87+}
+1-1
src/state/queries/handle-availability.ts
···6 BSKY_SERVICE_DID,
7 PUBLIC_BSKY_SERVICE,
8} from '#/lib/constants'
09import {createFullHandle} from '#/lib/strings/handles'
10-import {useDebouncedValue} from '#/components/live/utils'
11import {useAnalytics} from '#/analytics'
12import * as bsky from '#/types/bsky'
13import {Agent} from '../session/agent'
···6 BSKY_SERVICE_DID,
7 PUBLIC_BSKY_SERVICE,
8} from '#/lib/constants'
9+import {useDebouncedValue} from '#/lib/hooks/useDebouncedValue'
10import {createFullHandle} from '#/lib/strings/handles'
011import {useAnalytics} from '#/analytics'
12import * as bsky from '#/types/bsky'
13import {Agent} from '../session/agent'
+3-67
src/state/service-config.tsx
···23import {useLanguagePrefs} from '#/state/preferences/languages'
4import {useServiceConfigQuery} from '#/state/queries/service-config'
5-import {useSession} from '#/state/session'
6-import {useAnalytics} from '#/analytics'
7-import {IS_DEV} from '#/env'
8import {device} from '#/storage'
910type TrendingContext = {
11 enabled: boolean
12}
1314-type LiveNowContext = {
15- did: string
16- domains: string[]
17-}[]
18-19const TrendingContext = createContext<TrendingContext>({
20 enabled: false,
21})
22TrendingContext.displayName = 'TrendingContext'
23-24-const LiveNowContext = createContext<LiveNowContext>([])
25-LiveNowContext.displayName = 'LiveNowContext'
2627const CheckEmailConfirmedContext = createContext<boolean | null>(null)
28···60 return {enabled}
61 }, [isInitialLoad, config, langPrefs.contentLanguages])
6263- const liveNow = useMemo<LiveNowContext>(() => config?.liveNow ?? [], [config])
64-65 // probably true, so default to true when loading
66 // if the call fails, the query will set it to false for us
67 const checkEmailConfirmed = config?.checkEmailConfirmed ?? true
6869 return (
70 <TrendingContext.Provider value={trending}>
71- <LiveNowContext.Provider value={liveNow}>
72- <CheckEmailConfirmedContext.Provider value={checkEmailConfirmed}>
73- {children}
74- </CheckEmailConfirmedContext.Provider>
75- </LiveNowContext.Provider>
76 </TrendingContext.Provider>
77 )
78}
7980export function useTrendingConfig() {
81 return useContext(TrendingContext)
82-}
83-84-const DEFAULT_LIVE_ALLOWED_DOMAINS = [
85- 'twitch.tv',
86- 'www.twitch.tv',
87- 'stream.place',
88- 'bluecast.app',
89- 'www.bluecast.app',
90-]
91-export type LiveNowConfig = {
92- currentAccountAllowedHosts: Set<string>
93- defaultAllowedHosts: Set<string>
94- allowedHostsExceptionsByDid: Map<string, Set<string>>
95-}
96-export function useLiveNowConfig(): LiveNowConfig {
97- const ctx = useContext(LiveNowContext)
98- const canGoLive = useCanGoLive()
99- const {currentAccount} = useSession()
100- return useMemo(() => {
101- const defaultAllowedHosts = new Set(DEFAULT_LIVE_ALLOWED_DOMAINS)
102- const allowedHostsExceptionsByDid = new Map<string, Set<string>>()
103- for (const live of ctx) {
104- allowedHostsExceptionsByDid.set(
105- live.did,
106- new Set(DEFAULT_LIVE_ALLOWED_DOMAINS.concat(live.domains)),
107- )
108- }
109- if (!currentAccount?.did || !canGoLive)
110- return {
111- currentAccountAllowedHosts: new Set(),
112- defaultAllowedHosts,
113- allowedHostsExceptionsByDid,
114- }
115- const vip = ctx.find(live => live.did === currentAccount.did)
116- return {
117- currentAccountAllowedHosts: new Set(
118- DEFAULT_LIVE_ALLOWED_DOMAINS.concat(vip ? vip.domains : []),
119- ),
120- defaultAllowedHosts,
121- allowedHostsExceptionsByDid,
122- }
123- }, [ctx, currentAccount, canGoLive])
124-}
125-126-export function useCanGoLive() {
127- const ax = useAnalytics()
128- const {hasSession} = useSession()
129- if (!hasSession) return false
130- return IS_DEV ? true : !ax.features.enabled(ax.features.LiveNowBetaDisable)
131}
132133export function useCheckEmailConfirmed() {
···23import {useLanguagePrefs} from '#/state/preferences/languages'
4import {useServiceConfigQuery} from '#/state/queries/service-config'
0005import {device} from '#/storage'
67type TrendingContext = {
8 enabled: boolean
9}
100000011const TrendingContext = createContext<TrendingContext>({
12 enabled: false,
13})
14TrendingContext.displayName = 'TrendingContext'
0001516const CheckEmailConfirmedContext = createContext<boolean | null>(null)
17···49 return {enabled}
50 }, [isInitialLoad, config, langPrefs.contentLanguages])
510052 // probably true, so default to true when loading
53 // if the call fails, the query will set it to false for us
54 const checkEmailConfirmed = config?.checkEmailConfirmed ?? true
5556 return (
57 <TrendingContext.Provider value={trending}>
58+ <CheckEmailConfirmedContext.Provider value={checkEmailConfirmed}>
59+ {children}
60+ </CheckEmailConfirmedContext.Provider>
0061 </TrendingContext.Provider>
62 )
63}
6465export function useTrendingConfig() {
66 return useContext(TrendingContext)
000000000000000000000000000000000000000000000000067}
6869export function useCheckEmailConfirmed() {
+5-2
src/view/com/posts/PostFeed.tsx
···19import {useLingui} from '@lingui/react'
20import {useQueryClient} from '@tanstack/react-query'
2122-import {isStatusStillActive, isStatusValidForViewers} from '#/lib/actor-status'
23import {DISCOVER_FEED_URI, KNOWN_SHUTDOWN_FEEDS} from '#/lib/constants'
24import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
25import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
···40 RQKEY,
41 usePostFeedQuery,
42} from '#/state/queries/post-feed'
43-import {useLiveNowConfig} from '#/state/service-config'
44import {useSession} from '#/state/session'
45import {useProgressGuide} from '#/state/shell/progress-guide'
46import {useSelectedFeed} from '#/state/shell/selected-feed'
···63import {useAnalytics} from '#/analytics'
64import {IS_IOS, IS_NATIVE, IS_WEB} from '#/env'
65import {DiscoverFeedLiveEventFeedsAndTrendingBanner} from '#/features/liveEvents/components/DiscoverFeedLiveEventFeedsAndTrendingBanner'
0000066import {ComposerPrompt} from '../feeds/ComposerPrompt'
67import {DiscoverFallbackHeader} from './DiscoverFallbackHeader'
68import {FeedShutdownMsg} from './FeedShutdownMsg'
···19import {useLingui} from '@lingui/react'
20import {useQueryClient} from '@tanstack/react-query'
21022import {DISCOVER_FEED_URI, KNOWN_SHUTDOWN_FEEDS} from '#/lib/constants'
23import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
24import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
···39 RQKEY,
40 usePostFeedQuery,
41} from '#/state/queries/post-feed'
042import {useSession} from '#/state/session'
43import {useProgressGuide} from '#/state/shell/progress-guide'
44import {useSelectedFeed} from '#/state/shell/selected-feed'
···61import {useAnalytics} from '#/analytics'
62import {IS_IOS, IS_NATIVE, IS_WEB} from '#/env'
63import {DiscoverFeedLiveEventFeedsAndTrendingBanner} from '#/features/liveEvents/components/DiscoverFeedLiveEventFeedsAndTrendingBanner'
64+import {
65+ isStatusStillActive,
66+ isStatusValidForViewers,
67+ useLiveNowConfig,
68+} from '#/features/liveNow'
69import {ComposerPrompt} from '../feeds/ComposerPrompt'
70import {DiscoverFallbackHeader} from './DiscoverFallbackHeader'
71import {FeedShutdownMsg} from './FeedShutdownMsg'
+1-1
src/view/com/posts/PostFeedItem.tsx
···11} from '@atproto/api'
12import {useQueryClient} from '@tanstack/react-query'
1314-import {useActorStatus} from '#/lib/actor-status'
15import {type ReasonFeedSource} from '#/lib/api/feed/types'
16import {MAX_POST_LINES} from '#/lib/constants'
17import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
···48import {RichText} from '#/components/RichText'
49import {SubtleHover} from '#/components/SubtleHover'
50import {useAnalytics} from '#/analytics'
051import * as bsky from '#/types/bsky'
52import {PostFeedReason} from './PostFeedReason'
53
···11} from '@atproto/api'
12import {useQueryClient} from '@tanstack/react-query'
13014import {type ReasonFeedSource} from '#/lib/api/feed/types'
15import {MAX_POST_LINES} from '#/lib/constants'
16import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
···47import {RichText} from '#/components/RichText'
48import {SubtleHover} from '#/components/SubtleHover'
49import {useAnalytics} from '#/analytics'
50+import {useActorStatus} from '#/features/liveNow'
51import * as bsky from '#/types/bsky'
52import {PostFeedReason} from './PostFeedReason'
53
+5-6
src/view/com/profile/ProfileMenu.tsx
···5import {useNavigation} from '@react-navigation/native'
6import {useQueryClient} from '@tanstack/react-query'
78-import {useActorStatus} from '#/lib/actor-status'
9import {HITSLOP_20} from '#/lib/constants'
10import {makeProfileLink} from '#/lib/routes/links'
11import {type NavigationProp} from '#/lib/routes/types'
···20 useProfileFollowMutationQueue,
21 useProfileMuteMutationQueue,
22} from '#/state/queries/profile'
23-import {useCanGoLive} from '#/state/service-config'
24import {useSession} from '#/state/session'
25import {EventStopper} from '#/view/com/util/EventStopper'
26import * as Toast from '#/view/com/util/Toast'
···47import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
48import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as Unmute} from '#/components/icons/Speaker'
49import {StarterPack} from '#/components/icons/StarterPack'
50-import {EditLiveDialog} from '#/components/live/EditLiveDialog'
51-import {GoLiveDialog} from '#/components/live/GoLiveDialog'
52-import {GoLiveDisabledDialog} from '#/components/live/GoLiveDisabledDialog'
53import * as Menu from '#/components/Menu'
54import {
55 ReportDialog,
···61import {VerificationRemovePrompt} from '#/components/verification/VerificationRemovePrompt'
62import {useAnalytics} from '#/analytics'
63import {IS_WEB} from '#/env'
000064import {Dot} from '#/features/nuxs/components/Dot'
65import {Gradient} from '#/features/nuxs/components/Gradient'
66import {useDevMode} from '#/storage/hooks/dev-mode'
···85 const isLabelerAndNotBlocked = !!profile.associated?.labeler && !isBlocked
86 const [devModeEnabled] = useDevMode()
87 const verification = useFullVerificationState({profile})
88- const canGoLive = useCanGoLive()
89 const status = useActorStatus(profile)
90 const statusNudge = useNux(Nux.LiveNowBetaNudge)
91 const statusNudgeActive =
···5import {useNavigation} from '@react-navigation/native'
6import {useQueryClient} from '@tanstack/react-query'
708import {HITSLOP_20} from '#/lib/constants'
9import {makeProfileLink} from '#/lib/routes/links'
10import {type NavigationProp} from '#/lib/routes/types'
···19 useProfileFollowMutationQueue,
20 useProfileMuteMutationQueue,
21} from '#/state/queries/profile'
022import {useSession} from '#/state/session'
23import {EventStopper} from '#/view/com/util/EventStopper'
24import * as Toast from '#/view/com/util/Toast'
···45import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
46import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as Unmute} from '#/components/icons/Speaker'
47import {StarterPack} from '#/components/icons/StarterPack'
00048import * as Menu from '#/components/Menu'
49import {
50 ReportDialog,
···56import {VerificationRemovePrompt} from '#/components/verification/VerificationRemovePrompt'
57import {useAnalytics} from '#/analytics'
58import {IS_WEB} from '#/env'
59+import {useActorStatus, useLiveNowConfig} from '#/features/liveNow'
60+import {EditLiveDialog} from '#/features/liveNow/components/EditLiveDialog'
61+import {GoLiveDialog} from '#/features/liveNow/components/GoLiveDialog'
62+import {GoLiveDisabledDialog} from '#/features/liveNow/components/GoLiveDisabledDialog'
63import {Dot} from '#/features/nuxs/components/Dot'
64import {Gradient} from '#/features/nuxs/components/Gradient'
65import {useDevMode} from '#/storage/hooks/dev-mode'
···84 const isLabelerAndNotBlocked = !!profile.associated?.labeler && !isBlocked
85 const [devModeEnabled] = useDevMode()
86 const verification = useFullVerificationState({profile})
87+ const {canGoLive} = useLiveNowConfig()
88 const status = useActorStatus(profile)
89 const statusNudge = useNux(Nux.LiveNowBetaNudge)
90 const statusNudgeActive =
+1-1
src/view/com/util/PostMeta.tsx
···5import {useLingui} from '@lingui/react'
6import {useQueryClient} from '@tanstack/react-query'
78-import {useActorStatus} from '#/lib/actor-status'
9import {makeProfileLink} from '#/lib/routes/links'
10import {forceLTR} from '#/lib/strings/bidi'
11import {NON_BREAKING_SPACE} from '#/lib/strings/constants'
···21import {useSimpleVerificationState} from '#/components/verification'
22import {VerificationCheck} from '#/components/verification/VerificationCheck'
23import {IS_ANDROID} from '#/env'
024import {TimeElapsed} from './TimeElapsed'
25import {PreviewableUserAvatar} from './UserAvatar'
26
···5import {useLingui} from '@lingui/react'
6import {useQueryClient} from '@tanstack/react-query'
708import {makeProfileLink} from '#/lib/routes/links'
9import {forceLTR} from '#/lib/strings/bidi'
10import {NON_BREAKING_SPACE} from '#/lib/strings/constants'
···20import {useSimpleVerificationState} from '#/components/verification'
21import {VerificationCheck} from '#/components/verification/VerificationCheck'
22import {IS_ANDROID} from '#/env'
23+import {useActorStatus} from '#/features/liveNow'
24import {TimeElapsed} from './TimeElapsed'
25import {PreviewableUserAvatar} from './UserAvatar'
26
+3-3
src/view/com/util/UserAvatar.tsx
···15import {useLingui} from '@lingui/react'
16import {useQueryClient} from '@tanstack/react-query'
1718-import {useActorStatus} from '#/lib/actor-status'
19import {useHaptics} from '#/lib/haptics'
20import {
21 useCameraPermission,
···47import {StreamingLive_Stroke2_Corner0_Rounded as LibraryIcon} from '#/components/icons/StreamingLive'
48import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash'
49import {Link} from '#/components/Link'
50-import {LiveIndicator} from '#/components/live/LiveIndicator'
51-import {LiveStatusDialog} from '#/components/live/LiveStatusDialog'
52import {MediaInsetBorder} from '#/components/MediaInsetBorder'
53import * as Menu from '#/components/Menu'
54import {ProfileHoverCard} from '#/components/ProfileHoverCard'
55import {useAnalytics} from '#/analytics'
56import {IS_ANDROID, IS_NATIVE, IS_WEB, IS_WEB_TOUCH_DEVICE} from '#/env'
00057import type * as bsky from '#/types/bsky'
5859export type UserAvatarType = 'user' | 'algo' | 'list' | 'labeler'
···15import {useLingui} from '@lingui/react'
16import {useQueryClient} from '@tanstack/react-query'
17018import {useHaptics} from '#/lib/haptics'
19import {
20 useCameraPermission,
···46import {StreamingLive_Stroke2_Corner0_Rounded as LibraryIcon} from '#/components/icons/StreamingLive'
47import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash'
48import {Link} from '#/components/Link'
0049import {MediaInsetBorder} from '#/components/MediaInsetBorder'
50import * as Menu from '#/components/Menu'
51import {ProfileHoverCard} from '#/components/ProfileHoverCard'
52import {useAnalytics} from '#/analytics'
53import {IS_ANDROID, IS_NATIVE, IS_WEB, IS_WEB_TOUCH_DEVICE} from '#/env'
54+import {useActorStatus} from '#/features/liveNow'
55+import {LiveIndicator} from '#/features/liveNow/components/LiveIndicator'
56+import {LiveStatusDialog} from '#/features/liveNow/components/LiveStatusDialog'
57import type * as bsky from '#/types/bsky'
5859export type UserAvatarType = 'user' | 'algo' | 'list' | 'labeler'
+1-1
src/view/shell/Drawer.tsx
···5import {useLingui} from '@lingui/react'
6import {StackActions, useNavigation} from '@react-navigation/native'
78-import {useActorStatus} from '#/lib/actor-status'
9import {FEEDBACK_FORM_URL, HELP_DESK_URL} from '#/lib/constants'
10import {type PressableScale} from '#/lib/custom-animations/PressableScale'
11import {useNavigationTabState} from '#/lib/hooks/useNavigationTabState'
···57import {useSimpleVerificationState} from '#/components/verification'
58import {VerificationCheck} from '#/components/verification/VerificationCheck'
59import {IS_WEB} from '#/env'
06061const iconWidth = 26
62
···5import {useLingui} from '@lingui/react'
6import {StackActions, useNavigation} from '@react-navigation/native'
708import {FEEDBACK_FORM_URL, HELP_DESK_URL} from '#/lib/constants'
9import {type PressableScale} from '#/lib/custom-animations/PressableScale'
10import {useNavigationTabState} from '#/lib/hooks/useNavigationTabState'
···56import {useSimpleVerificationState} from '#/components/verification'
57import {VerificationCheck} from '#/components/verification/VerificationCheck'
58import {IS_WEB} from '#/env'
59+import {useActorStatus} from '#/features/liveNow'
6061const iconWidth = 26
62
+1-1
src/view/shell/bottom-bar/BottomBar.tsx
···7import {type BottomTabBarProps} from '@react-navigation/bottom-tabs'
8import {StackActions} from '@react-navigation/native'
910-import {useActorStatus} from '#/lib/actor-status'
11import {PressableScale} from '#/lib/custom-animations/PressableScale'
12import {BOTTOM_BAR_AVI} from '#/lib/demo'
13import {useHaptics} from '#/lib/haptics'
···49 Message_Stroke2_Corner0_Rounded_Filled as MessageFilled,
50} from '#/components/icons/Message'
51import {Text} from '#/components/Typography'
052import {useDemoMode} from '#/storage/hooks/demo-mode'
53import {styles} from './BottomBarStyles'
54
···7import {type BottomTabBarProps} from '@react-navigation/bottom-tabs'
8import {StackActions} from '@react-navigation/native'
9010import {PressableScale} from '#/lib/custom-animations/PressableScale'
11import {BOTTOM_BAR_AVI} from '#/lib/demo'
12import {useHaptics} from '#/lib/haptics'
···48 Message_Stroke2_Corner0_Rounded_Filled as MessageFilled,
49} from '#/components/icons/Message'
50import {Text} from '#/components/Typography'
51+import {useActorStatus} from '#/features/liveNow'
52import {useDemoMode} from '#/storage/hooks/demo-mode'
53import {styles} from './BottomBarStyles'
54
+1-1
src/view/shell/desktop/LeftNav.tsx
···5import {useLingui} from '@lingui/react'
6import {useNavigation, useNavigationState} from '@react-navigation/native'
78-import {useActorStatus} from '#/lib/actor-status'
9import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher'
10import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
11import {usePalette} from '#/lib/hooks/usePalette'
···74import * as Menu from '#/components/Menu'
75import * as Prompt from '#/components/Prompt'
76import {Text} from '#/components/Typography'
077import {PlatformInfo} from '../../../../modules/expo-bluesky-swiss-army'
78import {router} from '../../../routes'
79
···5import {useLingui} from '@lingui/react'
6import {useNavigation, useNavigationState} from '@react-navigation/native'
708import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher'
9import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
10import {usePalette} from '#/lib/hooks/usePalette'
···73import * as Menu from '#/components/Menu'
74import * as Prompt from '#/components/Prompt'
75import {Text} from '#/components/Typography'
76+import {useActorStatus} from '#/features/liveNow'
77import {PlatformInfo} from '../../../../modules/expo-bluesky-swiss-army'
78import {router} from '../../../routes'
79