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

[28725] Add call button #5715

Merged
merged 28 commits into from
Sep 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
10cd5d7
Add Call dropdown for plugins
larkox Jun 12, 2020
91f6c7a
Update i18n
larkox Jun 12, 2020
01ea0de
Merge branch 'master' into AddCallButton
larkox Jun 18, 2020
4f9dcfa
Remove plugings channel header call and reuse channel header plug
larkox Jun 18, 2020
25c98a3
Fix tooltip and i18n error
larkox Jun 18, 2020
1233224
Merge branch 'master' into AddCallButton
larkox Jul 1, 2020
b4a2372
Add minimum required version and remove unneeded react fragment
larkox Jul 1, 2020
ab0dd85
Merge branch 'master' into AddCallButton
larkox Jul 7, 2020
2796a9f
Add call button to post
larkox Jul 7, 2020
2ab1847
Extract i18n
larkox Jul 7, 2020
8f88964
Change icon by camera icon
larkox Jul 14, 2020
7fe77e2
Remove header button for calls
larkox Aug 4, 2020
3000715
Merge branch 'master' into AddCallButton
larkox Aug 4, 2020
e18602e
Fix tests and remove changes from the header button
larkox Aug 4, 2020
55b44ce
Merge branch 'master' into AddCallButton
larkox Aug 4, 2020
5d5976d
Fix lint
larkox Aug 4, 2020
a25e3e3
Update i18n
larkox Aug 4, 2020
b8dbbec
Merge branch 'master' into AddCallButton
larkox Aug 7, 2020
8cad316
Remove unneeded divs, change CameraIcon to functional component, simp…
larkox Aug 7, 2020
1b4e814
Remove channel header references, fix lint and remove unused style
larkox Aug 7, 2020
89392ec
Fix bug
larkox Aug 7, 2020
3cbc470
Change callbutton to functional component
larkox Aug 10, 2020
eb237df
Merge branch 'master' into AddCallButton
larkox Aug 10, 2020
e4c2236
Merge branch 'master' into AddCallButton
larkox Aug 20, 2020
77534a7
Merge branch 'master' into AddCallButton
larkox Aug 25, 2020
01bc11a
Fix CSS
larkox Aug 28, 2020
746f5e3
Merge branch 'master' into AddCallButton
larkox Sep 2, 2020
a516b6c
Merge branch 'master' into AddCallButton
larkox Sep 8, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions components/call_button/call_button.jsx
Original file line number Diff line number Diff line change
@@ -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 = (
<button
type='button'
className='style--none post-action icon icon--attachment'
onClick={() => {
if (item.action) {
item.action(props.currentChannel, props.channelMember);
}
}}
onTouchEnd={() => {
if (item.action) {
item.action(props.currentChannel, props.channelMember);
}
}}
>
{item.icon}
</button>
);
} else {
const pluginCallMethods = props.pluginCallMethods.map((item) => {
return (
<li
key={item.id}
onClick={(e) => {
e.preventDefault();
if (item.action) {
item.action(props.currentChannel, props.channelMember);
}
}}
>
<a href='#'>
<span className='call-plugin-icon'>
{item.icon}
</span>
{item.dropdownText}
</a>
</li>
);
});
bodyAction = (
<MenuWrapper>
<button
type='button'
className='style--none post-action'
>
<div
className='icon icon--attachment'
>
<CameraIcon className='d-flex'/>
</div>
</button>
<Menu
id='callOptions'
openLeft={true}
openUp={true}
ariaLabel={formatMessage({id: 'call_button.menuAriaLabel', defaultMessage: 'Call type selector'})}
customStyles={customStyles}
>
{pluginCallMethods}
</Menu>
</MenuWrapper>
);
}

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;
21 changes: 21 additions & 0 deletions components/call_button/index.js
Original file line number Diff line number Diff line change
@@ -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);
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ exports[`components/create_post Show tutorial 1`] = `
<span
className="post-body__actions"
>
<Connect(injectIntl(CallButton)) />
<Connect(injectIntl(FileUpload))
fileCount={0}
getTarget={[Function]}
Expand Down Expand Up @@ -224,6 +225,7 @@ exports[`components/create_post should match snapshot for center textbox 1`] = `
<span
className="post-body__actions"
>
<Connect(injectIntl(CallButton)) />
<Connect(injectIntl(FileUpload))
fileCount={0}
getTarget={[Function]}
Expand Down Expand Up @@ -483,6 +485,7 @@ exports[`components/create_post should match snapshot when cannot post 1`] = `
<span
className="post-body__actions"
>
<Connect(injectIntl(CallButton)) />
<Connect(injectIntl(FileUpload))
fileCount={0}
getTarget={[Function]}
Expand Down Expand Up @@ -630,6 +633,7 @@ exports[`components/create_post should match snapshot when file upload disabled
<span
className="post-body__actions"
>
<Connect(injectIntl(CallButton)) />
<Connect(injectIntl(FileUpload))
fileCount={0}
getTarget={[Function]}
Expand Down Expand Up @@ -777,6 +781,7 @@ exports[`components/create_post should match snapshot, init 1`] = `
<span
className="post-body__actions"
>
<Connect(injectIntl(CallButton)) />
<Connect(injectIntl(FileUpload))
fileCount={0}
getTarget={[Function]}
Expand Down
9 changes: 9 additions & 0 deletions components/create_post/create_post.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import EditChannelPurposeModal from 'components/edit_channel_purpose_modal';
import EmojiPickerOverlay from 'components/emoji_picker/emoji_picker_overlay.jsx';
import FilePreview from 'components/file_preview';
import FileUpload from 'components/file_upload';
import CallButton from 'components/call_button';
import LocalizedIcon from 'components/localized_icon';
import MsgTyping from 'components/msg_typing';
import PostDeletedModal from 'components/post_deleted_modal';
Expand Down Expand Up @@ -1406,6 +1407,13 @@ class CreatePost extends React.PureComponent {
attachmentsDisabled = ' post-create--attachment-disabled';
}

let callButton;
if (!readOnlyChannel && !this.props.shouldShowPreview) {
callButton = (
<CallButton/>
);
}

let fileUpload;
if (!readOnlyChannel && !this.props.shouldShowPreview) {
fileUpload = (
Expand Down Expand Up @@ -1519,6 +1527,7 @@ class CreatePost extends React.PureComponent {
ref='createPostControls'
className='post-body__actions'
>
{callButton}
{fileUpload}
{emojiPicker}
<a
Expand Down
41 changes: 41 additions & 0 deletions components/widgets/icons/camera_icon.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import React from 'react';
import {useIntl} from 'react-intl';

export default function CameraIcon(props) {
const intl = useIntl();
return (
<span {...props}>
<svg
width='14px'
height='10px'
viewBox='0 0 16 12'
role='img'
aria-label={intl.formatMessage({id: 'generic_icons.camera', defaultMessage: 'Camera Icon'})}
>
<g
stroke='none'
strokeWidth='1'
fill='inherit'
fillRule='evenodd'
>
<g
transform='translate(-696.000000, -34.000000)'
fillRule='nonzero'
fill='inherit'
>
<g transform='translate(-1.000000, 0.000000)'>
<g transform='translate(687.000000, 22.000000)'>
<g transform='translate(10.000000, 12.000000)'>
<path d='M15.105,1.447 L12,3 L12,1 C12,0.447 11.553,0 11,0 L1,0 C0.447,0 0,0.447 0,1 L0,11 C0,11.553 0.447,12 1,12 L11,12 C11.553,12 12,11.553 12,11 L12,9 L15.105,10.553 C15.6,10.8 16,10.553 16,10 L16,2 C16,1.447 15.6,1.2 15.105,1.447 Z M12.895,7.211 C12.612,7.07 12.306,7 12,7 L10.5,7 C10.224,7 10,7.224 10,7.5 L10,10 L2,10 L2,2 L10,2 L10,4.5 C10,4.776 10.224,5 10.5,5 L12,5 C12.306,5 12.612,4.93 12.895,4.789 L14,4.236 L14,7.763 L12.895,7.211 Z'/>
</g>
</g>
</g>
</g>
</g>
</svg>
</span>
);
}
2 changes: 2 additions & 0 deletions i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
36 changes: 36 additions & 0 deletions plugins/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
larkox marked this conversation as resolved.
Show resolved Hide resolved
// 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.
Expand Down
18 changes: 18 additions & 0 deletions sass/components/_dropdown.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down