import {useCallback, useState} from 'react' import {View} from 'react-native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {cleanError} from '#/lib/strings/errors' import {definitelyUrl} from '#/lib/strings/url-helpers' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useTickEveryMinute} from '#/state/shell' import {atoms as a, ios, native, platform, useTheme, web} from '#/alf' import {Admonition} from '#/components/Admonition' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' import * as TextField from '#/components/forms/TextField' import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning' import {Loader} from '#/components/Loader' import * as ProfileCard from '#/components/ProfileCard' import * as Select from '#/components/Select' import {Text} from '#/components/Typography' import type * as bsky from '#/types/bsky' import {LinkPreview} from './LinkPreview' import {useLiveLinkMetaQuery, useUpsertLiveStatusMutation} from './queries' import {displayDuration, useDebouncedValue} from './utils' export function GoLiveDialog({ control, profile, }: { control: Dialog.DialogControlProps profile: bsky.profile.AnyProfileView }) { return ( ) } // Possible durations: max 4 hours, 5 minute intervals const DURATIONS = Array.from({length: (4 * 60) / 5}).map((_, i) => (i + 1) * 5) function DialogInner({profile}: {profile: bsky.profile.AnyProfileView}) { const control = Dialog.useDialogContext() const {_, i18n} = useLingui() const t = useTheme() const [liveLink, setLiveLink] = useState('') const [liveLinkError, setLiveLinkError] = useState('') const [duration, setDuration] = useState(60) const moderationOpts = useModerationOpts() const tick = useTickEveryMinute() const time = useCallback( (offset: number) => { tick! const date = new Date() date.setMinutes(date.getMinutes() + offset) return i18n .date(date, {hour: 'numeric', minute: '2-digit', hour12: true}) .toLocaleUpperCase() .replace(' ', '') }, [tick, i18n], ) const onChangeDuration = useCallback((newDuration: string) => { setDuration(Number(newDuration)) }, []) const liveLinkUrl = definitelyUrl(liveLink) const debouncedUrl = useDebouncedValue(liveLinkUrl, 500) const { data: linkMeta, isSuccess: hasValidLinkMeta, isLoading: linkMetaLoading, error: linkMetaError, } = useLiveLinkMetaQuery(debouncedUrl) const { mutate: goLive, isPending: isGoingLive, error: goLiveError, } = useUpsertLiveStatusMutation(duration, linkMeta) const isSourceInvalid = !!liveLinkError || !!linkMetaError const hasLink = !!debouncedUrl && !isSourceInvalid return ( Go Live Add a temporary live status to your profile. When someone clicks on your avatar, they’ll see information about your live event. {moderationOpts && ( )} Live link setLiveLinkError('')} onBlur={() => { if (!definitelyUrl(liveLink)) { setLiveLinkError('Invalid URL') } }} returnKeyType="done" autoCapitalize="none" autoComplete="url" autoCorrect={false} /> {(liveLinkError || linkMetaError) && ( {liveLinkError ? ( This is not a valid link ) : ( cleanError(linkMetaError) )} )} {hasLink && ( Go live for {displayDuration(i18n, duration)} {' '} {time(duration)} { const label = displayDuration(i18n, item) return ( {label} {' '} {time(item)} ) }} items={DURATIONS} valueExtractor={d => String(d)} /> )} {goLiveError && ( {cleanError(goLiveError)} )} {hasLink && ( )} ) }