From 6570f56d8e22b22d099338c24731f525b860583a Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Thu, 7 Nov 2024 13:21:05 +0000 Subject: [PATCH 1/5] Increase memory on iOS (#6141) * increase memory on ios * add extended-virtual-addressing --- app.config.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app.config.js b/app.config.js index fed1b17a31..ecdbffb6c1 100644 --- a/app.config.js +++ b/app.config.js @@ -99,6 +99,8 @@ module.exports = function (config) { dark: DARK_SPLASH_CONFIG, }, entitlements: { + 'com.apple.developer.kernel.increased-memory-limit': true, + 'com.apple.developer.kernel.extended-virtual-addressing': true, 'com.apple.security.application-groups': 'group.app.bsky', }, privacyManifests: { From 5d0610d419906be0ef2c7c7ab0d1f66c366f3aed Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 8 Nov 2024 02:49:32 +0000 Subject: [PATCH 2/5] [Lightbox] New dismiss gesture (#6135) * Make iOS scrollview bounded to the image I've had to remove the dismiss handling because the scroll view no longer scrolls at rest. * Fix double-tap not working right after a vertical swipe It seems like for some reason the vertical swipe is still being handled by the scroll view, so double tap gets eaten while it's "coming back". But you don't really see it moving. Weird. * Add an intermediate LightboxImage component * Hoist useImageDimensions up * Implement xplat dismiss gesture This is now shared between platforms, letting us animate the backdrop and add a consistent "fly away" behavior. * Optimize Android compositing perf * Fix supertall images For example, https://bsky.app/profile/schlagteslinks.bsky.social/post/3l7y4l6yur72e * Fix oopsie --- .../ImageItem/ImageItem.android.tsx | 105 ++++----- .../components/ImageItem/ImageItem.ios.tsx | 95 ++++---- .../components/ImageItem/ImageItem.tsx | 9 +- src/view/com/lightbox/ImageViewing/index.tsx | 210 +++++++++++++++--- 4 files changed, 274 insertions(+), 145 deletions(-) diff --git a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx index ed6020000a..17c386771c 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx @@ -1,6 +1,10 @@ import React, {useState} from 'react' -import {ActivityIndicator, StyleSheet, View} from 'react-native' -import {Gesture, GestureDetector} from 'react-native-gesture-handler' +import {ActivityIndicator, StyleProp, StyleSheet, View} from 'react-native' +import { + Gesture, + GestureDetector, + PanGesture, +} from 'react-native-gesture-handler' import Animated, { AnimatedRef, measure, @@ -9,12 +13,10 @@ import Animated, { useAnimatedRef, useAnimatedStyle, useSharedValue, - withDecay, withSpring, } from 'react-native-reanimated' -import {Image} from 'expo-image' +import {Image, ImageStyle} from 'expo-image' -import {useImageDimensions} from '#/lib/media/image-sizes' import type {Dimensions as ImageDimensions, ImageSource} from '../../@types' import { applyRounding, @@ -26,6 +28,8 @@ import { TransformMatrix, } from '../../transforms' +const AnimatedImage = Animated.createAnimatedComponent(Image) + const MIN_SCREEN_ZOOM = 2 const MAX_ORIGINAL_IMAGE_ZOOM = 2 @@ -39,26 +43,28 @@ type Props = { isScrollViewBeingDragged: boolean showControls: boolean safeAreaRef: AnimatedRef + imageAspect: number | undefined + imageDimensions: ImageDimensions | undefined + imageStyle: StyleProp + dismissSwipePan: PanGesture } const ImageItem = ({ imageSrc, onTap, onZoom, - onRequestClose, isScrollViewBeingDragged, safeAreaRef, + imageAspect, + imageDimensions, + imageStyle, + dismissSwipePan, }: Props) => { const [isScaled, setIsScaled] = useState(false) - const [imageAspect, imageDimensions] = useImageDimensions({ - src: imageSrc.uri, - knownDimensions: imageSrc.dimensions, - }) const committedTransform = useSharedValue(initialTransform) const panTranslation = useSharedValue({x: 0, y: 0}) const pinchOrigin = useSharedValue({x: 0, y: 0}) const pinchScale = useSharedValue(1) const pinchTranslation = useSharedValue({x: 0, y: 0}) - const dismissSwipeTranslateY = useSharedValue(0) const containerRef = useAnimatedRef() // Keep track of when we're entering or leaving scaled rendering. @@ -97,19 +103,8 @@ const ImageItem = ({ prependPinch(t, pinchScale.value, pinchOrigin.value, pinchTranslation.value) prependTransform(t, committedTransform.value) const [translateX, translateY, scale] = readTransform(t) - - const dismissDistance = dismissSwipeTranslateY.value - const screenSize = measure(safeAreaRef) - const dismissProgress = screenSize - ? Math.min(Math.abs(dismissDistance) / (screenSize.height / 2), 1) - : 0 return { - opacity: 1 - dismissProgress, - transform: [ - {translateX}, - {translateY: translateY + dismissDistance}, - {scale}, - ], + transform: [{translateX}, {translateY: translateY}, {scale}], } }) @@ -307,28 +302,6 @@ const ImageItem = ({ committedTransform.value = withClampedSpring(finalTransform) }) - const dismissSwipePan = Gesture.Pan() - .enabled(!isScaled) - .activeOffsetY([-10, 10]) - .failOffsetX([-10, 10]) - .maxPointers(1) - .onUpdate(e => { - 'worklet' - dismissSwipeTranslateY.value = e.translationY - }) - .onEnd(e => { - 'worklet' - if (Math.abs(e.velocityY) > 1000) { - dismissSwipeTranslateY.value = withDecay({velocity: e.velocityY}) - runOnJS(onRequestClose)() - } else { - dismissSwipeTranslateY.value = withSpring(0, { - stiffness: 700, - damping: 50, - }) - } - }) - const composedGesture = isScrollViewBeingDragged ? // If the parent is not at rest, provide a no-op gesture. Gesture.Manual() @@ -340,26 +313,28 @@ const ImageItem = ({ ) return ( - - - - - - + + + + + + + + ) } diff --git a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx index a17d4fe66c..b4bbfb4d53 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx @@ -7,26 +7,27 @@ */ import React, {useState} from 'react' -import {ActivityIndicator, StyleSheet, View} from 'react-native' -import {Gesture, GestureDetector} from 'react-native-gesture-handler' +import {ActivityIndicator, StyleProp, StyleSheet, View} from 'react-native' +import { + Gesture, + GestureDetector, + PanGesture, +} from 'react-native-gesture-handler' import Animated, { AnimatedRef, - interpolate, measure, runOnJS, useAnimatedRef, useAnimatedStyle, - useSharedValue, } from 'react-native-reanimated' import {useSafeAreaFrame} from 'react-native-safe-area-context' -import {Image} from 'expo-image' +import {Image, ImageStyle} from 'expo-image' import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED' -import {useImageDimensions} from '#/lib/media/image-sizes' -import {ImageSource} from '../../@types' +import {Dimensions as ImageDimensions, ImageSource} from '../../@types' + +const AnimatedImage = Animated.createAnimatedComponent(Image) -const SWIPE_CLOSE_OFFSET = 75 -const SWIPE_CLOSE_VELOCITY = 1 const MAX_ORIGINAL_IMAGE_ZOOM = 2 const MIN_SCREEN_ZOOM = 2 @@ -38,24 +39,26 @@ type Props = { isScrollViewBeingDragged: boolean showControls: boolean safeAreaRef: AnimatedRef + imageAspect: number | undefined + imageDimensions: ImageDimensions | undefined + imageStyle: StyleProp + dismissSwipePan: PanGesture } const ImageItem = ({ imageSrc, onTap, onZoom, - onRequestClose, showControls, safeAreaRef, + imageAspect, + imageDimensions, + imageStyle, + dismissSwipePan, }: Props) => { const scrollViewRef = useAnimatedRef() - const translationY = useSharedValue(0) const [scaled, setScaled] = useState(false) const screenSizeDelayedForJSThreadOnly = useSafeAreaFrame() - const [imageAspect, imageDimensions] = useImageDimensions({ - src: imageSrc.uri, - knownDimensions: imageSrc.dimensions, - }) const maxZoomScale = Math.max( MIN_SCREEN_ZOOM, imageDimensions @@ -65,33 +68,21 @@ const ImageItem = ({ ) const animatedStyle = useAnimatedStyle(() => { + const screenSize = measure(safeAreaRef) ?? screenSizeDelayedForJSThreadOnly return { - flex: 1, - opacity: interpolate( - translationY.value, - [-SWIPE_CLOSE_OFFSET, 0, SWIPE_CLOSE_OFFSET], - [0.5, 1, 0.5], - ), + width: screenSize.width, + maxHeight: screenSize.height, + alignSelf: 'center', + aspectRatio: imageAspect, } }) const scrollHandler = useAnimatedScrollHandler({ onScroll(e) { - const nextIsScaled = e.zoomScale > 1 - translationY.value = nextIsScaled ? 0 : e.contentOffset.y - if (scaled !== nextIsScaled) { - runOnJS(handleZoom)(nextIsScaled) - } - }, - onEndDrag(e) { - const velocityY = e.velocity?.y ?? 0 const nextIsScaled = e.zoomScale > 1 if (scaled !== nextIsScaled) { runOnJS(handleZoom)(nextIsScaled) } - if (!nextIsScaled && Math.abs(velocityY) > SWIPE_CLOSE_VELOCITY) { - runOnJS(onRequestClose)() - } }, }) @@ -146,7 +137,11 @@ const ImageItem = ({ runOnJS(zoomTo)(nextZoomRect) }) - const composedGesture = Gesture.Exclusive(doubleTap, singleTap) + const composedGesture = Gesture.Exclusive( + dismissSwipePan, + doubleTap, + singleTap, + ) return ( @@ -158,21 +153,22 @@ const ImageItem = ({ showsVerticalScrollIndicator={false} maximumZoomScale={maxZoomScale} onScroll={scrollHandler} - contentContainerStyle={styles.scrollContainer}> - - - - + bounces={scaled} + bouncesZoom={true} + style={imageStyle} + centerContent> + + ) @@ -186,9 +182,6 @@ const styles = StyleSheet.create({ right: 0, bottom: 0, }, - scrollContainer: { - flex: 1, - }, image: { flex: 1, }, diff --git a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.tsx b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.tsx index 383bec9951..1cd6b00204 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.tsx @@ -1,10 +1,11 @@ // default implementation fallback for web import React from 'react' -import {View} from 'react-native' +import {ImageStyle, StyleProp, View} from 'react-native' +import {PanGesture} from 'react-native-gesture-handler' import {AnimatedRef} from 'react-native-reanimated' -import {ImageSource} from '../../@types' +import {Dimensions as ImageDimensions, ImageSource} from '../../@types' type Props = { imageSrc: ImageSource @@ -14,6 +15,10 @@ type Props = { isScrollViewBeingDragged: boolean showControls: boolean safeAreaRef: AnimatedRef + imageAspect: number | undefined + imageDimensions: ImageDimensions | undefined + imageStyle: StyleProp + dismissSwipePan: PanGesture } const ImageItem = (_props: Props) => { diff --git a/src/view/com/lightbox/ImageViewing/index.tsx b/src/view/com/lightbox/ImageViewing/index.tsx index 791701bca8..7a3a506914 100644 --- a/src/view/com/lightbox/ImageViewing/index.tsx +++ b/src/view/com/lightbox/ImageViewing/index.tsx @@ -10,17 +10,26 @@ import React, {useCallback, useState} from 'react' import {LayoutAnimation, Platform, StyleSheet, View} from 'react-native' +import {Gesture} from 'react-native-gesture-handler' import PagerView from 'react-native-pager-view' import Animated, { AnimatedRef, + cancelAnimation, + measure, + runOnJS, + SharedValue, + useAnimatedReaction, useAnimatedRef, useAnimatedStyle, + useSharedValue, + withDecay, withSpring, } from 'react-native-reanimated' import {Edge, SafeAreaView} from 'react-native-safe-area-context' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {Trans} from '@lingui/macro' +import {useImageDimensions} from '#/lib/media/image-sizes' import {colors, s} from '#/lib/styles' import {isIOS} from '#/platform/detection' import {Lightbox} from '#/state/lightbox' @@ -90,26 +99,55 @@ function ImageView({ const [isDragging, setIsDragging] = useState(false) const [imageIndex, setImageIndex] = useState(initialImageIndex) const [showControls, setShowControls] = useState(true) + const [isAltExpanded, setAltExpanded] = React.useState(false) + const dismissSwipeTranslateY = useSharedValue(0) + const isFlyingAway = useSharedValue(false) - const animatedHeaderStyle = useAnimatedStyle(() => ({ - pointerEvents: showControls ? 'box-none' : 'none', - opacity: withClampedSpring(showControls ? 1 : 0), - transform: [ - { - translateY: withClampedSpring(showControls ? 0 : -30), - }, - ], - })) - const animatedFooterStyle = useAnimatedStyle(() => ({ - flexGrow: 1, - pointerEvents: showControls ? 'box-none' : 'none', - opacity: withClampedSpring(showControls ? 1 : 0), - transform: [ - { - translateY: withClampedSpring(showControls ? 0 : 30), - }, - ], - })) + const containerStyle = useAnimatedStyle(() => { + if (isFlyingAway.value) { + return {pointerEvents: 'none'} + } + return {pointerEvents: 'auto'} + }) + const backdropStyle = useAnimatedStyle(() => { + const screenSize = measure(safeAreaRef) + let opacity = 1 + if (screenSize) { + const dragProgress = Math.min( + Math.abs(dismissSwipeTranslateY.value) / (screenSize.height / 2), + 1, + ) + opacity -= dragProgress + } + return { + opacity, + } + }) + const animatedHeaderStyle = useAnimatedStyle(() => { + const show = showControls && dismissSwipeTranslateY.value === 0 + return { + pointerEvents: show ? 'box-none' : 'none', + opacity: withClampedSpring(show ? 1 : 0), + transform: [ + { + translateY: withClampedSpring(show ? 0 : -30), + }, + ], + } + }) + const animatedFooterStyle = useAnimatedStyle(() => { + const show = showControls && dismissSwipeTranslateY.value === 0 + return { + flexGrow: 1, + pointerEvents: show ? 'box-none' : 'none', + opacity: withClampedSpring(show ? 1 : 0), + transform: [ + { + translateY: withClampedSpring(show ? 0 : 30), + }, + ], + } + }) const onTap = useCallback(() => { setShowControls(show => !show) @@ -123,7 +161,11 @@ function ImageView({ }, []) return ( - + + - {images.map(imageSrc => ( + {images.map((imageSrc, i) => ( - ))} - + - + setAltExpanded(e => !e)} onPressSave={onPressSave} onPressShare={onPressShare} /> - + + ) +} + +function LightboxImage({ + imageSrc, + onTap, + onZoom, + onRequestClose, + isScrollViewBeingDragged, + isScaled, + isFlyingAway, + isActive, + showControls, + safeAreaRef, + dismissSwipeTranslateY, +}: { + imageSrc: ImageSource + onRequestClose: () => void + onTap: () => void + onZoom: (scaled: boolean) => void + isScrollViewBeingDragged: boolean + isScaled: boolean + isActive: boolean + isFlyingAway: SharedValue + showControls: boolean + safeAreaRef: AnimatedRef + dismissSwipeTranslateY: SharedValue +}) { + const [imageAspect, imageDimensions] = useImageDimensions({ + src: imageSrc.uri, + knownDimensions: imageSrc.dimensions, + }) + + const dismissSwipePan = Gesture.Pan() + .enabled(isActive && !isScaled) + .activeOffsetY([-10, 10]) + .failOffsetX([-10, 10]) + .maxPointers(1) + .onUpdate(e => { + 'worklet' + dismissSwipeTranslateY.value = e.translationY + }) + .onEnd(e => { + 'worklet' + if (Math.abs(e.velocityY) > 1000) { + isFlyingAway.value = true + dismissSwipeTranslateY.value = withDecay({ + velocity: e.velocityY, + velocityFactor: Math.max(3000 / Math.abs(e.velocityY), 1), // Speed up if it's too slow. + deceleration: 1, // Danger! This relies on the reaction below stopping it. + }) + } else { + dismissSwipeTranslateY.value = withSpring(0, { + stiffness: 700, + damping: 50, + }) + } + }) + useAnimatedReaction( + () => { + const screenSize = measure(safeAreaRef) + return ( + !screenSize || + Math.abs(dismissSwipeTranslateY.value) > screenSize.height + ) + }, + (isOut, wasOut) => { + if (isOut && !wasOut) { + // Stop the animation from blocking the screen forever. + cancelAnimation(dismissSwipeTranslateY) + runOnJS(onRequestClose)() + } + }, + ) + + const imageStyle = useAnimatedStyle(() => { + return { + transform: [{translateY: dismissSwipeTranslateY.value}], + } + }) + return ( + ) } function LightboxFooter({ images, index, + isAltExpanded, + toggleAltExpanded, onPressSave, onPressShare, }: { images: ImageSource[] index: number + isAltExpanded: boolean + toggleAltExpanded: () => void onPressSave: (uri: string) => void onPressShare: (uri: string) => void }) { const {alt: altText, uri} = images[index] - const [isAltExpanded, setAltExpanded] = React.useState(false) const isMomentumScrolling = React.useRef(false) return ( !prev) + toggleAltExpanded() }} onLongPress={() => {}}> {altText} @@ -256,7 +405,14 @@ const styles = StyleSheet.create({ }, container: { flex: 1, + }, + backdrop: { backgroundColor: '#000', + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0, }, controls: { position: 'absolute', From 22dd4947f7d88166350c13367f2af7a51a55a36b Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 8 Nov 2024 02:52:03 +0000 Subject: [PATCH 3/5] [Lightbox] Add border radius to avatars (#6136) * Preserve shape in lightbox * Rename shapes to semantic meanings It looks like after all I do want to fork based on those. * Round avatars on the web * Oops --- src/screens/Profile/Header/Shell.tsx | 1 + .../com/lightbox/ImageViewing/@types/index.ts | 1 + .../ImageItem/ImageItem.android.tsx | 14 +- .../components/ImageItem/ImageItem.ios.tsx | 5 +- src/view/com/lightbox/Lightbox.web.tsx | 142 +++++++++++------- src/view/com/profile/ProfileSubpageHeader.tsx | 1 + src/view/com/util/post-embeds/index.tsx | 5 +- 7 files changed, 108 insertions(+), 61 deletions(-) diff --git a/src/screens/Profile/Header/Shell.tsx b/src/screens/Profile/Header/Shell.tsx index 925066d72e..fe325c1e5f 100644 --- a/src/screens/Profile/Header/Shell.tsx +++ b/src/screens/Profile/Header/Shell.tsx @@ -64,6 +64,7 @@ let ProfileHeaderShell = ({ height: 1000, width: 1000, }, + type: 'circle-avi', }, ], index: 0, diff --git a/src/view/com/lightbox/ImageViewing/@types/index.ts b/src/view/com/lightbox/ImageViewing/@types/index.ts index f5ab8bba9a..dc636a4495 100644 --- a/src/view/com/lightbox/ImageViewing/@types/index.ts +++ b/src/view/com/lightbox/ImageViewing/@types/index.ts @@ -21,4 +21,5 @@ export type ImageSource = { thumbUri: string alt?: string dimensions: Dimensions | null + type: 'image' | 'circle-avi' | 'rect-avi' } diff --git a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx index 17c386771c..f882dcf9eb 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx @@ -302,6 +302,11 @@ const ImageItem = ({ committedTransform.value = withClampedSpring(finalTransform) }) + const innerStyle = useAnimatedStyle(() => ({ + width: '100%', + aspectRatio: imageAspect, + })) + const composedGesture = isScrollViewBeingDragged ? // If the parent is not at rest, provide a no-op gesture. Gesture.Manual() @@ -312,6 +317,9 @@ const ImageItem = ({ singleTap, ) + const type = imageSrc.type + const borderRadius = + type === 'circle-avi' ? 1e5 : type === 'rect-avi' ? 20 : 0 return ( @@ -326,7 +334,7 @@ const ImageItem = ({ source={{uri: imageSrc.uri}} placeholderContentFit="contain" placeholder={{uri: imageSrc.thumbUri}} - style={[styles.image]} + style={[innerStyle, {borderRadius}]} accessibilityLabel={imageSrc.alt} accessibilityHint="" accessibilityIgnoresInvertColors @@ -342,9 +350,7 @@ const styles = StyleSheet.create({ container: { height: '100%', overflow: 'hidden', - }, - image: { - flex: 1, + justifyContent: 'center', }, loading: { position: 'absolute', diff --git a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx index b4bbfb4d53..e876479a39 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx @@ -143,6 +143,9 @@ const ImageItem = ({ singleTap, ) + const type = imageSrc.type + const borderRadius = + type === 'circle-avi' ? 1e5 : type === 'rect-avi' ? 20 : 0 return ( void }) { @@ -101,6 +97,8 @@ function LightboxInner({ return isTabletOrDesktop ? 32 : 24 }, [isTabletOrDesktop]) + const img = imgs[index] + const isAvi = img.type === 'circle-avi' || img.type === 'rect-avi' return ( - - - {canGoLeft && ( - - - - )} - {canGoRight && ( - - - - )} - + {isAvi ? ( + + {img.alt} + + ) : ( + + + {canGoLeft && ( + + + + )} + {canGoRight && ( + + + + )} + + )} - {imgs[index].alt ? ( + {img.alt ? ( - {imgs[index].alt} + {img.alt} @@ -203,6 +222,19 @@ const styles = StyleSheet.create({ height: '100%', resizeMode: 'contain', }, + aviCenterer: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + avi: { + // @ts-ignore web-only + maxWidth: `calc(min(400px, 100vw))`, + // @ts-ignore web-only + maxHeight: `calc(min(400px, 100vh))`, + padding: 16, + boxSizing: 'border-box', + }, icon: { color: colors.white, }, diff --git a/src/view/com/profile/ProfileSubpageHeader.tsx b/src/view/com/profile/ProfileSubpageHeader.tsx index b712b346b5..5208224c50 100644 --- a/src/view/com/profile/ProfileSubpageHeader.tsx +++ b/src/view/com/profile/ProfileSubpageHeader.tsx @@ -80,6 +80,7 @@ export function ProfileSubpageHeader({ height: 1000, width: 1000, }, + type: 'rect-avi', }, ], index: 0, diff --git a/src/view/com/util/post-embeds/index.tsx b/src/view/com/util/post-embeds/index.tsx index d686d2bd32..ea0badab00 100644 --- a/src/view/com/util/post-embeds/index.tsx +++ b/src/view/com/util/post-embeds/index.tsx @@ -152,7 +152,10 @@ export function PostEmbeds({ thumbDims: MeasuredDimensions | null, ) => { openLightbox({ - images: items, + images: items.map(item => ({ + ...item, + type: 'image', + })), index, thumbDims, }) From 468c4b8f5ae68f537f2844797472b4c3794b094b Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Fri, 8 Nov 2024 03:38:32 +0000 Subject: [PATCH 4/5] Improve chat performance (#6157) * fix worklet funcs on gestures * don't access .value in render --- src/components/dms/ActionsWrapper.tsx | 3 ++- src/screens/Messages/Conversation.tsx | 4 +--- src/screens/Messages/components/MessagesList.tsx | 10 +++++----- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/components/dms/ActionsWrapper.tsx b/src/components/dms/ActionsWrapper.tsx index 9b06bd0b20..b77516e7b7 100644 --- a/src/components/dms/ActionsWrapper.tsx +++ b/src/components/dms/ActionsWrapper.tsx @@ -53,9 +53,11 @@ export function ActionsWrapper({ .numberOfTaps(2) .hitSlop(HITSLOP_10) .onEnd(open) + .runOnJS(true) const pressAndHoldGesture = Gesture.LongPress() .onStart(() => { + 'worklet' scale.value = withTiming(1.05, {duration: 200}, finished => { if (!finished) return runOnJS(open)() @@ -65,7 +67,6 @@ export function ActionsWrapper({ .onTouchesUp(shrink) .onTouchesMove(shrink) .cancelsTouchesInView(false) - .runOnJS(true) const composedGestures = Gesture.Exclusive( doubleTapGesture, diff --git a/src/screens/Messages/Conversation.tsx b/src/screens/Messages/Conversation.tsx index 651915738a..e2e646a3d3 100644 --- a/src/screens/Messages/Conversation.tsx +++ b/src/screens/Messages/Conversation.tsx @@ -127,9 +127,7 @@ function Inner() { setHasScrolled={setHasScrolled} /> ) : ( - <> - - + )} {!readyToShow && ( layoutHeight.value) { convoState.fetchMessageHistory() } - }, [convoState, hasScrolled, layoutHeight.value]) + }, [convoState, hasScrolled, layoutHeight]) const onScroll = React.useCallback( (e: ReanimatedScrollEvent) => { @@ -374,7 +374,7 @@ export function MessagesList({ }, [ flatListRef, - keyboardIsOpening.value, + keyboardIsOpening, layoutScrollWithoutAnimation, layoutHeight, ], From 6b1ffffce91fcad23548e63b6f912f855f46df0c Mon Sep 17 00:00:00 2001 From: gpp-0 <52042597+gpp-0@users.noreply.github.com> Date: Fri, 8 Nov 2024 06:19:52 +0200 Subject: [PATCH 5/5] Fix non-home screen soft resetting when feed is selected from right nav (#6158) --- src/view/shell/desktop/Feeds.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view/shell/desktop/Feeds.tsx b/src/view/shell/desktop/Feeds.tsx index 2f5f954274..bb6b8cadd3 100644 --- a/src/view/shell/desktop/Feeds.tsx +++ b/src/view/shell/desktop/Feeds.tsx @@ -41,7 +41,7 @@ export function DesktopFeeds() { onPress={() => { setSelectedFeed(feed) navigation.navigate('Home') - if (feed === selectedFeed) { + if (route.name === 'Home' && feed === selectedFeed) { emitSoftReset() } }}