Skip to content
This repository has been archived by the owner on Mar 13, 2024. It is now read-only.

Commit

Permalink
MM-10657 Adding ability for plugin to hook the file upload button. (#…
Browse files Browse the repository at this point in the history
…1488)

* Adding ability for plugin to hook the file upload button.

* Tweaks and localization.

* Updating icons

* Fix docs.

* Move back to pure-component.
  • Loading branch information
crspeller authored and hmhealey committed Jul 31, 2018
1 parent 701f7bf commit ceb0ca0
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 12 deletions.
125 changes: 115 additions & 10 deletions components/file_upload/file_upload.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
};
}

Expand Down Expand Up @@ -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()) {
Expand All @@ -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 (
<span
ref='input'
className={uploadsRemaining <= 0 ? ' btn-file__disabled' : ''}
>
{this.props.canUploadFiles &&
let bodyAction;
if (this.props.pluginFileUploadMethods.length === 0) {
bodyAction = (
<div
id='fileUploadButton'
className='icon icon--attachment'
Expand All @@ -485,12 +512,90 @@ export default class FileUpload extends PureComponent {
ref='fileInput'
type='file'
onChange={this.handleChange}
onClick={onClick}
onClick={this.handleLocalFileUploaded}
multiple={multiple}
accept={accept}
/>
</div>
}
);
} else {
const pluginFileUploadMethods = this.props.pluginFileUploadMethods.map((item) => {
return (
<li
key={item.pluginId + '_fileuploadpluginmenuitem'}
onClick={() => {
if (item.action) {
item.action(this.pluginUploadFiles);
}
this.setState({menuOpen: false});
}}
>
<a>
{item.icon}
{item.text}
</a>
</li>
);
});
const FileDropdownComponent = (props) => {
return (
<button
onClick={props.onClick}
className='style--none'
>
<div
id='fileUploadButton'
className='icon icon--attachment'
>
<AttachmentIcon/>
</div>
</button>
);
};
bodyAction = (
<Dropdown
dropup={true}
pullRight={true}
id='fileInputDropdown'
open={this.state.menuOpen}
onToggle={this.toggleMenu}
>
<FileDropdownComponent bsRole='toggle'/>
<Dropdown.Menu className='dropdown-menu__icons'>
<li>
<a>
<i className='fa fa-laptop'/>
<FormattedMessage
id='yourcomputer'
defaultMessage='Your computer'
/>
<input
ref='fileInput'
type='file'
className='file-attachment-menu-item-input'
onChange={this.handleChange}
onClick={this.handleLocalFileUploaded}
multiple={multiple}
accept={accept}
/>
</a>
</li>
{pluginFileUploadMethods}
</Dropdown.Menu>
</Dropdown>
);
}

if (!this.props.canUploadFiles) {
bodyAction = null;
}

return (
<span
ref='input'
className={uploadsRemaining <= 0 ? ' btn-file__disabled' : ''}
>
{bodyAction}
</span>
);
}
Expand Down
1 change: 1 addition & 0 deletions components/file_upload/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ function mapStateToProps(state) {
uploadFile,
maxFileSize,
canUploadFiles: canUploadFiles(config),
pluginFileUploadMethods: state.plugins.components.FileUploadMethod,
};
}

Expand Down
1 change: 1 addition & 0 deletions i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
28 changes: 26 additions & 2 deletions plugins/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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.
Expand Down
10 changes: 10 additions & 0 deletions sass/components/_dropdown.scss
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,13 @@
.dropdown-menu__content {
display: none;
}

.dropdown-menu__icons {
li {
.fa {
width: 25px;
text-align: center;
margin-left: -5px;
}
}
}
14 changes: 14 additions & 0 deletions sass/layout/_post.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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%;
}

0 comments on commit ceb0ca0

Please sign in to comment.