import React from 'react' import {GestureResponderEvent, View} from 'react-native' import { AppBskyActorDefs, AppBskyFeedDefs, AppBskyGraphDefs, AtUri, RichText as RichTextApi, } from '@atproto/api' import {msg, plural, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useQueryClient} from '@tanstack/react-query' import {logger} from '#/logger' import { useAddSavedFeedsMutation, usePreferencesQuery, useRemoveFeedMutation, } from '#/state/queries/preferences' import {sanitizeHandle} from 'lib/strings/handles' import {precacheFeedFromGeneratorView} from 'state/queries/feed' import {useSession} from 'state/session' import {UserAvatar} from '#/view/com/util/UserAvatar' import * as Toast from 'view/com/util/Toast' import {useTheme} from '#/alf' import {atoms as a} from '#/alf' import {Button, ButtonIcon} from '#/components/Button' import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash' import {Link as InternalLink, LinkProps} from '#/components/Link' import {Loader} from '#/components/Loader' import * as Prompt from '#/components/Prompt' import {RichText, RichTextProps} from '#/components/RichText' import {Text} from '#/components/Typography' type Props = { view: AppBskyFeedDefs.GeneratorView } export function Default(props: Props) { const {view} = props return (
) } export function Link({ view, children, ...props }: Props & Omit) { const queryClient = useQueryClient() const href = React.useMemo(() => { return createProfileFeedHref({feed: view}) }, [view]) React.useEffect(() => { precacheFeedFromGeneratorView(queryClient, view) }, [view, queryClient]) return ( {children} ) } export function Outer({children}: {children: React.ReactNode}) { return {children} } export function Header({children}: {children: React.ReactNode}) { return {children} } export type AvatarProps = {src: string | undefined; size?: number} export function Avatar({src, size = 40}: AvatarProps) { return } export function AvatarPlaceholder({size = 40}: Omit) { const t = useTheme() return ( ) } export function TitleAndByline({ title, creator, }: { title: string creator?: AppBskyActorDefs.ProfileViewBasic }) { const t = useTheme() return ( {title} {creator && ( Feed by {sanitizeHandle(creator.handle, '@')} )} ) } export function TitleAndBylinePlaceholder({creator}: {creator?: boolean}) { const t = useTheme() return ( {creator && ( )} ) } export function Description({ description, ...rest }: {description?: string} & Partial) { const rt = React.useMemo(() => { if (!description) return const rt = new RichTextApi({text: description || ''}) rt.detectFacetsWithoutResolution() return rt }, [description]) if (!rt) return null return } export function DescriptionPlaceholder() { const t = useTheme() return ( ) } export function Likes({count}: {count: number}) { const t = useTheme() return ( {plural(count || 0, { one: 'Liked by # user', other: 'Liked by # users', })} ) } export function SaveButton({ view, pin, }: { view: AppBskyFeedDefs.GeneratorView | AppBskyGraphDefs.ListView pin?: boolean }) { const {hasSession} = useSession() if (!hasSession) return null return } function SaveButtonInner({ view, pin, }: { view: AppBskyFeedDefs.GeneratorView | AppBskyGraphDefs.ListView pin?: boolean }) { const {_} = useLingui() const {data: preferences} = usePreferencesQuery() const {isPending: isAddSavedFeedPending, mutateAsync: saveFeeds} = useAddSavedFeedsMutation() const {isPending: isRemovePending, mutateAsync: removeFeed} = useRemoveFeedMutation() const uri = view.uri const type = view.uri.includes('app.bsky.feed.generator') ? 'feed' : 'list' const savedFeedConfig = React.useMemo(() => { return preferences?.savedFeeds?.find(feed => feed.value === uri) }, [preferences?.savedFeeds, uri]) const removePromptControl = Prompt.usePromptControl() const isPending = isAddSavedFeedPending || isRemovePending const toggleSave = React.useCallback( async (e: GestureResponderEvent) => { e.preventDefault() e.stopPropagation() try { if (savedFeedConfig) { await removeFeed(savedFeedConfig) } else { await saveFeeds([ { type, value: uri, pinned: pin || false, }, ]) } Toast.show(_(msg`Feeds updated!`)) } catch (e: any) { logger.error(e, {context: `FeedCard: failed to update feeds`, pin}) Toast.show(_(msg`Failed to update feeds`)) } }, [_, pin, saveFeeds, removeFeed, uri, savedFeedConfig, type], ) const onPrompRemoveFeed = React.useCallback( async (e: GestureResponderEvent) => { e.preventDefault() e.stopPropagation() removePromptControl.open() }, [removePromptControl], ) return ( <> ) } export function createProfileFeedHref({ feed, }: { feed: AppBskyFeedDefs.GeneratorView }) { const urip = new AtUri(feed.uri) const handleOrDid = feed.creator.handle || feed.creator.did return `/profile/${handleOrDid}/feed/${urip.rkey}` }