Add OTA fallback for gallery embed
Pre-update native builds don't recognize app.bsky.embed.gallery (Photos v2, >4 images). Bump @atproto/api to 0.20.9 so the OTA bundle can parse the embed, and render a friendly "Something new is here" prompt with the photo count and a link to bsky.app/download in place of the unrenderable gallery. Native-only; web ships with full gallery support in the same release. APP-2308
This commit is contained in:
+1
-1
@@ -93,7 +93,7 @@
|
||||
"prettier": "prettier --check ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@atproto/api": "0.20.8",
|
||||
"@atproto/api": "0.20.9",
|
||||
"@atproto/syntax": "0.6.1",
|
||||
"@bitdrift/react-native": "^0.6.8",
|
||||
"@braintree/sanitize-url": "^6.0.2",
|
||||
|
||||
Generated
+5
-5
@@ -242,8 +242,8 @@ importers:
|
||||
.:
|
||||
dependencies:
|
||||
'@atproto/api':
|
||||
specifier: 0.20.8
|
||||
version: 0.20.8
|
||||
specifier: 0.20.9
|
||||
version: 0.20.9
|
||||
'@atproto/syntax':
|
||||
specifier: 0.6.1
|
||||
version: 0.6.1
|
||||
@@ -877,8 +877,8 @@ packages:
|
||||
graphql:
|
||||
optional: true
|
||||
|
||||
'@atproto/api@0.20.8':
|
||||
resolution: {integrity: sha512-rTkA6kOmA2axSrg6VgpdXpsCFWpofnHBOn6pKg69Ju5MpIHqk4haQMgjBcVh1G3kUxzwgSAr7SYrPS3dFe5Etg==}
|
||||
'@atproto/api@0.20.9':
|
||||
resolution: {integrity: sha512-Yuw7Ewn+yMJZ8GskbuvI3lKPW65rsXic1xjFA2Dpq6H8WjVYs6xNZ31bkwtTYDDwjKIZcJmAVbAVgdfjo4T9iw==}
|
||||
engines: {node: '>=22'}
|
||||
|
||||
'@atproto/common-web@0.5.0':
|
||||
@@ -9493,7 +9493,7 @@ snapshots:
|
||||
|
||||
'@0no-co/graphql.web@1.2.0': {}
|
||||
|
||||
'@atproto/api@0.20.8':
|
||||
'@atproto/api@0.20.9':
|
||||
dependencies:
|
||||
'@atproto/common-web': 0.5.0
|
||||
'@atproto/lexicon': 0.7.1
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import {Linking, View} from 'react-native'
|
||||
import {plural} from '@lingui/core/macro'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {BSKY_DOWNLOAD_URL} from '#/lib/constants'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import {Sparkle_Stroke2_Corner0_Rounded as Sparkle} from '#/components/icons/Sparkle'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {IS_NATIVE} from '#/env'
|
||||
|
||||
/**
|
||||
* OTA-able fallback that ships to native builds which don't yet know how to
|
||||
* render the new gallery embed (>4 images, Photos v2). Final copy and visual
|
||||
* treatment pending design from Darrin/Danielle/Alex.
|
||||
*
|
||||
* Native-only per APP-2308 - web builds receive the new gallery support in
|
||||
* the same release that adds it.
|
||||
*/
|
||||
export function GalleryFallbackEmbed({count}: {count?: number}) {
|
||||
const t = useTheme()
|
||||
const {t: l} = useLingui()
|
||||
|
||||
if (!IS_NATIVE) return null
|
||||
|
||||
const bodyStyle = [
|
||||
a.text_sm,
|
||||
a.text_center,
|
||||
a.leading_snug,
|
||||
t.atoms.text_contrast_high,
|
||||
]
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
a.mt_sm,
|
||||
a.rounded_md,
|
||||
a.border,
|
||||
a.p_lg,
|
||||
a.gap_sm,
|
||||
a.align_center,
|
||||
{
|
||||
borderColor: t.palette.primary_200,
|
||||
backgroundColor: t.palette.primary_25,
|
||||
},
|
||||
]}>
|
||||
<Sparkle size="lg" fill={t.palette.primary_500} />
|
||||
<Text style={[a.text_md, a.font_bold, a.text_center, t.atoms.text]}>
|
||||
<Trans>Something new is here</Trans>
|
||||
</Text>
|
||||
{count ? (
|
||||
<View>
|
||||
<Text style={bodyStyle}>
|
||||
{plural(count, {
|
||||
one: 'This post has # photo.',
|
||||
other: 'This post has # photos.',
|
||||
})}
|
||||
</Text>
|
||||
<Text style={bodyStyle}>
|
||||
{plural(count, {
|
||||
one: 'Update your app to see it.',
|
||||
other: 'Update your app to see them all.',
|
||||
})}
|
||||
</Text>
|
||||
</View>
|
||||
) : (
|
||||
<Text style={bodyStyle}>
|
||||
<Trans>Update your app to see it.</Trans>
|
||||
</Text>
|
||||
)}
|
||||
<Button
|
||||
label={l`Update your app`}
|
||||
size="small"
|
||||
color="primary"
|
||||
onPress={() => {
|
||||
void Linking.openURL(BSKY_DOWNLOAD_URL)
|
||||
}}
|
||||
style={[a.mt_xs]}>
|
||||
<ButtonText>
|
||||
<Trans>Update app</Trans>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
import {ChatInviteEmbed} from './ChatInviteEmbed'
|
||||
import {ExternalEmbed} from './ExternalEmbed'
|
||||
import {ModeratedFeedEmbed} from './FeedEmbed'
|
||||
import {GalleryFallbackEmbed} from './GalleryFallbackEmbed'
|
||||
import {ImageEmbed} from './ImageEmbed'
|
||||
import {ModeratedListEmbed} from './ListEmbed'
|
||||
import {PostPlaceholder as PostPlaceholderText} from './PostPlaceholder'
|
||||
@@ -55,7 +56,8 @@ export function Embed({embed: rawEmbed, ...rest}: EmbedProps) {
|
||||
switch (embed.type) {
|
||||
case 'images':
|
||||
case 'link':
|
||||
case 'video': {
|
||||
case 'video':
|
||||
case 'gallery': {
|
||||
return <MediaEmbed embed={embed} {...rest} />
|
||||
}
|
||||
case 'feed':
|
||||
@@ -148,6 +150,15 @@ function MediaEmbed({
|
||||
</ContentHider>
|
||||
)
|
||||
}
|
||||
case 'gallery': {
|
||||
return (
|
||||
<ContentHider
|
||||
modui={rest.moderation?.ui('contentMedia')}
|
||||
activeStyle={[a.mt_sm]}>
|
||||
<GalleryFallbackEmbed count={embed.view.items.length} />
|
||||
</ContentHider>
|
||||
)
|
||||
}
|
||||
default: {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
type $Typed,
|
||||
AppBskyEmbedExternal,
|
||||
AppBskyEmbedGallery,
|
||||
AppBskyEmbedImages,
|
||||
AppBskyEmbedRecord,
|
||||
AppBskyEmbedRecordWithMedia,
|
||||
@@ -55,6 +56,10 @@ export type Embed =
|
||||
type: 'video'
|
||||
view: $Typed<AppBskyEmbedVideo.View>
|
||||
}
|
||||
| {
|
||||
type: 'gallery'
|
||||
view: $Typed<AppBskyEmbedGallery.View>
|
||||
}
|
||||
| {
|
||||
type: 'post_with_media'
|
||||
view: Embed
|
||||
@@ -140,6 +145,11 @@ export function parseEmbed(embed: AppBskyFeedDefs.PostView['embed']): Embed {
|
||||
view: parseEmbedRecordView(embed.record),
|
||||
media: parseEmbed(embed.media),
|
||||
}
|
||||
} else if (AppBskyEmbedGallery.isView(embed)) {
|
||||
return {
|
||||
type: 'gallery',
|
||||
view: embed,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
type: 'unknown',
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import {View} from 'react-native'
|
||||
|
||||
import {atoms as a} from '#/alf'
|
||||
import {GalleryFallbackEmbed} from '#/components/Post/Embed/GalleryFallbackEmbed'
|
||||
import {H1, H3} from '#/components/Typography'
|
||||
|
||||
export function GalleryFallback() {
|
||||
return (
|
||||
<View style={[a.gap_md]}>
|
||||
<H1>Gallery fallback (APP-2308)</H1>
|
||||
|
||||
<H3>No count</H3>
|
||||
<GalleryFallbackEmbed />
|
||||
|
||||
<H3>1 photo</H3>
|
||||
<GalleryFallbackEmbed count={1} />
|
||||
|
||||
<H3>5 photos</H3>
|
||||
<GalleryFallbackEmbed count={5} />
|
||||
|
||||
<H3>10 photos</H3>
|
||||
<GalleryFallbackEmbed count={10} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import {Breakpoints} from './Breakpoints'
|
||||
import {Buttons} from './Buttons'
|
||||
import {Dialogs} from './Dialogs'
|
||||
import {Forms} from './Forms'
|
||||
import {GalleryFallback} from './GalleryFallback'
|
||||
import {Icons} from './Icons'
|
||||
import {Links} from './Links'
|
||||
import {Menus} from './Menus'
|
||||
@@ -120,6 +121,7 @@ export default function Storybook() {
|
||||
<Breakpoints />
|
||||
<Dialogs />
|
||||
<Admonitions />
|
||||
<GalleryFallback />
|
||||
<Settings />
|
||||
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user