diff --git a/native/media/media-gallery-media.react.js b/native/media/media-gallery-media.react.js
index cf1f31b03..46584eb94 100644
--- a/native/media/media-gallery-media.react.js
+++ b/native/media/media-gallery-media.react.js
@@ -1,324 +1,319 @@
// @flow
import Icon from '@expo/vector-icons/FontAwesome.js';
import MaterialIcon from '@expo/vector-icons/MaterialIcons.js';
import LottieView from 'lottie-react-native';
import * as React from 'react';
import {
TouchableOpacity,
StyleSheet,
View,
Text,
Platform,
Animated,
Easing,
} from 'react-native';
-import Reanimated, {
- EasingNode as ReanimatedEasing,
- useValue,
+import {
+ Easing as ReanimatedEasing,
+ interpolate,
+ useAnimatedStyle,
+ useSharedValue,
+ withTiming,
} from 'react-native-reanimated';
import Video from 'react-native-video';
import { type MediaLibrarySelection } from 'lib/types/media-types.js';
import GestureTouchableOpacity from '../components/gesture-touchable-opacity.react.js';
import { type DimensionsInfo } from '../redux/dimensions-updater.react.js';
import type { AnimatedValue } from '../types/react-native.js';
import {
AnimatedView,
AnimatedImage,
- type AnimatedViewStyle,
type AnimatedStyleObj,
} from '../types/styles.js';
const animatedSpec = {
duration: 400,
easing: Easing.inOut(Easing.ease),
useNativeDriver: true,
};
const reanimatedSpec = {
duration: 400,
easing: ReanimatedEasing.inOut(ReanimatedEasing.ease),
};
type Props = {
+selection: MediaLibrarySelection,
+containerHeight: number,
+queueModeActive: boolean,
+isQueued: boolean,
+setMediaQueued: (media: MediaLibrarySelection, isQueued: boolean) => void,
+sendMedia: (media: MediaLibrarySelection) => void,
+isFocused: boolean,
+setFocus: (media: MediaLibrarySelection, isFocused: boolean) => void,
+dimensions: DimensionsInfo,
};
function MediaGalleryMedia(props: Props): React.Node {
const {
selection,
containerHeight,
queueModeActive,
isQueued,
setMediaQueued,
sendMedia,
isFocused,
setFocus,
dimensions,
} = props;
- const focusProgress: Reanimated.Value = useValue(0);
+ const focusProgress = useSharedValue(0);
const checkProgress: AnimatedValue = React.useMemo(
() => new Animated.Value(0),
[],
);
- const buttonsStyle: AnimatedViewStyle = React.useMemo(() => {
- const buttonsScale = Reanimated.interpolateNode(focusProgress, {
- inputRange: [0, 1],
- outputRange: [1.3, 1],
- });
+ const buttonsStyleAnimated: AnimatedStyleObj = useAnimatedStyle(() => {
+ const buttonsScale = interpolate(focusProgress.value, [0, 1], [1.3, 1]);
+
return {
- ...styles.buttons,
- opacity: focusProgress,
+ opacity: focusProgress.value,
transform: [{ scale: buttonsScale }],
marginBottom: dimensions.bottomInset,
};
- }, [focusProgress, dimensions.bottomInset]);
+ });
+
+ const buttonsStyle = React.useMemo(
+ () => [buttonsStyleAnimated, styles.buttons],
+ [buttonsStyleAnimated],
+ );
- const mediaStyle: AnimatedStyleObj = React.useMemo(() => {
- const mediaScale = Reanimated.interpolateNode(focusProgress, {
- inputRange: [0, 1],
- outputRange: [1, 1.3],
- });
+ const mediaStyle: AnimatedStyleObj = useAnimatedStyle(() => {
+ const mediaScale = interpolate(focusProgress.value, [0, 1], [1, 1.3]);
return {
transform: [{ scale: mediaScale }],
};
- }, [focusProgress]);
+ });
const prevActivityStatus = React.useRef({
isFocused: false,
isQueued: false,
});
React.useEffect(() => {
const isActive = isFocused || isQueued;
const wasActive =
prevActivityStatus.current.isFocused ||
prevActivityStatus.current.isQueued;
if (isActive && !wasActive) {
- Reanimated.timing(focusProgress, {
- ...reanimatedSpec,
- toValue: 1,
- }).start();
+ focusProgress.value = withTiming(1, reanimatedSpec);
} else if (!isActive && wasActive) {
- Reanimated.timing(focusProgress, {
- ...reanimatedSpec,
- toValue: 0,
- }).start();
+ focusProgress.value = withTiming(0, reanimatedSpec);
}
if (isQueued && !prevActivityStatus.current.isQueued) {
// When I updated to React Native 0.60, I also updated Lottie. At that
// time, on iOS the last frame of the animation drops the circle outlining
// the checkmark. This is a hack to get around that
const maxValue = Platform.OS === 'ios' ? 0.99 : 1;
Animated.timing(checkProgress, {
...animatedSpec,
toValue: maxValue,
}).start();
} else if (!isQueued && prevActivityStatus.current.isQueued) {
Animated.timing(checkProgress, {
...animatedSpec,
toValue: 0,
}).start();
}
prevActivityStatus.current = {
isFocused: isFocused,
isQueued: isQueued,
};
}, [checkProgress, focusProgress, isFocused, isQueued]);
const onPressBackdrop = React.useCallback(() => {
if (isQueued) {
setMediaQueued(selection, false);
} else if (queueModeActive) {
setMediaQueued(selection, true);
} else {
setFocus(selection, !isFocused);
}
}, [
isQueued,
queueModeActive,
setMediaQueued,
selection,
setFocus,
isFocused,
]);
const onPressSend = React.useCallback(() => {
sendMedia(selection);
}, [selection, sendMedia]);
const onPressEnqueue = React.useCallback(() => {
setMediaQueued(selection, true);
}, [selection, setMediaQueued]);
const {
uri,
dimensions: { width, height },
step,
} = selection;
const active = isFocused || isQueued;
const dimensionsStyle: { +height: number, +width: number } =
React.useMemo(() => {
const scaledWidth = height ? (width * containerHeight) / height : 0;
return {
height: containerHeight,
width: Math.max(Math.min(scaledWidth, width), 150),
};
}, [containerHeight, height, width]);
let buttons = null;
if (!queueModeActive) {
buttons = (
<>
Send
Queue
>
);
}
const animatedImageStyle = React.useMemo(
() => [mediaStyle, dimensionsStyle],
[dimensionsStyle, mediaStyle],
);
let media;
const source = { uri };
if (step === 'video_library') {
let resizeMode = 'contain';
if (Platform.OS === 'ios') {
const [major, minor] = Platform.Version.split('.');
if (parseInt(major, 10) === 14 && parseInt(minor, 10) < 2) {
resizeMode = 'stretch';
}
}
media = (
);
} else {
media = ;
}
const overlay = (
);
const containerStyle = React.useMemo(
() => [styles.container, dimensionsStyle],
[dimensionsStyle],
);
return (
{media}
{buttons}
);
}
const buttonStyle = {
flexDirection: 'row',
alignItems: 'flex-start',
margin: 10,
borderRadius: 20,
paddingLeft: 20,
paddingRight: 20,
paddingTop: 10,
paddingBottom: 10,
};
const styles = StyleSheet.create({
buttonIcon: {
alignSelf: Platform.OS === 'android' ? 'center' : 'flex-end',
color: 'white',
fontSize: 18,
marginRight: 6,
paddingRight: 5,
},
buttonText: {
color: 'white',
fontSize: 16,
},
buttons: {
alignItems: 'center',
bottom: 0,
justifyContent: 'center',
left: 0,
position: 'absolute',
right: 0,
top: 0,
},
checkAnimation: {
position: 'absolute',
width: 128,
},
container: {
flex: 1,
overflow: 'hidden',
},
enqueueButton: {
...buttonStyle,
backgroundColor: '#2A78E5',
},
sendButton: {
...buttonStyle,
backgroundColor: '#7ED321',
paddingLeft: 18,
},
});
export default MediaGalleryMedia;