Unverified Commit 319ca7f0 authored by Diego Mello's avatar Diego Mello Committed by GitHub
Browse files

[FIX] Unify upload check (#1229)

parent ae7a9cba
......@@ -5,6 +5,7 @@ import {
} from 'react-native';
import { AudioRecorder, AudioUtils } from 'react-native-audio';
import { BorderlessButton } from 'react-native-gesture-handler';
import FileSystem from 'expo-file-system';
import styles from './styles';
import I18n from '../../i18n';
......@@ -68,7 +69,7 @@ export default class extends React.PureComponent {
//
AudioRecorder.onFinished = (data) => {
if (!this.recordingCanceled && isIOS) {
this.finishRecording(data.status === 'OK', data.audioFileURL);
this.finishRecording(data.status === 'OK', data.audioFileURL, data.audioFileSize);
}
};
AudioRecorder.startRecording();
......@@ -80,7 +81,7 @@ export default class extends React.PureComponent {
}
}
finishRecording = (didSucceed, filePath) => {
finishRecording = (didSucceed, filePath, size) => {
const { onFinish } = this.props;
if (!didSucceed) {
return onFinish && onFinish(didSucceed);
......@@ -90,9 +91,11 @@ export default class extends React.PureComponent {
}
const fileInfo = {
name: this.name,
mime: 'audio/aac',
type: 'audio/aac',
store: 'Uploads',
path: filePath
path: filePath,
size
};
return onFinish && onFinish(fileInfo);
}
......@@ -102,7 +105,8 @@ export default class extends React.PureComponent {
this.recording = false;
const filePath = await AudioRecorder.stopRecording();
if (isAndroid) {
this.finishRecording(true, filePath);
const data = await FileSystem.getInfoAsync(decodeURIComponent(filePath));
this.finishRecording(true, filePath, data.size);
}
} catch (err) {
this.finishRecording(false);
......
......@@ -2,7 +2,6 @@ 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';
......@@ -13,9 +12,8 @@ import Button from '../Button';
import I18n from '../../i18n';
import sharedStyles from '../../views/Styles';
import { isIOS } from '../../utils/deviceInfo';
import { canUploadFile } from '../../utils/media';
import {
COLOR_PRIMARY, COLOR_BACKGROUND_CONTAINER, COLOR_WHITE, COLOR_DANGER
COLOR_PRIMARY, COLOR_BACKGROUND_CONTAINER, COLOR_WHITE
} from '../../constants/colors';
import { CustomIcon } from '../../lib/Icons';
......@@ -75,23 +73,6 @@ 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,
......@@ -110,9 +91,7 @@ class UploadModal extends Component {
file: PropTypes.object,
close: PropTypes.func,
submit: PropTypes.func,
window: PropTypes.object,
FileUpload_MediaTypeWhiteList: PropTypes.string,
FileUpload_MaxFileSize: PropTypes.number
window: PropTypes.object
}
state = {
......@@ -154,79 +133,12 @@ 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 white list is empty, all media types are enabled
if (!FileUpload_MediaTypeWhiteList) {
return true;
}
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 (
<View style={[styles.container, { width: width - 32 }]}>
<View style={styles.titleContainer}>
<Text style={styles.title}>{I18n.t(errorMessage)}</Text>
</View>
<View style={styles.errorContainer}>
<CustomIcon name='circle-cross' size={120} style={styles.errorIcon} />
</View>
<Text style={styles.fileMime}>{ file.mime }</Text>
<View style={styles.buttonContainer}>
{
(isIOS)
? (
<Button
title={I18n.t('Cancel')}
type='secondary'
backgroundColor={cancelButtonColor}
style={styles.button}
onPress={close}
/>
)
: (
<TouchableHighlight
onPress={close}
style={[styles.androidButton, { backgroundColor: cancelButtonColor }]}
underlayColor={cancelButtonColor}
activeOpacity={0.5}
>
<Text style={[styles.androidButtonText, { ...sharedStyles.textBold, color: COLOR_PRIMARY }]}>{I18n.t('Cancel')}</Text>
</TouchableHighlight>
)
}
</View>
</View>
);
}
renderButtons = () => {
const { close } = this.props;
if (isIOS) {
......@@ -288,10 +200,9 @@ class UploadModal extends Component {
render() {
const {
window: { width }, isVisible, close, file, FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize
window: { width }, isVisible, close
} = this.props;
const { name, description } = this.state;
const showError = !canUploadFile(file, { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize });
return (
<Modal
isVisible={isVisible}
......@@ -304,37 +215,29 @@ class UploadModal extends Component {
hideModalContentWhileAnimating
avoidKeyboard
>
{(showError) ? this.renderError()
: (
<View style={[styles.container, { width: width - 32 }]}>
<View style={styles.titleContainer}>
<Text style={styles.title}>{I18n.t('Upload_file_question_mark')}</Text>
</View>
<ScrollView style={styles.scrollView}>
{this.renderPreview()}
<TextInput
placeholder={I18n.t('File_name')}
value={name}
onChangeText={value => this.setState({ name: value })}
/>
<TextInput
placeholder={I18n.t('File_description')}
value={description}
onChangeText={value => this.setState({ description: value })}
/>
</ScrollView>
{this.renderButtons()}
</View>
)}
<View style={[styles.container, { width: width - 32 }]}>
<View style={styles.titleContainer}>
<Text style={styles.title}>{I18n.t('Upload_file_question_mark')}</Text>
</View>
<ScrollView style={styles.scrollView}>
{this.renderPreview()}
<TextInput
placeholder={I18n.t('File_name')}
value={name}
onChangeText={value => this.setState({ name: value })}
/>
<TextInput
placeholder={I18n.t('File_description')}
value={description}
onChangeText={value => this.setState({ description: value })}
/>
</ScrollView>
{this.renderButtons()}
</View>
</Modal>
);
}
}
const mapStateToProps = state => ({
FileUpload_MediaTypeWhiteList: state.settings.FileUpload_MediaTypeWhiteList,
FileUpload_MaxFileSize: state.settings.FileUpload_MaxFileSize
});
export default responsive(connect(mapStateToProps)(UploadModal));
export default responsive(UploadModal);
......@@ -30,6 +30,7 @@ import LeftButtons from './LeftButtons';
import RightButtons from './RightButtons';
import { isAndroid } from '../../utils/deviceInfo';
import CommandPreview from './CommandPreview';
import { canUploadFile } from '../../utils/media';
const MENTIONS_TRACKING_TYPE_USERS = '@';
const MENTIONS_TRACKING_TYPE_EMOJIS = ':';
......@@ -73,6 +74,8 @@ class MessageBox extends Component {
roomType: PropTypes.string,
tmid: PropTypes.string,
replyWithMention: PropTypes.bool,
FileUpload_MediaTypeWhiteList: PropTypes.string,
FileUpload_MaxFileSize: PropTypes.number,
getCustomEmoji: PropTypes.func,
editCancel: PropTypes.func.isRequired,
editRequest: PropTypes.func.isRequired,
......@@ -435,6 +438,16 @@ class MessageBox extends Component {
this.setShowSend(false);
}
canUploadFile = (file) => {
const { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize } = this.props;
const result = canUploadFile(file, { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize });
if (result.success) {
return true;
}
Alert.alert(I18n.t('Error_uploading'), I18n.t(result.error));
return false;
}
sendMediaMessage = async(file) => {
const {
rid, tmid, baseUrl: server, user
......@@ -458,7 +471,9 @@ class MessageBox extends Component {
takePhoto = async() => {
try {
const image = await ImagePicker.openCamera(this.imagePickerConfig);
this.showUploadModal(image);
if (this.canUploadFile(image)) {
this.showUploadModal(image);
}
} catch (e) {
log(e);
}
......@@ -467,7 +482,9 @@ class MessageBox extends Component {
takeVideo = async() => {
try {
const video = await ImagePicker.openCamera(this.videoPickerConfig);
this.showUploadModal(video);
if (this.canUploadFile(video)) {
this.showUploadModal(video);
}
} catch (e) {
log(e);
}
......@@ -476,7 +493,9 @@ class MessageBox extends Component {
chooseFromLibrary = async() => {
try {
const image = await ImagePicker.openPicker(this.libraryPickerConfig);
this.showUploadModal(image);
if (this.canUploadFile(image)) {
this.showUploadModal(image);
}
} catch (e) {
log(e);
}
......@@ -487,12 +506,15 @@ class MessageBox extends Component {
const res = await DocumentPicker.pick({
type: [DocumentPicker.types.allFiles]
});
this.showUploadModal({
const file = {
filename: res.name,
size: res.size,
mime: res.type,
path: res.uri
});
};
if (this.canUploadFile(file)) {
this.showUploadModal(file);
}
} catch (e) {
if (!DocumentPicker.isCancel(e)) {
log(e);
......@@ -560,11 +582,10 @@ class MessageBox extends Component {
});
if (fileInfo) {
try {
await RocketChat.sendFileMessage(rid, fileInfo, tmid, server, user);
} catch (e) {
if (e && e.error === 'error-file-too-large') {
return Alert.alert(I18n.t(e.error));
if (this.canUploadFile(fileInfo)) {
await RocketChat.sendFileMessage(rid, fileInfo, tmid, server, user);
}
} catch (e) {
log(e);
}
}
......@@ -921,7 +942,9 @@ const mapStateToProps = state => ({
id: state.login.user && state.login.user.id,
username: state.login.user && state.login.user.username,
token: state.login.user && state.login.user.token
}
},
FileUpload_MediaTypeWhiteList: state.settings.FileUpload_MediaTypeWhiteList,
FileUpload_MaxFileSize: state.settings.FileUpload_MaxFileSize
});
const dispatchToProps = ({
......
......@@ -30,14 +30,9 @@ export function sendFileMessage(rid, fileInfo, tmid, server, user) {
const serversDB = database.servers;
const serversCollection = serversDB.collections.get('servers');
const serverInfo = await serversCollection.find(server);
const { FileUpload_MaxFileSize, id: Site_Url } = serverInfo;
const { id: Site_Url } = serverInfo;
const { id, token } = user;
// -1 maxFileSize means there is no limit
if (FileUpload_MaxFileSize > -1 && fileInfo.size > FileUpload_MaxFileSize) {
return reject({ error: 'error-file-too-large' }); // eslint-disable-line
}
const uploadUrl = `${ Site_Url }/api/v1/rooms.upload/${ rid }`;
const xhr = new XMLHttpRequest();
......
export const canUploadFile = (file, serverInfo) => {
const { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize } = serverInfo;
if (!(file && file.path)) {
return true;
return { success: true };
}
if (file.size > FileUpload_MaxFileSize) {
return false;
if (FileUpload_MaxFileSize > -1 && file.size > FileUpload_MaxFileSize) {
return { success: false, error: 'error-file-too-large' };
}
// if white list is empty, all media types are enabled
if (!FileUpload_MediaTypeWhiteList) {
return true;
return { success: true };
}
const allowedMime = FileUpload_MediaTypeWhiteList.split(',');
if (allowedMime.includes(file.mime)) {
return true;
return { success: true };
}
const wildCardGlob = '/*';
const wildCards = allowedMime.filter(item => item.indexOf(wildCardGlob) > 0);
if (wildCards.includes(file.mime.replace(/(\/.*)$/, wildCardGlob))) {
return true;
if (file.mime && wildCards.includes(file.mime.replace(/(\/.*)$/, wildCardGlob))) {
return { success: true };
}
return false;
return { success: false, error: 'error-invalid-file-type' };
};
......@@ -199,12 +199,14 @@ class ShareListView extends React.Component {
this.servers = await serversCollection.query().fetch();
this.chats = this.data.slice(0, LIMIT);
const serverInfo = await serversCollection.find(server);
const canUploadFileResult = canUploadFile(fileInfo || fileData, serverInfo);
this.internalSetState({
chats: this.chats ? this.chats.slice() : [],
servers: this.servers ? this.servers.slice() : [],
loading: false,
showError: !canUploadFile(fileInfo || fileData, serverInfo),
showError: !canUploadFileResult.success,
error: canUploadFileResult.error,
serverInfo
});
this.forceUpdate();
......@@ -378,12 +380,8 @@ class ShareListView extends React.Component {
renderError = () => {
const {
fileInfo: file, loading, searching, serverInfo
fileInfo: file, loading, searching, error
} = this.state;
const { FileUpload_MaxFileSize } = serverInfo;
const errorMessage = (FileUpload_MaxFileSize < file.size)
? 'error-file-too-large'
: 'error-invalid-file-type';
if (loading) {
return <ActivityIndicator style={styles.loading} />;
......@@ -400,7 +398,7 @@ class ShareListView extends React.Component {
: null
}
<View style={[styles.container, styles.centered]}>
<Text style={styles.title}>{I18n.t(errorMessage)}</Text>
<Text style={styles.title}>{I18n.t(error)}</Text>
<CustomIcon name='circle-cross' size={120} style={styles.errorIcon} />
<Text style={styles.fileMime}>{ file.mime }</Text>
</View>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment