diff options
Diffstat (limited to 'src/view/com/util/post-embeds')
-rw-r--r-- | src/view/com/util/post-embeds/ExternalLinkEmbed.tsx | 59 | ||||
-rw-r--r-- | src/view/com/util/post-embeds/QuoteEmbed.tsx | 85 | ||||
-rw-r--r-- | src/view/com/util/post-embeds/index.tsx | 98 |
3 files changed, 158 insertions, 84 deletions
diff --git a/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx b/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx index a4cbb3e29..81f1ca560 100644 --- a/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx +++ b/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx @@ -1,9 +1,11 @@ import React from 'react' +import {Image} from 'expo-image' import {Text} from '../text/Text' -import {AutoSizedImage} from '../images/AutoSizedImage' import {StyleSheet, View} from 'react-native' import {usePalette} from 'lib/hooks/usePalette' import {AppBskyEmbedExternal} from '@atproto/api' +import {isDesktopWeb} from 'platform/detection' +import {toNiceDomain} from 'lib/strings/url-helpers' export const ExternalLinkEmbed = ({ link, @@ -14,44 +16,71 @@ export const ExternalLinkEmbed = ({ }) => { const pal = usePalette('default') return ( - <> + <View style={styles.extContainer}> {link.thumb ? ( - <AutoSizedImage uri={link.thumb} style={styles.extImage}> + <View style={styles.extImageContainer}> + <Image + style={styles.extImage} + source={{uri: link.thumb}} + accessibilityIgnoresInvertColors + /> {imageChild} - </AutoSizedImage> + </View> ) : undefined} <View style={styles.extInner}> - <Text type="md-bold" numberOfLines={2} style={[pal.text]}> - {link.title || link.uri} - </Text> <Text type="sm" numberOfLines={1} style={[pal.textLight, styles.extUri]}> - {link.uri} + {toNiceDomain(link.uri)} + </Text> + <Text + type="lg-bold" + numberOfLines={isDesktopWeb ? 2 : 4} + style={[pal.text]}> + {link.title || link.uri} </Text> {link.description ? ( <Text - type="sm" - numberOfLines={2} + type="md" + numberOfLines={isDesktopWeb ? 2 : 4} style={[pal.text, styles.extDescription]}> {link.description} </Text> ) : undefined} </View> - </> + </View> ) } const styles = StyleSheet.create({ + extContainer: { + flexDirection: isDesktopWeb ? 'row' : 'column', + }, extInner: { - padding: 10, + paddingHorizontal: isDesktopWeb ? 14 : 10, + paddingTop: 8, + paddingBottom: 10, + flex: isDesktopWeb ? 1 : undefined, }, + extImageContainer: isDesktopWeb + ? { + borderTopLeftRadius: 6, + borderBottomLeftRadius: 6, + width: 120, + aspectRatio: 1, + overflow: 'hidden', + } + : { + borderTopLeftRadius: 6, + borderTopRightRadius: 6, + width: '100%', + height: 200, + overflow: 'hidden', + }, extImage: { - borderTopLeftRadius: 6, - borderTopRightRadius: 6, width: '100%', - maxHeight: 200, + height: 200, }, extUri: { marginTop: 2, diff --git a/src/view/com/util/post-embeds/QuoteEmbed.tsx b/src/view/com/util/post-embeds/QuoteEmbed.tsx index 4995562ac..f82b5b7df 100644 --- a/src/view/com/util/post-embeds/QuoteEmbed.tsx +++ b/src/view/com/util/post-embeds/QuoteEmbed.tsx @@ -1,6 +1,12 @@ import React from 'react' -import {StyleProp, StyleSheet, ViewStyle} from 'react-native' -import {AppBskyEmbedImages, AppBskyEmbedRecordWithMedia} from '@atproto/api' +import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' +import { + AppBskyEmbedRecord, + AppBskyFeedPost, + AppBskyEmbedImages, + AppBskyEmbedRecordWithMedia, + ModerationUI, +} from '@atproto/api' import {AtUri} from '@atproto/api' import {PostMeta} from '../PostMeta' import {Link} from '../Link' @@ -8,13 +14,68 @@ import {Text} from '../text/Text' import {usePalette} from 'lib/hooks/usePalette' import {ComposerOptsQuote} from 'state/models/ui/shell' import {PostEmbeds} from '.' +import {PostAlerts} from '../moderation/PostAlerts' import {makeProfileLink} from 'lib/routes/links' +import {InfoCircleIcon} from 'lib/icons' + +export function MaybeQuoteEmbed({ + embed, + moderation, + style, +}: { + embed: AppBskyEmbedRecord.View + moderation: ModerationUI + style?: StyleProp<ViewStyle> +}) { + const pal = usePalette('default') + if ( + AppBskyEmbedRecord.isViewRecord(embed.record) && + AppBskyFeedPost.isRecord(embed.record.value) && + 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, + embeds: embed.record.embeds, + }} + moderation={moderation} + style={style} + /> + ) + } else if (AppBskyEmbedRecord.isViewBlocked(embed.record)) { + return ( + <View style={[styles.errorContainer, pal.borderDark]}> + <InfoCircleIcon size={18} style={pal.text} /> + <Text type="lg" style={pal.text}> + Blocked + </Text> + </View> + ) + } else if (AppBskyEmbedRecord.isViewNotFound(embed.record)) { + return ( + <View style={[styles.errorContainer, pal.borderDark]}> + <InfoCircleIcon size={18} style={pal.text} /> + <Text type="lg" style={pal.text}> + Deleted + </Text> + </View> + ) + } + return null +} export function QuoteEmbed({ quote, + moderation, style, }: { quote: ComposerOptsQuote + moderation?: ModerationUI style?: StyleProp<ViewStyle> }) { const pal = usePalette('default') @@ -46,16 +107,19 @@ export function QuoteEmbed({ postHref={itemHref} timestamp={quote.indexedAt} /> + {moderation ? ( + <PostAlerts moderation={moderation} style={styles.alert} /> + ) : null} {!isEmpty ? ( <Text type="post-text" style={pal.text} numberOfLines={6}> {quote.text} </Text> ) : null} {AppBskyEmbedImages.isView(imagesEmbed) && ( - <PostEmbeds embed={imagesEmbed} /> + <PostEmbeds embed={imagesEmbed} moderation={{}} /> )} {AppBskyEmbedRecordWithMedia.isView(imagesEmbed) && ( - <PostEmbeds embed={imagesEmbed.media} /> + <PostEmbeds embed={imagesEmbed.media} moderation={{}} /> )} </Link> ) @@ -76,4 +140,17 @@ const styles = StyleSheet.create({ paddingLeft: 13, paddingRight: 8, }, + errorContainer: { + flexDirection: 'row', + alignItems: 'center', + gap: 4, + borderRadius: 8, + marginTop: 8, + paddingVertical: 14, + paddingHorizontal: 14, + borderWidth: 1, + }, + alert: { + marginBottom: 6, + }, }) diff --git a/src/view/com/util/post-embeds/index.tsx b/src/view/com/util/post-embeds/index.tsx index 7ffebff54..5d0090434 100644 --- a/src/view/com/util/post-embeds/index.tsx +++ b/src/view/com/util/post-embeds/index.tsx @@ -4,17 +4,18 @@ import { StyleProp, View, ViewStyle, - Image as RNImage, Text, + InteractionManager, } from 'react-native' +import {Image} from 'expo-image' import { AppBskyEmbedImages, AppBskyEmbedExternal, AppBskyEmbedRecord, AppBskyEmbedRecordWithMedia, - AppBskyFeedPost, AppBskyFeedDefs, AppBskyGraphDefs, + ModerationUI, } from '@atproto/api' import {Link} from '../Link' import {ImageLayoutGrid} from '../images/ImageLayoutGrid' @@ -24,11 +25,12 @@ import {usePalette} from 'lib/hooks/usePalette' import {YoutubeEmbed} from './YoutubeEmbed' import {ExternalLinkEmbed} from './ExternalLinkEmbed' import {getYoutubeVideoId} from 'lib/strings/url-helpers' -import QuoteEmbed from './QuoteEmbed' +import {MaybeQuoteEmbed} from './QuoteEmbed' import {AutoSizedImage} from '../images/AutoSizedImage' import {CustomFeedEmbed} from './CustomFeedEmbed' import {ListEmbed} from './ListEmbed' import {isDesktopWeb} from 'platform/detection' +import {isCauseALabelOnUri} from 'lib/moderation' type Embed = | AppBskyEmbedRecord.View @@ -39,9 +41,11 @@ type Embed = export function PostEmbeds({ embed, + moderation, style, }: { embed?: Embed + moderation: ModerationUI style?: StyleProp<ViewStyle> }) { const pal = usePalette('default') @@ -49,51 +53,37 @@ export function PostEmbeds({ // quote post with media // = - if ( - AppBskyEmbedRecordWithMedia.isView(embed) && - AppBskyEmbedRecord.isViewRecord(embed.record.record) && - AppBskyFeedPost.isRecord(embed.record.record.value) && - AppBskyFeedPost.validateRecord(embed.record.record.value).success - ) { + if (AppBskyEmbedRecordWithMedia.isView(embed)) { + const isModOnQuote = + AppBskyEmbedRecord.isViewRecord(embed.record.record) && + isCauseALabelOnUri(moderation.cause, embed.record.record.uri) + const mediaModeration = isModOnQuote ? {} : moderation + const quoteModeration = isModOnQuote ? moderation : {} return ( <View style={[styles.stackContainer, style]}> - <PostEmbeds embed={embed.media} /> - <QuoteEmbed - quote={{ - author: embed.record.record.author, - cid: embed.record.record.cid, - uri: embed.record.record.uri, - indexedAt: embed.record.record.indexedAt, - text: embed.record.record.value.text, - embeds: embed.record.record.embeds, - }} - /> + <PostEmbeds embed={embed.media} moderation={mediaModeration} /> + <MaybeQuoteEmbed embed={embed.record} moderation={quoteModeration} /> </View> ) } - // quote post - // = if (AppBskyEmbedRecord.isView(embed)) { - if ( - AppBskyEmbedRecord.isViewRecord(embed.record) && - AppBskyFeedPost.isRecord(embed.record.value) && - 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, - embeds: embed.record.embeds, - }} - style={style} - /> - ) + // custom feed embed (i.e. generator view) + // = + if (AppBskyFeedDefs.isGeneratorView(embed.record)) { + return <CustomFeedEmbed record={embed.record} /> } + + // list embed (e.g. mute lists; i.e. ListView) + if (AppBskyGraphDefs.isListView(embed.record)) { + return <ListEmbed item={embed.record} /> + } + + // quote post + // = + return ( + <MaybeQuoteEmbed embed={embed} style={style} moderation={moderation} /> + ) } // image embed @@ -106,14 +96,9 @@ export function PostEmbeds({ const openLightbox = (index: number) => { store.shell.openLightbox(new ImagesLightbox(items, index)) } - const onPressIn = (index: number) => { - const firstImageToShow = items[index].uri - RNImage.prefetch(firstImageToShow) - items.forEach(item => { - if (firstImageToShow !== item.uri) { - // First image already prefetched above - RNImage.prefetch(item.uri) - } + const onPressIn = (_: number) => { + InteractionManager.runAfterInteractions(() => { + Image.prefetch(items.map(i => i.uri)) }) } @@ -152,23 +137,6 @@ export function PostEmbeds({ } } - // custom feed embed (i.e. generator view) - // = - if ( - AppBskyEmbedRecord.isView(embed) && - AppBskyFeedDefs.isGeneratorView(embed.record) - ) { - return <CustomFeedEmbed record={embed.record} /> - } - - // list embed (e.g. mute lists; i.e. ListView) - if ( - AppBskyEmbedRecord.isView(embed) && - AppBskyGraphDefs.isListView(embed.record) - ) { - return <ListEmbed item={embed.record} /> - } - // external link embed // = if (AppBskyEmbedExternal.isView(embed)) { |