about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/view/com/post-thread/PostThreadItem.tsx4
-rw-r--r--src/view/com/posts/FeedItem.tsx123
-rw-r--r--src/view/com/util/PostMeta.tsx6
-rw-r--r--src/view/com/util/UserAvatar.tsx26
-rw-r--r--src/view/com/util/forms/PostDropdownBtn.tsx38
-rw-r--r--src/view/com/util/post-ctrls/PostCtrls.tsx42
-rw-r--r--src/view/com/util/post-ctrls/RepostButton.tsx8
7 files changed, 160 insertions, 87 deletions
diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx
index 2636fdfbd..8d1211707 100644
--- a/src/view/com/post-thread/PostThreadItem.tsx
+++ b/src/view/com/post-thread/PostThreadItem.tsx
@@ -328,7 +328,9 @@ let PostThreadItemLoaded = ({
             </View>
             <PostDropdownBtn
               testID="postDropdownBtn"
-              post={post}
+              postAuthor={post.author}
+              postCid={post.cid}
+              postUri={post.uri}
               record={record}
               style={{
                 paddingVertical: 6,
diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx
index f054a40f2..20d199745 100644
--- a/src/view/com/posts/FeedItem.tsx
+++ b/src/view/com/posts/FeedItem.tsx
@@ -102,10 +102,6 @@ let FeedItemInner = ({
 }): React.ReactNode => {
   const {openComposer} = useComposerControls()
   const pal = usePalette('default')
-  const [limitLines, setLimitLines] = useState(
-    () => countLines(richText.text) >= MAX_POST_LINES,
-  )
-
   const href = useMemo(() => {
     const urip = new AtUri(post.uri)
     return makeProfileLink(post.author, 'post', urip.rkey)
@@ -134,10 +130,6 @@ let FeedItemInner = ({
     })
   }, [post, record, openComposer])
 
-  const onPressShowMore = React.useCallback(() => {
-    setLimitLines(false)
-  }, [setLimitLines])
-
   const outerStyles = [
     styles.outer,
     pal.view,
@@ -286,48 +278,12 @@ let FeedItemInner = ({
               </Text>
             </View>
           )}
-          <ContentHider
-            testID="contentHider-post"
-            moderation={moderation.content}
-            ignoreMute
-            childContainerStyle={styles.contentHiderChild}>
-            <PostAlerts moderation={moderation.content} style={styles.alert} />
-            {richText.text ? (
-              <View style={styles.postTextContainer}>
-                <RichText
-                  testID="postText"
-                  type="post-text"
-                  richText={richText}
-                  lineHeight={1.3}
-                  numberOfLines={limitLines ? MAX_POST_LINES : undefined}
-                  style={s.flex1}
-                />
-              </View>
-            ) : undefined}
-            {limitLines ? (
-              <TextLink
-                text="Show More"
-                style={pal.link}
-                onPress={onPressShowMore}
-                href="#"
-              />
-            ) : undefined}
-            {post.embed ? (
-              <ContentHider
-                testID="contentHider-embed"
-                moderation={moderation.embed}
-                moderationDecisions={moderation.decisions}
-                ignoreMute={isEmbedByEmbedder(post.embed, post.author.did)}
-                ignoreQuoteDecisions
-                style={styles.embed}>
-                <PostEmbeds
-                  embed={post.embed}
-                  moderation={moderation.embed}
-                  moderationDecisions={moderation.decisions}
-                />
-              </ContentHider>
-            ) : null}
-          </ContentHider>
+          <PostContent
+            moderation={moderation}
+            richText={richText}
+            postEmbed={post.embed}
+            postAuthor={post.author}
+          />
           <PostCtrls post={post} record={record} onPressReply={onPressReply} />
         </View>
       </View>
@@ -336,6 +292,73 @@ let FeedItemInner = ({
 }
 FeedItemInner = memo(FeedItemInner)
 
+let PostContent = ({
+  moderation,
+  richText,
+  postEmbed,
+  postAuthor,
+}: {
+  moderation: PostModeration
+  richText: RichTextAPI
+  postEmbed: AppBskyFeedDefs.PostView['embed']
+  postAuthor: AppBskyFeedDefs.PostView['author']
+}): React.ReactNode => {
+  const pal = usePalette('default')
+  const [limitLines, setLimitLines] = useState(
+    () => countLines(richText.text) >= MAX_POST_LINES,
+  )
+
+  const onPressShowMore = React.useCallback(() => {
+    setLimitLines(false)
+  }, [setLimitLines])
+
+  return (
+    <ContentHider
+      testID="contentHider-post"
+      moderation={moderation.content}
+      ignoreMute
+      childContainerStyle={styles.contentHiderChild}>
+      <PostAlerts moderation={moderation.content} style={styles.alert} />
+      {richText.text ? (
+        <View style={styles.postTextContainer}>
+          <RichText
+            testID="postText"
+            type="post-text"
+            richText={richText}
+            lineHeight={1.3}
+            numberOfLines={limitLines ? MAX_POST_LINES : undefined}
+            style={s.flex1}
+          />
+        </View>
+      ) : undefined}
+      {limitLines ? (
+        <TextLink
+          text="Show More"
+          style={pal.link}
+          onPress={onPressShowMore}
+          href="#"
+        />
+      ) : undefined}
+      {postEmbed ? (
+        <ContentHider
+          testID="contentHider-embed"
+          moderation={moderation.embed}
+          moderationDecisions={moderation.decisions}
+          ignoreMute={isEmbedByEmbedder(postEmbed, postAuthor.did)}
+          ignoreQuoteDecisions
+          style={styles.embed}>
+          <PostEmbeds
+            embed={postEmbed}
+            moderation={moderation.embed}
+            moderationDecisions={moderation.decisions}
+          />
+        </ContentHider>
+      ) : null}
+    </ContentHider>
+  )
+}
+PostContent = memo(PostContent)
+
 const styles = StyleSheet.create({
   outer: {
     borderTopWidth: 1,
diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx
index fa5f12f6b..eef7094cd 100644
--- a/src/view/com/util/PostMeta.tsx
+++ b/src/view/com/util/PostMeta.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, {memo} from 'react'
 import {StyleProp, StyleSheet, TextStyle, View, ViewStyle} from 'react-native'
 import {Text} from './text/Text'
 import {TextLinkOnWebOnly} from './Link'
@@ -29,7 +29,7 @@ interface PostMetaOpts {
   style?: StyleProp<ViewStyle>
 }
 
-export function PostMeta(opts: PostMetaOpts) {
+let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
   const pal = usePalette('default')
   const displayName = opts.author.displayName || opts.author.handle
   const handle = opts.author.handle
@@ -92,6 +92,8 @@ export function PostMeta(opts: PostMetaOpts) {
     </View>
   )
 }
+PostMeta = memo(PostMeta)
+export {PostMeta}
 
 const styles = StyleSheet.create({
   container: {
diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx
index 395e9eb3a..00ff7e1ec 100644
--- a/src/view/com/util/UserAvatar.tsx
+++ b/src/view/com/util/UserAvatar.tsx
@@ -1,4 +1,4 @@
-import React, {useMemo} from 'react'
+import React, {memo, useMemo} from 'react'
 import {Image, StyleSheet, View} from 'react-native'
 import Svg, {Circle, Rect, Path} from 'react-native-svg'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
@@ -43,13 +43,13 @@ interface PreviewableUserAvatarProps extends BaseUserAvatarProps {
 
 const BLUR_AMOUNT = isWeb ? 5 : 100
 
-export function DefaultAvatar({
+let DefaultAvatar = ({
   type,
   size,
 }: {
   type: UserAvatarType
   size: number
-}) {
+}): React.ReactNode => {
   if (type === 'algo') {
     // Font Awesome Pro 6.4.0 by @fontawesome -https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc.
     return (
@@ -112,14 +112,16 @@ export function DefaultAvatar({
     </Svg>
   )
 }
+DefaultAvatar = memo(DefaultAvatar)
+export {DefaultAvatar}
 
-export function UserAvatar({
+let UserAvatar = ({
   type = 'user',
   size,
   avatar,
   moderation,
   usePlainRNImage = false,
-}: UserAvatarProps) {
+}: UserAvatarProps): React.ReactNode => {
   const pal = usePalette('default')
 
   const aviStyle = useMemo(() => {
@@ -182,13 +184,15 @@ export function UserAvatar({
     </View>
   )
 }
+UserAvatar = memo(UserAvatar)
+export {UserAvatar}
 
-export function EditableUserAvatar({
+let EditableUserAvatar = ({
   type = 'user',
   size,
   avatar,
   onSelectNewAvatar,
-}: EditableUserAvatarProps) {
+}: EditableUserAvatarProps): React.ReactNode => {
   const pal = usePalette('default')
   const {_} = useLingui()
   const {requestCameraAccessIfNeeded} = useCameraPermission()
@@ -323,14 +327,20 @@ export function EditableUserAvatar({
     </NativeDropdown>
   )
 }
+EditableUserAvatar = memo(EditableUserAvatar)
+export {EditableUserAvatar}
 
-export function PreviewableUserAvatar(props: PreviewableUserAvatarProps) {
+let PreviewableUserAvatar = (
+  props: PreviewableUserAvatarProps,
+): React.ReactNode => {
   return (
     <UserPreviewLink did={props.did} handle={props.handle}>
       <UserAvatar {...props} />
     </UserPreviewLink>
   )
 }
+PreviewableUserAvatar = memo(PreviewableUserAvatar)
+export {PreviewableUserAvatar}
 
 const styles = StyleSheet.create({
   editButtonContainer: {
diff --git a/src/view/com/util/forms/PostDropdownBtn.tsx b/src/view/com/util/forms/PostDropdownBtn.tsx
index 63590e92d..8c4b03dd9 100644
--- a/src/view/com/util/forms/PostDropdownBtn.tsx
+++ b/src/view/com/util/forms/PostDropdownBtn.tsx
@@ -1,8 +1,8 @@
-import React from 'react'
+import React, {memo} from 'react'
 import {Linking, StyleProp, View, ViewStyle} from 'react-native'
 import Clipboard from '@react-native-clipboard/clipboard'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {AppBskyFeedDefs, AppBskyFeedPost, AtUri} from '@atproto/api'
+import {AppBskyActorDefs, AppBskyFeedPost, AtUri} from '@atproto/api'
 import {toShareUrl} from 'lib/strings/url-helpers'
 import {useTheme} from 'lib/ThemeContext'
 import {shareUrl} from 'lib/sharing'
@@ -19,23 +19,26 @@ import {usePostDeleteMutation} from '#/state/queries/post'
 import {useMutedThreads, useToggleThreadMute} from '#/state/muted-threads'
 import {useLanguagePrefs} from '#/state/preferences'
 import {logger} from '#/logger'
-import {Shadow} from '#/state/cache/types'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {useSession} from '#/state/session'
 import {isWeb} from '#/platform/detection'
 
-export function PostDropdownBtn({
+let PostDropdownBtn = ({
   testID,
-  post,
+  postAuthor,
+  postCid,
+  postUri,
   record,
   style,
 }: {
   testID: string
-  post: Shadow<AppBskyFeedDefs.PostView>
+  postAuthor: AppBskyActorDefs.ProfileViewBasic
+  postCid: string
+  postUri: string
   record: AppBskyFeedPost.Record
   style?: StyleProp<ViewStyle>
-}) {
+}): React.ReactNode => {
   const {hasSession, currentAccount} = useSession()
   const theme = useTheme()
   const {_} = useLingui()
@@ -46,13 +49,13 @@ export function PostDropdownBtn({
   const toggleThreadMute = useToggleThreadMute()
   const postDeleteMutation = usePostDeleteMutation()
 
-  const rootUri = record.reply?.root?.uri || post.uri
+  const rootUri = record.reply?.root?.uri || postUri
   const isThreadMuted = mutedThreads.includes(rootUri)
-  const isAuthor = post.author.did === currentAccount?.did
+  const isAuthor = postAuthor.did === currentAccount?.did
   const href = React.useMemo(() => {
-    const urip = new AtUri(post.uri)
-    return makeProfileLink(post.author, 'post', urip.rkey)
-  }, [post.uri, post.author])
+    const urip = new AtUri(postUri)
+    return makeProfileLink(postAuthor, 'post', urip.rkey)
+  }, [postUri, postAuthor])
 
   const translatorUrl = getTranslatorLink(
     record.text,
@@ -60,7 +63,7 @@ export function PostDropdownBtn({
   )
 
   const onDeletePost = React.useCallback(() => {
-    postDeleteMutation.mutateAsync({uri: post.uri}).then(
+    postDeleteMutation.mutateAsync({uri: postUri}).then(
       () => {
         Toast.show('Post deleted')
       },
@@ -69,7 +72,7 @@ export function PostDropdownBtn({
         Toast.show('Failed to delete post, please try again')
       },
     )
-  }, [post, postDeleteMutation])
+  }, [postUri, postDeleteMutation])
 
   const onToggleThreadMute = React.useCallback(() => {
     try {
@@ -163,8 +166,8 @@ export function PostDropdownBtn({
         onPress() {
           openModal({
             name: 'report',
-            uri: post.uri,
-            cid: post.cid,
+            uri: postUri,
+            cid: postCid,
           })
         },
         testID: 'postDropdownReportBtn',
@@ -211,3 +214,6 @@ export function PostDropdownBtn({
     </EventStopper>
   )
 }
+
+PostDropdownBtn = memo(PostDropdownBtn)
+export {PostDropdownBtn}
diff --git a/src/view/com/util/post-ctrls/PostCtrls.tsx b/src/view/com/util/post-ctrls/PostCtrls.tsx
index c0c5d470e..83ea3e8c9 100644
--- a/src/view/com/util/post-ctrls/PostCtrls.tsx
+++ b/src/view/com/util/post-ctrls/PostCtrls.tsx
@@ -1,4 +1,4 @@
-import React, {useCallback} from 'react'
+import React, {memo, useCallback} from 'react'
 import {
   StyleProp,
   StyleSheet,
@@ -27,7 +27,7 @@ import {useComposerControls} from '#/state/shell/composer'
 import {Shadow} from '#/state/cache/types'
 import {useRequireAuth} from '#/state/session'
 
-export function PostCtrls({
+let PostCtrls = ({
   big,
   post,
   record,
@@ -39,7 +39,7 @@ export function PostCtrls({
   record: AppBskyFeedPost.Record
   style?: StyleProp<ViewStyle>
   onPressReply: () => void
-}) {
+}): React.ReactNode => {
   const theme = useTheme()
   const {openComposer} = useComposerControls()
   const {closeModal} = useModalControls()
@@ -71,7 +71,14 @@ export function PostCtrls({
         likeCount: post.likeCount || 0,
       })
     }
-  }, [post, postLikeMutation, postUnlikeMutation])
+  }, [
+    post.viewer?.like,
+    post.uri,
+    post.cid,
+    post.likeCount,
+    postLikeMutation,
+    postUnlikeMutation,
+  ])
 
   const onRepost = useCallback(() => {
     closeModal()
@@ -89,7 +96,15 @@ export function PostCtrls({
         repostCount: post.repostCount || 0,
       })
     }
-  }, [post, closeModal, postRepostMutation, postUnrepostMutation])
+  }, [
+    post.uri,
+    post.cid,
+    post.viewer?.repost,
+    post.repostCount,
+    closeModal,
+    postRepostMutation,
+    postUnrepostMutation,
+  ])
 
   const onQuote = useCallback(() => {
     closeModal()
@@ -103,7 +118,16 @@ export function PostCtrls({
       },
     })
     Haptics.default()
-  }, [post, record, openComposer, closeModal])
+  }, [
+    post.uri,
+    post.cid,
+    post.author,
+    post.indexedAt,
+    record.text,
+    openComposer,
+    closeModal,
+  ])
+
   return (
     <View style={[styles.ctrls, style]}>
       <TouchableOpacity
@@ -179,7 +203,9 @@ export function PostCtrls({
       {big ? undefined : (
         <PostDropdownBtn
           testID="postDropdownBtn"
-          post={post}
+          postAuthor={post.author}
+          postCid={post.cid}
+          postUri={post.uri}
           record={record}
           style={styles.ctrlPad}
         />
@@ -189,6 +215,8 @@ export function PostCtrls({
     </View>
   )
 }
+PostCtrls = memo(PostCtrls)
+export {PostCtrls}
 
 const styles = StyleSheet.create({
   ctrls: {
diff --git a/src/view/com/util/post-ctrls/RepostButton.tsx b/src/view/com/util/post-ctrls/RepostButton.tsx
index 1d34a88ab..620852d8e 100644
--- a/src/view/com/util/post-ctrls/RepostButton.tsx
+++ b/src/view/com/util/post-ctrls/RepostButton.tsx
@@ -1,4 +1,4 @@
-import React, {useCallback} from 'react'
+import React, {memo, useCallback} from 'react'
 import {StyleProp, StyleSheet, TouchableOpacity, ViewStyle} from 'react-native'
 import {RepostIcon} from 'lib/icons'
 import {s, colors} from 'lib/styles'
@@ -17,13 +17,13 @@ interface Props {
   onQuote: () => void
 }
 
-export const RepostButton = ({
+let RepostButton = ({
   isReposted,
   repostCount,
   big,
   onRepost,
   onQuote,
-}: Props) => {
+}: Props): React.ReactNode => {
   const theme = useTheme()
   const {openModal} = useModalControls()
   const requireAuth = useRequireAuth()
@@ -80,6 +80,8 @@ export const RepostButton = ({
     </TouchableOpacity>
   )
 }
+RepostButton = memo(RepostButton)
+export {RepostButton}
 
 const styles = StyleSheet.create({
   control: {