diff --git a/app/components/attachment_button.js b/app/components/attachment_button.js
index 44024a573..b7198a2cf 100644
--- a/app/components/attachment_button.js
+++ b/app/components/attachment_button.js
@@ -10,6 +10,8 @@ import {
StyleSheet,
TouchableOpacity,
} from 'react-native';
+import RNFetchBlob from 'rn-fetch-blob';
+
import Icon from 'react-native-vector-icons/Ionicons';
import {DocumentPicker} from 'react-native-document-picker';
import ImagePicker from 'react-native-image-picker';
@@ -27,8 +29,10 @@ export default class AttachmentButton extends PureComponent {
children: PropTypes.node,
fileCount: PropTypes.number,
maxFileCount: PropTypes.number.isRequired,
+ maxFileSize: PropTypes.number.isRequired,
navigator: PropTypes.object.isRequired,
onShowFileMaxWarning: PropTypes.func,
+ onShowFileSizeWarning: PropTypes.func,
theme: PropTypes.object.isRequired,
uploadFiles: PropTypes.func.isRequired,
wrapper: PropTypes.bool,
@@ -293,8 +297,19 @@ export default class AttachmentButton extends PureComponent {
return true;
};
- uploadFiles = (images) => {
- this.props.uploadFiles(images);
+ uploadFiles = async (files) => {
+ const file = files[0];
+ if (!file.fileSize | !file.fileName) {
+ const fileInfo = await RNFetchBlob.fs.stat(file.path);
+ file.fileSize = fileInfo.size;
+ file.fileName = fileInfo.filename;
+ }
+
+ if (file.fileSize > this.props.maxFileSize) {
+ this.props.onShowFileSizeWarning(file.fileName);
+ } else {
+ this.props.uploadFiles(files);
+ }
};
handleFileAttachmentOption = (action) => {
diff --git a/app/components/file_upload_preview/file_upload_preview.js b/app/components/file_upload_preview/file_upload_preview.js
index 06bfdf1b0..c33987c5d 100644
--- a/app/components/file_upload_preview/file_upload_preview.js
+++ b/app/components/file_upload_preview/file_upload_preview.js
@@ -6,6 +6,7 @@ import PropTypes from 'prop-types';
import {
ScrollView,
StyleSheet,
+ Text,
View,
} from 'react-native';
@@ -20,6 +21,7 @@ export default class FileUploadPreview extends PureComponent {
deviceHeight: PropTypes.number.isRequired,
files: PropTypes.array.isRequired,
filesUploadingForCurrentChannel: PropTypes.bool.isRequired,
+ fileSizeWarning: PropTypes.string,
rootId: PropTypes.string,
showFileMaxWarning: PropTypes.bool.isRequired,
theme: PropTypes.object.isRequired,
@@ -42,12 +44,17 @@ export default class FileUploadPreview extends PureComponent {
render() {
const {
showFileMaxWarning,
+ fileSizeWarning,
channelIsLoading,
filesUploadingForCurrentChannel,
deviceHeight,
files,
} = this.props;
- if (channelIsLoading || (!files.length && !filesUploadingForCurrentChannel)) {
+
+ if (
+ !fileSizeWarning && !showFileMaxWarning &&
+ (channelIsLoading || (!files.length && !filesUploadingForCurrentChannel))
+ ) {
return null;
}
@@ -68,7 +75,11 @@ export default class FileUploadPreview extends PureComponent {
defaultMessage='Uploads limited to 5 files maximum.'
/>
)}
-
+ {Boolean(fileSizeWarning) &&
+
+ {fileSizeWarning}
+
+ }
);
diff --git a/app/components/post_textbox/index.js b/app/components/post_textbox/index.js
index ca00d1f5e..303bedb56 100644
--- a/app/components/post_textbox/index.js
+++ b/app/components/post_textbox/index.js
@@ -20,6 +20,7 @@ import {handleCommentDraftChanged, handleCommentDraftSelectionChanged} from 'app
import {userTyping} from 'app/actions/views/typing';
import {getCurrentChannelDraft, getThreadDraft} from 'app/selectors/views';
import {getChannelMembersForDm} from 'app/selectors/channel';
+import {getAllowedServerMaxFileSize} from 'app/utils/file';
import PostTextbox from './post_textbox';
@@ -53,6 +54,7 @@ function mapStateToProps(state, ownProps) {
userIsOutOfOffice,
deactivatedChannel,
files: currentDraft.files,
+ maxFileSize: getAllowedServerMaxFileSize(config),
maxMessageLength: (config && parseInt(config.MaxPostSize || 0, 10)) || MAX_MESSAGE_LENGTH,
theme: getTheme(state),
uploadFileRequestStatus: state.requests.files.uploadFiles.status,
diff --git a/app/components/post_textbox/post_textbox.js b/app/components/post_textbox/post_textbox.js
index d6156771c..68c4201a9 100644
--- a/app/components/post_textbox/post_textbox.js
+++ b/app/components/post_textbox/post_textbox.js
@@ -8,6 +8,7 @@ import {intlShape} from 'react-intl';
import Button from 'react-native-button';
import {General, RequestStatus} from 'mattermost-redux/constants';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
+import {getFormattedFileSize} from 'mattermost-redux/utils/file_utils';
import AttachmentButton from 'app/components/attachment_button';
import Autocomplete from 'app/components/autocomplete';
@@ -51,6 +52,7 @@ export default class PostTextbox extends PureComponent {
currentUserId: PropTypes.string.isRequired,
deactivatedChannel: PropTypes.bool.isRequired,
files: PropTypes.array,
+ maxFileSize: PropTypes.number.isRequired,
maxMessageLength: PropTypes.number.isRequired,
navigator: PropTypes.object,
rootId: PropTypes.string,
@@ -79,6 +81,7 @@ export default class PostTextbox extends PureComponent {
contentHeight: INITIAL_HEIGHT,
cursorPosition: 0,
keyboardType: 'default',
+ fileSizeWarning: null,
top: 0,
value: props.value,
showFileMaxWarning: false,
@@ -458,6 +461,23 @@ export default class PostTextbox extends PureComponent {
});
};
+ onShowFileSizeWarning = (filename) => {
+ const {formatMessage} = this.context.intl;
+ const fileSizeWarning = formatMessage({
+ id: 'file_upload.fileAbove',
+ defaultMessage: 'File above {max}MB cannot be uploaded: {filename}',
+ }, {
+ max: getFormattedFileSize({size: this.props.maxFileSize}),
+ filename,
+ });
+
+ this.setState({fileSizeWarning}, () => {
+ setTimeout(() => {
+ this.setState({fileSizeWarning: null});
+ }, 3000);
+ });
+ };
+
onCloseChannelPress = () => {
const {onCloseChannel, channelTeamId} = this.props;
this.props.actions.selectPenultimateChannel(channelTeamId);
@@ -502,6 +522,7 @@ export default class PostTextbox extends PureComponent {
channelIsReadOnly,
deactivatedChannel,
files,
+ maxFileSize,
navigator,
rootId,
theme,
@@ -523,6 +544,7 @@ export default class PostTextbox extends PureComponent {
const {
contentHeight,
cursorPosition,
+ fileSizeWarning,
showFileMaxWarning,
top,
value,
@@ -549,8 +571,10 @@ export default class PostTextbox extends PureComponent {
theme={theme}
navigator={navigator}
fileCount={files.length}
+ maxFileSize={maxFileSize}
maxFileCount={MAX_FILE_COUNT}
onShowFileMaxWarning={this.onShowFileMaxWarning}
+ onShowFileSizeWarning={this.onShowFileSizeWarning}
uploadFiles={this.handleUploadFiles}
/>
);
@@ -564,6 +588,7 @@ export default class PostTextbox extends PureComponent {
diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json
index 29328d675..0520bbfd6 100644
--- a/assets/base/i18n/en.json
+++ b/assets/base/i18n/en.json
@@ -69,6 +69,7 @@
"edit_post.save": "Save",
"error.team_not_found.title": "Team Not Found",
"file_attachment.download": "Download",
+ "file_upload.fileAbove": "File above {max}MB cannot be uploaded: {filename}",
"get_post_link_modal.title": "Copy Permalink",
"integrations.add": "Add",
"intro_messages.anyMember": " Any member can join and read this channel.",
diff --git a/share_extension/android/extension_post/extension_post.js b/share_extension/android/extension_post/extension_post.js
index e96aae3b9..af3c53116 100644
--- a/share_extension/android/extension_post/extension_post.js
+++ b/share_extension/android/extension_post/extension_post.js
@@ -24,7 +24,9 @@ import RNFetchBlob from 'rn-fetch-blob';
import {Preferences} from 'mattermost-redux/constants';
import {getFormattedFileSize, lookupMimeType} from 'mattermost-redux/utils/file_utils';
+import Loading from 'app/components/loading';
import PaperPlane from 'app/components/paper_plane';
+import {MAX_FILE_COUNT} from 'app/constants/post_textbox';
import mattermostManaged from 'app/mattermost_managed';
import {getExtensionFromMime} from 'app/utils/file';
import {emptyFunction} from 'app/utils/general';
@@ -263,7 +265,7 @@ export default class ExtensionPost extends PureComponent {
};
loadData = async (items) => {
- const {token, url} = this.props;
+ const {maxFileSize, token, url} = this.props;
if (token && url) {
const text = [];
const files = [];
@@ -312,13 +314,13 @@ export default class ExtensionPost extends PureComponent {
const value = text.join('\n');
- if (!error) {
+ if (!error && files.length <= MAX_FILE_COUNT && totalSize <= maxFileSize) {
this.props.navigation.setParams({
post: this.onPost,
});
}
- this.setState({error, files, value, hasPermission: true, totalSize});
+ this.setState({error, files, value, hasPermission: true, totalSize, loaded: true});
}
};
@@ -489,7 +491,13 @@ export default class ExtensionPost extends PureComponent {
render() {
const {formatMessage} = this.context.intl;
const {maxFileSize, token, url} = this.props;
- const {error, hasPermission, files, totalSize} = this.state;
+ const {error, hasPermission, files, totalSize, loaded} = this.state;
+
+ if (!loaded) {
+ return (
+
+ );
+ }
if (error) {
return this.renderErrorMessage(error);
@@ -503,7 +511,7 @@ export default class ExtensionPost extends PureComponent {
});
return this.renderErrorMessage(storage);
- } else if (files.length > 5) {
+ } else if (files.length > MAX_FILE_COUNT) {
const fileCount = formatMessage({
id: 'mobile.extension.file_limit',
defaultMessage: 'Sharing is limited to a maximum of 5 files.',