import {useCallback, useMemo} from 'react' import {View} from 'react-native' import {Image} from 'expo-image' import { type AppBskyActorDefs, type AppBskyEmbedExternal, moderateStatus, } from '@atproto/api' import {Trans, useLingui} from '@lingui/react/macro' import {useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' import {useOpenLink} from '#/lib/hooks/useOpenLink' import {type NavigationProp} from '#/lib/routes/types' import {sanitizeHandle} from '#/lib/strings/handles' import {toNiceDomain} from '#/lib/strings/url-helpers' import {useImageCdnHost} from '#/state/preferences' import {maybeModifyImageCdnHost} from '#/state/preferences/image-cdn-host' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {unstableCacheProfileView} from '#/state/queries/profile' import {android, atoms as a, platform, tokens, useTheme, web} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfoIcon} from '#/components/icons/CircleInfo' import {Globe_Stroke2_Corner0_Rounded} from '#/components/icons/Globe' import {Image_Stroke2_Corner0_Rounded as ImageIcon} from '#/components/icons/Image' import {SquareArrowTopRight_Stroke2_Corner0_Rounded as SquareArrowTopRightIcon} from '#/components/icons/SquareArrowTopRight' import {createStaticClick, SimpleInlineLinkText} from '#/components/Link' import * as Hider from '#/components/moderation/Hider' import {useGlobalReportDialogControl} from '#/components/moderation/ReportDialog' import * as ProfileCard from '#/components/ProfileCard' import {Text} from '#/components/Typography' import {useAnalytics} from '#/analytics' import {LiveIndicator} from '#/features/liveNow/components/LiveIndicator' import type * as bsky from '#/types/bsky' export function LiveStatusDialog({ control, profile, embed, status, onPressViewAvatar, }: { control: Dialog.DialogControlProps profile: bsky.profile.AnyProfileView status: AppBskyActorDefs.StatusView embed: AppBskyEmbedExternal.View onPressViewAvatar?: () => void }) { const navigation = useNavigation() return ( ) } function DialogInner({ profile, embed, navigation, status, onPressViewAvatar, }: { profile: bsky.profile.AnyProfileView embed: AppBskyEmbedExternal.View navigation: NavigationProp status: AppBskyActorDefs.StatusView onPressViewAvatar?: () => void }) { const {t: l} = useLingui() const control = Dialog.useDialogContext() const onPressOpenProfile = useCallback(() => { control.close(() => { navigation.push('Profile', { name: profile.handle, }) }) }, [navigation, profile.handle, control]) const handlePressViewAvatar = useCallback(() => { control.close(onPressViewAvatar) }, [control, onPressViewAvatar]) return ( ) } export function LiveStatus({ status, profile, embed, padding = 'xl', onPressOpenProfile, onPressViewAvatar, }: { status: AppBskyActorDefs.StatusView profile: bsky.profile.AnyProfileView embed: AppBskyEmbedExternal.View padding?: 'lg' | 'xl' onPressOpenProfile: () => void onPressViewAvatar?: () => void }) { const ax = useAnalytics() const {t: l} = useLingui() const t = useTheme() const queryClient = useQueryClient() const openLink = useOpenLink() const imageCdnHost = useImageCdnHost() const moderationOpts = useModerationOpts() const reportDialogControl = useGlobalReportDialogControl() const dialogContext = Dialog.useDialogContext() const moderation = useMemo(() => { if (!moderationOpts) return undefined return moderateStatus(profile, moderationOpts) }, [profile, moderationOpts]) return ( <> {embed.external.thumb && ( )} {embed.external.title || embed.external.uri} {toNiceDomain(embed.external.uri)} {moderationOpts && ( {/* Ensure wide enough on web hover */} )} Live feature is in beta {status && ( { function open() { reportDialogControl.open({ subject: { ...status, $type: 'app.bsky.actor.defs#statusView', }, }) } if (dialogContext.isWithinDialog) { dialogContext.close(open) } else { open() } })} style={[a.text_sm, a.underline, t.atoms.text_contrast_medium]}> Report )} ) } function ModeratedImage() { const t = useTheme() const {t: l} = useLingui() const hider = Hider.useHider() return ( {hider.meta.allowOverride ? ( Image is hidden due to your moderation settings. ) : ( /* * In practice, if `allowOverride` is false, we won't even allow this * dialog to open. That is handled in * `#/features/liveNow/index.tsx`. But for clarity, I've included * this here. */ Image is unavailable. )} {hider.meta.allowOverride && ( { hider.setIsContentVisible(true) })}> Show anyway )} ) }