diff --git a/components/file_upload/file_upload.jsx b/components/file_upload/file_upload.jsx index eaaf90da9d20..d3266b92e738 100644 --- a/components/file_upload/file_upload.jsx +++ b/components/file_upload/file_upload.jsx @@ -5,8 +5,9 @@ import $ from 'jquery'; import PropTypes from 'prop-types'; import React, {PureComponent} from 'react'; import ReactDOM from 'react-dom'; -import {defineMessages, intlShape} from 'react-intl'; +import {defineMessages, intlShape, FormattedMessage} from 'react-intl'; import 'jquery-dragster/jquery.dragster.js'; +import {Dropdown} from 'react-bootstrap'; import Constants from 'utils/constants.jsx'; import DelayedAction from 'utils/delayed_action.jsx'; @@ -117,16 +118,26 @@ export default class FileUpload extends PureComponent { * Whether or not file upload is allowed. */ canUploadFiles: PropTypes.bool.isRequired, + + /** + * Plugin file upload methods to be added + */ + pluginFileUploadMethods: PropTypes.arrayOf(PropTypes.object), }; static contextTypes = { intl: intlShape, }; + static defaultProps = { + pluginFileUploadMethods: [], + }; + constructor(props) { super(props); this.state = { requests: {}, + menuOpen: false, }; } @@ -454,6 +465,26 @@ export default class FileUpload extends PureComponent { onUploadError(formatMessage(holders.limited, {count: Constants.MAX_UPLOAD_FILES})); } + toggleMenu = (open) => { + this.setState({menuOpen: open}); + } + + handleLocalFileUploaded = () => { + const uploadsRemaining = Constants.MAX_UPLOAD_FILES - this.props.fileCount; + if (uploadsRemaining > 0) { + if (this.props.onClick) { + this.props.onClick(); + } + } else { + this.handleMaxUploadReached(); + } + this.setState({menuOpen: false}); + } + + pluginUploadFiles = (files) => { + this.uploadFiles(files); + } + render() { let multiple = true; if (isMobileApp()) { @@ -468,14 +499,10 @@ export default class FileUpload extends PureComponent { } const uploadsRemaining = Constants.MAX_UPLOAD_FILES - this.props.fileCount; - const onClick = uploadsRemaining > 0 ? this.props.onClick : this.handleMaxUploadReached; - return ( - - {this.props.canUploadFiles && + let bodyAction; + if (this.props.pluginFileUploadMethods.length === 0) { + bodyAction = (
- } + ); + } else { + const pluginFileUploadMethods = this.props.pluginFileUploadMethods.map((item) => { + return ( +
  • { + if (item.action) { + item.action(this.pluginUploadFiles); + } + this.setState({menuOpen: false}); + }} + > + + {item.icon} + {item.text} + +
  • + ); + }); + const FileDropdownComponent = (props) => { + return ( + + ); + }; + bodyAction = ( + + + +
  • + + + + + +
  • + {pluginFileUploadMethods} +
    +
    + ); + } + + if (!this.props.canUploadFiles) { + bodyAction = null; + } + + return ( + + {bodyAction} ); } diff --git a/components/file_upload/index.js b/components/file_upload/index.js index e22d91adf14b..63c08a26707d 100644 --- a/components/file_upload/index.js +++ b/components/file_upload/index.js @@ -20,6 +20,7 @@ function mapStateToProps(state) { uploadFile, maxFileSize, canUploadFiles: canUploadFiles(config), + pluginFileUploadMethods: state.plugins.components.FileUploadMethod, }; } diff --git a/i18n/en.json b/i18n/en.json index c532f9e0b76d..e5e866d6a235 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -3537,5 +3537,6 @@ "webrtc.unmute_audio": "Unmute microphone", "webrtc.unpause_video": "Turn on camera", "webrtc.unsupported": "{username} client does not support video calls.", + "yourcomputer": "Your computer", "youtube_video.notFound": "Video not found" } diff --git a/plugins/registry.js b/plugins/registry.js index f087b620ef54..bee4b8e5b3be 100644 --- a/plugins/registry.js +++ b/plugins/registry.js @@ -145,7 +145,7 @@ export default class PluginRegistry { // - text - A string or JSX element to display in the menu // - action - A function to trigger when component is clicked on // Returns a unique identifier. - registerPostDropdownMenuAction = (text, action) => { + registerPostDropdownMenuAction(text, action) { const id = generateId(); store.dispatch({ @@ -164,10 +164,34 @@ export default class PluginRegistry { // Register a component at the bottom of the post dropdown menu. // Accepts a React component. Returns a unique identifier. - registerPostDropdownMenuComponent = (component) => { + registerPostDropdownMenuComponent(component) { return dispatchPluginComponentAction('PostDropdownMenuItem', this.id, component); } + // Register a file upload method by providing some text, an icon, and an action function. + // Accepts the following: + // - icon - JSX element to use as the button's icon + // - text - A string or JSX element to display in the file upload menu + // - action - A function to trigger when the menu item is selected. + // Returns a unique identifier. + registerFileUploadMethod(icon, action, text) { + const id = generateId(); + + store.dispatch({ + type: ActionTypes.RECEIVED_PLUGIN_COMPONENT, + name: 'FileUploadMethod', + data: { + id, + pluginId: this.id, + text, + action, + icon, + }, + }); + + return id; + } + // Unregister a component using the unique identifier returned after registration. // Accepts a string id. // Returns undefined in all cases. diff --git a/sass/components/_dropdown.scss b/sass/components/_dropdown.scss index 2843120774f5..c0e35087ad33 100644 --- a/sass/components/_dropdown.scss +++ b/sass/components/_dropdown.scss @@ -68,3 +68,13 @@ .dropdown-menu__content { display: none; } + +.dropdown-menu__icons { + li { + .fa { + width: 25px; + text-align: center; + margin-left: -5px; + } + } +} \ No newline at end of file diff --git a/sass/layout/_post.scss b/sass/layout/_post.scss index f7e794f1feb0..8eb0f0b9e051 100644 --- a/sass/layout/_post.scss +++ b/sass/layout/_post.scss @@ -1920,3 +1920,17 @@ height: 0; } } + +.file-attachment-menu-item-input { + cursor: pointer; + direction: ltr; + filter: alpha(opacity=0); + font-size: 23px; + height: 35px; + margin: 0; + opacity: 0; + position: absolute; + right: 0; + top: 0; + width: 100%; +}