Skip to content

Commit

Permalink
feat: creates a hook for the file download and preview
Browse files Browse the repository at this point in the history
  • Loading branch information
panoramix360 committed May 16, 2024
1 parent 4534094 commit 9eccae5
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 230 deletions.
111 changes: 4 additions & 107 deletions app/components/files/audio_file.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,17 @@ import {
TouchableOpacity,
Text,
} from 'react-native';
import FileViewer from 'react-native-file-viewer';
import FileSystem from 'react-native-fs';
import Video, {type OnLoadData, type OnProgressData} from 'react-native-video';

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 {emptyFunction} from '@utils/general';
import {logDebug} from '@utils/log';
import {useDownloadFileAndPreview} from '@hooks/files';
import {alertDownloadDocumentDisabled} from '@utils/document';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';

import CompassIcon from '../compass_icon';
import ProgressBar from '../progress_bar';

import type {Client} from '@client/rest';
import type {ClientResponse, ProgressPromise} from '@mattermost/react-native-network-client';

type Props = {
file: FileInfo;
canDownloadFiles: boolean;
Expand Down Expand Up @@ -70,7 +59,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
}));

const AudioFile = ({file, canDownloadFiles}: Props) => {
const serverUrl = useServerUrl();
const intl = useIntl();
const theme = useTheme();
const style = getStyleSheet(theme);
Expand All @@ -82,105 +70,19 @@ const AudioFile = ({file, canDownloadFiles}: Props) => {

const source = useMemo(() => ({uri: file.uri}), [file.uri]);

const [preview, setPreview] = useState(false);
const [didCancel, setDidCancel] = useState(false);
const [downloading, setDownloading] = useState(false);
const [downloadProgress, setDownloadProgress] = useState(0);
let client: Client | undefined;
try {
client = NetworkManager.getClient(serverUrl);
} catch {
// do nothing
}
const downloadTask = useRef<ProgressPromise<ClientResponse>>();
const {toggleDownloadAndPreview} = useDownloadFileAndPreview();

const onPlayPress = () => {
setHasPaused(!hasPaused);
};

const openDocument = () => {
if (!didCancel && !preview) {
const path = getLocalFilePathFromFile(serverUrl, file);
setPreview(true);
FileViewer.open(path!, {
displayName: file.name,
onDismiss: onDonePreviewingFile,
showOpenWithDialog: true,
showAppsSuggestions: true,
}).then(() => {
setDownloading(false);
setDownloadProgress(0);
}).catch(() => {
alertFailedToOpenDocument(file, intl);
onDonePreviewingFile();

if (path) {
FileSystem.unlink(path).catch(emptyFunction);
}
});
}
};

const onDonePreviewingFile = () => {
setDownloadProgress(0);
setDownloading(false);
setPreview(false);
};

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?.(setDownloadProgress);

await downloadTask.current;
setDownloadProgress(1);
openDocument();
}
} catch (error) {
if (path) {
FileSystem.unlink(path).catch(emptyFunction);
}
setDownloading(false);
setDownloadProgress(0);

if (!isErrorWithMessage(error) || error.message !== 'cancelled') {
logDebug('error on downloadAndPreviewFile', getFullErrorMessage(error));
alertDownloadFailed(intl);
}
}
};

const onDownloadPress = async () => {
if (!canDownloadFiles) {
alertDownloadDocumentDisabled(intl);
return;
}

if (downloading && progress < 1) {
cancelDownload();
} else if (downloading) {
setDownloadProgress(0);
setDidCancel(true);
setDownloading(false);
} else {
downloadAndPreviewFile();
}
toggleDownloadAndPreview(file);
};

const loadTimeInMinutes = throttle((timeInSeconds: number) => {
Expand Down Expand Up @@ -226,11 +128,6 @@ const AudioFile = ({file, canDownloadFiles}: Props) => {
size={24}
style={style.playIcon}
/>

<ProgressBar
progress={downloadProgress || 0.1}
color={theme.buttonBg}
/>
</TouchableOpacity>

<Video
Expand Down
127 changes: 6 additions & 121 deletions app/components/files/document_file.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,17 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import React, {forwardRef, useImperativeHandle, useRef, useState} from 'react';
import React, {forwardRef, useImperativeHandle} 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 FileSystem from 'react-native-fs';
import tinyColor from 'tinycolor2';
import {StyleSheet, TouchableOpacity, View} from 'react-native';

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 {emptyFunction} from '@utils/general';
import {logDebug} from '@utils/log';
import {useDownloadFileAndPreview} from '@hooks/files';
import {alertDownloadDocumentDisabled} from '@utils/document';

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;
}
Expand All @@ -46,120 +34,17 @@ const styles = StyleSheet.create({

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) {
FileSystem.unlink(path).catch(emptyFunction);
}
setDownloading(false);
setProgress(0);

if (!isErrorWithMessage(error) || error.message !== 'cancelled') {
logDebug('error on downloadAndPreviewFile', getFullErrorMessage(error));
alertDownloadFailed(intl);
}
}
};
const {downloading, progress, toggleDownloadAndPreview} = useDownloadFileAndPreview();

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) {
FileSystem.unlink(path).catch(emptyFunction);
}
});
}
};

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);
}
}
toggleDownloadAndPreview(file);
};

useImperativeHandle(ref, () => ({
Expand Down
Loading

0 comments on commit 9eccae5

Please sign in to comment.