about summary refs log tree commit diff
path: root/src/view/com/util/post-embeds
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/util/post-embeds')
-rw-r--r--src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx94
-rw-r--r--src/view/com/util/post-embeds/QuoteEmbed.tsx131
-rw-r--r--src/view/com/util/post-embeds/index.tsx106
3 files changed, 162 insertions, 169 deletions
diff --git a/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx b/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx
index d556e7669..cf2db5b33 100644
--- a/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx
+++ b/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx
@@ -21,7 +21,7 @@ import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {useNavigation} from '@react-navigation/native'
 import {AppBskyEmbedExternal} from '@atproto/api'
-import {EmbedPlayerParams, getPlayerHeight} from 'lib/strings/embed-player'
+import {EmbedPlayerParams, getPlayerAspect} from 'lib/strings/embed-player'
 import {EventStopper} from '../EventStopper'
 import {isNative} from 'platform/detection'
 import {NavigationProp} from 'lib/routes/types'
@@ -67,14 +67,12 @@ function PlaceholderOverlay({
 
 // This renders the webview/youtube player as a separate layer
 function Player({
-  height,
   params,
   onLoad,
   isPlayerActive,
 }: {
   isPlayerActive: boolean
   params: EmbedPlayerParams
-  height: number
   onLoad: () => void
 }) {
   // ensures we only load what's requested
@@ -91,25 +89,21 @@ function Player({
   if (!isPlayerActive) return null
 
   return (
-    <View style={[styles.layer, styles.playerLayer]}>
-      <EventStopper>
-        <View style={{height, width: '100%'}}>
-          <WebView
-            javaScriptEnabled={true}
-            onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
-            mediaPlaybackRequiresUserAction={false}
-            allowsInlineMediaPlayback
-            bounces={false}
-            allowsFullscreenVideo
-            nestedScrollEnabled
-            source={{uri: params.playerUri}}
-            onLoad={onLoad}
-            setSupportMultipleWindows={false} // Prevent any redirects from opening a new window (ads)
-            style={[styles.webview, styles.topRadius]}
-          />
-        </View>
-      </EventStopper>
-    </View>
+    <EventStopper style={[styles.layer, styles.playerLayer]}>
+      <WebView
+        javaScriptEnabled={true}
+        onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
+        mediaPlaybackRequiresUserAction={false}
+        allowsInlineMediaPlayback
+        bounces={false}
+        allowsFullscreenVideo
+        nestedScrollEnabled
+        source={{uri: params.playerUri}}
+        onLoad={onLoad}
+        style={styles.webview}
+        setSupportMultipleWindows={false} // Prevent any redirects from opening a new window (ads)
+      />
+    </EventStopper>
   )
 }
 
@@ -129,13 +123,16 @@ export function ExternalPlayer({
 
   const [isPlayerActive, setPlayerActive] = React.useState(false)
   const [isLoading, setIsLoading] = React.useState(true)
-  const [dim, setDim] = React.useState({
-    width: 0,
-    height: 0,
-  })
 
-  const viewRef = useAnimatedRef()
+  const aspect = React.useMemo(() => {
+    return getPlayerAspect({
+      type: params.type,
+      width: windowDims.width,
+      hasThumb: !!link.thumb,
+    })
+  }, [params.type, windowDims.width, link.thumb])
 
+  const viewRef = useAnimatedRef()
   const frameCallback = useFrameCallback(() => {
     const measurement = measure(viewRef)
     if (!measurement) return
@@ -180,17 +177,6 @@ export function ExternalPlayer({
     }
   }, [navigation, isPlayerActive, frameCallback])
 
-  // calculate height for the player and the screen size
-  const height = React.useMemo(
-    () =>
-      getPlayerHeight({
-        type: params.type,
-        width: dim.width,
-        hasThumb: !!link.thumb,
-      }),
-    [params.type, dim.width, link.thumb],
-  )
-
   const onLoad = React.useCallback(() => {
     setIsLoading(false)
   }, [])
@@ -216,32 +202,11 @@ export function ExternalPlayer({
     [externalEmbedsPrefs, openModal, params.source],
   )
 
-  // measure the layout to set sizing
-  const onLayout = React.useCallback(
-    (event: {nativeEvent: {layout: {width: any; height: any}}}) => {
-      setDim({
-        width: event.nativeEvent.layout.width,
-        height: event.nativeEvent.layout.height,
-      })
-    },
-    [],
-  )
-
   return (
-    <Animated.View
-      ref={viewRef}
-      style={{height}}
-      collapsable={false}
-      onLayout={onLayout}>
+    <Animated.View ref={viewRef} collapsable={false} style={[aspect]}>
       {link.thumb && (!isPlayerActive || isLoading) && (
         <Image
-          style={[
-            {
-              width: dim.width,
-              height,
-            },
-            styles.topRadius,
-          ]}
+          style={[{flex: 1}, styles.topRadius]}
           source={{uri: link.thumb}}
           accessibilityIgnoresInvertColors
         />
@@ -251,12 +216,7 @@ export function ExternalPlayer({
         isPlayerActive={isPlayerActive}
         onPress={onPlayPress}
       />
-      <Player
-        isPlayerActive={isPlayerActive}
-        params={params}
-        height={height}
-        onLoad={onLoad}
-      />
+      <Player isPlayerActive={isPlayerActive} params={params} onLoad={onLoad} />
     </Animated.View>
   )
 }
diff --git a/src/view/com/util/post-embeds/QuoteEmbed.tsx b/src/view/com/util/post-embeds/QuoteEmbed.tsx
index d9d84feb4..2b1c3e617 100644
--- a/src/view/com/util/post-embeds/QuoteEmbed.tsx
+++ b/src/view/com/util/post-embeds/QuoteEmbed.tsx
@@ -1,13 +1,15 @@
 import React from 'react'
 import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
 import {
+  AppBskyFeedDefs,
   AppBskyEmbedRecord,
   AppBskyFeedPost,
   AppBskyEmbedImages,
   AppBskyEmbedRecordWithMedia,
-  ModerationUI,
   AppBskyEmbedExternal,
   RichText as RichTextAPI,
+  moderatePost,
+  ModerationDecision,
 } from '@atproto/api'
 import {AtUri} from '@atproto/api'
 import {PostMeta} from '../PostMeta'
@@ -16,19 +18,20 @@ import {Text} from '../text/Text'
 import {usePalette} from 'lib/hooks/usePalette'
 import {ComposerOptsQuote} from 'state/shell/composer'
 import {PostEmbeds} from '.'
-import {PostAlerts} from '../moderation/PostAlerts'
+import {PostAlerts} from '../../../../components/moderation/PostAlerts'
 import {makeProfileLink} from 'lib/routes/links'
 import {InfoCircleIcon} from 'lib/icons'
 import {Trans} from '@lingui/macro'
-import {RichText} from 'view/com/util/text/RichText'
+import {useModerationOpts} from '#/state/queries/preferences'
+import {ContentHider} from '../../../../components/moderation/ContentHider'
+import {RichText} from '#/components/RichText'
+import {atoms as a} from '#/alf'
 
 export function MaybeQuoteEmbed({
   embed,
-  moderation,
   style,
 }: {
   embed: AppBskyEmbedRecord.View
-  moderation: ModerationUI
   style?: StyleProp<ViewStyle>
 }) {
   const pal = usePalette('default')
@@ -38,17 +41,9 @@ export function MaybeQuoteEmbed({
     AppBskyFeedPost.validateRecord(embed.record.value).success
   ) {
     return (
-      <QuoteEmbed
-        quote={{
-          author: embed.record.author,
-          cid: embed.record.cid,
-          uri: embed.record.uri,
-          indexedAt: embed.record.indexedAt,
-          text: embed.record.value.text,
-          facets: embed.record.value.facets,
-          embeds: embed.record.embeds,
-        }}
-        moderation={moderation}
+      <QuoteEmbedModerated
+        viewRecord={embed.record}
+        postRecord={embed.record.value}
         style={style}
       />
     )
@@ -74,19 +69,49 @@ export function MaybeQuoteEmbed({
   return null
 }
 
+function QuoteEmbedModerated({
+  viewRecord,
+  postRecord,
+  style,
+}: {
+  viewRecord: AppBskyEmbedRecord.ViewRecord
+  postRecord: AppBskyFeedPost.Record
+  style?: StyleProp<ViewStyle>
+}) {
+  const moderationOpts = useModerationOpts()
+  const moderation = React.useMemo(() => {
+    return moderationOpts
+      ? moderatePost(viewRecordToPostView(viewRecord), moderationOpts)
+      : undefined
+  }, [viewRecord, moderationOpts])
+
+  const quote = {
+    author: viewRecord.author,
+    cid: viewRecord.cid,
+    uri: viewRecord.uri,
+    indexedAt: viewRecord.indexedAt,
+    text: postRecord.text,
+    facets: postRecord.facets,
+    embeds: viewRecord.embeds,
+  }
+
+  return <QuoteEmbed quote={quote} moderation={moderation} style={style} />
+}
+
 export function QuoteEmbed({
   quote,
   moderation,
   style,
 }: {
   quote: ComposerOptsQuote
-  moderation?: ModerationUI
+  moderation?: ModerationDecision
   style?: StyleProp<ViewStyle>
 }) {
   const pal = usePalette('default')
   const itemUrip = new AtUri(quote.uri)
   const itemHref = makeProfileLink(quote.author, 'post', itemUrip.rkey)
   const itemTitle = `Post by ${quote.author.handle}`
+
   const richText = React.useMemo(
     () =>
       quote.text.trim()
@@ -94,6 +119,7 @@ export function QuoteEmbed({
         : undefined,
     [quote.text, quote.facets],
   )
+
   const embed = React.useMemo(() => {
     const e = quote.embeds?.[0]
 
@@ -107,39 +133,52 @@ export function QuoteEmbed({
       return e.media
     }
   }, [quote.embeds])
+
   return (
-    <Link
-      style={[styles.container, pal.borderDark, style]}
-      hoverStyle={{borderColor: pal.colors.borderLinkHover}}
-      href={itemHref}
-      title={itemTitle}>
-      <View pointerEvents="none">
-        <PostMeta
-          author={quote.author}
-          showAvatar
-          authorHasWarning={false}
-          postHref={itemHref}
-          timestamp={quote.indexedAt}
-        />
-      </View>
-      {moderation ? (
-        <PostAlerts moderation={moderation} style={styles.alert} />
-      ) : null}
-      {richText ? (
-        <RichText
-          richText={richText}
-          type="post-text"
-          style={pal.text}
-          numberOfLines={20}
-          noLinks
-        />
-      ) : null}
-      {embed && <PostEmbeds embed={embed} moderation={{}} />}
-    </Link>
+    <ContentHider modui={moderation?.ui('contentList')}>
+      <Link
+        style={[styles.container, pal.borderDark, style]}
+        hoverStyle={{borderColor: pal.colors.borderLinkHover}}
+        href={itemHref}
+        title={itemTitle}>
+        <View pointerEvents="none">
+          <PostMeta
+            author={quote.author}
+            moderation={moderation}
+            showAvatar
+            authorHasWarning={false}
+            postHref={itemHref}
+            timestamp={quote.indexedAt}
+          />
+        </View>
+        {moderation ? (
+          <PostAlerts modui={moderation.ui('contentView')} style={[a.py_xs]} />
+        ) : null}
+        {richText ? (
+          <RichText
+            value={richText}
+            style={[a.text_md]}
+            numberOfLines={20}
+            disableLinks
+          />
+        ) : null}
+        {embed && <PostEmbeds embed={embed} moderation={moderation} />}
+      </Link>
+    </ContentHider>
   )
 }
 
-export default QuoteEmbed
+function viewRecordToPostView(
+  viewRecord: AppBskyEmbedRecord.ViewRecord,
+): AppBskyFeedDefs.PostView {
+  const {value, embeds, ...rest} = viewRecord
+  return {
+    ...rest,
+    $type: 'app.bsky.feed.defs#postView',
+    record: value,
+    embed: embeds?.[0],
+  }
+}
 
 const styles = StyleSheet.create({
   container: {
diff --git a/src/view/com/util/post-embeds/index.tsx b/src/view/com/util/post-embeds/index.tsx
index 7e235babb..47091fbb0 100644
--- a/src/view/com/util/post-embeds/index.tsx
+++ b/src/view/com/util/post-embeds/index.tsx
@@ -15,8 +15,7 @@ import {
   AppBskyEmbedRecordWithMedia,
   AppBskyFeedDefs,
   AppBskyGraphDefs,
-  ModerationUI,
-  PostModeration,
+  ModerationDecision,
 } from '@atproto/api'
 import {Link} from '../Link'
 import {ImageLayoutGrid} from '../images/ImageLayoutGrid'
@@ -26,9 +25,8 @@ import {ExternalLinkEmbed} from './ExternalLinkEmbed'
 import {MaybeQuoteEmbed} from './QuoteEmbed'
 import {AutoSizedImage} from '../images/AutoSizedImage'
 import {ListEmbed} from './ListEmbed'
-import {isCauseALabelOnUri, isQuoteBlurred} from 'lib/moderation'
 import {FeedSourceCard} from 'view/com/feeds/FeedSourceCard'
-import {ContentHider} from '../moderation/ContentHider'
+import {ContentHider} from '../../../../components/moderation/ContentHider'
 import {isNative} from '#/platform/detection'
 import {shareUrl} from '#/lib/sharing'
 
@@ -42,12 +40,10 @@ type Embed =
 export function PostEmbeds({
   embed,
   moderation,
-  moderationDecisions,
   style,
 }: {
   embed?: Embed
-  moderation: ModerationUI
-  moderationDecisions?: PostModeration['decisions']
+  moderation?: ModerationDecision
   style?: StyleProp<ViewStyle>
 }) {
   const pal = usePalette('default')
@@ -66,18 +62,10 @@ export function PostEmbeds({
   // quote post with media
   // =
   if (AppBskyEmbedRecordWithMedia.isView(embed)) {
-    const isModOnQuote =
-      (AppBskyEmbedRecord.isViewRecord(embed.record.record) &&
-        isCauseALabelOnUri(moderation.cause, embed.record.record.uri)) ||
-      (moderationDecisions && isQuoteBlurred(moderationDecisions))
-    const mediaModeration = isModOnQuote ? {} : moderation
-    const quoteModeration = isModOnQuote ? moderation : {}
     return (
       <View style={style}>
-        <PostEmbeds embed={embed.media} moderation={mediaModeration} />
-        <ContentHider moderation={quoteModeration}>
-          <MaybeQuoteEmbed embed={embed.record} moderation={quoteModeration} />
-        </ContentHider>
+        <PostEmbeds embed={embed.media} moderation={moderation} />
+        <MaybeQuoteEmbed embed={embed.record} />
       </View>
     )
   }
@@ -86,6 +74,7 @@ export function PostEmbeds({
     // custom feed embed (i.e. generator view)
     // =
     if (AppBskyFeedDefs.isGeneratorView(embed.record)) {
+      // TODO moderation
       return (
         <FeedSourceCard
           feedUri={embed.record.uri}
@@ -97,16 +86,13 @@ export function PostEmbeds({
 
     // list embed
     if (AppBskyGraphDefs.isListView(embed.record)) {
+      // TODO moderation
       return <ListEmbed item={embed.record} />
     }
 
     // quote post
     // =
-    return (
-      <ContentHider moderation={moderation}>
-        <MaybeQuoteEmbed embed={embed} style={style} moderation={moderation} />
-      </ContentHider>
-    )
+    return <MaybeQuoteEmbed embed={embed} style={style} />
   }
 
   // image embed
@@ -132,35 +118,41 @@ export function PostEmbeds({
       if (images.length === 1) {
         const {alt, thumb, aspectRatio} = images[0]
         return (
-          <View style={[styles.imagesContainer, style]}>
-            <AutoSizedImage
-              alt={alt}
-              uri={thumb}
-              dimensionsHint={aspectRatio}
-              onPress={() => _openLightbox(0)}
-              onPressIn={() => onPressIn(0)}
-              style={[styles.singleImage]}>
-              {alt === '' ? null : (
-                <View style={styles.altContainer}>
-                  <Text style={styles.alt} accessible={false}>
-                    ALT
-                  </Text>
-                </View>
-              )}
-            </AutoSizedImage>
-          </View>
+          <ContentHider modui={moderation?.ui('contentMedia')}>
+            <View style={[styles.imagesContainer, style]}>
+              <AutoSizedImage
+                alt={alt}
+                uri={thumb}
+                dimensionsHint={aspectRatio}
+                onPress={() => _openLightbox(0)}
+                onPressIn={() => onPressIn(0)}
+                style={[styles.singleImage]}>
+                {alt === '' ? null : (
+                  <View style={styles.altContainer}>
+                    <Text style={styles.alt} accessible={false}>
+                      ALT
+                    </Text>
+                  </View>
+                )}
+              </AutoSizedImage>
+            </View>
+          </ContentHider>
         )
       }
 
       return (
-        <View style={[styles.imagesContainer, style]}>
-          <ImageLayoutGrid
-            images={embed.images}
-            onPress={_openLightbox}
-            onPressIn={onPressIn}
-            style={embed.images.length === 1 ? [styles.singleImage] : undefined}
-          />
-        </View>
+        <ContentHider modui={moderation?.ui('contentMedia')}>
+          <View style={[styles.imagesContainer, style]}>
+            <ImageLayoutGrid
+              images={embed.images}
+              onPress={_openLightbox}
+              onPressIn={onPressIn}
+              style={
+                embed.images.length === 1 ? [styles.singleImage] : undefined
+              }
+            />
+          </View>
+        </ContentHider>
       )
     }
   }
@@ -171,15 +163,17 @@ export function PostEmbeds({
     const link = embed.external
 
     return (
-      <Link
-        asAnchor
-        anchorNoUnderline
-        href={link.uri}
-        style={[styles.extOuter, pal.view, pal.borderDark, style]}
-        hoverStyle={{borderColor: pal.colors.borderLinkHover}}
-        onLongPress={onShareExternal}>
-        <ExternalLinkEmbed link={link} />
-      </Link>
+      <ContentHider modui={moderation?.ui('contentMedia')}>
+        <Link
+          asAnchor
+          anchorNoUnderline
+          href={link.uri}
+          style={[styles.extOuter, pal.view, pal.borderDark, style]}
+          hoverStyle={{borderColor: pal.colors.borderLinkHover}}
+          onLongPress={onShareExternal}>
+          <ExternalLinkEmbed link={link} />
+        </Link>
+      </ContentHider>
     )
   }