diff --git a/app/utils/file.js b/app/utils/file.js index 74550510a..074e16912 100644 --- a/app/utils/file.js +++ b/app/utils/file.js @@ -6,6 +6,7 @@ import {lookupMimeType} from 'mattermost-redux/utils/file_utils'; import {DeviceTypes} from 'app/constants/'; const {DOCUMENTS_PATH, VIDEOS_PATH} = DeviceTypes; +const DEFAULT_SERVER_MAX_FILE_SIZE = 50 * 1024 * 1024;// 50 Mb export function generateId() { // Implementation taken from http://stackoverflow.com/a/2117523 @@ -79,3 +80,7 @@ export function buildFileUploadData(file) { export const encodeHeaderURIStringToUTF8 = (string) => { return encodeURIComponent(string) + '"; filename*="utf-8\'\'' + encodeURIComponent(string); }; + +export const getAllowedServerMaxFileSize = (config) => { + return config.MaxFileSize ? parseInt(config.MaxFileSize, 10) : DEFAULT_SERVER_MAX_FILE_SIZE; +}; diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index c3a439fd7..50bfca07d 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -2024,7 +2024,7 @@ "mobile.error_handler.title": "Unexpected error occurred", "mobile.extension.authentication_required": "Authentication required: Please first login using the app.", "mobile.extension.file_limit": "Sharing is limited to a maximum of 5 files.", - "mobile.extension.max_file_size": "File attachments shared in Mattermost must be less than 20 Mb.", + "mobile.extension.max_file_size": "File attachments shared in Mattermost must be less than {size}.", "mobile.extension.permission": "Mattermost needs access to the device storage to share files.", "mobile.extension.posting": "Posting...", "mobile.extension.title": "Share in Mattermost", diff --git a/share_extension/android/extension_post/extension_post.js b/share_extension/android/extension_post/extension_post.js index 383e97f90..749e950a8 100644 --- a/share_extension/android/extension_post/extension_post.js +++ b/share_extension/android/extension_post/extension_post.js @@ -18,9 +18,10 @@ import { import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; import Video from 'react-native-video'; import LocalAuth from 'react-native-local-auth'; +import RNFetchBlob from 'react-native-fetch-blob'; import {Preferences} from 'mattermost-redux/constants'; -import {lookupMimeType} from 'mattermost-redux/utils/file_utils'; +import {getFormattedFileSize, lookupMimeType} from 'mattermost-redux/utils/file_utils'; import PaperPlane from 'app/components/paper_plane'; import mattermostManaged from 'app/mattermost_managed'; @@ -57,6 +58,7 @@ export default class ExtensionPost extends PureComponent { static propTypes = { channelId: PropTypes.string.isRequired, currentUserId: PropTypes.string.isRequired, + maxFileSize: PropTypes.number.isRequired, navigation: PropTypes.object.isRequired, teamId: PropTypes.string.isRequired, token: PropTypes.string, @@ -129,6 +131,7 @@ export default class ExtensionPost extends PureComponent { files: [], hasPermission: null, teamId: props.teamId, + totalSize: 0, value: '' }; } @@ -261,6 +264,7 @@ export default class ExtensionPost extends PureComponent { if (token && url) { const text = []; const files = []; + let totalSize = 0; this.props.navigation.setParams({ post: this.onPost @@ -276,11 +280,14 @@ export default class ExtensionPost extends PureComponent { const fullPath = item.value; const filename = fullPath.replace(/^.*[\\/]/, ''); const extension = filename.split('.').pop(); + const fileSize = await RNFetchBlob.fs.stat(fullPath); + totalSize += fileSize.size; files.push({ extension, filename, fullPath, mimeType: lookupMimeType(filename.toLowerCase()), + size: getFormattedFileSize(fileSize), type: item.type }); break; @@ -290,7 +297,7 @@ export default class ExtensionPost extends PureComponent { const value = text.join('\n'); - this.setState({files, value, hasPermission: true}); + this.setState({files, value, hasPermission: true, totalSize}); } }; @@ -355,6 +362,20 @@ export default class ExtensionPost extends PureComponent { ); }; + renderErrorMessage = (message) => { + return ( + + + + {message} + + + + ); + }; + renderFiles = () => { const {files} = this.state; @@ -425,7 +446,7 @@ export default class ExtensionPost extends PureComponent { numberOfLines={1} style={styles.filename} > - {file.filename} + {`${file.size} - ${file.filename}`} ); @@ -446,40 +467,33 @@ export default class ExtensionPost extends PureComponent { render() { const {formatMessage} = this.context.intl; - const {token, url} = this.props; - const {hasPermission, files} = this.state; + const {maxFileSize, token, url} = this.props; + const {hasPermission, files, totalSize} = this.state; + + if (token && url) { + if (hasPermission === false) { + const storage = formatMessage({ + id: 'mobile.extension.permission', + defaultMessage: 'Mattermost needs access to the device storage to share files.' + }); + + return this.renderErrorMessage(storage); + } else if (files.length > 5) { + const fileCount = formatMessage({ + id: 'mobile.extension.file_limit', + defaultMessage: 'Sharing is limited to a maximum of 5 files.' + }); + + return this.renderErrorMessage(fileCount); + } else if (totalSize > maxFileSize) { + const maxSize = formatMessage({ + id: 'mobile.extension.max_file_size', + defaultMessage: 'File attachments shared in Mattermost must be less than {size}.' + }, {size: getFormattedFileSize({size: maxFileSize})}); + + return this.renderErrorMessage(maxSize); + } - if (hasPermission === false) { - return ( - - - - {formatMessage({ - id: 'mobile.extension.permission', - defaultMessage: 'Mattermost needs access to the device storage to share files.' - })} - - - - ); - } else if (files.length > 5) { - return ( - - - - {formatMessage({ - id: 'mobile.extension.file_limit', - defaultMessage: 'Sharing is limited to a maximum of 5 files.' - })} - - - - ); - } else if (token && url) { return ( @@ -493,16 +507,12 @@ export default class ExtensionPost extends PureComponent { ); } - return ( - - - {formatMessage({ - id: 'mobile.extension.authentication_required', - defaultMessage: 'Authentication required: Please first login using the app.' - })} - - - ); + const loginNeeded = formatMessage({ + id: 'mobile.extension.authentication_required', + defaultMessage: 'Authentication required: Please first login using the app.' + }); + + return this.renderErrorMessage(loginNeeded); } } diff --git a/share_extension/android/extension_post/index.js b/share_extension/android/extension_post/index.js index 8cf2c4252..6893718ef 100644 --- a/share_extension/android/extension_post/index.js +++ b/share_extension/android/extension_post/index.js @@ -7,14 +7,18 @@ import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels' import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams'; import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; +import {getAllowedServerMaxFileSize} from 'app/utils/file'; + import ExtensionPost from './extension_post'; function mapStateToProps(state) { - const {token, url} = state.entities.general.credentials; + const {config, credentials} = state.entities.general; + const {token, url} = credentials; return { channelId: getCurrentChannelId(state), currentUserId: getCurrentUserId(state), + maxFileSize: getAllowedServerMaxFileSize(config), teamId: getCurrentTeamId(state), token, url diff --git a/share_extension/ios/extension_post.js b/share_extension/ios/extension_post.js index 817c1f6bb..0dfca21fe 100644 --- a/share_extension/ios/extension_post.js +++ b/share_extension/ios/extension_post.js @@ -24,7 +24,7 @@ import {Client4} from 'mattermost-redux/client'; import {getFormattedFileSize, lookupMimeType} from 'mattermost-redux/utils/file_utils'; import mattermostBucket from 'app/mattermost_bucket'; -import {generateId} from 'app/utils/file'; +import {generateId, getAllowedServerMaxFileSize} from 'app/utils/file'; import {wrapWithPreventDoubleTap} from 'app/utils/tap'; import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; import Config from 'assets/config'; @@ -258,7 +258,10 @@ export default class ExtensionPost extends PureComponent { renderBody = (styles) => { const {formatMessage} = this.context.intl; const {authenticated, theme} = this.props; - const {error, sending, totalSize, value} = this.state; + const {entities, error, sending, totalSize, value} = this.state; + const {config} = entities.general; + const serverMaxFileSize = getAllowedServerMaxFileSize(config); + const maxSize = Math.min(MAX_FILE_SIZE, serverMaxFileSize); if (sending) { return ( @@ -274,14 +277,14 @@ export default class ExtensionPost extends PureComponent { ); } - if (totalSize >= MAX_FILE_SIZE) { + if (totalSize >= maxSize) { return ( {formatMessage({ id: 'mobile.extension.max_file_size', - defaultMessage: 'File attachments shared in Mattermost must be less than 20 Mb.' - })} + defaultMessage: 'File attachments shared in Mattermost must be less than {size}.' + }, {size: getFormattedFileSize({size: maxSize})})} );