diff --git a/package.json b/package.json
index c515ecdcee..a879182d9f 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d0136363f7..0ac8974cf9 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -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
diff --git a/src/components/Post/Embed/GalleryFallbackEmbed.tsx b/src/components/Post/Embed/GalleryFallbackEmbed.tsx
new file mode 100644
index 0000000000..60592ccc6c
--- /dev/null
+++ b/src/components/Post/Embed/GalleryFallbackEmbed.tsx
@@ -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 (
+
+
+
+ Something new is here
+
+ {count ? (
+
+
+ {plural(count, {
+ one: 'This post has # photo.',
+ other: 'This post has # photos.',
+ })}
+
+
+ {plural(count, {
+ one: 'Update your app to see it.',
+ other: 'Update your app to see them all.',
+ })}
+
+
+ ) : (
+
+ Update your app to see it.
+
+ )}
+
+
+ )
+}
diff --git a/src/components/Post/Embed/index.tsx b/src/components/Post/Embed/index.tsx
index ab6f5dc439..211a7a7687 100644
--- a/src/components/Post/Embed/index.tsx
+++ b/src/components/Post/Embed/index.tsx
@@ -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
}
case 'feed':
@@ -148,6 +150,15 @@ function MediaEmbed({
)
}
+ case 'gallery': {
+ return (
+
+
+
+ )
+ }
default: {
return null
}
diff --git a/src/types/bsky/post.ts b/src/types/bsky/post.ts
index fada39da81..dd54c1ebdb 100644
--- a/src/types/bsky/post.ts
+++ b/src/types/bsky/post.ts
@@ -1,6 +1,7 @@
import {
type $Typed,
AppBskyEmbedExternal,
+ AppBskyEmbedGallery,
AppBskyEmbedImages,
AppBskyEmbedRecord,
AppBskyEmbedRecordWithMedia,
@@ -55,6 +56,10 @@ export type Embed =
type: 'video'
view: $Typed
}
+ | {
+ type: 'gallery'
+ view: $Typed
+ }
| {
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',
diff --git a/src/view/screens/Storybook/GalleryFallback.tsx b/src/view/screens/Storybook/GalleryFallback.tsx
new file mode 100644
index 0000000000..7cca087dba
--- /dev/null
+++ b/src/view/screens/Storybook/GalleryFallback.tsx
@@ -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 (
+
+ Gallery fallback (APP-2308)
+
+ No count
+
+
+ 1 photo
+
+
+ 5 photos
+
+
+ 10 photos
+
+
+ )
+}
diff --git a/src/view/screens/Storybook/Storybook.tsx b/src/view/screens/Storybook/Storybook.tsx
index 8fb85e8e0a..33c31a9ee6 100644
--- a/src/view/screens/Storybook/Storybook.tsx
+++ b/src/view/screens/Storybook/Storybook.tsx
@@ -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() {
+