diff --git a/native/chat/multimedia-message-multimedia.react.js b/native/chat/multimedia-message-multimedia.react.js index 7e981acac..753b6e55a 100644 --- a/native/chat/multimedia-message-multimedia.react.js +++ b/native/chat/multimedia-message-multimedia.react.js @@ -1,251 +1,237 @@ // @flow import type { LeafRoute } from '@react-navigation/core'; import { useRoute } from '@react-navigation/native'; import invariant from 'invariant'; import * as React from 'react'; import { View, StyleSheet } from 'react-native'; -import Animated from 'react-native-reanimated'; +import { + useAnimatedStyle, + interpolate, + Extrapolate, +} from 'react-native-reanimated'; import { type MediaInfo } from 'lib/types/media-types.js'; import InlineMultimedia from './inline-multimedia.react.js'; import { getMediaKey } from './multimedia-message-utils.js'; import { type PendingMultimediaUpload } from '../input/input-state.js'; import { type KeyboardState, KeyboardContext, } from '../keyboard/keyboard-state.js'; import { OverlayContext, type OverlayContextType, } from '../navigation/overlay-context.js'; import { ImageModalRouteName } from '../navigation/route-names.js'; import { type Colors, useColors } from '../themes/colors.js'; import type { ChatMultimediaMessageInfoItem } from '../types/chat-types.js'; import type { VerticalBounds, LayoutCoordinates, } from '../types/layout-types.js'; import { type ViewStyle, type AnimatedStyleObj, AnimatedView, } from '../types/styles.js'; -const { Node, sub, interpolateNode, Extrapolate } = Animated; - type BaseProps = { +mediaInfo: MediaInfo, +item: ChatMultimediaMessageInfoItem, +verticalBounds: ?VerticalBounds, +style: ViewStyle, +postInProgress: boolean, +pendingUpload: ?PendingMultimediaUpload, +onPressMultimedia?: ( mediaInfo: MediaInfo, initialCoordinates: LayoutCoordinates, ) => void, +clickable: boolean, +setClickable: boolean => void, }; type Props = { ...BaseProps, +route: LeafRoute<>, // Redux state +colors: Colors, // withKeyboardState +keyboardState: ?KeyboardState, // withOverlayContext +overlayContext: ?OverlayContextType, +viewRef: { current: ?React.ElementRef }, +onPress: () => void, +onLayout: () => void, + +animatedWrapperStyle: AnimatedStyleObj, }; -type State = { - +opacity: number | Node, -}; -class MultimediaMessageMultimedia extends React.PureComponent { - constructor(props: Props) { - super(props); - this.state = { - opacity: this.getOpacity(), - }; - } +class MultimediaMessageMultimedia extends React.PureComponent { static getOverlayContext(props: Props): OverlayContextType { const { overlayContext } = props; invariant( overlayContext, 'MultimediaMessageMultimedia should have OverlayContext', ); return overlayContext; } - static getModalOverlayPosition(props: Props): ?Animated.Value { - const overlayContext = MultimediaMessageMultimedia.getOverlayContext(props); - const { visibleOverlays } = overlayContext; - for (const overlay of visibleOverlays) { - if ( - overlay.routeName === ImageModalRouteName && - overlay.presentedFrom === props.route.key && - overlay.routeKey === getMediaKey(props.item, props.mediaInfo) - ) { - return overlay.position; - } - } - return undefined; - } - - getOpacity(): number | Animated.Node { - const overlayPosition = MultimediaMessageMultimedia.getModalOverlayPosition( - this.props, - ); - if (!overlayPosition) { - return 1; - } - return sub( - 1, - interpolateNode(overlayPosition, { - inputRange: [0.1, 0.11], - outputRange: [0, 1], - extrapolate: Extrapolate.CLAMP, - }), - ); - } - componentDidUpdate(prevProps: Props) { - const overlayPosition = MultimediaMessageMultimedia.getModalOverlayPosition( - this.props, - ); - const prevOverlayPosition = - MultimediaMessageMultimedia.getModalOverlayPosition(prevProps); - if (overlayPosition !== prevOverlayPosition) { - this.setState({ opacity: this.getOpacity() }); - } - const scrollIsDisabled = MultimediaMessageMultimedia.getOverlayContext(this.props) .scrollBlockingModalStatus !== 'closed'; const scrollWasDisabled = MultimediaMessageMultimedia.getOverlayContext(prevProps) .scrollBlockingModalStatus !== 'closed'; if (!scrollIsDisabled && scrollWasDisabled) { this.props.setClickable(true); } } render(): React.Node { - const { opacity } = this.state; - const animatedWrapperStyle: AnimatedStyleObj = { opacity }; - const wrapperStyles = [ - styles.container, - animatedWrapperStyle, - this.props.style, - ]; - const { mediaInfo, pendingUpload, postInProgress, viewRef, onPress, onLayout, + animatedWrapperStyle, } = this.props; + + const wrapperStyles = [ + styles.container, + animatedWrapperStyle, + this.props.style, + ]; return ( ); } } const styles = StyleSheet.create({ container: { flex: 1, overflow: 'hidden', }, expand: { flex: 1, }, }); const ConnectedMultimediaMessageMultimedia: React.ComponentType = React.memo(function ConnectedMultimediaMessageMultimedia( props: BaseProps, ) { const colors = useColors(); const keyboardState = React.useContext(KeyboardContext); const overlayContext = React.useContext(OverlayContext); invariant( overlayContext, 'MultimediaMessageMultimedia should have OverlayContext', ); const route = useRoute(); const viewRef = React.useRef>(); const dismissKeyboardIfShowing = React.useCallback((): boolean => { return !!(keyboardState && keyboardState.dismissKeyboardIfShowing()); }, [keyboardState]); const onPress = React.useCallback(() => { const { clickable, verticalBounds, onPressMultimedia, setClickable, mediaInfo, } = props; if (!clickable) { return; } if (dismissKeyboardIfShowing()) { return; } const view = viewRef.current; if (!view || !verticalBounds) { return; } const measureCallback = onPressMultimedia; if (!measureCallback) { return; } setClickable(false); overlayContext.setScrollBlockingModalStatus('open'); view.measure((x, y, width, height, pageX, pageY) => { const coordinates = { x: pageX, y: pageY, width, height }; measureCallback(mediaInfo, coordinates); }); }, [dismissKeyboardIfShowing, overlayContext, props]); const onLayout = React.useCallback(() => {}, []); + const overlayPosition = (() => { + const { visibleOverlays } = overlayContext; + for (const overlay of visibleOverlays) { + if ( + overlay.routeName === ImageModalRouteName && + overlay.presentedFrom === route.key && + overlay.routeKey === getMediaKey(props.item, props.mediaInfo) + ) { + return overlay.positionV2; + } + } + return undefined; + })(); + + const animatedWrapperStyle = useAnimatedStyle(() => { + let opacity; + if (overlayPosition) { + opacity = + 1 - + interpolate( + overlayPosition.value, + [0.1, 0.11], + [0, 1], + Extrapolate.CLAMP, + ); + } else { + opacity = 1; + } + return { + opacity, + }; + }); + return ( ); }); export default ConnectedMultimediaMessageMultimedia;