about summary refs log tree commit diff
path: root/src/view/com/notifications/FeedItem.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/notifications/FeedItem.tsx')
-rw-r--r--src/view/com/notifications/FeedItem.tsx185
1 files changed, 91 insertions, 94 deletions
diff --git a/src/view/com/notifications/FeedItem.tsx b/src/view/com/notifications/FeedItem.tsx
index c38ab3fd5..aaa2ea2c6 100644
--- a/src/view/com/notifications/FeedItem.tsx
+++ b/src/view/com/notifications/FeedItem.tsx
@@ -1,5 +1,4 @@
-import React, {useMemo, useState, useEffect} from 'react'
-import {observer} from 'mobx-react-lite'
+import React, {memo, useMemo, useState, useEffect} from 'react'
 import {
   Animated,
   TouchableOpacity,
@@ -9,6 +8,9 @@ import {
 } from 'react-native'
 import {
   AppBskyEmbedImages,
+  AppBskyFeedDefs,
+  AppBskyFeedPost,
+  ModerationOpts,
   ProfileModeration,
   moderateProfile,
   AppBskyEmbedRecordWithMedia,
@@ -19,8 +21,7 @@ import {
   FontAwesomeIconStyle,
   Props,
 } from '@fortawesome/react-native-fontawesome'
-import {NotificationsFeedItemModel} from 'state/models/feeds/notifications'
-import {PostThreadModel} from 'state/models/content/post-thread'
+import {FeedNotification} from '#/state/queries/notifications/feed'
 import {s, colors} from 'lib/styles'
 import {niceDate} from 'lib/strings/time'
 import {sanitizeDisplayName} from 'lib/strings/display-names'
@@ -33,13 +34,14 @@ import {UserPreviewLink} from '../util/UserPreviewLink'
 import {ImageHorzList} from '../util/images/ImageHorzList'
 import {Post} from '../post/Post'
 import {Link, TextLink} from '../util/Link'
-import {useStores} from 'state/index'
 import {usePalette} from 'lib/hooks/usePalette'
 import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'
 import {formatCount} from '../util/numeric/format'
 import {makeProfileLink} from 'lib/routes/links'
 import {TimeElapsed} from '../util/TimeElapsed'
 import {isWeb} from 'platform/detection'
+import {Trans, msg} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
 
 const MAX_AUTHORS = 5
 
@@ -54,40 +56,34 @@ interface Author {
   moderation: ProfileModeration
 }
 
-export const FeedItem = observer(function FeedItemImpl({
+let FeedItem = ({
   item,
+  moderationOpts,
 }: {
-  item: NotificationsFeedItemModel
-}) {
-  const store = useStores()
+  item: FeedNotification
+  moderationOpts: ModerationOpts
+}): React.ReactNode => {
   const pal = usePalette('default')
   const [isAuthorsExpanded, setAuthorsExpanded] = useState<boolean>(false)
   const itemHref = useMemo(() => {
-    if (item.isLike || item.isRepost) {
-      const urip = new AtUri(item.subjectUri)
-      return `/profile/${urip.host}/post/${urip.rkey}`
-    } else if (item.isFollow) {
-      return makeProfileLink(item.author)
-    } else if (item.isReply) {
-      const urip = new AtUri(item.uri)
+    if (item.type === 'post-like' || item.type === 'repost') {
+      if (item.subjectUri) {
+        const urip = new AtUri(item.subjectUri)
+        return `/profile/${urip.host}/post/${urip.rkey}`
+      }
+    } else if (item.type === 'follow') {
+      return makeProfileLink(item.notification.author)
+    } else if (item.type === 'reply') {
+      const urip = new AtUri(item.notification.uri)
       return `/profile/${urip.host}/post/${urip.rkey}`
-    } else if (item.isCustomFeedLike) {
-      const urip = new AtUri(item.subjectUri)
-      return `/profile/${urip.host}/feed/${urip.rkey}`
+    } else if (item.type === 'feedgen-like') {
+      if (item.subjectUri) {
+        const urip = new AtUri(item.subjectUri)
+        return `/profile/${urip.host}/feed/${urip.rkey}`
+      }
     }
     return ''
   }, [item])
-  const itemTitle = useMemo(() => {
-    if (item.isLike || item.isRepost) {
-      return 'Post'
-    } else if (item.isFollow) {
-      return item.author.handle
-    } else if (item.isReply) {
-      return 'Post'
-    } else if (item.isCustomFeedLike) {
-      return 'Custom Feed'
-    }
-  }, [item])
 
   const onToggleAuthorsExpanded = () => {
     setAuthorsExpanded(currentlyExpanded => !currentlyExpanded)
@@ -96,15 +92,12 @@ export const FeedItem = observer(function FeedItemImpl({
   const authors: Author[] = useMemo(() => {
     return [
       {
-        href: makeProfileLink(item.author),
-        did: item.author.did,
-        handle: item.author.handle,
-        displayName: item.author.displayName,
-        avatar: item.author.avatar,
-        moderation: moderateProfile(
-          item.author,
-          store.preferences.moderationOpts,
-        ),
+        href: makeProfileLink(item.notification.author),
+        did: item.notification.author.did,
+        handle: item.notification.author.handle,
+        displayName: item.notification.author.displayName,
+        avatar: item.notification.author.avatar,
+        moderation: moderateProfile(item.notification.author, moderationOpts),
       },
       ...(item.additional?.map(({author}) => {
         return {
@@ -113,33 +106,35 @@ export const FeedItem = observer(function FeedItemImpl({
           handle: author.handle,
           displayName: author.displayName,
           avatar: author.avatar,
-          moderation: moderateProfile(author, store.preferences.moderationOpts),
+          moderation: moderateProfile(author, moderationOpts),
         }
       }) || []),
     ]
-  }, [store, item.additional, item.author])
+  }, [item, moderationOpts])
 
-  if (item.additionalPost?.notFound) {
+  if (item.subjectUri && !item.subject) {
     // don't render anything if the target post was deleted or unfindable
     return <View />
   }
 
-  if (item.isReply || item.isMention || item.isQuote) {
-    if (!item.additionalPost || item.additionalPost?.error) {
-      // hide errors - it doesnt help the user to show them
-      return <View />
+  if (
+    item.type === 'reply' ||
+    item.type === 'mention' ||
+    item.type === 'quote'
+  ) {
+    if (!item.subject) {
+      return null
     }
     return (
       <Link
-        testID={`feedItem-by-${item.author.handle}`}
+        testID={`feedItem-by-${item.notification.author.handle}`}
         href={itemHref}
-        title={itemTitle}
         noFeedback
         accessible={false}>
         <Post
-          view={item.additionalPost}
+          post={item.subject}
           style={
-            item.isRead
+            item.notification.isRead
               ? undefined
               : {
                   backgroundColor: pal.colors.unreadNotifBg,
@@ -154,23 +149,25 @@ export const FeedItem = observer(function FeedItemImpl({
   let action = ''
   let icon: Props['icon'] | 'HeartIconSolid'
   let iconStyle: Props['style'] = []
-  if (item.isLike) {
+  if (item.type === 'post-like') {
     action = 'liked your post'
     icon = 'HeartIconSolid'
     iconStyle = [
       s.likeColor as FontAwesomeIconStyle,
       {position: 'relative', top: -4},
     ]
-  } else if (item.isRepost) {
+  } else if (item.type === 'repost') {
     action = 'reposted your post'
     icon = 'retweet'
     iconStyle = [s.green3 as FontAwesomeIconStyle]
-  } else if (item.isFollow) {
+  } else if (item.type === 'follow') {
     action = 'followed you'
     icon = 'user-plus'
     iconStyle = [s.blue3 as FontAwesomeIconStyle]
-  } else if (item.isCustomFeedLike) {
-    action = `liked your custom feed '${new AtUri(item.subjectUri).rkey}'`
+  } else if (item.type === 'feedgen-like') {
+    action = `liked your custom feed${
+      item.subjectUri ? ` '${new AtUri(item.subjectUri).rkey}}'` : ''
+    }`
     icon = 'HeartIconSolid'
     iconStyle = [
       s.likeColor as FontAwesomeIconStyle,
@@ -182,12 +179,12 @@ export const FeedItem = observer(function FeedItemImpl({
 
   return (
     <Link
-      testID={`feedItem-by-${item.author.handle}`}
+      testID={`feedItem-by-${item.notification.author.handle}`}
       style={[
         styles.outer,
         pal.view,
         pal.border,
-        item.isRead
+        item.notification.isRead
           ? undefined
           : {
               backgroundColor: pal.colors.unreadNotifBg,
@@ -195,9 +192,11 @@ export const FeedItem = observer(function FeedItemImpl({
             },
       ]}
       href={itemHref}
-      title={itemTitle}
       noFeedback
-      accessible={(item.isLike && authors.length === 1) || item.isRepost}>
+      accessible={
+        (item.type === 'post-like' && authors.length === 1) ||
+        item.type === 'repost'
+      }>
       <View style={styles.layoutIcon}>
         {/* TODO: Prevent conditional rendering and move toward composable
         notifications for clearer accessibility labeling */}
@@ -232,7 +231,10 @@ export const FeedItem = observer(function FeedItemImpl({
             />
             {authors.length > 1 ? (
               <>
-                <Text style={[pal.text]}> and </Text>
+                <Text style={[pal.text, s.mr5, s.ml5]}>
+                  {' '}
+                  <Trans>and</Trans>{' '}
+                </Text>
                 <Text style={[pal.text, s.bold]}>
                   {formatCount(authors.length - 1)}{' '}
                   {pluralize(authors.length - 1, 'other')}
@@ -240,24 +242,26 @@ export const FeedItem = observer(function FeedItemImpl({
               </>
             ) : undefined}
             <Text style={[pal.text]}> {action}</Text>
-            <TimeElapsed timestamp={item.indexedAt}>
+            <TimeElapsed timestamp={item.notification.indexedAt}>
               {({timeElapsed}) => (
                 <Text
                   style={[pal.textLight, styles.pointer]}
-                  title={niceDate(item.indexedAt)}>
+                  title={niceDate(item.notification.indexedAt)}>
                   {' ' + timeElapsed}
                 </Text>
               )}
             </TimeElapsed>
           </Text>
         </ExpandListPressable>
-        {item.isLike || item.isRepost || item.isQuote ? (
-          <AdditionalPostText additionalPost={item.additionalPost} />
+        {item.type === 'post-like' || item.type === 'repost' ? (
+          <AdditionalPostText post={item.subject} />
         ) : null}
       </View>
     </Link>
   )
-})
+}
+FeedItem = memo(FeedItem)
+export {FeedItem}
 
 function ExpandListPressable({
   hasMultipleAuthors,
@@ -292,6 +296,8 @@ function CondensedAuthorsList({
   onToggleAuthorsExpanded: () => void
 }) {
   const pal = usePalette('default')
+  const {_} = useLingui()
+
   if (!visible) {
     return (
       <View style={styles.avis}>
@@ -299,7 +305,7 @@ function CondensedAuthorsList({
           style={styles.expandedAuthorsCloseBtn}
           onPress={onToggleAuthorsExpanded}
           accessibilityRole="button"
-          accessibilityLabel="Hide user list"
+          accessibilityLabel={_(msg`Hide user list`)}
           accessibilityHint="Collapses list of users for a given notification">
           <FontAwesomeIcon
             icon="angle-up"
@@ -307,7 +313,7 @@ function CondensedAuthorsList({
             style={[styles.expandedAuthorsCloseBtnIcon, pal.text]}
           />
           <Text type="sm-medium" style={pal.text}>
-            Hide
+            <Trans>Hide</Trans>
           </Text>
         </TouchableOpacity>
       </View>
@@ -328,7 +334,7 @@ function CondensedAuthorsList({
   }
   return (
     <TouchableOpacity
-      accessibilityLabel="Show users"
+      accessibilityLabel={_(msg`Show users`)}
       accessibilityHint="Opens an expanded list of users in this notification"
       onPress={onToggleAuthorsExpanded}>
       <View style={styles.avis}>
@@ -417,34 +423,25 @@ function ExpandedAuthorsList({
   )
 }
 
-function AdditionalPostText({
-  additionalPost,
-}: {
-  additionalPost?: PostThreadModel
-}) {
+function AdditionalPostText({post}: {post?: AppBskyFeedDefs.PostView}) {
   const pal = usePalette('default')
-  if (
-    !additionalPost ||
-    !additionalPost.thread?.postRecord ||
-    additionalPost.error
-  ) {
-    return <View />
+  if (post && AppBskyFeedPost.isRecord(post?.record)) {
+    const text = post.record.text
+    const images = AppBskyEmbedImages.isView(post.embed)
+      ? post.embed.images
+      : AppBskyEmbedRecordWithMedia.isView(post.embed) &&
+        AppBskyEmbedImages.isView(post.embed.media)
+      ? post.embed.media.images
+      : undefined
+    return (
+      <>
+        {text?.length > 0 && <Text style={pal.textLight}>{text}</Text>}
+        {images && images?.length > 0 && (
+          <ImageHorzList images={images} style={styles.additionalPostImages} />
+        )}
+      </>
+    )
   }
-  const text = additionalPost.thread?.postRecord.text
-  const images = AppBskyEmbedImages.isView(additionalPost.thread.post.embed)
-    ? additionalPost.thread.post.embed.images
-    : AppBskyEmbedRecordWithMedia.isView(additionalPost.thread.post.embed) &&
-      AppBskyEmbedImages.isView(additionalPost.thread.post.embed.media)
-    ? additionalPost.thread.post.embed.media.images
-    : undefined
-  return (
-    <>
-      {text?.length > 0 && <Text style={pal.textLight}>{text}</Text>}
-      {images && images?.length > 0 && (
-        <ImageHorzList images={images} style={styles.additionalPostImages} />
-      )}
-    </>
-  )
 }
 
 const styles = StyleSheet.create({