mattermost-mobile/app/components/files/document_file.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

199 lines
6.1 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {deleteAsync} from 'expo-file-system';
import React, {forwardRef, useImperativeHandle, useRef, useState} from 'react';
import {useIntl} from 'react-intl';
import {Platform, StatusBar, type StatusBarStyle, StyleSheet, TouchableOpacity, View} from 'react-native';
import FileViewer from 'react-native-file-viewer';
import tinyColor from 'tinycolor2';
import ProgressBar from '@components/progress_bar';
import {DOWNLOAD_TIMEOUT} from '@constants/network';
import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import NetworkManager from '@managers/network_manager';
import {alertDownloadDocumentDisabled, alertDownloadFailed, alertFailedToOpenDocument} from '@utils/document';
import {getFullErrorMessage, isErrorWithMessage} from '@utils/errors';
import {fileExists, getLocalFilePathFromFile} from '@utils/file';
import {logDebug} from '@utils/log';
import FileIcon from './file_icon';
import type {Client} from '@client/rest';
import type {ClientResponse, ProgressPromise} from '@mattermost/react-native-network-client';
export type DocumentFileRef = {
handlePreviewPress: () => void;
}
type DocumentFileProps = {
backgroundColor?: string;
canDownloadFiles: boolean;
file: FileInfo;
}
const styles = StyleSheet.create({
progress: {
justifyContent: 'flex-end',
height: 48,
left: 2,
top: 5,
width: 44,
},
});
const DocumentFile = forwardRef<DocumentFileRef, DocumentFileProps>(({backgroundColor, canDownloadFiles, file}: DocumentFileProps, ref) => {
const intl = useIntl();
const serverUrl = useServerUrl();
const theme = useTheme();
const [didCancel, setDidCancel] = useState(false);
const [downloading, setDownloading] = useState(false);
const [preview, setPreview] = useState(false);
const [progress, setProgress] = useState(0);
let client: Client | undefined;
try {
client = NetworkManager.getClient(serverUrl);
} catch {
// do nothing
}
const downloadTask = useRef<ProgressPromise<ClientResponse>>();
const cancelDownload = () => {
setDidCancel(true);
if (downloadTask.current?.cancel) {
downloadTask.current.cancel();
}
};
const downloadAndPreviewFile = async () => {
setDidCancel(false);
let path;
try {
path = getLocalFilePathFromFile(serverUrl, file);
const exists = await fileExists(path);
if (exists) {
openDocument();
} else {
setDownloading(true);
downloadTask.current = client?.apiClient.download(client?.getFileRoute(file.id!), path!.replace('file://', ''), {timeoutInterval: DOWNLOAD_TIMEOUT});
downloadTask.current?.progress?.(setProgress);
await downloadTask.current;
setProgress(1);
openDocument();
}
} catch (error) {
if (path) {
deleteAsync(path, {idempotent: true});
}
setDownloading(false);
setProgress(0);
if (!isErrorWithMessage(error) || error.message !== 'cancelled') {
logDebug('error on downloadAndPreviewFile', getFullErrorMessage(error));
alertDownloadFailed(intl);
}
}
};
const handlePreviewPress = async () => {
if (!canDownloadFiles) {
alertDownloadDocumentDisabled(intl);
return;
}
if (downloading && progress < 1) {
cancelDownload();
} else if (downloading) {
setProgress(0);
setDidCancel(true);
setDownloading(false);
} else {
downloadAndPreviewFile();
}
};
const onDonePreviewingFile = () => {
setProgress(0);
setDownloading(false);
setPreview(false);
setStatusBarColor();
};
const openDocument = () => {
if (!didCancel && !preview) {
const path = getLocalFilePathFromFile(serverUrl, file);
setPreview(true);
setStatusBarColor('dark-content');
FileViewer.open(path!, {
displayName: file.name,
onDismiss: onDonePreviewingFile,
showOpenWithDialog: true,
showAppsSuggestions: true,
}).then(() => {
setDownloading(false);
setProgress(0);
}).catch(() => {
alertFailedToOpenDocument(file, intl);
onDonePreviewingFile();
if (path) {
deleteAsync(path, {idempotent: true});
}
});
}
};
const setStatusBarColor = (style: StatusBarStyle = 'light-content') => {
if (Platform.OS === 'ios') {
if (style) {
StatusBar.setBarStyle(style, true);
} else {
const headerColor = tinyColor(theme.sidebarHeaderBg);
let barStyle: StatusBarStyle = 'light-content';
if (headerColor.isLight() && Platform.OS === 'ios') {
barStyle = 'dark-content';
}
StatusBar.setBarStyle(barStyle, true);
}
}
};
useImperativeHandle(ref, () => ({
handlePreviewPress,
}), []);
const icon = (
<FileIcon
backgroundColor={backgroundColor}
file={file}
/>
);
let fileAttachmentComponent = icon;
if (downloading) {
fileAttachmentComponent = (
<>
{icon}
<View style={[StyleSheet.absoluteFill, styles.progress]}>
<ProgressBar
progress={progress || 0.1}
color={theme.buttonBg}
/>
</View>
</>
);
}
return (
<TouchableOpacity onPress={handlePreviewPress}>
{fileAttachmentComponent}
</TouchableOpacity>
);
});
DocumentFile.displayName = 'DocumentFile';
export default DocumentFile;