import React from 'react' import { Linking, Pressable, StyleProp, StyleSheet, View, ViewStyle, } from 'react-native' import {AtUri} from '@atproto/api' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {msg, Plural, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {logger} from '#/logger' import {FeedSourceInfo, useFeedSourceInfoQuery} from '#/state/queries/feed' import { useAddSavedFeedsMutation, usePreferencesQuery, UsePreferencesQueryResponse, useRemoveFeedMutation, } from '#/state/queries/preferences' import {useNavigationDeduped} from 'lib/hooks/useNavigationDeduped' import {usePalette} from 'lib/hooks/usePalette' import {sanitizeHandle} from 'lib/strings/handles' import {s} from 'lib/styles' import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' import * as Toast from 'view/com/util/Toast' import {useTheme} from '#/alf' import {atoms as a} from '#/alf' import * as Prompt from '#/components/Prompt' import {RichText} from '#/components/RichText' import {Text} from '../util/text/Text' import {UserAvatar} from '../util/UserAvatar' import hairlineWidth = StyleSheet.hairlineWidth import {shouldClickOpenNewTab} from '#/platform/urls' export function FeedSourceCard({ feedUri, style, showSaveBtn = false, showDescription = false, showLikes = false, pinOnSave = false, showMinimalPlaceholder, hideTopBorder, }: { feedUri: string style?: StyleProp showSaveBtn?: boolean showDescription?: boolean showLikes?: boolean pinOnSave?: boolean showMinimalPlaceholder?: boolean hideTopBorder?: boolean }) { const {data: preferences} = usePreferencesQuery() const {data: feed} = useFeedSourceInfoQuery({uri: feedUri}) return ( ) } export function FeedSourceCardLoaded({ feedUri, feed, preferences, style, showSaveBtn = false, showDescription = false, showLikes = false, pinOnSave = false, showMinimalPlaceholder, hideTopBorder, }: { feedUri: string feed?: FeedSourceInfo preferences?: UsePreferencesQueryResponse style?: StyleProp showSaveBtn?: boolean showDescription?: boolean showLikes?: boolean pinOnSave?: boolean showMinimalPlaceholder?: boolean hideTopBorder?: boolean }) { const t = useTheme() const pal = usePalette('default') const {_} = useLingui() const removePromptControl = Prompt.usePromptControl() const navigation = useNavigationDeduped() const {isPending: isAddSavedFeedPending, mutateAsync: addSavedFeeds} = useAddSavedFeedsMutation() const {isPending: isRemovePending, mutateAsync: removeFeed} = useRemoveFeedMutation() const savedFeedConfig = preferences?.savedFeeds?.find( f => f.value === feedUri, ) const isSaved = Boolean(savedFeedConfig) const onSave = React.useCallback(async () => { if (!feed || isSaved) return try { await addSavedFeeds([ { type: 'feed', value: feed.uri, pinned: pinOnSave, }, ]) Toast.show(_(msg`Added to my feeds`)) } catch (e) { Toast.show(_(msg`There was an issue contacting your server`)) logger.error('Failed to save feed', {message: e}) } }, [_, feed, pinOnSave, addSavedFeeds, isSaved]) const onUnsave = React.useCallback(async () => { if (!savedFeedConfig) return try { await removeFeed(savedFeedConfig) // await item.unsave() Toast.show(_(msg`Removed from my feeds`)) } catch (e) { Toast.show(_(msg`There was an issue contacting your server`)) logger.error('Failed to unsave feed', {message: e}) } }, [_, removeFeed, savedFeedConfig]) const onToggleSaved = React.useCallback(async () => { if (isSaved) { removePromptControl.open() } else { await onSave() } }, [isSaved, removePromptControl, onSave]) /* * LOAD STATE * * This state also captures the scenario where a feed can't load for whatever * reason. */ if (!feed || !preferences) return ( {showMinimalPlaceholder ? ( ) : ( )} {showSaveBtn && ( )} ) return ( <> { const shouldOpenInNewTab = shouldClickOpenNewTab(e) if (feed.type === 'feed') { if (shouldOpenInNewTab) { Linking.openURL( `/profile/${feed.creatorDid}/feed/${new AtUri(feed.uri).rkey}`, ) } else { navigation.push('ProfileFeed', { name: feed.creatorDid, rkey: new AtUri(feed.uri).rkey, }) } } else if (feed.type === 'list') { if (shouldOpenInNewTab) { Linking.openURL( `/profile/${feed.creatorDid}/lists/${new AtUri(feed.uri).rkey}`, ) } else { navigation.push('ProfileList', { name: feed.creatorDid, rkey: new AtUri(feed.uri).rkey, }) } } }} key={feed.uri}> {feed.displayName} {feed.type === 'feed' ? ( Feed by {sanitizeHandle(feed.creatorHandle, '@')} ) : ( List by {sanitizeHandle(feed.creatorHandle, '@')} )} {showSaveBtn && ( {isSaved ? ( ) : ( )} )} {showDescription && feed.description ? ( ) : null} {showLikes && feed.type === 'feed' ? ( ) : null} ) } const styles = StyleSheet.create({ container: { paddingHorizontal: 18, paddingVertical: 20, flexDirection: 'column', flex: 1, gap: 14, }, headerContainer: { flexDirection: 'row', }, headerTextContainer: { flexDirection: 'column', columnGap: 4, flex: 1, }, description: { flex: 1, flexWrap: 'wrap', }, btn: { paddingVertical: 6, }, })