Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NEW] File upload #882

Merged
Merged
Show file tree
Hide file tree
Changes from all 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'
}
};
140 changes: 118 additions & 22 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,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;
Expand Down Expand Up @@ -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,
Expand All @@ -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 = {
Expand Down Expand Up @@ -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 (
<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 @@ -200,6 +292,7 @@ export default class UploadModal extends Component {
render() {
const { window: { width }, isVisible, close } = this.props;
const { name, description } = this.state;
const showError = !this.canUploadFile();
return (
<Modal
isVisible={isVisible}
Expand All @@ -212,26 +305,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}>
{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>
{(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
30 changes: 27 additions & 3 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 @@ -61,6 +62,7 @@ const FILE_CANCEL_INDEX = 0;
const FILE_PHOTO_INDEX = 1;
const FILE_VIDEO_INDEX = 2;
const FILE_LIBRARY_INDEX = 3;
const FILE_DOCUMENT_INDEX = 4;

class MessageBox extends Component {
static propTypes = {
Expand Down Expand Up @@ -111,7 +113,8 @@ class MessageBox extends Component {
I18n.t('Cancel'),
I18n.t('Take_a_photo'),
I18n.t('Take_a_video'),
I18n.t('Choose_from_library')
I18n.t('Choose_from_library'),
I18n.t('Choose_file')
];
const libPickerLabels = {
cropperChooseText: I18n.t('Choose'),
Expand Down Expand Up @@ -487,7 +490,6 @@ class MessageBox extends Component {

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

this.setState({ file: { isVisible: false } });
const fileInfo = {
name: file.name,
Expand All @@ -500,7 +502,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 Down Expand Up @@ -531,6 +533,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 @@ -555,6 +576,9 @@ class MessageBox extends Component {
case FILE_LIBRARY_INDEX:
this.chooseFromLibrary();
break;
case FILE_DOCUMENT_INDEX:
this.chooseFile();
break;
default:
break;
}
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',
pranavpandey1998official marked this conversation as resolved.
Show resolved Hide 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
8 changes: 7 additions & 1 deletion ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ PODS:
- QBImagePickerController (3.4.0)
- React (0.59.8):
- React/Core (= 0.59.8)
- react-native-document-picker (3.2.2):
- React
- react-native-orientation-locker (1.1.5):
- React
- react-native-splash-screen (3.2.0):
Expand Down Expand Up @@ -184,6 +186,7 @@ DEPENDENCIES:
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- GoogleIDFASupport (~> 3.14.0)
- react-native-document-picker (from `../node_modules/react-native-document-picker`)
- react-native-orientation-locker (from `../node_modules/react-native-orientation-locker`)
- react-native-splash-screen (from `../node_modules/react-native-splash-screen`)
- react-native-webview (from `../node_modules/react-native-webview`)
Expand Down Expand Up @@ -264,6 +267,8 @@ EXTERNAL SOURCES:
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
React:
:path: "../node_modules/react-native"
react-native-document-picker:
:path: "../node_modules/react-native-document-picker"
react-native-orientation-locker:
:path: "../node_modules/react-native-orientation-locker"
react-native-splash-screen:
Expand Down Expand Up @@ -346,6 +351,7 @@ SPEC CHECKSUMS:
Protobuf: 7a877b7f3e5964e3fce995e2eb323dbc6831bb5a
QBImagePickerController: d54cf93db6decf26baf6ed3472f336ef35cae022
React: 76e6aa2b87d05eb6cccb6926d72685c9a07df152
react-native-document-picker: 94a07ce0494c559e2ae9fa86621d6c624d810fec
react-native-orientation-locker: 132a63bab4dddd2a5709f6f7935ad9676b0af7c5
react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865
react-native-webview: f3e28b48461c78db833f727feec08b13285e7b61
Expand All @@ -368,6 +374,6 @@ SPEC CHECKSUMS:
UMTaskManagerInterface: 296793ab2a7e181fe5ebe2ba9b40ae208ab4b8fa
yoga: 92b2102c3d373d1a790db4ab761d2b0ffc634f64

PODFILE CHECKSUM: e913a7016ba7fbc295edc5178996383a1f54742e
PODFILE CHECKSUM: 1aa23cca853663acc12b10c4eb90f42b7b91533a

COCOAPODS: 1.6.2

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading