Rocket.Chat.ReactNative/app/containers/ImageViewer/ImageViewer.tsx

132 lines
3.7 KiB
TypeScript
Raw Normal View History

import React, { useState } from 'react';
import { LayoutChangeEvent, StyleSheet, StyleProp, ViewStyle, ImageStyle, View } from 'react-native';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, { withTiming, useSharedValue, useAnimatedStyle, withSpring } from 'react-native-reanimated';
import { useTheme } from '../../theme';
import { ImageComponent } from './ImageComponent';
interface ImageViewerProps {
style?: StyleProp<ImageStyle>;
containerStyle?: StyleProp<ViewStyle>;
imageContainerStyle?: StyleProp<ViewStyle>;
uri: string;
imageComponentType?: string;
width: number;
height: number;
onLoadEnd?: () => void;
}
const styles = StyleSheet.create({
flex: {
flex: 1
},
image: {
flex: 1
}
});
export const ImageViewer = ({ uri = '', imageComponentType, width, height, ...props }: ImageViewerProps): React.ReactElement => {
const [centerX, setCenterX] = useState(0);
const [centerY, setCenterY] = useState(0);
const onLayout = ({
nativeEvent: {
layout: { x, y, width, height }
}
}: LayoutChangeEvent) => {
setCenterX(x + width / 2);
setCenterY(y + height / 2);
};
const translationX = useSharedValue<number>(0);
const translationY = useSharedValue<number>(0);
const offsetX = useSharedValue<number>(0);
const offsetY = useSharedValue<number>(0);
const scale = useSharedValue<number>(1);
const scaleOffset = useSharedValue<number>(1);
const style = useAnimatedStyle(() => ({
transform: [{ translateX: translationX.value }, { translateY: translationY.value }, { scale: scale.value }]
}));
const resetScaleAnimation = () => {
'worklet';
scaleOffset.value = 1;
offsetX.value = 0;
offsetY.value = 0;
scale.value = withSpring(1);
translationX.value = withSpring(0, { overshootClamping: true });
translationY.value = withSpring(0, { overshootClamping: true });
};
const clamp = (value: number, min: number, max: number) => {
'worklet';
return Math.max(Math.min(value, max), min);
};
const pinchGesture = Gesture.Pinch()
.onUpdate(event => {
scale.value = clamp(scaleOffset.value * (event.scale > 0 ? event.scale : 1), 1, 4);
})
.onEnd(() => {
scaleOffset.value = scale.value > 0 ? scale.value : 1;
});
const panGesture = Gesture.Pan()
.maxPointers(2)
.onStart(() => {
translationX.value = offsetX.value;
translationY.value = offsetY.value;
})
.onUpdate(event => {
const scaleFactor = scale.value - 1;
translationX.value = clamp(event.translationX + offsetX.value, -scaleFactor * centerX, scaleFactor * centerX);
translationY.value = clamp(event.translationY + offsetY.value, -scaleFactor * centerY, scaleFactor * centerY);
})
.onEnd(() => {
offsetX.value = translationX.value;
offsetY.value = translationY.value;
if (scale.value === 1) resetScaleAnimation();
});
const doubleTapGesture = Gesture.Tap()
.numberOfTaps(2)
.maxDelay(120)
.maxDistance(70)
.onEnd(event => {
if (scaleOffset.value > 1) resetScaleAnimation();
else {
scale.value = withTiming(2, { duration: 200 });
translationX.value = withTiming(centerX - event.x, { duration: 200 });
offsetX.value = centerX - event.x;
scaleOffset.value = 2;
}
});
const gesture = Gesture.Simultaneous(pinchGesture, panGesture, doubleTapGesture);
feat: add media auto-download (#5076) * feat: media auto-download view * media auto download view completed and saving the settings in mmkv * audio download preference * audio auto download when the user who sent the audio is the same logged on mobile * creation of isAutoDownloadEnabled, evaluate hist hook, Image Full Size preload done * minor tweak audio show play button after download * refactor audioFile to handleMediaDownload and fixed the audio download * desestructured params to download too * image download and autoDownload, algo fix the formatAttachmentUrl to show the image from local * add the possibility to cancel image download and clear local images * refactor blur component * video download and auto download, also keeped the behavior to download unsuportted videos to the gallery * add the possibility to start downloading a video, then exit the room, back again to room and cancel the video previously downloading * remove the custom hook for autoDownload * remove blurcomponent, fix the blur style in image.tsx, minor tweak video function name * send messageId to video * introducing the reducer to keep the downloads in progress * create a media download selector * remove all the redux stuff and do the same as file upload * video download behavior * done for image and audio * fix the try catch download media * clean up * image container uiKit * fix lint * change rn-fetch-blob to expo-filesystem * add pt-br * pass the correct message id when there is an attachment on reply * refactor some changes requested * fix audio and move the netInfo from autoDownloadPreference to redux * variable isAutoDownloadEnable name and handleMediaDownload getExtension * message/Image refactored, change the component to show the image from FastImage to Image * refactor handleMediaDownload and deleteMedia * minor tweak * refactor audio * refactor video * fix the type on the messagesView(the view of files) * minor tweak * fix the name of searchMediaFIleAsync's result * minor tweak, add the default behavior, add the OFF as label * minor tweaks * verify if the media auto download exists on settings view * fix media auto download view layout and minor tweak wifi * avoid auto download from reply * minor tweak at comment * tweak list.section * change the name to netInfoState and Local_document_directory * remove mediaType and refactor audio and image * separate blurview * thumbnail video and video behavior * add Audio to i18n and minor tweak * set the blur as always dark and add the possibility to overlay * don't need to controle the filepath in the view * fix the loading in image and video at begin * save the file with a similar filename as expected * removed the necessity of messageId or id * minor tweak * switch useLayoutEffect to useEffect * avoid onpress do some edge case because of cached at video * minor tweak * tweak at audio comment extension * minor tweak type userpreferences * remove test id from mediaAutoDownloadView * change action's name to SET_NET_INFO_STATE * caching and deleting video's thumbnails * remove generate thumbnail * minor tweak in image * update camera-roll and save the file from local url * remove local_cache_directory and deleteThumbnail * update blur to fix error on android * fix blur is hiding the file description * avoid download unsupported video * return void when it is loading the audio
2023-08-07 14:02:30 +00:00
const Component = ImageComponent({ type: imageComponentType, uri });
const { colors } = useTheme();
return (
<View style={[styles.flex, { width, height, backgroundColor: colors.previewBackground }]}>
<GestureDetector gesture={gesture}>
<Animated.View onLayout={onLayout} style={[styles.flex, style]}>
<Component
// @ts-ignore
style={styles.image}
resizeMode='contain'
source={{ uri }}
{...props}
/>
</Animated.View>
</GestureDetector>
</View>
);
};