Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -60,6 +61,7 @@ public boolean getUseDeveloperSupport() {
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new DocumentPickerPackage(),
new RNFirebasePackage(),
new RNFirebaseCrashlyticsPackage(),
new RNFirebaseAnalyticsPackage(),
Expand Down
2 changes: 2 additions & 0 deletions android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
6 changes: 6 additions & 0 deletions app/constants/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,11 @@ export default {
},
AutoTranslate_Enabled: {
type: 'valueAsBoolean'
},
FileUpload_MediaTypeWhiteList: {
type: 'valueAsString'
},
FileUpload_MaxFileSize: {
type: 'valueAsNumber'
}
};
157 changes: 134 additions & 23 deletions app/containers/MessageBox/UploadModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -12,7 +13,10 @@ 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;

Expand Down Expand Up @@ -63,18 +67,47 @@ const styles = StyleSheet.create({
androidButtonText: {
fontSize: 18,
textAlign: 'center'
},
fileIcon: {
color: COLOR_PRIMARY,
margin: 20,
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'
}

});

@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 = {
Expand Down Expand Up @@ -116,12 +149,86 @@ 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 });
}

renderPreview() {
const { file } = this.props;
if (file.mime && file.mime.match(/image/)) {
return (<Image source={{ isStatic: true, uri: file.path }} style={styles.image} />);
}
return (<CustomIcon name='file-generic' size={72} style={styles.fileIcon} />);
}

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) {
Expand Down Expand Up @@ -168,7 +275,8 @@ export default class UploadModal extends Component {

render() {
const { window: { width }, isVisible, close } = this.props;
const { name, description, file } = this.state;
const { name, description } = this.state;
const showError = !this.canUploadFile();
return (
<Modal
isVisible={isVisible}
Expand All @@ -181,26 +289,29 @@ export default class UploadModal extends Component {
hideModalContentWhileAnimating
avoidKeyboard
>
<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}>
<Image source={{ isStatic: true, uri: file.path }} style={styles.image} />
<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>
{(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>
)}
</Modal>
);
}
Expand Down
34 changes: 29 additions & 5 deletions app/containers/MessageBox/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { emojify } from 'react-emojione';
import { KeyboardAccessoryView } from 'react-native-keyboard-input';
import ImagePicker from 'react-native-image-crop-picker';
import equal from 'deep-equal';
import DocumentPicker from 'react-native-document-picker';
import ActionSheet from 'react-native-action-sheet';

import { userTyping as userTypingAction } from '../../actions/room';
Expand Down Expand Up @@ -52,6 +53,7 @@ const imagePickerConfig = {
const FILE_CANCEL_INDEX = 0;
const FILE_PHOTO_INDEX = 1;
const FILE_LIBRARY_INDEX = 2;
const FILE_DOCUMENT_INDEX = 3;

class MessageBox extends Component {
static propTypes = {
Expand Down Expand Up @@ -101,7 +103,8 @@ class MessageBox extends Component {
this.fileOptions = [
I18n.t('Cancel'),
I18n.t('Take_a_photo'),
I18n.t('Choose_from_library')
I18n.t('Choose_from_library'),
I18n.t('Choose_file')
];
this.imagePickerConfig = {
...imagePickerConfig,
Expand Down Expand Up @@ -463,9 +466,8 @@ class MessageBox extends Component {
this.setShowSend(false);
}

sendImageMessage = async(file) => {
sendMediaMessage = async(file) => {
const { rid, tmid } = this.props;

this.setState({ file: { isVisible: false } });
const fileInfo = {
name: file.name,
Expand All @@ -478,7 +480,7 @@ class MessageBox extends Component {
try {
await RocketChat.sendFileMessage(rid, fileInfo, tmid);
} catch (e) {
log('err_send_image', e);
log('err_send_media_message', e);
}
}

Expand All @@ -500,6 +502,25 @@ class MessageBox extends Component {
}
}

chooseFile = async() => {
try {
const res = await DocumentPicker.pick({
type: [DocumentPicker.types.allFiles]
});
this.showUploadModal({
filename: res.name,
size: res.size,
mime: res.type,
path: res.uri
});
} catch (error) {
if (!DocumentPicker.isCancel(error)) {
log('chooseFile', error);
}
}
}


showUploadModal = (file) => {
this.setState({ file: { ...file, isVisible: true } });
}
Expand All @@ -521,6 +542,9 @@ class MessageBox extends Component {
case FILE_LIBRARY_INDEX:
this.chooseFromLibrary();
break;
case FILE_DOCUMENT_INDEX:
this.chooseFile();
break;
default:
break;
}
Expand Down Expand Up @@ -896,7 +920,7 @@ class MessageBox extends Component {
isVisible={(file && file.isVisible)}
file={file}
close={() => this.setState({ file: {} })}
submit={this.sendImageMessage}
submit={this.sendMediaMessage}
/>
</React.Fragment>
);
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export default {
Close_emoji_selector: 'Close emoji selector',
Choose: 'Choose',
Choose_from_library: 'Choose from library',
Choose_file: 'Choose file',
Comment thread
pranavpandey1998official marked this conversation as resolved.
Code: 'Code',
Collaborative: 'Collaborative',
Confirm: 'Confirm',
Expand Down
1 change: 1 addition & 0 deletions app/i18n/locales/pt-BR.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export default {
Close_emoji_selector: 'Fechar seletor de emojis',
Choose: 'Escolher',
Choose_from_library: 'Escolha da biblioteca',
Choose_file: 'Enviar arquivo',
Code: 'Código',
Collaborative: 'Colaborativo',
Confirm: 'Confirmar',
Expand Down
1 change: 1 addition & 0 deletions ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ target 'RocketChatRN' do
pod 'Crashlytics', '~> 3.12.0'
pod 'GoogleIDFASupport', '~> 3.14.0'
pod 'Firebase/Performance', '~> 5.20.1'
pod 'react-native-document-picker', :path => '../node_modules/react-native-document-picker'

use_unimodules!

Expand Down
Loading