diff --git a/android/app/build.gradle b/android/app/build.gradle index 1067fb0f66..9d2271ef0a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -174,6 +174,7 @@ android { dependencies { addUnimodulesDependencies() implementation "org.webkit:android-jsc:r241213" + implementation project(':react-native-document-picker') implementation project(':react-native-firebase') implementation project(':react-native-webview') implementation project(':react-native-orientation-locker') diff --git a/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java b/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java index d87d301f8a..a2b1eed847 100644 --- a/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java +++ b/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java @@ -3,6 +3,7 @@ import android.app.Application; import com.facebook.react.ReactApplication; +import io.github.elyx0.reactnativedocumentpicker.DocumentPickerPackage; import io.invertase.firebase.RNFirebasePackage; import io.invertase.firebase.fabric.crashlytics.RNFirebaseCrashlyticsPackage; import io.invertase.firebase.analytics.RNFirebaseAnalyticsPackage; @@ -60,6 +61,7 @@ public boolean getUseDeveloperSupport() { protected List getPackages() { return Arrays.asList( new MainReactPackage(), + new DocumentPickerPackage(), new RNFirebasePackage(), new RNFirebaseCrashlyticsPackage(), new RNFirebaseAnalyticsPackage(), diff --git a/android/settings.gradle b/android/settings.gradle index cdbc352b40..e5b09cafc2 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -2,6 +2,8 @@ apply from: '../node_modules/react-native-unimodules/gradle.groovy' includeUnimodulesProjects() rootProject.name = 'RocketChatRN' +include ':react-native-document-picker' +project(':react-native-document-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-document-picker/android') include ':react-native-firebase' project(':react-native-firebase').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-firebase/android') include ':react-native-webview' diff --git a/app/constants/settings.js b/app/constants/settings.js index 4e4d993956..17eb1560f1 100644 --- a/app/constants/settings.js +++ b/app/constants/settings.js @@ -73,5 +73,11 @@ export default { }, AutoTranslate_Enabled: { type: 'valueAsBoolean' + }, + FileUpload_MediaTypeWhiteList: { + type: 'valueAsString' + }, + FileUpload_MaxFileSize: { + type: 'valueAsNumber' } }; diff --git a/app/containers/MessageBox/UploadModal.js b/app/containers/MessageBox/UploadModal.js index d1d3f87f78..4a5baa3c1e 100644 --- a/app/containers/MessageBox/UploadModal.js +++ b/app/containers/MessageBox/UploadModal.js @@ -2,6 +2,7 @@ import React, { Component } from 'react'; import { View, Text, StyleSheet, Image, ScrollView, TouchableHighlight } from 'react-native'; +import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import Modal from 'react-native-modal'; import { responsive } from 'react-native-responsive-ui'; @@ -12,7 +13,9 @@ import Button from '../Button'; import I18n from '../../i18n'; import sharedStyles from '../../views/Styles'; import { isIOS } from '../../utils/deviceInfo'; -import { COLOR_PRIMARY, COLOR_BACKGROUND_CONTAINER, COLOR_WHITE } from '../../constants/colors'; +import { + COLOR_PRIMARY, COLOR_BACKGROUND_CONTAINER, COLOR_WHITE, COLOR_DANGER +} from '../../constants/colors'; import { CustomIcon } from '../../lib/Icons'; const cancelButtonColor = COLOR_BACKGROUND_CONTAINER; @@ -71,6 +74,23 @@ const styles = StyleSheet.create({ flex: 1, textAlign: 'center' }, + errorIcon: { + color: COLOR_DANGER + }, + fileMime: { + ...sharedStyles.textColorTitle, + ...sharedStyles.textBold, + textAlign: 'center', + fontSize: 20, + marginBottom: 20 + }, + errorContainer: { + margin: 20, + flex: 1, + textAlign: 'center', + justifyContent: 'center', + alignItems: 'center' + }, video: { flex: 1, borderRadius: 4, @@ -84,13 +104,19 @@ const styles = StyleSheet.create({ }); @responsive +@connect(state => ({ + FileUpload_MediaTypeWhiteList: state.settings.FileUpload_MediaTypeWhiteList, + FileUpload_MaxFileSize: state.settings.FileUpload_MaxFileSize +})) export default class UploadModal extends Component { static propTypes = { isVisible: PropTypes.bool, file: PropTypes.object, close: PropTypes.func, submit: PropTypes.func, - window: PropTypes.object + window: PropTypes.object, + FileUpload_MediaTypeWhiteList: PropTypes.string, + FileUpload_MaxFileSize: PropTypes.number } state = { @@ -132,12 +158,78 @@ export default class UploadModal extends Component { return false; } + canUploadFile = () => { + const { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize, file } = this.props; + if (!(file && file.path)) { + return true; + } + if (file.size > FileUpload_MaxFileSize) { + return false; + } + if (!FileUpload_MediaTypeWhiteList) { + return false; + } + const allowedMime = FileUpload_MediaTypeWhiteList.split(','); + if (allowedMime.includes(file.mime)) { + return true; + } + const wildCardGlob = '/*'; + const wildCards = allowedMime.filter(item => item.indexOf(wildCardGlob) > 0); + if (wildCards.includes(file.mime.replace(/(\/.*)$/, wildCardGlob))) { + return true; + } + return false; + } + submit = () => { const { file, submit } = this.props; const { name, description } = this.state; submit({ ...file, name, description }); } + renderError = () => { + const { file, FileUpload_MaxFileSize, close } = this.props; + const { window: { width } } = this.props; + const errorMessage = (FileUpload_MaxFileSize < file.size) + ? 'error-file-too-large' + : 'error-invalid-file-type'; + return ( + + + {I18n.t(errorMessage)} + + + + + { file.mime } + + { + (isIOS) + ? ( +