···44import {msg, Trans} from '@lingui/macro'
55import {useLingui} from '@lingui/react'
6677-import {useActorStatus} from '#/lib/actor-status'
87import {isJwtExpired} from '#/lib/jwt'
98import {sanitizeDisplayName} from '#/lib/strings/display-names'
109import {sanitizeHandle} from '#/lib/strings/handles'
···1918import {Text} from '#/components/Typography'
2019import {useSimpleVerificationState} from '#/components/verification'
2120import {VerificationCheck} from '#/components/verification/VerificationCheck'
2121+import {useActorStatus} from '#/features/liveNow'
22222323export function AccountList({
2424 onSelectAccount,
+1-1
src/components/ProfileCard.tsx
···1414import {msg} from '@lingui/macro'
1515import {useLingui} from '@lingui/react'
16161717-import {useActorStatus} from '#/lib/actor-status'
1817import {getModerationCauseKey} from '#/lib/moderation'
1918import {forceLTR} from '#/lib/strings/bidi'
2019import {NON_BREAKING_SPACE} from '#/lib/strings/constants'
···4746import {useSimpleVerificationState} from '#/components/verification'
4847import {VerificationCheck} from '#/components/verification/VerificationCheck'
4948import {type Metrics} from '#/analytics'
4949+import {useActorStatus} from '#/features/liveNow'
5050import type * as bsky from '#/types/bsky'
51515252export function Default({
+2-2
src/components/ProfileHoverCard/index.web.tsx
···1010import {useLingui} from '@lingui/react'
1111import {useNavigation} from '@react-navigation/native'
12121313-import {useActorStatus} from '#/lib/actor-status'
1413import {getModerationCauseKey} from '#/lib/moderation'
1514import {makeProfileLink} from '#/lib/routes/links'
1615import {type NavigationProp} from '#/lib/routes/types'
···3433 shouldShowKnownFollowers,
3534} from '#/components/KnownFollowers'
3635import {InlineLinkText, Link} from '#/components/Link'
3737-import {LiveStatus} from '#/components/live/LiveStatusDialog'
3836import {Loader} from '#/components/Loader'
3937import * as Pills from '#/components/Pills'
4038import {Portal} from '#/components/Portal'
···4341import {useSimpleVerificationState} from '#/components/verification'
4442import {VerificationCheck} from '#/components/verification/VerificationCheck'
4543import {IS_WEB_TOUCH_DEVICE} from '#/env'
4444+import {useActorStatus} from '#/features/liveNow'
4545+import {LiveStatus} from '#/features/liveNow/components/LiveStatusDialog'
4646import {type ProfileHoverCardProps} from './types'
47474848const floatingMiddlewares = [
···99import {useLingui} from '@lingui/react'
1010import {differenceInMinutes} from 'date-fns'
11111212+import {useDebouncedValue} from '#/lib/hooks/useDebouncedValue'
1213import {cleanError} from '#/lib/strings/errors'
1314import {definitelyUrl} from '#/lib/strings/url-helpers'
1415import {useTickEveryMinute} from '#/state/shell'
···2021import {Clock_Stroke2_Corner0_Rounded as ClockIcon} from '#/components/icons/Clock'
2122import {Loader} from '#/components/Loader'
2223import {Text} from '#/components/Typography'
2323-import {LinkPreview} from './LinkPreview'
2424import {
2525+ displayDuration,
2526 useLiveLinkMetaQuery,
2627 useRemoveLiveStatusMutation,
2728 useUpsertLiveStatusMutation,
2828-} from './queries'
2929-import {displayDuration, useDebouncedValue} from './utils'
2929+} from '#/features/liveNow'
3030+import {LinkPreview} from '#/features/liveNow/components/LinkPreview'
30313132export function EditLiveDialog({
3233 control,
···33import {msg, Trans} from '@lingui/macro'
44import {useLingui} from '@lingui/react'
5566+import {useDebouncedValue} from '#/lib/hooks/useDebouncedValue'
67import {cleanError} from '#/lib/strings/errors'
78import {definitelyUrl} from '#/lib/strings/url-helpers'
89import {useModerationOpts} from '#/state/preferences/moderation-opts'
99-import {useLiveNowConfig} from '#/state/service-config'
1010import {useTickEveryMinute} from '#/state/shell'
1111import {atoms as a, ios, native, platform, useTheme, web} from '#/alf'
1212import {Admonition} from '#/components/Admonition'
1313import {Button, ButtonIcon, ButtonText} from '#/components/Button'
1414import * as Dialog from '#/components/Dialog'
1515import * as TextField from '#/components/forms/TextField'
1616-import {
1717- displayDuration,
1818- getLiveServiceNames,
1919- useDebouncedValue,
2020-} from '#/components/live/utils'
2116import {Loader} from '#/components/Loader'
2217import * as ProfileCard from '#/components/ProfileCard'
2318import * as Select from '#/components/Select'
2419import {Text} from '#/components/Typography'
2020+import {
2121+ displayDuration,
2222+ getLiveServiceNames,
2323+ useLiveLinkMetaQuery,
2424+ useLiveNowConfig,
2525+ useUpsertLiveStatusMutation,
2626+} from '#/features/liveNow'
2527import type * as bsky from '#/types/bsky'
2628import {LinkPreview} from './LinkPreview'
2727-import {useLiveLinkMetaQuery, useUpsertLiveStatusMutation} from './queries'
28292930export function GoLiveDialog({
3031 control,
···1717import {Button, ButtonIcon, ButtonText} from '#/components/Button'
1818import * as Dialog from '#/components/Dialog'
1919import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfoIcon} from '#/components/icons/CircleInfo'
2020+import {Globe_Stroke2_Corner0_Rounded} from '#/components/icons/Globe'
2121+import {SquareArrowTopRight_Stroke2_Corner0_Rounded as SquareArrowTopRightIcon} from '#/components/icons/SquareArrowTopRight'
2022import {createStaticClick, SimpleInlineLinkText} from '#/components/Link'
2123import {useGlobalReportDialogControl} from '#/components/moderation/ReportDialog'
2224import * as ProfileCard from '#/components/ProfileCard'
2325import {Text} from '#/components/Typography'
2426import {useAnalytics} from '#/analytics'
2727+import {LiveIndicator} from '#/features/liveNow/components/LiveIndicator'
2528import type * as bsky from '#/types/bsky'
2626-import {Globe_Stroke2_Corner0_Rounded} from '../icons/Globe'
2727-import {SquareArrowTopRight_Stroke2_Corner0_Rounded as SquareArrowTopRightIcon} from '../icons/SquareArrowTopRight'
2828-import {LiveIndicator} from './LiveIndicator'
29293030export function LiveStatusDialog({
3131 control,
···11+import {useEffect, useState} from 'react'
22+33+/**
44+ * Returns a debounced version of the input value that only updates after the
55+ * specified delay has passed without any changes to the input value.
66+ */
77+export function useDebouncedValue<T>(val: T, delayMs: number): T {
88+ const [prev, setPrev] = useState(val)
99+1010+ useEffect(() => {
1111+ const timeout = setTimeout(() => setPrev(val), delayMs)
1212+ return () => clearTimeout(timeout)
1313+ }, [val, delayMs])
1414+1515+ return prev
1616+}
···1010import {msg, Plural, Trans} from '@lingui/macro'
1111import {useLingui} from '@lingui/react'
12121313-import {useActorStatus} from '#/lib/actor-status'
1413import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
1514import {useTranslate} from '#/lib/hooks/useTranslate'
1615import {makeProfileLink} from '#/lib/routes/links'
···6059import {VerificationCheckButton} from '#/components/verification/VerificationCheckButton'
6160import {WhoCanReply} from '#/components/WhoCanReply'
6261import {useAnalytics} from '#/analytics'
6262+import {useActorStatus} from '#/features/liveNow'
6363import * as bsky from '#/types/bsky'
64646565export function ThreadItemAnchor({
···88} from '@atproto/api'
99import {Trans} from '@lingui/macro'
10101111-import {useActorStatus} from '#/lib/actor-status'
1211import {MAX_POST_LINES} from '#/lib/constants'
1312import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
1413import {makeProfileLink} from '#/lib/routes/links'
···4443import * as Skele from '#/components/Skeleton'
4544import {SubtleHover} from '#/components/SubtleHover'
4645import {Text} from '#/components/Typography'
4646+import {useActorStatus} from '#/features/liveNow'
47474848export type ThreadItemPostProps = {
4949 item: Extract<ThreadItem, {type: 'threadPost'}>
···1010import {msg, Trans} from '@lingui/macro'
1111import {useLingui} from '@lingui/react'
12121313-import {useActorStatus} from '#/lib/actor-status'
1413import {useHaptics} from '#/lib/haptics'
1514import {sanitizeDisplayName} from '#/lib/strings/display-names'
1615import {sanitizeHandle} from '#/lib/strings/handles'
···3938import {Text} from '#/components/Typography'
4039import {VerificationCheckButton} from '#/components/verification/VerificationCheckButton'
4140import {IS_IOS} from '#/env'
4141+import {useActorStatus} from '#/features/liveNow'
4242import {GermButton} from '../components/GermButton'
4343import {EditProfileDialog} from './EditProfileDialog'
4444import {ProfileHeaderHandle} from './Handle'
+4-4
src/screens/Profile/Header/Shell.tsx
···1414import {useLingui} from '@lingui/react'
1515import {useNavigation} from '@react-navigation/native'
16161717-import {useActorStatus} from '#/lib/actor-status'
1817import {BACK_HITSLOP} from '#/lib/constants'
1918import {useHaptics} from '#/lib/haptics'
2019import {type NavigationProp} from '#/lib/routes/types'
···2827import {Button} from '#/components/Button'
2928import {useDialogControl} from '#/components/Dialog'
3029import {ArrowLeft_Stroke2_Corner0_Rounded as ArrowLeftIcon} from '#/components/icons/Arrow'
3131-import {EditLiveDialog} from '#/components/live/EditLiveDialog'
3232-import {LiveIndicator} from '#/components/live/LiveIndicator'
3333-import {LiveStatusDialog} from '#/components/live/LiveStatusDialog'
3430import {LabelsOnMe} from '#/components/moderation/LabelsOnMe'
3531import {ProfileHeaderAlerts} from '#/components/moderation/ProfileHeaderAlerts'
3632import {useAnalytics} from '#/analytics'
3733import {IS_IOS} from '#/env'
3434+import {useActorStatus} from '#/features/liveNow'
3535+import {EditLiveDialog} from '#/features/liveNow/components/EditLiveDialog'
3636+import {LiveIndicator} from '#/features/liveNow/components/LiveIndicator'
3737+import {LiveStatusDialog} from '#/features/liveNow/components/LiveStatusDialog'
3838import {GrowableAvatar} from './GrowableAvatar'
3939import {GrowableBanner} from './GrowableBanner'
4040import {StatusBarShadow} from './StatusBarShadow'
+1-1
src/screens/Settings/Settings.tsx
···77import {useNavigation} from '@react-navigation/native'
88import {type NativeStackScreenProps} from '@react-navigation/native-stack'
991010-import {useActorStatus} from '#/lib/actor-status'
1110import {HELP_DESK_URL} from '#/lib/constants'
1211import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher'
1312import {useApplyPullRequestOTAUpdate} from '#/lib/hooks/useOTAUpdates'
···7069} from '#/components/verification/VerificationCheckButton'
7170import {useAnalytics} from '#/analytics'
7271import {IS_INTERNAL, IS_IOS, IS_NATIVE} from '#/env'
7272+import {useActorStatus} from '#/features/liveNow'
7373import {device, useStorage} from '#/storage'
7474import {useActivitySubscriptionsNudged} from '#/storage/hooks/activity-subscriptions-nudged'
7575
+87
src/state/appConfig.tsx
···11+import {createContext, useContext} from 'react'
22+import {QueryClient, useQuery} from '@tanstack/react-query'
33+44+import {APP_CONFIG_URL} from '#/env'
55+66+const qc = new QueryClient()
77+const appConfigQueryKey = ['app-config']
88+99+/**
1010+ * Matches the types defined in our `app-config` worker
1111+ */
1212+type AppConfigResponse = {
1313+ liveNow: {
1414+ allow: string[]
1515+ exceptions: {
1616+ did: string
1717+ allow: string[]
1818+ }[]
1919+ }
2020+}
2121+2222+export const DEFAULT_APP_CONFIG_RESPONSE: AppConfigResponse = {
2323+ liveNow: {
2424+ allow: [],
2525+ exceptions: [],
2626+ },
2727+}
2828+2929+let fetchAppConfigPromise: Promise<AppConfigResponse> | undefined
3030+3131+async function fetchAppConfig(): Promise<AppConfigResponse | null> {
3232+ try {
3333+ if (!fetchAppConfigPromise) {
3434+ fetchAppConfigPromise = (async () => {
3535+ const r = await fetch(`${APP_CONFIG_URL}/config`)
3636+ if (!r.ok) throw new Error(await r.text())
3737+ const data = await r.json()
3838+ return data
3939+ })()
4040+ }
4141+ return await fetchAppConfigPromise
4242+ } catch (e) {
4343+ fetchAppConfigPromise = undefined
4444+ throw e
4545+ }
4646+}
4747+4848+const Context = createContext<AppConfigResponse>(DEFAULT_APP_CONFIG_RESPONSE)
4949+5050+export function Provider({children}: React.PropsWithChildren<{}>) {
5151+ const {data} = useQuery<AppConfigResponse | null>(
5252+ {
5353+ staleTime: Infinity,
5454+ queryKey: appConfigQueryKey,
5555+ refetchInterval: query => {
5656+ // refetch regularly if fetch failed, otherwise never refetch
5757+ return query.state.status === 'error' ? 60e3 : Infinity
5858+ },
5959+ async queryFn() {
6060+ return fetchAppConfig()
6161+ },
6262+ },
6363+ qc,
6464+ )
6565+ return (
6666+ <Context.Provider value={data ?? DEFAULT_APP_CONFIG_RESPONSE}>
6767+ {children}
6868+ </Context.Provider>
6969+ )
7070+}
7171+7272+export async function prefetchAppConfig() {
7373+ try {
7474+ const data = await fetchAppConfig()
7575+ if (data) {
7676+ qc.setQueryData(appConfigQueryKey, data)
7777+ }
7878+ } catch {}
7979+}
8080+8181+export function useAppConfig() {
8282+ const ctx = useContext(Context)
8383+ if (!ctx) {
8484+ throw new Error('useAppConfig must be used within a Provider')
8585+ }
8686+ return ctx
8787+}
+1-1
src/state/queries/handle-availability.ts
···66 BSKY_SERVICE_DID,
77 PUBLIC_BSKY_SERVICE,
88} from '#/lib/constants'
99+import {useDebouncedValue} from '#/lib/hooks/useDebouncedValue'
910import {createFullHandle} from '#/lib/strings/handles'
1010-import {useDebouncedValue} from '#/components/live/utils'
1111import {useAnalytics} from '#/analytics'
1212import * as bsky from '#/types/bsky'
1313import {Agent} from '../session/agent'
+3-67
src/state/service-config.tsx
···2233import {useLanguagePrefs} from '#/state/preferences/languages'
44import {useServiceConfigQuery} from '#/state/queries/service-config'
55-import {useSession} from '#/state/session'
66-import {useAnalytics} from '#/analytics'
77-import {IS_DEV} from '#/env'
85import {device} from '#/storage'
96107type TrendingContext = {
118 enabled: boolean
129}
13101414-type LiveNowContext = {
1515- did: string
1616- domains: string[]
1717-}[]
1818-1911const TrendingContext = createContext<TrendingContext>({
2012 enabled: false,
2113})
2214TrendingContext.displayName = 'TrendingContext'
2323-2424-const LiveNowContext = createContext<LiveNowContext>([])
2525-LiveNowContext.displayName = 'LiveNowContext'
26152716const CheckEmailConfirmedContext = createContext<boolean | null>(null)
2817···6049 return {enabled}
6150 }, [isInitialLoad, config, langPrefs.contentLanguages])
62516363- const liveNow = useMemo<LiveNowContext>(() => config?.liveNow ?? [], [config])
6464-6552 // probably true, so default to true when loading
6653 // if the call fails, the query will set it to false for us
6754 const checkEmailConfirmed = config?.checkEmailConfirmed ?? true
68556956 return (
7057 <TrendingContext.Provider value={trending}>
7171- <LiveNowContext.Provider value={liveNow}>
7272- <CheckEmailConfirmedContext.Provider value={checkEmailConfirmed}>
7373- {children}
7474- </CheckEmailConfirmedContext.Provider>
7575- </LiveNowContext.Provider>
5858+ <CheckEmailConfirmedContext.Provider value={checkEmailConfirmed}>
5959+ {children}
6060+ </CheckEmailConfirmedContext.Provider>
7661 </TrendingContext.Provider>
7762 )
7863}
79648065export function useTrendingConfig() {
8166 return useContext(TrendingContext)
8282-}
8383-8484-const DEFAULT_LIVE_ALLOWED_DOMAINS = [
8585- 'twitch.tv',
8686- 'www.twitch.tv',
8787- 'stream.place',
8888- 'bluecast.app',
8989- 'www.bluecast.app',
9090-]
9191-export type LiveNowConfig = {
9292- currentAccountAllowedHosts: Set<string>
9393- defaultAllowedHosts: Set<string>
9494- allowedHostsExceptionsByDid: Map<string, Set<string>>
9595-}
9696-export function useLiveNowConfig(): LiveNowConfig {
9797- const ctx = useContext(LiveNowContext)
9898- const canGoLive = useCanGoLive()
9999- const {currentAccount} = useSession()
100100- return useMemo(() => {
101101- const defaultAllowedHosts = new Set(DEFAULT_LIVE_ALLOWED_DOMAINS)
102102- const allowedHostsExceptionsByDid = new Map<string, Set<string>>()
103103- for (const live of ctx) {
104104- allowedHostsExceptionsByDid.set(
105105- live.did,
106106- new Set(DEFAULT_LIVE_ALLOWED_DOMAINS.concat(live.domains)),
107107- )
108108- }
109109- if (!currentAccount?.did || !canGoLive)
110110- return {
111111- currentAccountAllowedHosts: new Set(),
112112- defaultAllowedHosts,
113113- allowedHostsExceptionsByDid,
114114- }
115115- const vip = ctx.find(live => live.did === currentAccount.did)
116116- return {
117117- currentAccountAllowedHosts: new Set(
118118- DEFAULT_LIVE_ALLOWED_DOMAINS.concat(vip ? vip.domains : []),
119119- ),
120120- defaultAllowedHosts,
121121- allowedHostsExceptionsByDid,
122122- }
123123- }, [ctx, currentAccount, canGoLive])
124124-}
125125-126126-export function useCanGoLive() {
127127- const ax = useAnalytics()
128128- const {hasSession} = useSession()
129129- if (!hasSession) return false
130130- return IS_DEV ? true : !ax.features.enabled(ax.features.LiveNowBetaDisable)
13167}
1326813369export function useCheckEmailConfirmed() {
+5-2
src/view/com/posts/PostFeed.tsx
···1919import {useLingui} from '@lingui/react'
2020import {useQueryClient} from '@tanstack/react-query'
21212222-import {isStatusStillActive, isStatusValidForViewers} from '#/lib/actor-status'
2322import {DISCOVER_FEED_URI, KNOWN_SHUTDOWN_FEEDS} from '#/lib/constants'
2423import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
2524import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
···4039 RQKEY,
4140 usePostFeedQuery,
4241} from '#/state/queries/post-feed'
4343-import {useLiveNowConfig} from '#/state/service-config'
4442import {useSession} from '#/state/session'
4543import {useProgressGuide} from '#/state/shell/progress-guide'
4644import {useSelectedFeed} from '#/state/shell/selected-feed'
···6361import {useAnalytics} from '#/analytics'
6462import {IS_IOS, IS_NATIVE, IS_WEB} from '#/env'
6563import {DiscoverFeedLiveEventFeedsAndTrendingBanner} from '#/features/liveEvents/components/DiscoverFeedLiveEventFeedsAndTrendingBanner'
6464+import {
6565+ isStatusStillActive,
6666+ isStatusValidForViewers,
6767+ useLiveNowConfig,
6868+} from '#/features/liveNow'
6669import {ComposerPrompt} from '../feeds/ComposerPrompt'
6770import {DiscoverFallbackHeader} from './DiscoverFallbackHeader'
6871import {FeedShutdownMsg} from './FeedShutdownMsg'
+1-1
src/view/com/posts/PostFeedItem.tsx
···1111} from '@atproto/api'
1212import {useQueryClient} from '@tanstack/react-query'
13131414-import {useActorStatus} from '#/lib/actor-status'
1514import {type ReasonFeedSource} from '#/lib/api/feed/types'
1615import {MAX_POST_LINES} from '#/lib/constants'
1716import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
···4847import {RichText} from '#/components/RichText'
4948import {SubtleHover} from '#/components/SubtleHover'
5049import {useAnalytics} from '#/analytics'
5050+import {useActorStatus} from '#/features/liveNow'
5151import * as bsky from '#/types/bsky'
5252import {PostFeedReason} from './PostFeedReason'
5353
+5-6
src/view/com/profile/ProfileMenu.tsx
···55import {useNavigation} from '@react-navigation/native'
66import {useQueryClient} from '@tanstack/react-query'
7788-import {useActorStatus} from '#/lib/actor-status'
98import {HITSLOP_20} from '#/lib/constants'
109import {makeProfileLink} from '#/lib/routes/links'
1110import {type NavigationProp} from '#/lib/routes/types'
···2019 useProfileFollowMutationQueue,
2120 useProfileMuteMutationQueue,
2221} from '#/state/queries/profile'
2323-import {useCanGoLive} from '#/state/service-config'
2422import {useSession} from '#/state/session'
2523import {EventStopper} from '#/view/com/util/EventStopper'
2624import * as Toast from '#/view/com/util/Toast'
···4745import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
4846import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as Unmute} from '#/components/icons/Speaker'
4947import {StarterPack} from '#/components/icons/StarterPack'
5050-import {EditLiveDialog} from '#/components/live/EditLiveDialog'
5151-import {GoLiveDialog} from '#/components/live/GoLiveDialog'
5252-import {GoLiveDisabledDialog} from '#/components/live/GoLiveDisabledDialog'
5348import * as Menu from '#/components/Menu'
5449import {
5550 ReportDialog,
···6156import {VerificationRemovePrompt} from '#/components/verification/VerificationRemovePrompt'
6257import {useAnalytics} from '#/analytics'
6358import {IS_WEB} from '#/env'
5959+import {useActorStatus, useLiveNowConfig} from '#/features/liveNow'
6060+import {EditLiveDialog} from '#/features/liveNow/components/EditLiveDialog'
6161+import {GoLiveDialog} from '#/features/liveNow/components/GoLiveDialog'
6262+import {GoLiveDisabledDialog} from '#/features/liveNow/components/GoLiveDisabledDialog'
6463import {Dot} from '#/features/nuxs/components/Dot'
6564import {Gradient} from '#/features/nuxs/components/Gradient'
6665import {useDevMode} from '#/storage/hooks/dev-mode'
···8584 const isLabelerAndNotBlocked = !!profile.associated?.labeler && !isBlocked
8685 const [devModeEnabled] = useDevMode()
8786 const verification = useFullVerificationState({profile})
8888- const canGoLive = useCanGoLive()
8787+ const {canGoLive} = useLiveNowConfig()
8988 const status = useActorStatus(profile)
9089 const statusNudge = useNux(Nux.LiveNowBetaNudge)
9190 const statusNudgeActive =
+1-1
src/view/com/util/PostMeta.tsx
···55import {useLingui} from '@lingui/react'
66import {useQueryClient} from '@tanstack/react-query'
7788-import {useActorStatus} from '#/lib/actor-status'
98import {makeProfileLink} from '#/lib/routes/links'
109import {forceLTR} from '#/lib/strings/bidi'
1110import {NON_BREAKING_SPACE} from '#/lib/strings/constants'
···2120import {useSimpleVerificationState} from '#/components/verification'
2221import {VerificationCheck} from '#/components/verification/VerificationCheck'
2322import {IS_ANDROID} from '#/env'
2323+import {useActorStatus} from '#/features/liveNow'
2424import {TimeElapsed} from './TimeElapsed'
2525import {PreviewableUserAvatar} from './UserAvatar'
2626
+3-3
src/view/com/util/UserAvatar.tsx
···1515import {useLingui} from '@lingui/react'
1616import {useQueryClient} from '@tanstack/react-query'
17171818-import {useActorStatus} from '#/lib/actor-status'
1918import {useHaptics} from '#/lib/haptics'
2019import {
2120 useCameraPermission,
···4746import {StreamingLive_Stroke2_Corner0_Rounded as LibraryIcon} from '#/components/icons/StreamingLive'
4847import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash'
4948import {Link} from '#/components/Link'
5050-import {LiveIndicator} from '#/components/live/LiveIndicator'
5151-import {LiveStatusDialog} from '#/components/live/LiveStatusDialog'
5249import {MediaInsetBorder} from '#/components/MediaInsetBorder'
5350import * as Menu from '#/components/Menu'
5451import {ProfileHoverCard} from '#/components/ProfileHoverCard'
5552import {useAnalytics} from '#/analytics'
5653import {IS_ANDROID, IS_NATIVE, IS_WEB, IS_WEB_TOUCH_DEVICE} from '#/env'
5454+import {useActorStatus} from '#/features/liveNow'
5555+import {LiveIndicator} from '#/features/liveNow/components/LiveIndicator'
5656+import {LiveStatusDialog} from '#/features/liveNow/components/LiveStatusDialog'
5757import type * as bsky from '#/types/bsky'
58585959export type UserAvatarType = 'user' | 'algo' | 'list' | 'labeler'
+1-1
src/view/shell/Drawer.tsx
···55import {useLingui} from '@lingui/react'
66import {StackActions, useNavigation} from '@react-navigation/native'
7788-import {useActorStatus} from '#/lib/actor-status'
98import {FEEDBACK_FORM_URL, HELP_DESK_URL} from '#/lib/constants'
109import {type PressableScale} from '#/lib/custom-animations/PressableScale'
1110import {useNavigationTabState} from '#/lib/hooks/useNavigationTabState'
···5756import {useSimpleVerificationState} from '#/components/verification'
5857import {VerificationCheck} from '#/components/verification/VerificationCheck'
5958import {IS_WEB} from '#/env'
5959+import {useActorStatus} from '#/features/liveNow'
60606161const iconWidth = 26
6262
+1-1
src/view/shell/bottom-bar/BottomBar.tsx
···77import {type BottomTabBarProps} from '@react-navigation/bottom-tabs'
88import {StackActions} from '@react-navigation/native'
991010-import {useActorStatus} from '#/lib/actor-status'
1110import {PressableScale} from '#/lib/custom-animations/PressableScale'
1211import {BOTTOM_BAR_AVI} from '#/lib/demo'
1312import {useHaptics} from '#/lib/haptics'
···4948 Message_Stroke2_Corner0_Rounded_Filled as MessageFilled,
5049} from '#/components/icons/Message'
5150import {Text} from '#/components/Typography'
5151+import {useActorStatus} from '#/features/liveNow'
5252import {useDemoMode} from '#/storage/hooks/demo-mode'
5353import {styles} from './BottomBarStyles'
5454
+1-1
src/view/shell/desktop/LeftNav.tsx
···55import {useLingui} from '@lingui/react'
66import {useNavigation, useNavigationState} from '@react-navigation/native'
7788-import {useActorStatus} from '#/lib/actor-status'
98import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher'
109import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
1110import {usePalette} from '#/lib/hooks/usePalette'
···7473import * as Menu from '#/components/Menu'
7574import * as Prompt from '#/components/Prompt'
7675import {Text} from '#/components/Typography'
7676+import {useActorStatus} from '#/features/liveNow'
7777import {PlatformInfo} from '../../../../modules/expo-bluesky-swiss-army'
7878import {router} from '../../../routes'
7979