Port post embeds to new arch (#7408)
* Direct port of embeds to new arch (cherry picked from commit cc3fa1f6cea396dd9222486c633a508bfee1ecd6) * Re-org * Split out ListEmbed and FeedEmbed * Split out ImageEmbed * DRY up a bit * Port over ExternalLinkEmbed * Port over Player and Gif embeds * Migrate ComposerReplyTo * Replace other usages of old post-embeds * Migrate view contexts * Copy pasta VideoEmbed * Copy pasta GifEmbed * Swap in new file location * Clean up * Fix up native * Add back in correct moderation on List and Feed embeds * Format * Prettier * delete old video utils * move bandwidth-estimate.ts * Remove log * Add LazyQuoteEmbed for composer use * Clean up unused things * Remove remaining items * Prettier * Fix imports * Handle nested quotes same as prod * Add back silenced error handling * Fix lint --------- Co-authored-by: Samuel Newman <mozzius@protonmail.com>
This commit is contained in:
@@ -72,7 +72,7 @@ import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||
import {mimeToExt} from '#/lib/media/video/util'
|
||||
import {logEvent} from '#/lib/statsig/statsig'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {colors, s} from '#/lib/styles'
|
||||
import {colors} from '#/lib/styles'
|
||||
import {logger} from '#/logger'
|
||||
import {isAndroid, isIOS, isNative, isWeb} from '#/platform/detection'
|
||||
import {useDialogStateControlContext} from '#/state/dialogs'
|
||||
@@ -97,6 +97,7 @@ import {
|
||||
ExternalEmbedGif,
|
||||
ExternalEmbedLink,
|
||||
} from '#/view/com/composer/ExternalEmbed'
|
||||
import {ExternalEmbedRemoveBtn} from '#/view/com/composer/ExternalEmbedRemoveBtn'
|
||||
import {GifAltTextDialog} from '#/view/com/composer/GifAltText'
|
||||
import {LabelsBtn} from '#/view/com/composer/labels/LabelsBtn'
|
||||
import {Gallery} from '#/view/com/composer/photos/Gallery'
|
||||
@@ -116,7 +117,6 @@ import {SelectVideoBtn} from '#/view/com/composer/videos/SelectVideoBtn'
|
||||
import {SubtitleDialogBtn} from '#/view/com/composer/videos/SubtitleDialog'
|
||||
import {VideoPreview} from '#/view/com/composer/videos/VideoPreview'
|
||||
import {VideoTranscodeProgress} from '#/view/com/composer/videos/VideoTranscodeProgress'
|
||||
import {LazyQuoteEmbed, QuoteX} from '#/view/com/util/post-embeds/QuoteEmbed'
|
||||
import {Text} from '#/view/com/util/text/Text'
|
||||
import * as Toast from '#/view/com/util/Toast'
|
||||
import {UserAvatar} from '#/view/com/util/UserAvatar'
|
||||
@@ -125,6 +125,7 @@ import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
||||
import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmile} from '#/components/icons/Emoji'
|
||||
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
|
||||
import {LazyQuoteEmbed} from '#/components/Post/Embed/LazyQuoteEmbed'
|
||||
import * as Prompt from '#/components/Prompt'
|
||||
import {Text as NewText} from '#/components/Typography'
|
||||
import {BottomSheetPortalProvider} from '../../../../modules/bottom-sheet'
|
||||
@@ -1149,13 +1150,17 @@ function ComposerEmbeds({
|
||||
)}
|
||||
</LayoutAnimationConfig>
|
||||
{embed.quote?.uri ? (
|
||||
<View style={!video ? [a.mt_md] : []}>
|
||||
<View style={[s.mt5, s.mb2, isWeb && s.mb10]}>
|
||||
<View
|
||||
style={[a.pb_sm, video ? [a.pt_md] : [a.pt_xl], isWeb && [a.pb_md]]}>
|
||||
<View style={[a.relative]}>
|
||||
<View style={{pointerEvents: 'none'}}>
|
||||
<LazyQuoteEmbed uri={embed.quote.uri} />
|
||||
</View>
|
||||
{canRemoveQuote && (
|
||||
<QuoteX onRemove={() => dispatch({type: 'embed_remove_quote'})} />
|
||||
<ExternalEmbedRemoveBtn
|
||||
onRemove={() => dispatch({type: 'embed_remove_quote'})}
|
||||
style={{top: 16}}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -13,12 +13,13 @@ import {useLingui} from '@lingui/react'
|
||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||
import {type ComposerOptsPostRef} from '#/state/shell/composer'
|
||||
import {MaybeQuoteEmbed} from '#/view/com/util/post-embeds/QuoteEmbed'
|
||||
import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
|
||||
import {atoms as a, useTheme, web} from '#/alf'
|
||||
import {QuoteEmbed} from '#/components/Post/Embed'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useSimpleVerificationState} from '#/components/verification'
|
||||
import {VerificationCheck} from '#/components/verification/VerificationCheck'
|
||||
import {parseEmbed} from '#/types/bsky/post'
|
||||
|
||||
export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) {
|
||||
const t = useTheme()
|
||||
@@ -51,6 +52,12 @@ export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) {
|
||||
}
|
||||
return null
|
||||
}, [embed])
|
||||
const parsedQuoteEmbed = quoteEmbed
|
||||
? parseEmbed({
|
||||
$type: 'app.bsky.embed.record#view',
|
||||
...quoteEmbed,
|
||||
})
|
||||
: null
|
||||
|
||||
const images = useMemo(() => {
|
||||
if (AppBskyEmbedImages.isView(embed)) {
|
||||
@@ -124,7 +131,9 @@ export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) {
|
||||
<ComposerReplyToImages images={images} showFull={showFull} />
|
||||
)}
|
||||
</View>
|
||||
{showFull && quoteEmbed && <MaybeQuoteEmbed embed={quoteEmbed} />}
|
||||
{showFull && parsedQuoteEmbed && parsedQuoteEmbed.type === 'post' && (
|
||||
<QuoteEmbed embed={parsedQuoteEmbed} />
|
||||
)}
|
||||
</View>
|
||||
</Pressable>
|
||||
)
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import React from 'react'
|
||||
import {StyleProp, View, ViewStyle} from 'react-native'
|
||||
import {type StyleProp, View, type ViewStyle} from 'react-native'
|
||||
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {
|
||||
useResolveGifQuery,
|
||||
useResolveLinkQuery,
|
||||
} from '#/state/queries/resolve-link'
|
||||
import {Gif} from '#/state/queries/tenor'
|
||||
import {type Gif} from '#/state/queries/tenor'
|
||||
import {ExternalEmbedRemoveBtn} from '#/view/com/composer/ExternalEmbedRemoveBtn'
|
||||
import {ExternalLinkEmbed} from '#/view/com/util/post-embeds/ExternalLinkEmbed'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import {ExternalEmbed} from '#/components/Post/Embed/ExternalEmbed'
|
||||
import {ModeratedFeedEmbed} from '#/components/Post/Embed/FeedEmbed'
|
||||
import {ModeratedListEmbed} from '#/components/Post/Embed/ListEmbed'
|
||||
import {Embed as StarterPackEmbed} from '#/components/StarterPack/StarterPackCard'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {MaybeFeedCard, MaybeListCard} from '../util/post-embeds'
|
||||
|
||||
export const ExternalEmbedGif = ({
|
||||
onRemove,
|
||||
@@ -44,7 +45,7 @@ export const ExternalEmbedGif = ({
|
||||
<View style={[a.overflow_hidden, t.atoms.border_contrast_medium]}>
|
||||
{linkInfo ? (
|
||||
<View style={{pointerEvents: 'auto'}}>
|
||||
<ExternalLinkEmbed link={linkInfo} hideAlt />
|
||||
<ExternalEmbed link={linkInfo} hideAlt />
|
||||
</View>
|
||||
) : error ? (
|
||||
<Container style={[a.align_start, a.p_md, a.gap_xs]}>
|
||||
@@ -80,7 +81,7 @@ export const ExternalEmbedLink = ({
|
||||
if (data) {
|
||||
if (data.type === 'external') {
|
||||
return (
|
||||
<ExternalLinkEmbed
|
||||
<ExternalEmbed
|
||||
link={{
|
||||
title: data.title || uri,
|
||||
uri,
|
||||
@@ -91,9 +92,29 @@ export const ExternalEmbedLink = ({
|
||||
/>
|
||||
)
|
||||
} else if (data.kind === 'feed') {
|
||||
return <MaybeFeedCard view={data.view} />
|
||||
return (
|
||||
<ModeratedFeedEmbed
|
||||
embed={{
|
||||
type: 'feed',
|
||||
view: {
|
||||
$type: 'app.bsky.feed.defs#generatorView',
|
||||
...data.view,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)
|
||||
} else if (data.kind === 'list') {
|
||||
return <MaybeListCard view={data.view} />
|
||||
return (
|
||||
<ModeratedListEmbed
|
||||
embed={{
|
||||
type: 'list',
|
||||
view: {
|
||||
$type: 'app.bsky.graph.defs#listView',
|
||||
...data.view,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)
|
||||
} else if (data.kind === 'starter-pack') {
|
||||
return <StarterPackEmbed starterPack={data.view} />
|
||||
}
|
||||
|
||||
@@ -2,22 +2,27 @@ import {View} from 'react-native'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {atoms as a} from '#/alf'
|
||||
import {atoms as a, useTheme, type ViewStyleProp} from '#/alf'
|
||||
import {Button, ButtonIcon} from '#/components/Button'
|
||||
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
|
||||
|
||||
export function ExternalEmbedRemoveBtn({onRemove}: {onRemove: () => void}) {
|
||||
export function ExternalEmbedRemoveBtn({
|
||||
onRemove,
|
||||
style,
|
||||
}: {onRemove: () => void} & ViewStyleProp) {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
|
||||
return (
|
||||
<View style={[a.absolute, {top: 8, right: 8}, a.z_50]}>
|
||||
<View style={[a.absolute, {top: 8, right: 8}, a.z_50, style]}>
|
||||
<Button
|
||||
label={_(msg`Remove attachment`)}
|
||||
onPress={onRemove}
|
||||
size="small"
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
shape="round">
|
||||
shape="round"
|
||||
style={[t.atoms.shadow_sm]}>
|
||||
<ButtonIcon icon={X} size="sm" />
|
||||
</Button>
|
||||
</View>
|
||||
|
||||
@@ -6,23 +6,23 @@ import {useLingui} from '@lingui/react'
|
||||
import {HITSLOP_10, MAX_ALT_TEXT} from '#/lib/constants'
|
||||
import {parseAltFromGIFDescription} from '#/lib/gif-alt-text'
|
||||
import {
|
||||
EmbedPlayerParams,
|
||||
type EmbedPlayerParams,
|
||||
parseEmbedPlayerFromUrl,
|
||||
} from '#/lib/strings/embed-player'
|
||||
import {isAndroid} from '#/platform/detection'
|
||||
import {useResolveGifQuery} from '#/state/queries/resolve-link'
|
||||
import {Gif} from '#/state/queries/tenor'
|
||||
import {type Gif} from '#/state/queries/tenor'
|
||||
import {AltTextCounterWrapper} from '#/view/com/composer/AltTextCounterWrapper'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {DialogControlProps} from '#/components/Dialog'
|
||||
import {type DialogControlProps} from '#/components/Dialog'
|
||||
import * as TextField from '#/components/forms/TextField'
|
||||
import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
|
||||
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
||||
import {PlusSmall_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
|
||||
import {GifEmbed} from '#/components/Post/Embed/ExternalEmbed/Gif'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {GifEmbed} from '../util/post-embeds/GifEmbed'
|
||||
import {AltTextReminder} from './photos/Gallery'
|
||||
|
||||
export function GifAltTextDialog({
|
||||
|
||||
@@ -4,10 +4,10 @@ import {useLingui} from '@lingui/react'
|
||||
|
||||
import {
|
||||
ADULT_CONTENT_LABELS,
|
||||
AdultSelfLabel,
|
||||
type AdultSelfLabel,
|
||||
OTHER_SELF_LABELS,
|
||||
OtherSelfLabel,
|
||||
SelfLabel,
|
||||
type OtherSelfLabel,
|
||||
type SelfLabel,
|
||||
} from '#/lib/moderation'
|
||||
import {isWeb} from '#/platform/detection'
|
||||
import {atoms as a, native, useTheme, web} from '#/alf'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import {ImageStyle, useWindowDimensions, View} from 'react-native'
|
||||
import {type ImageStyle, useWindowDimensions, View} from 'react-native'
|
||||
import {Image} from 'expo-image'
|
||||
import {msg, Plural, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
@@ -7,12 +7,12 @@ import {useLingui} from '@lingui/react'
|
||||
import {MAX_ALT_TEXT} from '#/lib/constants'
|
||||
import {enforceLen} from '#/lib/strings/helpers'
|
||||
import {isAndroid, isWeb} from '#/platform/detection'
|
||||
import {ComposerImage} from '#/state/gallery'
|
||||
import {type ComposerImage} from '#/state/gallery'
|
||||
import {AltTextCounterWrapper} from '#/view/com/composer/AltTextCounterWrapper'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {DialogControlProps} from '#/components/Dialog'
|
||||
import {type DialogControlProps} from '#/components/Dialog'
|
||||
import * as TextField from '#/components/forms/TextField'
|
||||
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
@@ -8,7 +8,7 @@ import {useCameraPermission} from '#/lib/hooks/usePermissions'
|
||||
import {openCamera} from '#/lib/media/picker'
|
||||
import {logger} from '#/logger'
|
||||
import {isMobileWeb, isNative} from '#/platform/detection'
|
||||
import {ComposerImage, createComposerImage} from '#/state/gallery'
|
||||
import {type ComposerImage, createComposerImage} from '#/state/gallery'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Button} from '#/components/Button'
|
||||
import {Camera_Stroke2_Corner0_Rounded as Camera} from '#/components/icons/Camera'
|
||||
|
||||
Reference in New Issue
Block a user