RN-80 Add ability to download files (#866)
* RN-80 Add ability to download files * Add android permission check and toasts * Add check for ability to download from config
This commit is contained in:
parent
95e89c39ee
commit
220fb588b9
15 changed files with 743 additions and 15 deletions
72
NOTICE.txt
72
NOTICE.txt
|
|
@ -1279,3 +1279,75 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## react-native-circular-progress
|
||||
|
||||
React Native component for creating animated, circular progress with ReactART
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/bgryszko/react-native-circular-progress
|
||||
|
||||
* LICENSE:
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Bart Gryszko
|
||||
|
||||
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## react-native-fetch-blob
|
||||
|
||||
A project committed to making file access and data transfer easier, efficient for React Native developers.
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/wkh237/react-native-fetch-blob#user-content-upload-example--dropbox-files-upload-api
|
||||
|
||||
* LICENSE:
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 xeiyan@gmail.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':react-native-fetch-blob')
|
||||
compile fileTree(dir: "libs", include: ["*.jar"])
|
||||
compile "com.android.support:appcompat-v7:25.0.1"
|
||||
compile "com.facebook.react:react-native:+" // From node_modules
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
|
||||
<uses-permission android:name="com.google.android.c2dm.permission.SEND" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
|
@ -42,6 +43,7 @@
|
|||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import android.content.Context;
|
|||
import android.os.Bundle;
|
||||
|
||||
import com.facebook.react.ReactApplication;
|
||||
import com.RNFetchBlob.RNFetchBlobPackage;
|
||||
import com.gantix.JailMonkey.JailMonkeyPackage;
|
||||
import io.tradle.react.LocalAuthPackage;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
|
|
@ -58,6 +59,7 @@ public class MainApplication extends NavigationApplication implements INotificat
|
|||
new RNNotificationsPackage(this),
|
||||
new LocalAuthPackage(),
|
||||
new JailMonkeyPackage(),
|
||||
new RNFetchBlobPackage(),
|
||||
new MattermostManagedPackage()
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
rootProject.name = 'Mattermost'
|
||||
include ':react-native-fetch-blob'
|
||||
project(':react-native-fetch-blob').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fetch-blob/android')
|
||||
include ':jail-monkey'
|
||||
project(':jail-monkey').projectDir = new File(rootProject.projectDir, '../node_modules/jail-monkey/android')
|
||||
include ':react-native-local-auth'
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ class PostTextbox extends PureComponent {
|
|||
};
|
||||
|
||||
ImagePicker.launchCamera(options, (response) => {
|
||||
if (response.error) {
|
||||
if (response.error || response.didCancel) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -267,7 +267,7 @@ class PostTextbox extends PureComponent {
|
|||
}
|
||||
|
||||
ImagePicker.launchImageLibrary(options, (response) => {
|
||||
if (response.error) {
|
||||
if (response.error || response.didCancel) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -283,7 +283,7 @@ class PostTextbox extends PureComponent {
|
|||
};
|
||||
|
||||
ImagePicker.launchImageLibrary(options, (response) => {
|
||||
if (response.error) {
|
||||
if (response.error || response.didCancel) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
127
app/screens/image_preview/downloader.android.js
Normal file
127
app/screens/image_preview/downloader.android.js
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
// Copyright (c) 2017-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 'react-native-fetch-blob';
|
||||
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import {emptyFunction} from 'app/utils/general';
|
||||
|
||||
const EXTERNAL_STORAGE_PERMISSION = 'android.permission.WRITE_EXTERNAL_STORAGE';
|
||||
const HEADER_HEIGHT = 64;
|
||||
const OPTION_LIST_WIDTH = 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
|
||||
};
|
||||
|
||||
checkForPermissions = async () => {
|
||||
const canWriteToStorage = await PermissionsAndroid.check(EXTERNAL_STORAGE_PERMISSION);
|
||||
if (!canWriteToStorage) {
|
||||
const permissionRequest = await PermissionsAndroid.request(EXTERNAL_STORAGE_PERMISSION, 'We need access to the downloads folder to save files.');
|
||||
return permissionRequest === 'granted';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
handleDownload = async () => {
|
||||
const {file, onDownloadCancel, onDownloadStart, onDownloadSuccess} = this.props;
|
||||
|
||||
const canWriteToStorage = await this.checkForPermissions();
|
||||
if (!canWriteToStorage) {
|
||||
onDownloadCancel();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ToastAndroid.show('Download started', ToastAndroid.SHORT);
|
||||
onDownloadStart();
|
||||
|
||||
const imageUrl = Client4.getFileUrl(file.id);
|
||||
|
||||
const task = RNFetchBlob.config({
|
||||
fileCache: true,
|
||||
addAndroidDownloads: {
|
||||
useDownloadManager: true,
|
||||
notification: true,
|
||||
path: `${RNFetchBlob.fs.dirs.DownloadDir}/${file.name}`,
|
||||
title: `${file.name} download successful`,
|
||||
mime: file.mime_type,
|
||||
description: file.name,
|
||||
mediaScannable: true
|
||||
}
|
||||
}).fetch('GET', imageUrl, {
|
||||
Authorization: `Bearer ${Client4.token}`
|
||||
});
|
||||
|
||||
await task;
|
||||
|
||||
ToastAndroid.show('Download complete', ToastAndroid.SHORT);
|
||||
onDownloadSuccess();
|
||||
} catch (error) {
|
||||
ToastAndroid.show('Download failed', ToastAndroid.SHORT);
|
||||
onDownloadCancel();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {show} = this.props;
|
||||
|
||||
return (
|
||||
<View style={[styles.wrapper, {height: show ? OPTION_LIST_WIDTH : 0}]}>
|
||||
<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',
|
||||
top: HEADER_HEIGHT,
|
||||
right: 0,
|
||||
width: 150
|
||||
}
|
||||
});
|
||||
287
app/screens/image_preview/downloader.ios.js
Normal file
287
app/screens/image_preview/downloader.ios.js
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Animated,
|
||||
CameraRoll,
|
||||
Dimensions,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View
|
||||
} from 'react-native';
|
||||
import RNFetchBlob from 'react-native-fetch-blob';
|
||||
import {AnimatedCircularProgress} from 'react-native-circular-progress';
|
||||
import Icon from 'react-native-vector-icons/Ionicons';
|
||||
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import {emptyFunction} from 'app/utils/general';
|
||||
|
||||
const {View: AnimatedView} = Animated;
|
||||
const {height: deviceHeight, width: deviceWidth} = Dimensions.get('window');
|
||||
|
||||
export default class Downloader extends PureComponent {
|
||||
static propTypes = {
|
||||
file: PropTypes.object.isRequired,
|
||||
onDownloadCancel: PropTypes.func,
|
||||
onDownloadSuccess: PropTypes.func,
|
||||
show: PropTypes.bool
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
onCancelPress: emptyFunction,
|
||||
onDownloadSuccess: emptyFunction,
|
||||
show: false
|
||||
};
|
||||
|
||||
state = {
|
||||
downloaderTop: new Animated.Value(deviceHeight),
|
||||
progress: 0
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (!this.props.show && nextProps.show) {
|
||||
this.toggleDownloader();
|
||||
this.setState({
|
||||
didCancel: false,
|
||||
progress: 0
|
||||
});
|
||||
} else if (!nextProps.show && this.props.show) {
|
||||
this.toggleDownloader(false);
|
||||
}
|
||||
}
|
||||
|
||||
toggleDownloader = (show = true) => {
|
||||
const top = show ? (deviceHeight / 2) - 100 : deviceHeight;
|
||||
|
||||
Animated.spring(this.state.downloaderTop, {
|
||||
toValue: top,
|
||||
tension: 8,
|
||||
friction: 5
|
||||
}).start(() => {
|
||||
if (show) {
|
||||
this.startDownload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
startDownload = async () => {
|
||||
try {
|
||||
const {file} = this.props;
|
||||
const imageUrl = Client4.getFileUrl(file.id);
|
||||
|
||||
this.downloadTask = RNFetchBlob.config({
|
||||
fileCache: true,
|
||||
appendExt: file.extension
|
||||
}).fetch('GET', imageUrl).progress((received, total) => {
|
||||
const progress = (received / total) * 100;
|
||||
this.setState({
|
||||
progress
|
||||
});
|
||||
});
|
||||
|
||||
const res = await this.downloadTask;
|
||||
const path = res.path();
|
||||
const newPath = await CameraRoll.saveToCameraRoll(path);
|
||||
|
||||
this.setState({
|
||||
progress: 100
|
||||
}, () => {
|
||||
// need to wait a bit for the progress circle UI to update to the give progress
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
// handles the case of a late cancellation by the user
|
||||
// and ensures that we remove the file if they did cancel late.
|
||||
if (this.state.didCancel) {
|
||||
// TODO: There's issue with deleting files from the cameraRoll on iOS here https://github.com/wkh237/react-native-fetch-blob/issues/479
|
||||
// This only occurs if the user cancels when the download is around 80% or more
|
||||
await RNFetchBlob.fs.unlink(newPath);
|
||||
this.props.onDownloadCancel();
|
||||
} else {
|
||||
this.props.onDownloadSuccess();
|
||||
}
|
||||
} catch (error) {
|
||||
// ensure the downloader at least closes if a error
|
||||
this.props.onDownloadCancel();
|
||||
}
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
res.flush(); // remove the temp file
|
||||
this.downloadTask = null;
|
||||
} catch (error) {
|
||||
// cancellation throws so we need to catch
|
||||
}
|
||||
}
|
||||
|
||||
handleCancelDownload = () => {
|
||||
this.setState({
|
||||
didCancel: true
|
||||
});
|
||||
|
||||
if (this.downloadTask) {
|
||||
this.downloadTask.cancel(() => {
|
||||
this.props.onDownloadCancel();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderProgress = (fill) => {
|
||||
const realFill = Number(fill.toFixed(0));
|
||||
|
||||
let component;
|
||||
if (realFill === 100) {
|
||||
component = (
|
||||
<Icon
|
||||
name='ios-checkmark'
|
||||
size={64}
|
||||
color='white'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
component = (
|
||||
<View style={styles.progressCirclePercentage}>
|
||||
<Text style={styles.progressText}>
|
||||
{`${fill.toFixed(0)}%`}
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
style={styles.cancelButton}
|
||||
onPress={this.handleCancelDownload}
|
||||
>
|
||||
<FormattedText
|
||||
id='channel_modal.cancel'
|
||||
defaultMessage='Cancel'
|
||||
style={styles.cancelText}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.progressContent}>
|
||||
{component}
|
||||
<View style={styles.bottomContent}>
|
||||
{(realFill < 100 || this.state.didCancel) ?
|
||||
<FormattedText
|
||||
id='mobile.downloader.downloading'
|
||||
defaultMessage='Downloading...'
|
||||
style={styles.bottomText}
|
||||
/> :
|
||||
<FormattedText
|
||||
id='mobile.downloader.success'
|
||||
defaultMessage='Image Saved'
|
||||
style={styles.bottomText}
|
||||
/>
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {show} = this.props;
|
||||
const {didCancel, progress} = this.state;
|
||||
|
||||
const trueProgress = didCancel ? 0 : progress;
|
||||
|
||||
const containerHeight = show ? deviceHeight : 0;
|
||||
return (
|
||||
<View style={[styles.container, {height: containerHeight}]}>
|
||||
<AnimatedView style={[styles.downloader, {top: this.state.downloaderTop}]}>
|
||||
<View style={styles.progressCircleContent}>
|
||||
<AnimatedCircularProgress
|
||||
size={120}
|
||||
fill={trueProgress}
|
||||
width={4}
|
||||
backgroundColor='rgba(255, 255, 255, 0.5)'
|
||||
tintColor='white'
|
||||
rotation={0}
|
||||
style={styles.progressCircle}
|
||||
>
|
||||
{this.renderProgress}
|
||||
</AnimatedCircularProgress>
|
||||
</View>
|
||||
</AnimatedView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
bottomContent: {
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
bottomText: {
|
||||
color: 'white',
|
||||
fontSize: 16,
|
||||
fontWeight: '600'
|
||||
},
|
||||
cancelButton: {
|
||||
height: 30,
|
||||
width: 60,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginTop: 5
|
||||
},
|
||||
cancelText: {
|
||||
color: 'white',
|
||||
fontSize: 12
|
||||
},
|
||||
container: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: deviceWidth
|
||||
},
|
||||
downloader: {
|
||||
alignItems: 'center',
|
||||
left: (deviceWidth / 2) - 118,
|
||||
height: 220,
|
||||
width: 236,
|
||||
borderRadius: 8,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.8)'
|
||||
},
|
||||
progressContainer: {
|
||||
flex: 1
|
||||
},
|
||||
progressContent: {
|
||||
position: 'absolute',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
top: 0,
|
||||
left: 0,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
progressCircle: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
progressCircleContent: {
|
||||
width: 200,
|
||||
height: 200,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
progressCirclePercentage: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
marginTop: 80
|
||||
},
|
||||
progressText: {
|
||||
color: 'white',
|
||||
fontSize: 18
|
||||
}
|
||||
});
|
||||
|
|
@ -20,8 +20,13 @@ import Icon from 'react-native-vector-icons/Ionicons';
|
|||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import Orientation from 'react-native-orientation';
|
||||
|
||||
import FileAttachmentIcon from 'app/components/file_attachment_list/file_attachment_icon';
|
||||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
|
||||
import FileAttachmentIcon from 'app/components/file_attachment_list/file_attachment_icon';
|
||||
import {NavigationTypes} from 'app/constants';
|
||||
import {emptyFunction} from 'app/utils/general';
|
||||
|
||||
import Downloader from './downloader';
|
||||
import Previewer from './previewer';
|
||||
|
||||
const {View: AnimatedView} = Animated;
|
||||
|
|
@ -40,6 +45,7 @@ export default class ImagePreview extends PureComponent {
|
|||
actions: PropTypes.shape({
|
||||
addFileToFetchCache: PropTypes.func.isRequired
|
||||
}),
|
||||
canDownloadFiles: PropTypes.bool.isRequired,
|
||||
fetchCache: PropTypes.object.isRequired,
|
||||
fileId: PropTypes.string.isRequired,
|
||||
files: PropTypes.array.isRequired,
|
||||
|
|
@ -144,6 +150,7 @@ export default class ImagePreview extends PureComponent {
|
|||
};
|
||||
|
||||
handleImageTap = () => {
|
||||
this.hideDownloader(false);
|
||||
this.setHeaderAndFileInfoVisible(!this.state.showFileInfo);
|
||||
};
|
||||
|
||||
|
|
@ -204,6 +211,103 @@ export default class ImagePreview extends PureComponent {
|
|||
}
|
||||
};
|
||||
|
||||
showDownloadOptions = () => {
|
||||
if (Platform.OS === 'android') {
|
||||
if (this.state.showDownloader) {
|
||||
this.hideDownloader();
|
||||
} else {
|
||||
this.showDownloader();
|
||||
}
|
||||
} else {
|
||||
this.showIOSDownloadOptions();
|
||||
}
|
||||
}
|
||||
|
||||
showIOSDownloadOptions = () => {
|
||||
this.setHeaderAndFileInfoVisible(false);
|
||||
|
||||
const options = {
|
||||
title: this.props.files[this.state.currentFile].name,
|
||||
items: [{
|
||||
action: this.showDownloader,
|
||||
text: {
|
||||
id: 'mobile.image_preview.save',
|
||||
defaultMessage: 'Save Image'
|
||||
}
|
||||
}],
|
||||
onCancelPress: () => this.setHeaderAndFileInfoVisible(true)
|
||||
};
|
||||
|
||||
this.props.navigator.showModal({
|
||||
screen: 'OptionsModal',
|
||||
title: '',
|
||||
animationType: 'none',
|
||||
passProps: {
|
||||
...options
|
||||
},
|
||||
navigatorStyle: {
|
||||
navBarHidden: true,
|
||||
statusBarHidden: false,
|
||||
statusBarHideWithNavBar: false,
|
||||
screenBackgroundColor: 'transparent',
|
||||
modalPresentationStyle: 'overCurrentContext'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
showDownloader = () => {
|
||||
EventEmitter.emit(NavigationTypes.NAVIGATION_CLOSE_MODAL);
|
||||
|
||||
this.setState({
|
||||
showDownloader: true
|
||||
});
|
||||
}
|
||||
|
||||
hideDownloader = (hideFileInfo = true) => {
|
||||
this.setState({showDownloader: false});
|
||||
if (hideFileInfo) {
|
||||
this.setHeaderAndFileInfoVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
renderDownloadButton = () => {
|
||||
const {canDownloadFiles, files} = this.props;
|
||||
const file = files[this.state.currentFile];
|
||||
|
||||
let icon;
|
||||
let action = emptyFunction;
|
||||
if (canDownloadFiles) {
|
||||
if (Platform.OS === 'android') {
|
||||
action = this.showDownloadOptions;
|
||||
icon = (
|
||||
<Icon
|
||||
name='md-more'
|
||||
size={32}
|
||||
color='#fff'
|
||||
/>
|
||||
);
|
||||
} else if (file.has_preview_image) {
|
||||
action = this.showDownloadOptions;
|
||||
icon = (
|
||||
<Icon
|
||||
name='ios-download-outline'
|
||||
size={26}
|
||||
color='#fff'
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={action}
|
||||
style={style.headerIcon}
|
||||
>
|
||||
{icon}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const maxImageHeight = this.state.deviceHeight - STATUSBAR_HEIGHT;
|
||||
|
||||
|
|
@ -303,16 +407,7 @@ export default class ImagePreview extends PureComponent {
|
|||
<Text style={style.title}>
|
||||
{`${this.state.currentFile + 1}/${this.props.files.length}`}
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
onPress={() => true}
|
||||
style={style.headerIcon}
|
||||
>
|
||||
{/*<Icon
|
||||
name='download'
|
||||
size={15}
|
||||
color='#fff'
|
||||
/>*/}
|
||||
</TouchableOpacity>
|
||||
{this.renderDownloadButton()}
|
||||
</View>
|
||||
</View>
|
||||
<LinearGradient
|
||||
|
|
@ -328,6 +423,13 @@ export default class ImagePreview extends PureComponent {
|
|||
</LinearGradient>
|
||||
</AnimatedView>
|
||||
</AnimatedView>
|
||||
<Downloader
|
||||
show={this.state.showDownloader}
|
||||
file={this.props.files[this.state.currentFile]}
|
||||
onDownloadCancel={this.hideDownloader}
|
||||
onDownloadStart={this.hideDownloader}
|
||||
onDownloadSuccess={this.hideDownloader}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {Platform} from 'react-native';
|
|||
|
||||
import {addFileToFetchCache} from 'app/actions/views/file_preview';
|
||||
import {getTheme} from 'app/selectors/preferences';
|
||||
import {canDownloadFilesOnMobile} from 'mattermost-redux/selectors/entities/general';
|
||||
import {makeGetFilesForPost} from 'mattermost-redux/selectors/entities/files';
|
||||
|
||||
import ImagePreview from './image_preview';
|
||||
|
|
@ -18,6 +19,7 @@ function makeMapStateToProps() {
|
|||
return function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
...ownProps,
|
||||
canDownloadFiles: canDownloadFilesOnMobile(state),
|
||||
fetchCache: state.views.fetchCache,
|
||||
files: getFilesForPost(state, ownProps.postId),
|
||||
theme: getTheme(state),
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
|
||||
import {NavigationTypes} from 'app/constants';
|
||||
import {emptyFunction} from 'app/utils/general';
|
||||
|
||||
import OptionsModalList from './options_modal_list';
|
||||
|
||||
|
|
@ -25,12 +26,17 @@ export default class OptionsModal extends PureComponent {
|
|||
static propTypes = {
|
||||
items: PropTypes.array.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
onCancelPress: PropTypes.func,
|
||||
title: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.object
|
||||
])
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
onCancelPress: emptyFunction
|
||||
}
|
||||
|
||||
state = {
|
||||
top: new Animated.Value(deviceHeight)
|
||||
};
|
||||
|
|
@ -47,6 +53,11 @@ export default class OptionsModal extends PureComponent {
|
|||
EventEmitter.off(NavigationTypes.NAVIGATION_CLOSE_MODAL, this.close);
|
||||
}
|
||||
|
||||
handleCancel = () => {
|
||||
this.props.onCancelPress();
|
||||
this.close();
|
||||
}
|
||||
|
||||
close = () => {
|
||||
Animated.timing(this.state.top, {
|
||||
toValue: deviceHeight,
|
||||
|
|
@ -70,7 +81,7 @@ export default class OptionsModal extends PureComponent {
|
|||
<AnimatedView style={{height: deviceHeight, left: 0, top: this.state.top, width: deviceWidth}}>
|
||||
<OptionsModalList
|
||||
items={items}
|
||||
onCancelPress={this.close}
|
||||
onCancelPress={this.handleCancel}
|
||||
title={title}
|
||||
/>
|
||||
</AnimatedView>
|
||||
|
|
|
|||
|
|
@ -1864,6 +1864,7 @@
|
|||
"mobile.file_upload.more": "More",
|
||||
"mobile.file_upload.video": "Video Library",
|
||||
"mobile.help.title": "Help",
|
||||
"mobile.image_preview.save": "Save Image",
|
||||
"mobile.intro_messages.DM": "This is the start of your direct message history with {teammate}. Direct messages and files shared here are not shown to people outside this area.",
|
||||
"mobile.intro_messages.default_message": "This is the first channel teammates see when they sign up - use it for posting updates everyone needs to know.",
|
||||
"mobile.intro_messages.default_welcome": "Welcome to {name}!",
|
||||
|
|
|
|||
|
|
@ -25,10 +25,13 @@
|
|||
146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
|
||||
1BCA51319AC6442991C6A208 /* Zocial.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0A091BF1A3D04650AD306A0D /* Zocial.ttf */; };
|
||||
1C1F002AC5DE465FAE9E0D74 /* libFastImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C0C75A424714EAC8E876A8F /* libFastImage.a */; };
|
||||
2B2679D44CFA48E6A60D68BC /* libRNFetchBlob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B19E38FBCB94C57BB03B8E6 /* libRNFetchBlob.a */; };
|
||||
2B4C9B708010475DA575B81D /* SimpleLineIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F1F071EE85494E269A50AE88 /* SimpleLineIcons.ttf */; };
|
||||
2D5296A8926B4D7FBAF2D6E2 /* OpenSans-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6561AEAC21CC40B8A72ABB93 /* OpenSans-Light.ttf */; };
|
||||
2E3655C7E15049DBBC3A666B /* libRNImagePicker.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DFE628F5A526417D85AD635E /* libRNImagePicker.a */; };
|
||||
374634801E8480C2005E1244 /* libRCTOrientation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 374634671E848085005E1244 /* libRCTOrientation.a */; };
|
||||
375218501F4B9EE70035444B /* libRCTCameraRoll.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3752184F1F4B9E980035444B /* libRCTCameraRoll.a */; };
|
||||
37ABD3C81F4CE142001FDE6B /* libART.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 37ABD39C1F4CE13B001FDE6B /* libART.a */; };
|
||||
382D94CF15EE4FC292C3F341 /* Foundation.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 51F5A483EA9F4A9A87ACFB59 /* Foundation.ttf */; };
|
||||
38FB8B0A3AAD4F839EF6EF0B /* libJailMonkey.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 77810F0A063349439B0D8B6B /* libJailMonkey.a */; };
|
||||
3D38ABA732A34A9BB3294F90 /* EvilIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 349FBA7338E74D9BBD709528 /* EvilIcons.ttf */; };
|
||||
|
|
@ -135,6 +138,20 @@
|
|||
remoteGlobalIDString = 134814201AA4EA6300B7C361;
|
||||
remoteInfo = RCTOrientation;
|
||||
};
|
||||
375218401F4B9E320035444B /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 546CB0A5BB8F453890CBA559 /* RNFetchBlob.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = A15C300E1CD25C330074CB35;
|
||||
remoteInfo = RNFetchBlob;
|
||||
};
|
||||
3752184E1F4B9E980035444B /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 3752184A1F4B9E980035444B /* RCTCameraRoll.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 58B5115D1A9E6B3D00147676;
|
||||
remoteInfo = RCTCameraRoll;
|
||||
};
|
||||
3779BE9F1EB1235400D081C1 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 85E82BCA0FD245EEA520D106 /* RNImagePicker.xcodeproj */;
|
||||
|
|
@ -142,6 +159,13 @@
|
|||
remoteGlobalIDString = 014A3B5C1C6CF33500B6D375;
|
||||
remoteInfo = RNImagePicker;
|
||||
};
|
||||
37ABD39B1F4CE13B001FDE6B /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 37ABD3971F4CE13B001FDE6B /* ART.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 0CF68AC11AF0540F00FF9E5C;
|
||||
remoteInfo = ART;
|
||||
};
|
||||
37D8FEC11E80B5230091F3BD /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 7DCC3D826CE640AF8F491692 /* BVLinearGradient.xcodeproj */;
|
||||
|
|
@ -414,9 +438,13 @@
|
|||
349FBA7338E74D9BBD709528 /* EvilIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = EvilIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf"; sourceTree = "<group>"; };
|
||||
356C9186FA374641A00EB2EA /* MaterialIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = MaterialIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf"; sourceTree = "<group>"; };
|
||||
3647DF63D6764CF093375861 /* OpenSans-ExtraBold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "OpenSans-ExtraBold.ttf"; path = "../assets/fonts/OpenSans-ExtraBold.ttf"; sourceTree = "<group>"; };
|
||||
3752184A1F4B9E980035444B /* RCTCameraRoll.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTCameraRoll.xcodeproj; path = "../node_modules/react-native/Libraries/CameraRoll/RCTCameraRoll.xcodeproj"; sourceTree = "<group>"; };
|
||||
37ABD3971F4CE13B001FDE6B /* ART.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ART.xcodeproj; path = "../node_modules/react-native/Libraries/ART/ART.xcodeproj"; sourceTree = "<group>"; };
|
||||
41F3AFE83AAF4B74878AB78A /* OpenSans-Italic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "OpenSans-Italic.ttf"; path = "../assets/fonts/OpenSans-Italic.ttf"; sourceTree = "<group>"; };
|
||||
50ECB1D221E44F51B5690DF2 /* FastImage.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = FastImage.xcodeproj; path = "../node_modules/react-native-fast-image/ios/FastImage.xcodeproj"; sourceTree = "<group>"; };
|
||||
4B19E38FBCB94C57BB03B8E6 /* libRNFetchBlob.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFetchBlob.a; sourceTree = "<group>"; };
|
||||
51F5A483EA9F4A9A87ACFB59 /* Foundation.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Foundation.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Foundation.ttf"; sourceTree = "<group>"; };
|
||||
546CB0A5BB8F453890CBA559 /* RNFetchBlob.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNFetchBlob.xcodeproj; path = "../node_modules/react-native-fetch-blob/ios/RNFetchBlob.xcodeproj"; sourceTree = "<group>"; };
|
||||
5C3B95629BA74FB3A8377CB7 /* RCTOrientation.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RCTOrientation.xcodeproj; path = "../node_modules/react-native-orientation/iOS/RCTOrientation.xcodeproj"; sourceTree = "<group>"; };
|
||||
5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = "<group>"; };
|
||||
6561AEAC21CC40B8A72ABB93 /* OpenSans-Light.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "OpenSans-Light.ttf"; path = "../assets/fonts/OpenSans-Light.ttf"; sourceTree = "<group>"; };
|
||||
|
|
@ -469,6 +497,8 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
37ABD3C81F4CE142001FDE6B /* libART.a in Frameworks */,
|
||||
375218501F4B9EE70035444B /* libRCTCameraRoll.a in Frameworks */,
|
||||
7FBB5E9A1E1F5A4B000DE18A /* libRNSVG.a in Frameworks */,
|
||||
7FBB5E9B1E1F5A4B000DE18A /* libRNVectorIcons.a in Frameworks */,
|
||||
5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */,
|
||||
|
|
@ -494,6 +524,7 @@
|
|||
FDEF24E9D9AB47728E11033B /* libRNPasscodeStatus.a in Frameworks */,
|
||||
38FB8B0A3AAD4F839EF6EF0B /* libJailMonkey.a in Frameworks */,
|
||||
1C1F002AC5DE465FAE9E0D74 /* libFastImage.a in Frameworks */,
|
||||
2B2679D44CFA48E6A60D68BC /* libRNFetchBlob.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -648,6 +679,22 @@
|
|||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3752181C1F4B9E320035444B /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
375218411F4B9E320035444B /* libRNFetchBlob.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3752184B1F4B9E980035444B /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3752184F1F4B9E980035444B /* libRCTCameraRoll.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3779BE7C1EB1235400D081C1 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -656,6 +703,14 @@
|
|||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
37ABD3981F4CE13B001FDE6B /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
37ABD39C1F4CE13B001FDE6B /* libART.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
37D8FEBE1E80B5230091F3BD /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -783,6 +838,8 @@
|
|||
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
37ABD3971F4CE13B001FDE6B /* ART.xcodeproj */,
|
||||
3752184A1F4B9E980035444B /* RCTCameraRoll.xcodeproj */,
|
||||
7F26919B1EE1DC51007574FE /* RNNotifications.xcodeproj */,
|
||||
7FC200BC1EBB65100099331B /* ReactNativeNavigation.xcodeproj */,
|
||||
7F6877AA1E7835E50094B63F /* ToolTipMenu.xcodeproj */,
|
||||
|
|
@ -809,6 +866,7 @@
|
|||
EBA6063A99C141098D40C67A /* RNPasscodeStatus.xcodeproj */,
|
||||
27A6EA89298440439DA9F98D /* JailMonkey.xcodeproj */,
|
||||
50ECB1D221E44F51B5690DF2 /* FastImage.xcodeproj */,
|
||||
546CB0A5BB8F453890CBA559 /* RNFetchBlob.xcodeproj */,
|
||||
);
|
||||
name = Libraries;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -921,6 +979,10 @@
|
|||
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectReferences = (
|
||||
{
|
||||
ProductGroup = 37ABD3981F4CE13B001FDE6B /* Products */;
|
||||
ProjectRef = 37ABD3971F4CE13B001FDE6B /* ART.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 37D8FEBE1E80B5230091F3BD /* Products */;
|
||||
ProjectRef = 7DCC3D826CE640AF8F491692 /* BVLinearGradient.xcodeproj */;
|
||||
|
|
@ -941,6 +1003,10 @@
|
|||
ProductGroup = 5E91572E1DD0AC6500FF2AA8 /* Products */;
|
||||
ProjectRef = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 3752184B1F4B9E980035444B /* Products */;
|
||||
ProjectRef = 3752184A1F4B9E980035444B /* RCTCameraRoll.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */;
|
||||
ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */;
|
||||
|
|
@ -997,6 +1063,10 @@
|
|||
ProductGroup = 37DD11071E79EBE1004111BA /* Products */;
|
||||
ProjectRef = B97AA6F961BB47D3A3297E8E /* RNDeviceInfo.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 3752181C1F4B9E320035444B /* Products */;
|
||||
ProjectRef = 546CB0A5BB8F453890CBA559 /* RNFetchBlob.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 3779BE7C1EB1235400D081C1 /* Products */;
|
||||
ProjectRef = 85E82BCA0FD245EEA520D106 /* RNImagePicker.xcodeproj */;
|
||||
|
|
@ -1098,6 +1168,20 @@
|
|||
remoteRef = 374634661E848085005E1244 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
375218411F4B9E320035444B /* libRNFetchBlob.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libRNFetchBlob.a;
|
||||
remoteRef = 375218401F4B9E320035444B /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
3752184F1F4B9E980035444B /* libRCTCameraRoll.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libRCTCameraRoll.a;
|
||||
remoteRef = 3752184E1F4B9E980035444B /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
3779BEA01EB1235400D081C1 /* libRNImagePicker.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
|
|
@ -1105,6 +1189,13 @@
|
|||
remoteRef = 3779BE9F1EB1235400D081C1 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
37ABD39C1F4CE13B001FDE6B /* libART.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libART.a;
|
||||
remoteRef = 37ABD39B1F4CE13B001FDE6B /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
37D8FEC21E80B5230091F3BD /* libBVLinearGradient.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
|
|
@ -1481,6 +1572,7 @@
|
|||
"$(SRCROOT)/../node_modules/jail-monkey/JailMonkey",
|
||||
"$(SRCROOT)/../node_modules/react-native/Libraries/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-fast-image/ios/FastImage/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-fetch-blob/ios/**",
|
||||
);
|
||||
INFOPLIST_FILE = Mattermost/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
|
|
@ -1520,6 +1612,7 @@
|
|||
"$(SRCROOT)/../node_modules/jail-monkey/JailMonkey",
|
||||
"$(SRCROOT)/../node_modules/react-native/Libraries/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-fast-image/ios/FastImage/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-fetch-blob/ios/**",
|
||||
);
|
||||
INFOPLIST_FILE = Mattermost/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
|
|
|
|||
|
|
@ -22,10 +22,12 @@
|
|||
"react-native-animatable": "1.2.2",
|
||||
"react-native-bottom-sheet": "1.0.1",
|
||||
"react-native-button": "1.8.2",
|
||||
"react-native-circular-progress": "0.0.8",
|
||||
"react-native-cookies": "3.1.0",
|
||||
"react-native-device-info": "0.11.0",
|
||||
"react-native-drawer": "2.3.0",
|
||||
"react-native-exception-handler": "1.1.0",
|
||||
"react-native-fetch-blob": "0.10.8",
|
||||
"react-native-fast-image": "1.0.0",
|
||||
"react-native-image-picker": "jp928/react-native-image-picker#6ee35b69f3dbd6c7c66f580fd4d9eabf398703d4",
|
||||
"react-native-keyboard-aware-scroll-view": "0.2.8",
|
||||
|
|
|
|||
24
yarn.lock
24
yarn.lock
|
|
@ -2631,6 +2631,17 @@ glob-to-regexp@^0.3.0:
|
|||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
|
||||
|
||||
glob@7.0.6:
|
||||
version "7.0.6"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a"
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^3.0.2"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@7.1.1, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
|
||||
|
|
@ -4475,6 +4486,12 @@ react-native-button@1.8.2:
|
|||
version "1.8.2"
|
||||
resolved "https://registry.yarnpkg.com/react-native-button/-/react-native-button-1.8.2.tgz#49e5cadd22ee443adea52a85539d288aa2117ade"
|
||||
|
||||
react-native-circular-progress@0.0.8:
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/react-native-circular-progress/-/react-native-circular-progress-0.0.8.tgz#53ccdbe57357ce2459f8ab1b67119a3e2a5014b4"
|
||||
dependencies:
|
||||
art "^0.10.0"
|
||||
|
||||
react-native-cookies@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-cookies/-/react-native-cookies-3.1.0.tgz#7816a60be1ae0484c9ddd47380d77f98ec0ffaca"
|
||||
|
|
@ -4495,6 +4512,13 @@ react-native-exception-handler@1.1.0:
|
|||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-exception-handler/-/react-native-exception-handler-1.1.0.tgz#320b7fc9c104e11d66173a2b0daeac97831a1a37"
|
||||
|
||||
react-native-fetch-blob@0.10.8:
|
||||
version "0.10.8"
|
||||
resolved "https://registry.yarnpkg.com/react-native-fetch-blob/-/react-native-fetch-blob-0.10.8.tgz#4fc256abae0cb5f10e7c41f28c11b3ff330d72a9"
|
||||
dependencies:
|
||||
base-64 "0.1.0"
|
||||
glob "7.0.6"
|
||||
|
||||
react-native-fast-image@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-fast-image/-/react-native-fast-image-1.0.0.tgz#a88337efa18fd34d530bab9ee05680374f3e2a2d"
|
||||
|
|
|
|||
Loading…
Reference in a new issue