* Remove mattermost-redux * Move mm-redux files into app/redux * Add @redux path to tsconfig.json * Fix imports * Install missing dependencies * Fix tsc errors * Fix i18n_utils test * Fix more imports * Remove redux websocket * Fix tests * Rename @redux * Apply changes from mattermost-redux PR 1103 * Remove mattermost-redux mention in template * Add missing imports * Rename app/redux/ to app/mm-redux/ * Remove test file * Fix fetching Sidebar GM profiles Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
195 lines
6.1 KiB
JavaScript
195 lines
6.1 KiB
JavaScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import React, {PureComponent} from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import {
|
|
PermissionsAndroid,
|
|
StyleSheet,
|
|
ToastAndroid,
|
|
TouchableOpacity,
|
|
View,
|
|
} from 'react-native';
|
|
import RNFetchBlob from 'rn-fetch-blob';
|
|
import {intlShape} from 'react-intl';
|
|
|
|
import {Client4} from '@mm-redux/client';
|
|
|
|
import {DeviceTypes} from 'app/constants/';
|
|
import FormattedText from 'app/components/formatted_text';
|
|
import {getLocalFilePathFromFile, isDocument, isVideo} from 'app/utils/file';
|
|
import {emptyFunction} from 'app/utils/general';
|
|
|
|
const {DOCUMENTS_PATH, VIDEOS_PATH} = DeviceTypes;
|
|
const EXTERNAL_STORAGE_PERMISSION = 'android.permission.WRITE_EXTERNAL_STORAGE';
|
|
const HEADER_HEIGHT = 46;
|
|
const OPTION_LIST_HEIGHT = 39;
|
|
|
|
export default class Downloader extends PureComponent {
|
|
static propTypes = {
|
|
file: PropTypes.object.isRequired,
|
|
onDownloadCancel: PropTypes.func,
|
|
onDownloadStart: PropTypes.func,
|
|
onDownloadSuccess: PropTypes.func,
|
|
show: PropTypes.bool,
|
|
};
|
|
|
|
static defaultProps = {
|
|
onCancelPress: emptyFunction,
|
|
onDownloadStart: emptyFunction,
|
|
onDownloadSuccess: emptyFunction,
|
|
show: false,
|
|
};
|
|
|
|
static contextTypes = {
|
|
intl: intlShape,
|
|
};
|
|
|
|
checkForPermissions = async () => {
|
|
const canWriteToStorage = await PermissionsAndroid.check(EXTERNAL_STORAGE_PERMISSION);
|
|
if (!canWriteToStorage) {
|
|
const {intl} = this.context;
|
|
const description = intl.formatMessage({
|
|
id: 'mobile.downloader.android_permission',
|
|
defaultMessage: 'We need access to the downloads folder to save files.',
|
|
});
|
|
|
|
const permissionRequest = await PermissionsAndroid.request(EXTERNAL_STORAGE_PERMISSION, description);
|
|
return permissionRequest === 'granted';
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
handleDownload = async () => {
|
|
const {file, onDownloadCancel, onDownloadStart, onDownloadSuccess} = this.props;
|
|
const {intl} = this.context;
|
|
const {data} = file;
|
|
|
|
const canWriteToStorage = await this.checkForPermissions();
|
|
if (!canWriteToStorage) {
|
|
onDownloadCancel();
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const started = intl.formatMessage({
|
|
id: 'mobile.downloader.android_started',
|
|
defaultMessage: 'Download started',
|
|
});
|
|
const complete = intl.formatMessage({
|
|
id: 'mobile.downloader.android_complete',
|
|
defaultMessage: 'Download complete',
|
|
});
|
|
|
|
ToastAndroid.show(started, ToastAndroid.SHORT);
|
|
onDownloadStart();
|
|
|
|
let dest = `${RNFetchBlob.fs.dirs.DownloadDir}/${data.id}-${data.name}`;
|
|
let downloadFile = true;
|
|
|
|
if (data.localPath) {
|
|
const exists = await RNFetchBlob.fs.exists(data.localPath);
|
|
|
|
if (exists) {
|
|
downloadFile = false;
|
|
await RNFetchBlob.fs.cp(data.localPath, dest);
|
|
}
|
|
} else if (isVideo(data)) {
|
|
const path = getLocalFilePathFromFile(VIDEOS_PATH, file);
|
|
const exists = await RNFetchBlob.fs.exists(path);
|
|
|
|
if (exists) {
|
|
downloadFile = false;
|
|
dest = getLocalFilePathFromFile(RNFetchBlob.fs.dirs.DownloadDir, file);
|
|
await RNFetchBlob.fs.cp(path, dest);
|
|
}
|
|
} else if (isDocument(data)) {
|
|
const path = getLocalFilePathFromFile(DOCUMENTS_PATH, file);
|
|
const exists = await RNFetchBlob.fs.exists(path);
|
|
|
|
if (exists) {
|
|
downloadFile = false;
|
|
await RNFetchBlob.fs.cp(path, dest);
|
|
}
|
|
}
|
|
|
|
if (downloadFile) {
|
|
const imageUrl = Client4.getFileUrl(data.id);
|
|
|
|
const task = RNFetchBlob.config({
|
|
fileCache: true,
|
|
addAndroidDownloads: {
|
|
useDownloadManager: true,
|
|
notification: true,
|
|
path: dest,
|
|
title: `${file.caption}`,
|
|
mime: data.mime_type,
|
|
description: data.name,
|
|
mediaScannable: true,
|
|
},
|
|
}).fetch('GET', imageUrl, {
|
|
Authorization: `Bearer ${Client4.token}`,
|
|
'X-Requested-With': 'XMLHttpRequest',
|
|
});
|
|
|
|
await task;
|
|
}
|
|
|
|
ToastAndroid.show(complete, ToastAndroid.SHORT);
|
|
onDownloadSuccess();
|
|
} catch (error) {
|
|
const failed = intl.formatMessage({
|
|
id: 'mobile.downloader.android_failed',
|
|
defaultMessage: 'Download failed',
|
|
});
|
|
|
|
ToastAndroid.show(failed, ToastAndroid.SHORT);
|
|
onDownloadCancel();
|
|
}
|
|
};
|
|
|
|
render() {
|
|
const {show} = this.props;
|
|
|
|
if (!show) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<View style={styles.wrapper}>
|
|
<TouchableOpacity
|
|
style={styles.downloadButton}
|
|
onPress={this.handleDownload}
|
|
>
|
|
<FormattedText
|
|
id='file_attachment.download'
|
|
defaultMessage='Download'
|
|
style={styles.downloadButtonText}
|
|
/>
|
|
</TouchableOpacity>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
downloadButton: {
|
|
flex: 1,
|
|
justifyContent: 'center',
|
|
},
|
|
downloadButtonText: {
|
|
color: 'white',
|
|
fontSize: 15,
|
|
paddingLeft: 15,
|
|
},
|
|
wrapper: {
|
|
position: 'absolute',
|
|
backgroundColor: '#575757',
|
|
height: OPTION_LIST_HEIGHT,
|
|
top: HEADER_HEIGHT,
|
|
right: 0,
|
|
width: 150,
|
|
marginRight: 5,
|
|
},
|
|
});
|