about summary refs log tree commit diff
path: root/src/view/com/feeds/FeedSourceCard.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/feeds/FeedSourceCard.tsx')
-rw-r--r--src/view/com/feeds/FeedSourceCard.tsx144
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: {