import React from 'react' import {View} from 'react-native' import {AtUri} from '@atproto/api' import {msg, Plural, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useHaptics} from '#/lib/haptics' import {makeProfileLink} from '#/lib/routes/links' import {makeCustomFeedLink} from '#/lib/routes/links' import {shareUrl} from '#/lib/sharing' import {sanitizeHandle} from '#/lib/strings/handles' import {toShareUrl} from '#/lib/strings/url-helpers' import {logger} from '#/logger' import {isWeb} from '#/platform/detection' import {FeedSourceFeedInfo} from '#/state/queries/feed' import {useLikeMutation, useUnlikeMutation} from '#/state/queries/like' import { useAddSavedFeedsMutation, usePreferencesQuery, useRemoveFeedMutation, useUpdateSavedFeedsMutation, } from '#/state/queries/preferences' import {useSession} from '#/state/session' import {formatCount} from '#/view/com/util/numeric/format' import * as Toast from '#/view/com/util/Toast' import {UserAvatar} from '#/view/com/util/UserAvatar' import {atoms as a, useBreakpoints, useTheme, web} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' import {Divider} from '#/components/Divider' import {useRichText} from '#/components/hooks/useRichText' import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox' import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' import {DotGrid_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid' import { Heart2_Filled_Stroke2_Corner0_Rounded as HeartFilled, Heart2_Stroke2_Corner0_Rounded as Heart, } from '#/components/icons/Heart2' import { Pin_Filled_Corner0_Rounded as PinFilled, Pin_Stroke2_Corner0_Rounded as Pin, } from '#/components/icons/Pin' import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash' import * as Layout from '#/components/Layout' import {InlineLinkText} from '#/components/Link' import * as Menu from '#/components/Menu' import { ReportDialog, useReportDialogControl, } from '#/components/moderation/ReportDialog' import {RichText} from '#/components/RichText' import {Text} from '#/components/Typography' export function ProfileFeedHeaderSkeleton() { const t = useTheme() return ( ) } export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) { const t = useTheme() const {_, i18n} = useLingui() const {hasSession} = useSession() const {gtMobile} = useBreakpoints() const infoControl = Dialog.useDialogControl() const playHaptic = useHaptics() const {data: preferences} = usePreferencesQuery() const [likeUri, setLikeUri] = React.useState(info.likeUri || '') const isLiked = !!likeUri const likeCount = isLiked && likeUri ? (info.likeCount || 0) + 1 : info.likeCount || 0 const {mutateAsync: addSavedFeeds, isPending: isAddSavedFeedPending} = useAddSavedFeedsMutation() const {mutateAsync: removeFeed, isPending: isRemovePending} = useRemoveFeedMutation() const {mutateAsync: updateSavedFeeds, isPending: isUpdateFeedPending} = useUpdateSavedFeedsMutation() const isFeedStateChangePending = isAddSavedFeedPending || isRemovePending || isUpdateFeedPending const savedFeedConfig = preferences?.savedFeeds?.find( f => f.value === info.uri, ) const isSaved = Boolean(savedFeedConfig) const isPinned = Boolean(savedFeedConfig?.pinned) const onToggleSaved = React.useCallback(async () => { try { playHaptic() if (savedFeedConfig) { await removeFeed(savedFeedConfig) Toast.show(_(msg`Removed from your feeds`)) } else { await addSavedFeeds([ { type: 'feed', value: info.uri, pinned: false, }, ]) Toast.show(_(msg`Saved to your feeds`)) } } catch (err) { Toast.show( _( msg`There was an issue updating your feeds, please check your internet connection and try again.`, ), 'xmark', ) logger.error('Failed to update feeds', {message: err}) } }, [_, playHaptic, info, removeFeed, addSavedFeeds, savedFeedConfig]) const onTogglePinned = React.useCallback(async () => { try { playHaptic() if (savedFeedConfig) { const pinned = !savedFeedConfig.pinned await updateSavedFeeds([ { ...savedFeedConfig, pinned, }, ]) if (pinned) { Toast.show(_(msg`Pinned ${info.displayName} to Home`)) } else { Toast.show(_(msg`Unpinned ${info.displayName} from Home`)) } } else { await addSavedFeeds([ { type: 'feed', value: info.uri, pinned: true, }, ]) Toast.show(_(msg`Pinned ${info.displayName} to Home`)) } } catch (e) { Toast.show(_(msg`There was an issue contacting the server`), 'xmark') logger.error('Failed to toggle pinned feed', {message: e}) } }, [playHaptic, info, _, savedFeedConfig, updateSavedFeeds, addSavedFeeds]) return ( <> {hasSession && ( {isPinned ? ( {({props}) => { return ( ) }} {_(msg`Unpin from home`)} {isSaved ? _(msg`Remove from my feeds`) : _(msg`Save to my feeds`)} ) : ( )} )} ) } function DialogInner({ info, likeUri, setLikeUri, likeCount, isPinned, onTogglePinned, isFeedStateChangePending, }: { info: FeedSourceFeedInfo likeUri: string setLikeUri: (uri: string) => void likeCount: number isPinned: boolean onTogglePinned: () => void isFeedStateChangePending: boolean }) { const t = useTheme() const {_} = useLingui() const {hasSession} = useSession() const playHaptic = useHaptics() const control = Dialog.useDialogContext() const reportDialogControl = useReportDialogControl() const [rt] = useRichText(info.description.text) const {mutateAsync: likeFeed, isPending: isLikePending} = useLikeMutation() const {mutateAsync: unlikeFeed, isPending: isUnlikePending} = useUnlikeMutation() const isLiked = !!likeUri const feedRkey = React.useMemo(() => new AtUri(info.uri).rkey, [info.uri]) const onToggleLiked = React.useCallback(async () => { try { playHaptic() if (isLiked && likeUri) { await unlikeFeed({uri: likeUri}) setLikeUri('') } else { const res = await likeFeed({uri: info.uri, cid: info.cid}) setLikeUri(res.uri) } } catch (err) { Toast.show( _( msg`There was an issue contacting the server, please check your internet connection and try again.`, ), 'xmark', ) logger.error('Failed to toggle like', {message: err}) } }, [playHaptic, isLiked, likeUri, unlikeFeed, setLikeUri, likeFeed, info, _]) const onPressShare = React.useCallback(() => { playHaptic() const url = toShareUrl(info.route.href) shareUrl(url) }, [info, playHaptic]) const onPressReport = React.useCallback(() => { reportDialogControl.open() }, [reportDialogControl]) return ( {info.displayName} By{' '} control.close()}> {sanitizeHandle(info.creatorHandle, '@')} {typeof likeCount === 'number' && ( control.close()}> Liked by )} {hasSession && ( <> Something wrong? Let us know. {info.view && ( )} )} ) }