Merge pull request #5693 from matrix-org/travis/move-call-buttons
Move call buttons to the room header
This commit is contained in:
commit
cf0f591cba
7 changed files with 48 additions and 144 deletions
|
@ -227,18 +227,6 @@ limitations under the License.
|
||||||
mask-image: url('$(res)/img/element-icons/room/composer/attach.svg');
|
mask-image: url('$(res)/img/element-icons/room/composer/attach.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageComposer_hangup::before {
|
|
||||||
mask-image: url('$(res)/img/element-icons/call/hangup.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_MessageComposer_voicecall::before {
|
|
||||||
mask-image: url('$(res)/img/element-icons/call/voice-call.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_MessageComposer_videocall::before {
|
|
||||||
mask-image: url('$(res)/img/element-icons/call/video-call.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_MessageComposer_emoji::before {
|
.mx_MessageComposer_emoji::before {
|
||||||
mask-image: url('$(res)/img/element-icons/room/composer/emoji.svg');
|
mask-image: url('$(res)/img/element-icons/room/composer/emoji.svg');
|
||||||
}
|
}
|
||||||
|
|
|
@ -252,6 +252,19 @@ limitations under the License.
|
||||||
mask-image: url('$(res)/img/element-icons/room/search-inset.svg');
|
mask-image: url('$(res)/img/element-icons/room/search-inset.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_RoomHeader_voiceCallButton::before {
|
||||||
|
mask-image: url('$(res)/img/element-icons/call/voice-call.svg');
|
||||||
|
|
||||||
|
// The call button SVG is padded slightly differently, so match it up to the size
|
||||||
|
// of the other icons
|
||||||
|
mask-size: 20px;
|
||||||
|
mask-position: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomHeader_videoCallButton::before {
|
||||||
|
mask-image: url('$(res)/img/element-icons/call/video-call.svg');
|
||||||
|
}
|
||||||
|
|
||||||
.mx_RoomHeader_showPanel {
|
.mx_RoomHeader_showPanel {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ import ResizeNotifier from '../../utils/ResizeNotifier';
|
||||||
import ContentMessages from '../../ContentMessages';
|
import ContentMessages from '../../ContentMessages';
|
||||||
import Modal from '../../Modal';
|
import Modal from '../../Modal';
|
||||||
import * as sdk from '../../index';
|
import * as sdk from '../../index';
|
||||||
import CallHandler from '../../CallHandler';
|
import CallHandler, { PlaceCallType } from '../../CallHandler';
|
||||||
import dis from '../../dispatcher/dispatcher';
|
import dis from '../../dispatcher/dispatcher';
|
||||||
import Tinter from '../../Tinter';
|
import Tinter from '../../Tinter';
|
||||||
import rateLimitedFunc from '../../ratelimitedfunc';
|
import rateLimitedFunc from '../../ratelimitedfunc';
|
||||||
|
@ -1352,6 +1352,14 @@ export default class RoomView extends React.Component<IProps, IState> {
|
||||||
SettingsStore.setValue("PinnedEvents.isOpen", roomId, SettingLevel.ROOM_DEVICE, nowShowingPinned);
|
SettingsStore.setValue("PinnedEvents.isOpen", roomId, SettingLevel.ROOM_DEVICE, nowShowingPinned);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private onCallPlaced = (type: PlaceCallType) => {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'place_call',
|
||||||
|
type: type,
|
||||||
|
room_id: this.state.room.roomId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
private onSettingsClick = () => {
|
private onSettingsClick = () => {
|
||||||
dis.dispatch({ action: "open_room_settings" });
|
dis.dispatch({ action: "open_room_settings" });
|
||||||
};
|
};
|
||||||
|
@ -2031,6 +2039,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
||||||
e2eStatus={this.state.e2eStatus}
|
e2eStatus={this.state.e2eStatus}
|
||||||
onAppsClick={this.state.hasPinnedWidgets ? this.onAppsClick : null}
|
onAppsClick={this.state.hasPinnedWidgets ? this.onAppsClick : null}
|
||||||
appsShown={this.state.showApps}
|
appsShown={this.state.showApps}
|
||||||
|
onCallPlaced={this.onCallPlaced}
|
||||||
/>
|
/>
|
||||||
<MainSplit panel={rightPanel} resizeNotifier={this.props.resizeNotifier}>
|
<MainSplit panel={rightPanel} resizeNotifier={this.props.resizeNotifier}>
|
||||||
<div className="mx_RoomView_body">
|
<div className="mx_RoomView_body">
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015-2018, 2020, 2021 The Matrix.org Foundation C.I.C.
|
||||||
Copyright 2017, 2018 New Vector Ltd
|
|
||||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -19,7 +17,6 @@ import React, {createRef} from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import CallHandler from '../../../CallHandler';
|
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
|
@ -33,11 +30,8 @@ import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||||
import ReplyPreview from "./ReplyPreview";
|
import ReplyPreview from "./ReplyPreview";
|
||||||
import {UIFeature} from "../../../settings/UIFeature";
|
import {UIFeature} from "../../../settings/UIFeature";
|
||||||
import WidgetStore from "../../../stores/WidgetStore";
|
import WidgetStore from "../../../stores/WidgetStore";
|
||||||
import WidgetUtils from "../../../utils/WidgetUtils";
|
|
||||||
import {UPDATE_EVENT} from "../../../stores/AsyncStore";
|
import {UPDATE_EVENT} from "../../../stores/AsyncStore";
|
||||||
import ActiveWidgetStore from "../../../stores/ActiveWidgetStore";
|
import ActiveWidgetStore from "../../../stores/ActiveWidgetStore";
|
||||||
import { PlaceCallType } from "../../../CallHandler";
|
|
||||||
import { CallState } from 'matrix-js-sdk/src/webrtc/call';
|
|
||||||
|
|
||||||
function ComposerAvatar(props) {
|
function ComposerAvatar(props) {
|
||||||
const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar');
|
const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar');
|
||||||
|
@ -50,97 +44,6 @@ ComposerAvatar.propTypes = {
|
||||||
me: PropTypes.object.isRequired,
|
me: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
function CallButton(props) {
|
|
||||||
const onVoiceCallClick = (ev) => {
|
|
||||||
dis.dispatch({
|
|
||||||
action: 'place_call',
|
|
||||||
type: PlaceCallType.Voice,
|
|
||||||
room_id: props.roomId,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (<AccessibleTooltipButton
|
|
||||||
className="mx_MessageComposer_button mx_MessageComposer_voicecall"
|
|
||||||
onClick={onVoiceCallClick}
|
|
||||||
title={_t('Voice call')}
|
|
||||||
/>);
|
|
||||||
}
|
|
||||||
|
|
||||||
CallButton.propTypes = {
|
|
||||||
roomId: PropTypes.string.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
function VideoCallButton(props) {
|
|
||||||
const onCallClick = (ev) => {
|
|
||||||
dis.dispatch({
|
|
||||||
action: 'place_call',
|
|
||||||
type: ev.shiftKey ? PlaceCallType.ScreenSharing : PlaceCallType.Video,
|
|
||||||
room_id: props.roomId,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return <AccessibleTooltipButton
|
|
||||||
className="mx_MessageComposer_button mx_MessageComposer_videocall"
|
|
||||||
onClick={onCallClick}
|
|
||||||
title={_t('Video call')}
|
|
||||||
/>;
|
|
||||||
}
|
|
||||||
|
|
||||||
VideoCallButton.propTypes = {
|
|
||||||
roomId: PropTypes.string.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
function HangupButton(props) {
|
|
||||||
const onHangupClick = () => {
|
|
||||||
if (props.isConference) {
|
|
||||||
dis.dispatch({
|
|
||||||
action: props.canEndConference ? 'end_conference' : 'hangup_conference',
|
|
||||||
room_id: props.roomId,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const call = CallHandler.sharedInstance().getCallForRoom(props.roomId);
|
|
||||||
if (!call) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const action = call.state === CallState.Ringing ? 'reject' : 'hangup';
|
|
||||||
|
|
||||||
dis.dispatch({
|
|
||||||
action,
|
|
||||||
// hangup the call for this room. NB. We use the room in props as the room ID
|
|
||||||
// as call.roomId may be the 'virtual room', and the dispatch actions always
|
|
||||||
// use the user-facing room (there was a time when we deliberately used
|
|
||||||
// call.roomId and *not* props.roomId, but that was for the old
|
|
||||||
// style Freeswitch conference calls and those times are gone.)
|
|
||||||
room_id: props.roomId,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
let tooltip = _t("Hangup");
|
|
||||||
if (props.isConference && props.canEndConference) {
|
|
||||||
tooltip = _t("End conference");
|
|
||||||
}
|
|
||||||
|
|
||||||
const canLeaveConference = !props.isConference ? true : props.isInConference;
|
|
||||||
return (
|
|
||||||
<AccessibleTooltipButton
|
|
||||||
className="mx_MessageComposer_button mx_MessageComposer_hangup"
|
|
||||||
onClick={onHangupClick}
|
|
||||||
title={tooltip}
|
|
||||||
disabled={!canLeaveConference}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
HangupButton.propTypes = {
|
|
||||||
roomId: PropTypes.string.isRequired,
|
|
||||||
isConference: PropTypes.bool.isRequired,
|
|
||||||
canEndConference: PropTypes.bool,
|
|
||||||
isInConference: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
const EmojiButton = ({addEmoji}) => {
|
const EmojiButton = ({addEmoji}) => {
|
||||||
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
|
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
|
||||||
|
|
||||||
|
@ -265,7 +168,6 @@ export default class MessageComposer extends React.Component {
|
||||||
this.state = {
|
this.state = {
|
||||||
tombstone: this._getRoomTombstone(),
|
tombstone: this._getRoomTombstone(),
|
||||||
canSendMessages: this.props.room.maySendMessage(),
|
canSendMessages: this.props.room.maySendMessage(),
|
||||||
showCallButtons: SettingsStore.getValue("showCallButtonsInComposer"),
|
|
||||||
hasConference: WidgetStore.instance.doesRoomHaveConference(this.props.room),
|
hasConference: WidgetStore.instance.doesRoomHaveConference(this.props.room),
|
||||||
joinedConference: WidgetStore.instance.isJoinedToConferenceIn(this.props.room),
|
joinedConference: WidgetStore.instance.isJoinedToConferenceIn(this.props.room),
|
||||||
};
|
};
|
||||||
|
@ -405,12 +307,7 @@ export default class MessageComposer extends React.Component {
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!this.state.tombstone && this.state.canSendMessages) {
|
if (!this.state.tombstone && this.state.canSendMessages) {
|
||||||
// This also currently includes the call buttons. Really we should
|
|
||||||
// check separately for whether we can call, but this is slightly
|
|
||||||
// complex because of conference calls.
|
|
||||||
|
|
||||||
const SendMessageComposer = sdk.getComponent("rooms.SendMessageComposer");
|
const SendMessageComposer = sdk.getComponent("rooms.SendMessageComposer");
|
||||||
const callInProgress = this.props.callState && this.props.callState !== 'ended';
|
|
||||||
|
|
||||||
controls.push(
|
controls.push(
|
||||||
<SendMessageComposer
|
<SendMessageComposer
|
||||||
|
@ -430,30 +327,6 @@ export default class MessageComposer extends React.Component {
|
||||||
SettingsStore.getValue("MessageComposerInput.showStickersButton")) {
|
SettingsStore.getValue("MessageComposerInput.showStickersButton")) {
|
||||||
controls.push(<Stickerpicker key="stickerpicker_controls_button" room={this.props.room} />);
|
controls.push(<Stickerpicker key="stickerpicker_controls_button" room={this.props.room} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.showCallButtons) {
|
|
||||||
if (this.state.hasConference) {
|
|
||||||
const canEndConf = WidgetUtils.canUserModifyWidgets(this.props.room.roomId);
|
|
||||||
controls.push(
|
|
||||||
<HangupButton
|
|
||||||
key="controls_hangup"
|
|
||||||
roomId={this.props.room.roomId}
|
|
||||||
isConference={true}
|
|
||||||
canEndConference={canEndConf}
|
|
||||||
isInConference={this.state.joinedConference}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
} else if (callInProgress) {
|
|
||||||
controls.push(
|
|
||||||
<HangupButton key="controls_hangup" roomId={this.props.room.roomId} isConference={false} />,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
controls.push(
|
|
||||||
<CallButton key="controls_call" roomId={this.props.room.roomId} />,
|
|
||||||
<VideoCallButton key="controls_videocall" roomId={this.props.room.roomId} />,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (this.state.tombstone) {
|
} else if (this.state.tombstone) {
|
||||||
const replacementRoomId = this.state.tombstone.getContent()['replacement_room'];
|
const replacementRoomId = this.state.tombstone.getContent()['replacement_room'];
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ import {DefaultTagID} from "../../../stores/room-list/models";
|
||||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||||
import RoomTopic from "../elements/RoomTopic";
|
import RoomTopic from "../elements/RoomTopic";
|
||||||
import RoomName from "../elements/RoomName";
|
import RoomName from "../elements/RoomName";
|
||||||
|
import {PlaceCallType} from "../../../CallHandler";
|
||||||
|
|
||||||
export default class RoomHeader extends React.Component {
|
export default class RoomHeader extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -45,6 +46,7 @@ export default class RoomHeader extends React.Component {
|
||||||
e2eStatus: PropTypes.string,
|
e2eStatus: PropTypes.string,
|
||||||
onAppsClick: PropTypes.func,
|
onAppsClick: PropTypes.func,
|
||||||
appsShown: PropTypes.bool,
|
appsShown: PropTypes.bool,
|
||||||
|
onCallPlaced: PropTypes.func, // (PlaceCallType) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -226,8 +228,26 @@ export default class RoomHeader extends React.Component {
|
||||||
title={_t("Search")} />;
|
title={_t("Search")} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let voiceCallButton;
|
||||||
|
let videoCallButton;
|
||||||
|
if (this.props.inRoom && SettingsStore.getValue("showCallButtonsInComposer")) {
|
||||||
|
voiceCallButton =
|
||||||
|
<AccessibleTooltipButton
|
||||||
|
className="mx_RoomHeader_button mx_RoomHeader_voiceCallButton"
|
||||||
|
onClick={() => this.props.onCallPlaced(PlaceCallType.Voice)}
|
||||||
|
title={_t("Voice call")} />;
|
||||||
|
videoCallButton =
|
||||||
|
<AccessibleTooltipButton
|
||||||
|
className="mx_RoomHeader_button mx_RoomHeader_videoCallButton"
|
||||||
|
onClick={(ev) => this.props.onCallPlaced(
|
||||||
|
ev.shiftKey ? PlaceCallType.ScreenSharing : PlaceCallType.Video)}
|
||||||
|
title={_t("Video call")} />;
|
||||||
|
}
|
||||||
|
|
||||||
const rightRow =
|
const rightRow =
|
||||||
<div className="mx_RoomHeader_buttons">
|
<div className="mx_RoomHeader_buttons">
|
||||||
|
{ videoCallButton }
|
||||||
|
{ voiceCallButton }
|
||||||
{ pinnedEventsButton }
|
{ pinnedEventsButton }
|
||||||
{ forgetButton }
|
{ forgetButton }
|
||||||
{ appsButton }
|
{ appsButton }
|
||||||
|
|
|
@ -1416,9 +1416,6 @@
|
||||||
"Invited": "Invited",
|
"Invited": "Invited",
|
||||||
"Filter room members": "Filter room members",
|
"Filter room members": "Filter room members",
|
||||||
"%(userName)s (power %(powerLevelNumber)s)": "%(userName)s (power %(powerLevelNumber)s)",
|
"%(userName)s (power %(powerLevelNumber)s)": "%(userName)s (power %(powerLevelNumber)s)",
|
||||||
"Voice call": "Voice call",
|
|
||||||
"Video call": "Video call",
|
|
||||||
"Hangup": "Hangup",
|
|
||||||
"Emoji picker": "Emoji picker",
|
"Emoji picker": "Emoji picker",
|
||||||
"Upload file": "Upload file",
|
"Upload file": "Upload file",
|
||||||
"Send an encrypted reply…": "Send an encrypted reply…",
|
"Send an encrypted reply…": "Send an encrypted reply…",
|
||||||
|
@ -1476,6 +1473,8 @@
|
||||||
"Hide Widgets": "Hide Widgets",
|
"Hide Widgets": "Hide Widgets",
|
||||||
"Show Widgets": "Show Widgets",
|
"Show Widgets": "Show Widgets",
|
||||||
"Search": "Search",
|
"Search": "Search",
|
||||||
|
"Voice call": "Voice call",
|
||||||
|
"Video call": "Video call",
|
||||||
"Start a Conversation": "Start a Conversation",
|
"Start a Conversation": "Start a Conversation",
|
||||||
"Open dial pad": "Open dial pad",
|
"Open dial pad": "Open dial pad",
|
||||||
"Invites": "Invites",
|
"Invites": "Invites",
|
||||||
|
|
|
@ -632,6 +632,8 @@ export const SETTINGS: {[setting: string]: ISetting} = {
|
||||||
default: 3000,
|
default: 3000,
|
||||||
},
|
},
|
||||||
"showCallButtonsInComposer": {
|
"showCallButtonsInComposer": {
|
||||||
|
// Dev note: This is no longer "in composer" but is instead "in room header".
|
||||||
|
// TODO: Rename with settings v3
|
||||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
|
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
|
||||||
default: true,
|
default: true,
|
||||||
controller: new UIFeatureController(UIFeature.Voip),
|
controller: new UIFeatureController(UIFeature.Voip),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue