about summary refs log tree commit diff
path: root/src/view/com/post/Post.tsx
diff options
context:
space:
mode:
authorPaul Frazee <pfrazee@gmail.com>2023-04-12 18:26:38 -0700
committerGitHub <noreply@github.com>2023-04-12 18:26:38 -0700
commit2fed6c402159c6084dd481ab87c5e8b034e910ac (patch)
tree5907b2b67c900ef78de89e12ad9ae4c0d5ef6715 /src/view/com/post/Post.tsx
parenta20d034ba5b18c4512f3a36f733bb5cd2199424e (diff)
downloadvoidsky-2fed6c402159c6084dd481ab87c5e8b034e910ac.tar.zst
Add first round of labeling tools (#467)
* Rework notifications to sync locally in full and give users better control

* Fix positioning of load more btn on web

* Improve behavior of load more notifications btn

* Fix to post rendering

* Fix notification fetch abort condition

* Add start of post-hiding by labels

* Create a standard postcontainer and improve show/hide UI on posts

* Add content hiding to expanded post form

* Improve label rendering to give more context to users when appropriate

* Fix rendering bug

* Add user/profile labeling

* Implement content filtering preferences

* Filter notifications by content prefs

* Update test-pds config

* Bump deps
Diffstat (limited to 'src/view/com/post/Post.tsx')
-rw-r--r--src/view/com/post/Post.tsx217
1 files changed, 135 insertions, 82 deletions
diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx
index e8e741269..60d46f5cc 100644
--- a/src/view/com/post/Post.tsx
+++ b/src/view/com/post/Post.tsx
@@ -7,17 +7,22 @@ import {
   View,
   ViewStyle,
 } from 'react-native'
+import {AppBskyFeedPost as FeedPost} from '@atproto/api'
 import {observer} from 'mobx-react-lite'
 import Clipboard from '@react-native-clipboard/clipboard'
 import {AtUri} from '@atproto/api'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {PostThreadModel} from 'state/models/content/post-thread'
+import {
+  PostThreadModel,
+  PostThreadItemModel,
+} from 'state/models/content/post-thread'
 import {Link} from '../util/Link'
 import {UserInfoText} from '../util/UserInfoText'
 import {PostMeta} from '../util/PostMeta'
 import {PostEmbeds} from '../util/post-embeds'
 import {PostCtrls} from '../util/PostCtrls'
-import {PostMutedWrapper} from '../util/PostMuted'
+import {PostHider} from '../util/moderation/PostHider'
+import {ContentHider} from '../util/moderation/ContentHider'
 import {Text} from '../util/text/Text'
 import {RichText} from '../util/text/RichText'
 import * as Toast from '../util/Toast'
@@ -61,7 +66,11 @@ export const Post = observer(function Post({
 
   // loading
   // =
-  if (!view || view.isLoading || view.params.uri !== uri) {
+  if (
+    !view ||
+    (!view.hasContent && view.isLoading) ||
+    view.params.uri !== uri
+  ) {
     return (
       <View style={pal.view}>
         <ActivityIndicator />
@@ -84,85 +93,122 @@ export const Post = observer(function Post({
 
   // loaded
   // =
-  const item = view.thread
-  const record = view.thread.postRecord
 
-  const itemUri = item.post.uri
-  const itemCid = item.post.cid
-  const itemUrip = new AtUri(item.post.uri)
-  const itemHref = `/profile/${item.post.author.handle}/post/${itemUrip.rkey}`
-  const itemTitle = `Post by ${item.post.author.handle}`
-  const authorHref = `/profile/${item.post.author.handle}`
-  const authorTitle = item.post.author.handle
-  let replyAuthorDid = ''
-  if (record.reply) {
-    const urip = new AtUri(record.reply.parent?.uri || record.reply.root.uri)
-    replyAuthorDid = urip.hostname
-  }
-  const onPressReply = () => {
-    store.shell.openComposer({
-      replyTo: {
-        uri: item.post.uri,
-        cid: item.post.cid,
-        text: record.text as string,
-        author: {
-          handle: item.post.author.handle,
-          displayName: item.post.author.displayName,
-          avatar: item.post.author.avatar,
+  return (
+    <PostLoaded
+      item={view.thread}
+      record={view.thread.postRecord}
+      setDeleted={setDeleted}
+      showReplyLine={showReplyLine}
+      style={style}
+    />
+  )
+})
+
+const PostLoaded = observer(
+  ({
+    item,
+    record,
+    setDeleted,
+    showReplyLine,
+    style,
+  }: {
+    item: PostThreadItemModel
+    record: FeedPost.Record
+    setDeleted: (v: boolean) => void
+    showReplyLine?: boolean
+    style?: StyleProp<ViewStyle>
+  }) => {
+    const pal = usePalette('default')
+    const store = useStores()
+
+    const itemUri = item.post.uri
+    const itemCid = item.post.cid
+    const itemUrip = new AtUri(item.post.uri)
+    const itemHref = `/profile/${item.post.author.handle}/post/${itemUrip.rkey}`
+    const itemTitle = `Post by ${item.post.author.handle}`
+    const authorHref = `/profile/${item.post.author.handle}`
+    const authorTitle = item.post.author.handle
+    let replyAuthorDid = ''
+    if (record.reply) {
+      const urip = new AtUri(record.reply.parent?.uri || record.reply.root.uri)
+      replyAuthorDid = urip.hostname
+    }
+    const onPressReply = React.useCallback(() => {
+      store.shell.openComposer({
+        replyTo: {
+          uri: item.post.uri,
+          cid: item.post.cid,
+          text: record.text as string,
+          author: {
+            handle: item.post.author.handle,
+            displayName: item.post.author.displayName,
+            avatar: item.post.author.avatar,
+          },
         },
-      },
-    })
-  }
-  const onPressToggleRepost = () => {
-    return item
-      .toggleRepost()
-      .catch(e => store.log.error('Failed to toggle repost', e))
-  }
-  const onPressToggleLike = () => {
-    return item
-      .toggleLike()
-      .catch(e => store.log.error('Failed to toggle like', e))
-  }
-  const onCopyPostText = () => {
-    Clipboard.setString(record.text)
-    Toast.show('Copied to clipboard')
-  }
-  const onOpenTranslate = () => {
-    Linking.openURL(
-      encodeURI(`https://translate.google.com/#auto|en|${record?.text || ''}`),
-    )
-  }
-  const onDeletePost = () => {
-    item.delete().then(
-      () => {
-        setDeleted(true)
-        Toast.show('Post deleted')
-      },
-      e => {
-        store.log.error('Failed to delete post', e)
-        Toast.show('Failed to delete post, please try again')
-      },
-    )
-  }
+      })
+    }, [store, item, record])
 
-  return (
-    <PostMutedWrapper isMuted={item.post.author.viewer?.muted === true}>
-      <Link
-        style={[styles.outer, pal.view, pal.border, style]}
+    const onPressToggleRepost = React.useCallback(() => {
+      return item
+        .toggleRepost()
+        .catch(e => store.log.error('Failed to toggle repost', e))
+    }, [item, store])
+
+    const onPressToggleLike = React.useCallback(() => {
+      return item
+        .toggleLike()
+        .catch(e => store.log.error('Failed to toggle like', e))
+    }, [item, store])
+
+    const onCopyPostText = React.useCallback(() => {
+      Clipboard.setString(record.text)
+      Toast.show('Copied to clipboard')
+    }, [record])
+
+    const onOpenTranslate = React.useCallback(() => {
+      Linking.openURL(
+        encodeURI(
+          `https://translate.google.com/#auto|en|${record?.text || ''}`,
+        ),
+      )
+    }, [record])
+
+    const onDeletePost = React.useCallback(() => {
+      item.delete().then(
+        () => {
+          setDeleted(true)
+          Toast.show('Post deleted')
+        },
+        e => {
+          store.log.error('Failed to delete post', e)
+          Toast.show('Failed to delete post, please try again')
+        },
+      )
+    }, [item, setDeleted, store])
+
+    return (
+      <PostHider
         href={itemHref}
-        title={itemTitle}
-        noFeedback>
+        style={[styles.outer, pal.view, pal.border, style]}
+        isMuted={item.post.author.viewer?.muted === true}
+        labels={item.post.labels}>
         {showReplyLine && <View style={styles.replyLine} />}
         <View style={styles.layout}>
           <View style={styles.layoutAvi}>
             <Link href={authorHref} title={authorTitle} asAnchor>
-              <UserAvatar size={52} avatar={item.post.author.avatar} />
+              <UserAvatar
+                size={52}
+                avatar={item.post.author.avatar}
+                hasWarning={!!item.post.author.labels?.length}
+              />
             </Link>
           </View>
           <View style={styles.layoutContent}>
             <PostMeta
               authorHandle={item.post.author.handle}
               authorDisplayName={item.post.author.displayName}
+              authorHasWarning={!!item.post.author.labels?.length}
               timestamp={item.post.indexedAt}
               postHref={itemHref}
               did={item.post.author.did}
@@ -185,16 +231,20 @@ export const Post = observer(function Post({
                 />
               </View>
             )}
-            {item.richText?.text ? (
-              <View style={styles.postTextContainer}>
-                <RichText
-                  type="post-text"
-                  richText={item.richText}
-                  lineHeight={1.3}
-                />
-              </View>
-            ) : undefined}
-            <PostEmbeds embed={item.post.embed} style={s.mb10} />
+            <ContentHider
+              labels={item.post.labels}
+              containerStyle={styles.contentHider}>
+              {item.richText?.text ? (
+                <View style={styles.postTextContainer}>
+                  <RichText
+                    type="post-text"
+                    richText={item.richText}
+                    lineHeight={1.3}
+                  />
+                </View>
+              ) : undefined}
+              <PostEmbeds embed={item.post.embed} style={s.mb10} />
+            </ContentHider>
             <PostCtrls
               itemUri={itemUri}
               itemCid={itemCid}
@@ -222,10 +272,10 @@ export const Post = observer(function Post({
             />
           </View>
         </View>
-      </Link>
-    </PostMutedWrapper>
-  )
-})
+      </PostHider>
+    )
+  },
+)
 
 const styles = StyleSheet.create({
   outer: {
@@ -257,4 +307,7 @@ const styles = StyleSheet.create({
     borderLeftWidth: 2,
     borderLeftColor: colors.gray2,
   },
+  contentHider: {
+    marginTop: 4,
+  },
 })