diff options
Diffstat (limited to 'src/view/com/feeds/FeedSourceCard.tsx')
-rw-r--r-- | src/view/com/feeds/FeedSourceCard.tsx | 144 |
1 files changed, 110 insertions, 34 deletions
diff --git a/src/view/com/feeds/FeedSourceCard.tsx b/src/view/com/feeds/FeedSourceCard.tsx index 2c4335dc1..1f2af069b 100644 --- a/src/view/com/feeds/FeedSourceCard.tsx +++ b/src/view/com/feeds/FeedSourceCard.tsx @@ -6,43 +6,110 @@ import {RichText} from '../util/text/RichText' import {usePalette} from 'lib/hooks/usePalette' import {s} from 'lib/styles' import {UserAvatar} from '../util/UserAvatar' -import {observer} from 'mobx-react-lite' -import {FeedSourceModel} from 'state/models/content/feed-source' import {useNavigation} from '@react-navigation/native' import {NavigationProp} from 'lib/routes/types' -import {useStores} from 'state/index' import {pluralize} from 'lib/strings/helpers' import {AtUri} from '@atproto/api' import * as Toast from 'view/com/util/Toast' import {sanitizeHandle} from 'lib/strings/handles' import {logger} from '#/logger' +import {useModalControls} from '#/state/modals' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import { + usePinFeedMutation, + UsePreferencesQueryResponse, + usePreferencesQuery, + useSaveFeedMutation, + useRemoveFeedMutation, +} from '#/state/queries/preferences' +import {useFeedSourceInfoQuery, FeedSourceInfo} from '#/state/queries/feed' +import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' -export const FeedSourceCard = observer(function FeedSourceCardImpl({ - item, +export function FeedSourceCard({ + feedUri, style, showSaveBtn = false, showDescription = false, showLikes = false, + LoadingComponent, + pinOnSave = false, }: { - item: FeedSourceModel + feedUri: string style?: StyleProp<ViewStyle> showSaveBtn?: boolean showDescription?: boolean showLikes?: boolean + LoadingComponent?: JSX.Element + pinOnSave?: boolean +}) { + const {data: preferences} = usePreferencesQuery() + const {data: feed} = useFeedSourceInfoQuery({uri: feedUri}) + + if (!feed || !preferences) { + return LoadingComponent ? ( + LoadingComponent + ) : ( + <FeedLoadingPlaceholder style={{flex: 1}} /> + ) + } + + return ( + <FeedSourceCardLoaded + feed={feed} + preferences={preferences} + style={style} + showSaveBtn={showSaveBtn} + showDescription={showDescription} + showLikes={showLikes} + pinOnSave={pinOnSave} + /> + ) +} + +export function FeedSourceCardLoaded({ + feed, + preferences, + style, + showSaveBtn = false, + showDescription = false, + showLikes = false, + pinOnSave = false, +}: { + feed: FeedSourceInfo + preferences: UsePreferencesQueryResponse + style?: StyleProp<ViewStyle> + showSaveBtn?: boolean + showDescription?: boolean + showLikes?: boolean + pinOnSave?: boolean }) { - const store = useStores() const pal = usePalette('default') + const {_} = useLingui() const navigation = useNavigation<NavigationProp>() + const {openModal} = useModalControls() + + const {isPending: isSavePending, mutateAsync: saveFeed} = + useSaveFeedMutation() + const {isPending: isRemovePending, mutateAsync: removeFeed} = + useRemoveFeedMutation() + const {isPending: isPinPending, mutateAsync: pinFeed} = usePinFeedMutation() + + const isSaved = Boolean(preferences?.feeds?.saved?.includes(feed.uri)) const onToggleSaved = React.useCallback(async () => { - if (item.isSaved) { - store.shell.openModal({ + // Only feeds can be un/saved, lists are handled elsewhere + if (feed?.type !== 'feed') return + + if (isSaved) { + openModal({ name: 'confirm', - title: 'Remove from my feeds', - message: `Remove ${item.displayName} from my feeds?`, + title: _(msg`Remove from my feeds`), + message: _(msg`Remove ${feed.displayName} from my feeds?`), onPressConfirm: async () => { try { - await item.unsave() + await removeFeed({uri: feed.uri}) + // await item.unsave() Toast.show('Removed from my feeds') } catch (e) { Toast.show('There was an issue contacting your server') @@ -52,58 +119,67 @@ export const FeedSourceCard = observer(function FeedSourceCardImpl({ }) } else { try { - await item.save() + if (pinOnSave) { + await pinFeed({uri: feed.uri}) + } else { + await saveFeed({uri: feed.uri}) + } Toast.show('Added to my feeds') } catch (e) { Toast.show('There was an issue contacting your server') logger.error('Failed to save feed', {error: e}) } } - }, [store, item]) + }, [isSaved, openModal, feed, removeFeed, saveFeed, _, pinOnSave, pinFeed]) + + if (!feed || !preferences) return null return ( <Pressable - testID={`feed-${item.displayName}`} + testID={`feed-${feed.displayName}`} accessibilityRole="button" style={[styles.container, pal.border, style]} onPress={() => { - if (item.type === 'feed-generator') { + if (feed.type === 'feed') { navigation.push('ProfileFeed', { - name: item.creatorDid, - rkey: new AtUri(item.uri).rkey, + name: feed.creatorDid, + rkey: new AtUri(feed.uri).rkey, }) - } else if (item.type === 'list') { + } else if (feed.type === 'list') { navigation.push('ProfileList', { - name: item.creatorDid, - rkey: new AtUri(item.uri).rkey, + name: feed.creatorDid, + rkey: new AtUri(feed.uri).rkey, }) } }} - key={item.uri}> + key={feed.uri}> <View style={[styles.headerContainer]}> <View style={[s.mr10]}> - <UserAvatar type="algo" size={36} avatar={item.avatar} /> + <UserAvatar type="algo" size={36} avatar={feed.avatar} /> </View> <View style={[styles.headerTextContainer]}> <Text style={[pal.text, s.bold]} numberOfLines={3}> - {item.displayName} + {feed.displayName} </Text> <Text style={[pal.textLight]} numberOfLines={3}> - by {sanitizeHandle(item.creatorHandle, '@')} + {feed.type === 'feed' ? 'Feed' : 'List'} by{' '} + {sanitizeHandle(feed.creatorHandle, '@')} </Text> </View> - {showSaveBtn && ( + + {showSaveBtn && feed.type === 'feed' && ( <View> <Pressable + disabled={isSavePending || isPinPending || isRemovePending} accessibilityRole="button" accessibilityLabel={ - item.isSaved ? 'Remove from my feeds' : 'Add to my feeds' + isSaved ? 'Remove from my feeds' : 'Add to my feeds' } accessibilityHint="" onPress={onToggleSaved} hitSlop={15} style={styles.btn}> - {item.isSaved ? ( + {isSaved ? ( <FontAwesomeIcon icon={['far', 'trash-can']} size={19} @@ -121,23 +197,23 @@ export const FeedSourceCard = observer(function FeedSourceCardImpl({ )} </View> - {showDescription && item.descriptionRT ? ( + {showDescription && feed.description ? ( <RichText style={[pal.textLight, styles.description]} - richText={item.descriptionRT} + richText={feed.description} numberOfLines={3} /> ) : null} - {showLikes ? ( + {showLikes && feed.type === 'feed' ? ( <Text type="sm-medium" style={[pal.text, pal.textLight]}> - Liked by {item.likeCount || 0}{' '} - {pluralize(item.likeCount || 0, 'user')} + Liked by {feed.likeCount || 0}{' '} + {pluralize(feed.likeCount || 0, 'user')} </Text> ) : null} </Pressable> ) -}) +} const styles = StyleSheet.create({ container: { |