From adb578d99f0066da4d65eae541720d92564e2d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Espino=20Garc=C3=ADa?= Date: Tue, 8 Sep 2020 17:37:19 +0200 Subject: [PATCH] Add call button (#5715) * Add Call dropdown for plugins * Update i18n * Remove plugings channel header call and reuse channel header plug * Fix tooltip and i18n error * Add minimum required version and remove unneeded react fragment * Add call button to post * Extract i18n * Change icon by camera icon * Remove header button for calls * Fix tests and remove changes from the header button * Fix lint * Update i18n * Remove unneeded divs, change CameraIcon to functional component, simplify aria label, prevent default on # link and fix typo * Remove channel header references, fix lint and remove unused style * Fix bug * Change callbutton to functional component * Fix CSS --- components/call_button/call_button.jsx | 111 ++++++++++++++++++ components/call_button/index.js | 21 ++++ .../__snapshots__/create_post.test.jsx.snap | 5 + components/create_post/create_post.jsx | 9 ++ components/widgets/icons/camera_icon.jsx | 41 +++++++ i18n/en.json | 2 + plugins/registry.js | 36 ++++++ sass/components/_dropdown.scss | 18 +++ 8 files changed, 243 insertions(+) create mode 100644 components/call_button/call_button.jsx create mode 100644 components/call_button/index.js create mode 100644 components/widgets/icons/camera_icon.jsx diff --git a/components/call_button/call_button.jsx b/components/call_button/call_button.jsx new file mode 100644 index 000000000000..da0c95cc19df --- /dev/null +++ b/components/call_button/call_button.jsx @@ -0,0 +1,111 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import PropTypes from 'prop-types'; +import React from 'react'; +import {injectIntl} from 'react-intl'; + +import {intlShape} from 'utils/react_intl'; + +import MenuWrapper from 'components/widgets/menu/menu_wrapper'; +import Menu from 'components/widgets/menu/menu'; +import CameraIcon from 'components/widgets/icons/camera_icon'; + +const customStyles = { + left: 'inherit', + right: 0, + bottom: '100%', + top: 'auto', +}; + +function CallButton(props) { + const {formatMessage} = props.intl; + + let bodyAction; + + if (props.pluginCallMethods.length === 0) { + bodyAction = null; + } else if (props.pluginCallMethods.length === 1) { + const item = props.pluginCallMethods[0]; + bodyAction = ( + + ); + } else { + const pluginCallMethods = props.pluginCallMethods.map((item) => { + return ( +
  • { + e.preventDefault(); + if (item.action) { + item.action(props.currentChannel, props.channelMember); + } + }} + > + + + {item.icon} + + {item.dropdownText} + +
  • + ); + }); + bodyAction = ( + + + + {pluginCallMethods} + + + ); + } + + return bodyAction; +} + +CallButton.propTypes = { + currentChannel: PropTypes.object.isRequired, + channelMember: PropTypes.object, + intl: intlShape.isRequired, + locale: PropTypes.string.isRequired, + pluginCallMethods: PropTypes.arrayOf(PropTypes.object), +}; + +CallButton.defaultProps = { + pluginCallMethods: [], +}; + +const wrappedComponent = injectIntl(CallButton); +wrappedComponent.displayName = 'injectIntl(CallButton)'; +export default wrappedComponent; diff --git a/components/call_button/index.js b/components/call_button/index.js new file mode 100644 index 000000000000..2b8203d49e15 --- /dev/null +++ b/components/call_button/index.js @@ -0,0 +1,21 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {connect} from 'react-redux'; + +import {getCurrentChannel, getMyCurrentChannelMembership} from 'mattermost-redux/selectors/entities/channels'; + +import {getCurrentLocale} from 'selectors/i18n'; + +import CallButton from './call_button.jsx'; + +function mapStateToProps(state) { + return { + currentChannel: getCurrentChannel(state), + locale: getCurrentLocale(state), + pluginCallMethods: state.plugins.components.CallButton, + channelMember: getMyCurrentChannelMembership(state), + }; +} + +export default connect(mapStateToProps)(CallButton); diff --git a/components/create_post/__snapshots__/create_post.test.jsx.snap b/components/create_post/__snapshots__/create_post.test.jsx.snap index bbcec12bd0f8..e42f5764a295 100644 --- a/components/create_post/__snapshots__/create_post.test.jsx.snap +++ b/components/create_post/__snapshots__/create_post.test.jsx.snap @@ -46,6 +46,7 @@ exports[`components/create_post Show tutorial 1`] = ` + + + + + + ); + } + let fileUpload; if (!readOnlyChannel && !this.props.shouldShowPreview) { fileUpload = ( @@ -1519,6 +1527,7 @@ class CreatePost extends React.PureComponent { ref='createPostControls' className='post-body__actions' > + {callButton} {fileUpload} {emojiPicker} + + + + + + + + + + + + + + + ); +} diff --git a/i18n/en.json b/i18n/en.json index a6216162a3c8..1bf041143367 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -2106,6 +2106,7 @@ "bots.token.confirm": "Delete", "bots.token.confirm_text": "Are you sure you want to delete the token?", "bots.token.delete": "Delete Token", + "call_button.menuAriaLabel": "Call type selector", "center_panel.archived.closeChannel": "Close Channel", "center_panel.direct.closeDirectMessage": "Close Direct Message", "center_panel.direct.closeGroupMessage": "Close Group Message", @@ -2549,6 +2550,7 @@ "generic_icons.back": "Back Icon", "generic_icons.bot": "Bot Icon", "generic_icons.breadcrumb": "Breadcrumb Icon", + "generic_icons.camera": "Camera Icon", "generic_icons.channel.arrow-right": "Arrow right", "generic_icons.channel.draft": "Channel Draft Icon", "generic_icons.channel.private": "Private Channel Icon", diff --git a/plugins/registry.js b/plugins/registry.js index ec58a43309e9..597e583e5f2a 100644 --- a/plugins/registry.js +++ b/plugins/registry.js @@ -132,6 +132,42 @@ export default class PluginRegistry { return id; } + // Add a "call button"" next to the attach file button. If there are more than one button registered by any + // plugin, a dropdown menu is created to contain all the call plugin buttons. + // Accepts the following: + // - icon - React element to use as the button's icon + // - action - a function called when the button is clicked, passed the channel and channel member as arguments + // - dropdown_text - string or React element shown for the dropdown button description + // - tooltip_text - string shown for tooltip appear on hover + // Returns an unique identifier + // Minimum required version: 5.28 + registerCallButtonAction(icon, action, dropdownText, tooltipText) { + const id = generateId(); + + const data = { + id, + pluginId: this.id, + icon: resolveReactElement(icon), + action, + dropdownText: resolveReactElement(dropdownText), + tooltipText, + }; + + store.dispatch({ + type: ActionTypes.RECEIVED_PLUGIN_COMPONENT, + name: 'CallButton', + data, + }); + + store.dispatch({ + type: ActionTypes.RECEIVED_PLUGIN_COMPONENT, + name: 'MobileChannelHeaderButton', + data, + }); + + return id; + } + // Register a component to render a custom body for posts with a specific type. // Custom post types must be prefixed with 'custom_'. // Custom post types can also apply for ephemeral posts. diff --git a/sass/components/_dropdown.scss b/sass/components/_dropdown.scss index 017383276cb9..a4a8ac475dbb 100644 --- a/sass/components/_dropdown.scss +++ b/sass/components/_dropdown.scss @@ -27,6 +27,24 @@ } } + .call-plugin-icon { + vertical-align: middle; + text-align: center; + width: 20px; + height: 20px; + margin-right: 8px; + font-size: 1.6rem; + fill: v(center-channel-color-56); + color: v(center-channel-color-56); + + svg, + img, + .icon { + width: 16px; + height: 16px; + } + } + .a11y--focused { box-shadow: none !important; }