diff --git a/res/css/views/rooms/_MessageComposer.scss b/res/css/views/rooms/_MessageComposer.scss index 71c0db947ed..69e8b505018 100644 --- a/res/css/views/rooms/_MessageComposer.scss +++ b/res/css/views/rooms/_MessageComposer.scss @@ -227,18 +227,6 @@ limitations under the License. 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 { mask-image: url('$(res)/img/element-icons/room/composer/emoji.svg'); } diff --git a/res/css/views/rooms/_RoomHeader.scss b/res/css/views/rooms/_RoomHeader.scss index a23a44906f8..387d1588a38 100644 --- a/res/css/views/rooms/_RoomHeader.scss +++ b/res/css/views/rooms/_RoomHeader.scss @@ -252,6 +252,19 @@ limitations under the License. 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 { height: 16px; } diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 68ab3c6e0c1..6c8560f42c1 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -34,7 +34,7 @@ import ResizeNotifier from '../../utils/ResizeNotifier'; import ContentMessages from '../../ContentMessages'; import Modal from '../../Modal'; import * as sdk from '../../index'; -import CallHandler from '../../CallHandler'; +import CallHandler, { PlaceCallType } from '../../CallHandler'; import dis from '../../dispatcher/dispatcher'; import Tinter from '../../Tinter'; import rateLimitedFunc from '../../ratelimitedfunc'; @@ -1352,6 +1352,14 @@ export default class RoomView extends React.Component { 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 = () => { dis.dispatch({ action: "open_room_settings" }); }; @@ -2031,6 +2039,7 @@ export default class RoomView extends React.Component { e2eStatus={this.state.e2eStatus} onAppsClick={this.state.hasPinnedWidgets ? this.onAppsClick : null} appsShown={this.state.showApps} + onCallPlaced={this.onCallPlaced} />
diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 6a867386f7b..eea6a6b802d 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -1,7 +1,5 @@ /* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017, 2018 New Vector Ltd -Copyright 2020 The Matrix.org Foundation C.I.C. +Copyright 2015-2018, 2020, 2021 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (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 PropTypes from 'prop-types'; import { _t } from '../../../languageHandler'; -import CallHandler from '../../../CallHandler'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; import * as sdk from '../../../index'; import dis from '../../../dispatcher/dispatcher'; @@ -33,11 +30,8 @@ import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import ReplyPreview from "./ReplyPreview"; import {UIFeature} from "../../../settings/UIFeature"; import WidgetStore from "../../../stores/WidgetStore"; -import WidgetUtils from "../../../utils/WidgetUtils"; import {UPDATE_EVENT} from "../../../stores/AsyncStore"; import ActiveWidgetStore from "../../../stores/ActiveWidgetStore"; -import { PlaceCallType } from "../../../CallHandler"; -import { CallState } from 'matrix-js-sdk/src/webrtc/call'; function ComposerAvatar(props) { const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar'); @@ -50,97 +44,6 @@ ComposerAvatar.propTypes = { me: PropTypes.object.isRequired, }; -function CallButton(props) { - const onVoiceCallClick = (ev) => { - dis.dispatch({ - action: 'place_call', - type: PlaceCallType.Voice, - room_id: props.roomId, - }); - }; - - return (); -} - -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 ; -} - -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 ( - - ); -} - -HangupButton.propTypes = { - roomId: PropTypes.string.isRequired, - isConference: PropTypes.bool.isRequired, - canEndConference: PropTypes.bool, - isInConference: PropTypes.bool, -}; - const EmojiButton = ({addEmoji}) => { const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu(); @@ -265,7 +168,6 @@ export default class MessageComposer extends React.Component { this.state = { tombstone: this._getRoomTombstone(), canSendMessages: this.props.room.maySendMessage(), - showCallButtons: SettingsStore.getValue("showCallButtonsInComposer"), hasConference: WidgetStore.instance.doesRoomHaveConference(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) { - // 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 callInProgress = this.props.callState && this.props.callState !== 'ended'; controls.push( ); } - - if (this.state.showCallButtons) { - if (this.state.hasConference) { - const canEndConf = WidgetUtils.canUserModifyWidgets(this.props.room.roomId); - controls.push( - , - ); - } else if (callInProgress) { - controls.push( - , - ); - } else { - controls.push( - , - , - ); - } - } } else if (this.state.tombstone) { const replacementRoomId = this.state.tombstone.getContent()['replacement_room']; diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index 93055c69f5a..6736600bc8e 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -31,6 +31,7 @@ import {DefaultTagID} from "../../../stores/room-list/models"; import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import RoomTopic from "../elements/RoomTopic"; import RoomName from "../elements/RoomName"; +import {PlaceCallType} from "../../../CallHandler"; export default class RoomHeader extends React.Component { static propTypes = { @@ -45,6 +46,7 @@ export default class RoomHeader extends React.Component { e2eStatus: PropTypes.string, onAppsClick: PropTypes.func, appsShown: PropTypes.bool, + onCallPlaced: PropTypes.func, // (PlaceCallType) => void; }; static defaultProps = { @@ -226,8 +228,26 @@ export default class RoomHeader extends React.Component { title={_t("Search")} />; } + let voiceCallButton; + let videoCallButton; + if (this.props.inRoom && SettingsStore.getValue("showCallButtonsInComposer")) { + voiceCallButton = + this.props.onCallPlaced(PlaceCallType.Voice)} + title={_t("Voice call")} />; + videoCallButton = + this.props.onCallPlaced( + ev.shiftKey ? PlaceCallType.ScreenSharing : PlaceCallType.Video)} + title={_t("Video call")} />; + } + const rightRow =
+ { videoCallButton } + { voiceCallButton } { pinnedEventsButton } { forgetButton } { appsButton } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f443c4961b8..0be7e6e02ba 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1416,9 +1416,6 @@ "Invited": "Invited", "Filter room members": "Filter room members", "%(userName)s (power %(powerLevelNumber)s)": "%(userName)s (power %(powerLevelNumber)s)", - "Voice call": "Voice call", - "Video call": "Video call", - "Hangup": "Hangup", "Emoji picker": "Emoji picker", "Upload file": "Upload file", "Send an encrypted reply…": "Send an encrypted reply…", @@ -1476,6 +1473,8 @@ "Hide Widgets": "Hide Widgets", "Show Widgets": "Show Widgets", "Search": "Search", + "Voice call": "Voice call", + "Video call": "Video call", "Start a Conversation": "Start a Conversation", "Open dial pad": "Open dial pad", "Invites": "Invites", diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index b452f10e735..43210021e56 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -632,6 +632,8 @@ export const SETTINGS: {[setting: string]: ISetting} = { default: 3000, }, "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, default: true, controller: new UIFeatureController(UIFeature.Voip),