From c254225141a23f06b5b5c853145726181fbe3459 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Tue, 14 Apr 2026 16:55:15 -0500 Subject: [PATCH] Let's keep it simple and use existing composer solution --- package.json | 14 +-- src/view/com/composer/photos/Gallery.tsx | 148 +++++++++++++---------- yarn.lock | 92 ++++++++------ 3 files changed, 147 insertions(+), 107 deletions(-) diff --git a/package.json b/package.json index 51231cef06..66f6ddd0a6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bsky.app", - "version": "1.120.0", + "version": "1.121.0", "private": true, "engines": { "node": ">=20" @@ -81,14 +81,15 @@ "icons:optimize": "svgo -f ./assets/icons" }, "dependencies": { - "@atproto/api": "^0.19.6", + "@atproto/api": "^0.19.8", "@bitdrift/react-native": "^0.6.8", "@braintree/sanitize-url": "^6.0.2", "@bsky.app/alf": "^0.1.7", "@bsky.app/expo-image-crop-tool": "^0.5.0", + "@bsky.app/expo-scroll-edge-effect": "^0.1.4", "@bsky.app/expo-translate-text": "^0.2.9", "@bsky.app/react-native-mmkv": "2.12.5", - "@bsky.app/sift": "^0.3.1", + "@bsky.app/sift": "^0.3.2", "@bsky.app/tapper": "^0.5.0", "@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet", "@emoji-mart/data": "^1.2.1", @@ -142,7 +143,6 @@ "bcp-47-match": "^2.0.3", "date-fns": "^2.30.0", "email-validator": "^2.0.4", - "embla-carousel-react": "^8.6.0", "emoji-mart": "^5.6.0", "emoji-regex": "^10.4.0", "eventemitter3": "^5.0.1", @@ -157,6 +157,7 @@ "expo-device": "~8.0.10", "expo-file-system": "~19.0.21", "expo-font": "~14.0.11", + "expo-glass-effect": "55.0.8", "expo-haptics": "~15.0.8", "expo-image": "~3.0.11", "expo-image-manipulator": "~14.0.8", @@ -169,7 +170,7 @@ "expo-location": "~19.0.8", "expo-media-library": "~18.2.1", "expo-notifications": "~0.32.16", - "expo-paste-input": "^0.1.12", + "expo-paste-input": "^0.1.15", "expo-privacy-sensitive": "^0.1.0", "expo-screen-orientation": "~9.0.8", "expo-sharing": "~14.0.8", @@ -214,7 +215,7 @@ "react-native-drawer-layout": "^4.2.2", "react-native-edge-to-edge": "^1.6.0", "react-native-gesture-handler": "~2.28.0", - "react-native-keyboard-controller": "^1.21.0", + "react-native-keyboard-controller": "^1.21.5", "react-native-pager-view": "6.8.0", "react-native-progress": "bluesky-social/react-native-progress", "react-native-qrcode-styled": "^0.3.3", @@ -246,7 +247,6 @@ "@babel/runtime": "^7.26.0", "@crowdin/cli": "^4.14.1", "@eslint/js": "^9.39.2", - "@expo/config-plugins": "~54.0.4", "@lingui/babel-plugin-lingui-macro": "^5.9.2", "@lingui/cli": "^5.9.2", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15", diff --git a/src/view/com/composer/photos/Gallery.tsx b/src/view/com/composer/photos/Gallery.tsx index 28e7c859c4..0df31da625 100644 --- a/src/view/com/composer/photos/Gallery.tsx +++ b/src/view/com/composer/photos/Gallery.tsx @@ -1,12 +1,14 @@ -import {memo, useState} from 'react' +import {memo, useMemo, useState} from 'react' import { findNodeHandle, + type ImageStyle, Keyboard, type LayoutChangeEvent, Platform, StyleSheet, TouchableOpacity, View, + type ViewStyle, } from 'react-native' import {Image} from 'expo-image' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' @@ -14,10 +16,10 @@ import {msg} from '@lingui/core/macro' import {useLingui} from '@lingui/react' import {Trans} from '@lingui/react/macro' +import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {type Dimensions} from '#/lib/media/types' import {colors} from '#/lib/styles' import {type ComposerImage, cropImage} from '#/state/gallery' -import {DraggableScrollView} from '#/view/com/pager/DraggableScrollView' import {atoms as a, tokens, useTheme} from '#/alf' import {Admonition} from '#/components/Admonition' import * as Dialog from '#/components/Dialog' @@ -30,7 +32,6 @@ import {EditImageDialog} from './EditImageDialog' import {ImageAltTextDialog} from './ImageAltTextDialog' const IMAGE_GAP = 8 -const CONTAINER_HEIGHT = 200 interface GalleryProps { images: ComposerImage[] @@ -62,32 +63,53 @@ interface GalleryInnerProps extends GalleryProps { containerInfo: Dimensions } -const getItemWidth = (image: ComposerImage, height: number) => { - const source = image.transformed ?? image.source - if (source.width > 0 && source.height > 0) { - const ratio = source.width / source.height - const w = height * ratio - // Clamp: at least 60% of height, at most 1.5x height - return Math.max(height * 0.6, Math.min(w, height * 1.5)) - } - return height -} +const GalleryInner = ({images, containerInfo, dispatch}: GalleryInnerProps) => { + const {isMobile} = useWebMediaQueries() + + const {altTextControlStyle, imageControlsStyle, imageStyle} = useMemo(() => { + const side = + images.length === 1 + ? 250 + : (containerInfo.width - IMAGE_GAP * (images.length - 1)) / + images.length + + const isOverflow = isMobile && images.length > 2 + + return { + altTextControlStyle: isOverflow + ? {left: 4, bottom: 4} + : !isMobile && images.length < 3 + ? {left: 8, top: 8} + : {left: 4, top: 4}, + imageControlsStyle: { + display: 'flex' as const, + flexDirection: 'row' as const, + position: 'absolute' as const, + ...(isOverflow + ? {top: 4, right: 4, gap: 4} + : !isMobile && images.length < 3 + ? {top: 8, right: 8, gap: 8} + : {top: 4, right: 4, gap: 4}), + zIndex: 1, + }, + imageStyle: { + height: side, + width: side, + }, + } + }, [images.length, containerInfo, isMobile]) -const GalleryInner = ({images, dispatch}: GalleryInnerProps) => { return images.length !== 0 ? ( <> - + {images.map(image => { return ( { dispatch({type: 'embed_update_image', image: next}) }} @@ -97,7 +119,7 @@ const GalleryInner = ({images, dispatch}: GalleryInnerProps) => { /> ) })} - + {images.some(image => !image.alt) && ( @@ -112,16 +134,18 @@ const GalleryInner = ({images, dispatch}: GalleryInnerProps) => { type GalleryItemProps = { image: ComposerImage - itemWidth: number - itemHeight: number + altTextControlStyle?: ViewStyle + imageControlsStyle?: ViewStyle + imageStyle?: ImageStyle onChange: (next: ComposerImage) => void onRemove: () => void } const GalleryItem = ({ image, - itemWidth, - itemHeight, + altTextControlStyle, + imageControlsStyle, + imageStyle, onChange, onRemove, }: GalleryItemProps): React.ReactNode => { @@ -134,6 +158,7 @@ const GalleryItem = ({ const [altBtnViewTag, setAltBtnViewTag] = useState() const altBtnRef = (node: View | null) => { + // for iOS 26 fluid transition if (IS_IOS && node) { const tag = findNodeHandle(node) if (tag != null) setAltBtnViewTag(tag) @@ -162,8 +187,33 @@ const GalleryItem = ({ return ( + + {image.alt.length !== 0 ? ( + + ) : ( + + )} + + ALT + + - - - {image.alt.length !== 0 ? ( - - ) : ( - - )} - - ALT - - -