mattermost-mobile/app/screens/gallery/gallery.tsx
Elias Nahum b8c088cc70
Upgrade RN as well as update or replace other dependencies (#8011)
* start upgrade to RN 74

* migrate react-native-fs to expo-file-system

* exclude expo-asset module

* fix database manager remove directory

* fix: android network helper

* include expo on android

* temporarily disable android dep lock

* replace react-native-create-thumbnail with expo-video-thumnails

* update patches file version

* fix android build on 74

* create local library to replace MattermostManaged, Notifications and SplitView modules with new arch support

* migrate app to use new mattermost-rnutils library

* remove unused flipper class for android unsigned

* fix mattermost-rnutils android foldedObserver lifecycle

* use mattermost-rnutils on Android

* use mattermost-rnutils on iOS

* path react-native-navigation to not crash when activity is not NavigationActivity

* create local library for android share extension with new arch support

* Replace ShareModule with @mattermost/rnshare library

* remove ShareModule from android native code

* update react-intl

* update nodejs to 20.x.x npm to 10.x.x and dev deps

* update @gorhom/bottom-sheet

* use MattermostShare conditionally based on the platform

* update @react-native-camera-roll/camera-roll

* remove unused react-native-calendars

* fix metro config

* fix terminate session race condition

* remove unused analytics

* replace react-native-device-info with expo-application and expo-device

* update @react-native-clipboard/clipboard

* update @react-native-community/datetimepicker

* update @react-native-community/netinfo

* update @sentry/react-native

* update react-native-document-picker

* update react-native-gesture-handler

* update react-native-share

* update react-native-svg and react-native-svg-transformer

* update react-native-vector-icons

* update babel

* update react-native-shadow-2

* update semver

* remove react-native-svg-transformer and convert svg files to svg components

* fix @mattermost/rnshare new-arch build on android

* remove react-native-create-thumbnail resolution in build.gradle

* create @mattermost/hardware-keyboard library to replace hw-keyboard-events

* fix hardware-keyboard library

* fix rnutils library

* create @mattermost/keyboard-tracker library

* replace react-native-keyboard-tracking-view with @mattermost/keyboard-tracker

* fix: rnutils to not crash on lateinit context

* fix: rnutils delete database

* revert changes to session_manager

* Removed react-native-webview and added expo-web-browser instead

With expo-web-browser we no longer need the webview for SSO login
the SSO login is now done by using "custom Chrome tabs" on Android
and ASWebAuthenticationSession on iOS

* remove patch for react-native-keyboard-tracking-view

* replace react-native-linear-gradient with expo-linear-gradient

* replace react-native-in-app-review with expo-store-review

* fix: shared group database directory on ios

* replace react-native-fast-image with expo-image

* remove unusued type def for react-native-keyboard-tracking-view

* replace react-native-elements and react-native-button with @rneui

* node version

* update sizzling methods

* fix tests using jest-expo

* replace jail-monkey with expo-device

* update babel deps

* update typescript eslint

* update rn and expo

* react-native-document-picker @react-native-camera-roll/camera-roll @react-native-community/datetimepicker react-native-reanimated react-native-safe-area-context

* update patches

* update @sentry/react-native

* upgrade react-native-navigation

* update expo & expo-image

* upgrade to working version of @sentry/react-native

* update node, cocoapods, bundler, fastlane versions

* @testing-library/react-native and eslint-plugin-jest

* fix: FloatingTextInput causing a crash with reanimated

* update sentry, localize, @types/lodash and uuid

* fix floating text input label

* update react-native-video

* fix: cannot calculate shadow efficiently on some components

* fix: reduce motion warning for bottomSheet

* fix: shadow on YouTube component

* update react-native-webrtc expo and @typescript-eslint

* audit fix

* fix swizzling bad merge

* temp use of github dependency for @mattermost libraries

* feedback review

* feedback review

* npm audit fix

* update bundle deps

* update @mattermost/react-native-turbo-log

* update deps
2024-06-19 09:33:45 +08:00

214 lines
6.3 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Image} from 'expo-image';
import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react';
import {BackHandler} from 'react-native';
import Animated, {runOnJS, runOnUI, useAnimatedReaction} from 'react-native-reanimated';
import {useGallery} from '@context/gallery';
import {freezeOtherScreens, measureItem} from '@utils/gallery';
import DocumentRenderer from './document_renderer';
import LightboxSwipeout, {type LightboxSwipeoutRef, type RenderItemInfo} from './lightbox_swipeout';
import Backdrop, {type BackdropProps} from './lightbox_swipeout/backdrop';
import VideoRenderer from './video_renderer';
import GalleryViewer from './viewer';
import type {ImageRendererProps} from './image_renderer';
import type {GalleryItemType} from '@typings/screens/gallery';
const AnimatedImage = Animated.createAnimatedComponent(Image);
interface GalleryProps {
galleryIdentifier: string;
initialIndex: number;
items: GalleryItemType[];
onIndexChange?: (index: number) => void;
onHide: () => void;
targetDimensions: { width: number; height: number };
onShouldHideControls: (hide: boolean) => void;
}
export interface GalleryRef {
close: () => void;
}
const Gallery = forwardRef<GalleryRef, GalleryProps>(({
galleryIdentifier,
initialIndex,
items,
onHide,
targetDimensions,
onShouldHideControls,
onIndexChange,
}: GalleryProps, ref) => {
const {refsByIndexSV, sharedValues} = useGallery(galleryIdentifier);
const [localIndex, setLocalIndex] = useState(initialIndex);
const lightboxRef = useRef<LightboxSwipeoutRef>(null);
const item = items[localIndex];
const close = () => {
lightboxRef.current?.closeLightbox();
};
const onLocalIndex = (index: number) => {
setLocalIndex(index);
onIndexChange?.(index);
};
useEffect(() => {
runOnUI(() => {
'worklet';
const tw = targetDimensions.width;
sharedValues.targetWidth.value = tw;
const scaleFactor = item.width / targetDimensions.width;
const th = item.height / scaleFactor;
sharedValues.targetHeight.value = th;
})();
}, [item, targetDimensions.width]);
useEffect(() => {
const listener = BackHandler.addEventListener('hardwareBackPress', () => {
lightboxRef.current?.closeLightbox();
return true;
});
return () => listener.remove();
}, []);
useImperativeHandle(ref, () => ({
close,
}));
useAnimatedReaction(
() => {
return sharedValues.activeIndex.value;
},
(index) => {
const galleryItems = refsByIndexSV.value;
if (index > -1 && galleryItems[index]) {
measureItem(galleryItems[index].ref, sharedValues);
}
},
);
const onIndexChangeWorklet = useCallback((nextIndex: number) => {
'worklet';
runOnJS(onLocalIndex)(nextIndex);
sharedValues.activeIndex.value = nextIndex;
}, []);
const renderBackdropComponent = useCallback(
({animatedStyles, translateY}: BackdropProps) => {
return (
<Backdrop
animatedStyles={animatedStyles}
translateY={translateY}
/>
);
},
[],
);
function onSwipeActive(translateY: number) {
'worklet';
if (Math.abs(translateY) > 8) {
onShouldHideControls(true);
}
}
function onSwipeFailure() {
'worklet';
runOnJS(freezeOtherScreens)(true);
onShouldHideControls(false);
}
function hideLightboxItem() {
'worklet';
sharedValues.width.value = 0;
sharedValues.height.value = 0;
sharedValues.opacity.value = 1;
sharedValues.activeIndex.value = -1;
sharedValues.x.value = 0;
sharedValues.y.value = 0;
runOnJS(onHide)();
}
const onRenderItem = useCallback((info: RenderItemInfo) => {
if (item.type === 'video' && item.posterUri) {
return (
<AnimatedImage
source={{uri: item.posterUri}}
style={info.itemStyles}
/>
);
}
return null;
}, [item]);
const onRenderPage = useCallback((props: ImageRendererProps, idx: number) => {
switch (props.item.type) {
case 'video':
return (
<VideoRenderer
{...props}
index={idx}
initialIndex={initialIndex}
onShouldHideControls={onShouldHideControls}
/>
);
case 'file':
return (
<DocumentRenderer
item={props.item}
onShouldHideControls={onShouldHideControls}
/>
);
default:
return null;
}
}, []);
return (
<LightboxSwipeout
ref={lightboxRef}
target={item}
onAnimationFinished={hideLightboxItem}
sharedValues={sharedValues}
source={item.uri}
onSwipeActive={onSwipeActive}
onSwipeFailure={onSwipeFailure}
renderBackdropComponent={renderBackdropComponent}
targetDimensions={targetDimensions}
renderItem={onRenderItem}
>
{({onGesture, shouldHandleEvent}) => (
<GalleryViewer
items={items}
onIndexChange={onIndexChangeWorklet}
shouldPagerHandleGestureEvent={shouldHandleEvent}
onShouldHideControls={onShouldHideControls}
height={targetDimensions.height}
width={targetDimensions.width}
initialIndex={initialIndex}
onPagerEnabledGesture={onGesture}
numToRender={1}
renderPage={onRenderPage}
/>
)}
</LightboxSwipeout>
);
});
Gallery.displayName = 'Gallery';
export default Gallery;