From 6d07d5d30c753829952cc574123c8e72b09a1c47 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 10 Jun 2021 18:01:51 +0100 Subject: [PATCH 01/22] Add ability to transfer by dial pad Also moves more logic out of views and into callhandler via a dispatch First cut: UI not done yet, put in as a Tab View (the layout doesn't work at all and overlaps horribly). --- src/CallHandler.tsx | 48 ++++++ src/components/views/dialogs/InviteDialog.tsx | 151 +++++++++++------- src/dispatcher/actions.ts | 12 ++ .../payloads/TransferCallPayload.ts | 33 ++++ src/i18n/strings/en_EN.json | 6 +- 5 files changed, 194 insertions(+), 56 deletions(-) create mode 100644 src/dispatcher/payloads/TransferCallPayload.ts diff --git a/src/CallHandler.tsx b/src/CallHandler.tsx index bf7cb3473dd..f02f50d577f 100644 --- a/src/CallHandler.tsx +++ b/src/CallHandler.tsx @@ -871,6 +871,12 @@ export default class CallHandler extends EventEmitter { case Action.DialNumber: this.dialNumber(payload.number); break; + case Action.TransferCallToMatrixID: + this.startTransferToMatrixID(payload.call, payload.destination, payload.consultFirst); + break; + case Action.TransferCallToPhoneNumber: + this.startTransferToPhoneNumber(payload.call, payload.destination, payload.consultFirst); + break; } } @@ -905,6 +911,48 @@ export default class CallHandler extends EventEmitter { }); } + private async startTransferToPhoneNumber(call: MatrixCall, destination: string, consultFirst: boolean) { + const results = await this.pstnLookup(destination); + if (!results || results.length === 0 || !results[0].userid) { + Modal.createTrackedDialog('', '', ErrorDialog, { + title: _t("Unable to transfer call"), + description: _t("There was an error looking up the phone number"), + }); + return; + } + + await this.startTransferToMatrixID(call, results[0].userid, consultFirst); + } + + private async startTransferToMatrixID(call: MatrixCall, destination: string, consultFirst: boolean) { + if (consultFirst) { + const dmRoomId = await ensureDMExists(MatrixClientPeg.get(), destination); + + dis.dispatch({ + action: 'place_call', + type: call.type, + room_id: dmRoomId, + transferee: call, + }); + dis.dispatch({ + action: 'view_room', + room_id: dmRoomId, + should_peek: false, + joining: false, + }); + } else { + try { + await call.transfer(destination); + } catch (e) { + console.log("Failed to transfer call", e); + Modal.createTrackedDialog('Failed to transfer call', '', ErrorDialog, { + title: _t('Transfer Failed'), + description: _t('Failed to transfer call'), + }); + } + } + } + setActiveCallRoomId(activeCallRoomId: string) { logger.info("Setting call in room " + activeCallRoomId + " active"); diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index b006205f11d..052478c7e8b 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -30,7 +30,7 @@ import IdentityAuthClient from "../../../IdentityAuthClient"; import Modal from "../../../Modal"; import {humanizeTime} from "../../../utils/humanize"; import createRoom, { - canEncryptToAllUsers, ensureDMExists, findDMForUser, privateShouldBeEncrypted, + canEncryptToAllUsers, findDMForUser, privateShouldBeEncrypted, IInvite3PID, } from "../../../createRoom"; import {inviteMultipleToRoom, showCommunityInviteDialog} from "../../../RoomInvite"; @@ -50,6 +50,10 @@ import {getAddressType} from "../../../UserAddress"; import BaseAvatar from '../avatars/BaseAvatar'; import AccessibleButton from '../elements/AccessibleButton'; import { compare } from '../../../utils/strings'; +import TabbedView, { Tab } from '../../structures/TabbedView'; +import Dialpad from '../voip/DialPad'; +import Field from '../elements/Field'; +import { TransferCallPayload } from '../../../dispatcher/payloads/TransferCallPayload'; // we have a number of types defined from the Matrix spec which can't reasonably be altered here. /* eslint-disable camelcase */ @@ -338,6 +342,7 @@ interface IInviteDialogState { canUseIdentityServer: boolean; tryingIdentityServer: boolean; consultFirst: boolean; + dialPadValue: string; // These two flags are used for the 'Go' button to communicate what is going on. busy: boolean, @@ -388,6 +393,7 @@ export default class InviteDialog extends React.PureComponent { if (this.state.busy) return; @@ -1238,6 +1222,34 @@ export default class InviteDialog extends React.PureComponent { + ev.preventDefault(); + this.onDialPress(); + } + + private onDialChange = ev => { + this.setState({dialPadValue: ev.target.value}); + } + + private onDigitPress = digit => { + this.setState({dialPadValue: this.state.dialPadValue + digit}); + } + + private onDialPress = () => { + dis.dispatch({ + action: Action.TransferCallToPhoneNumber, + call: this.props.call, + destination: this.state.dialPadValue, + consultFirst: this.state.consultFirst, + } as TransferCallPayload); + this.props.onFinished(); + } + + private onDeletePress = () => { + if (this.state.dialPadValue.length === 0) return; + this.setState({dialPadValue: this.state.dialPadValue.slice(0, -1)}); + } + render() { const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); @@ -1389,6 +1401,59 @@ export default class InviteDialog extends React.PureComponent 0 || (this.state.filterText && this.state.filterText.includes('@')); + const usersSection = +

{helpText}

+
+ {this.renderEditor()} +
+ + {buttonText} + + {spinner} +
+
+ {keySharingWarning} + {this.renderIdentityServerWarning()} +
{this.state.errorText}
+
+ {this.renderSection('recents')} + {this.renderSection('suggestions')} +
+ {consultSection} +
; + + let dialogContent; + if (this.props.kind === KIND_CALL_TRANSFER) { + const tabs = []; + tabs.push(new Tab('UsersTab', _td("Users"), null, usersSection)); + + const dialPadSection = +
+ + +
+ +
+ {consultSection} +
; + tabs.push(new Tab('DialPadTab', _td("Dial pad"), null, dialPadSection)); + dialogContent = ; + } else { + dialogContent = usersSection; + } + return (
-

{helpText}

-
- {this.renderEditor()} -
- - {buttonText} - - {spinner} -
-
- {keySharingWarning} - {this.renderIdentityServerWarning()} -
{this.state.errorText}
-
- {this.renderSection('recents')} - {this.renderSection('suggestions')} -
- {consultSection} + {dialogContent}
); diff --git a/src/dispatcher/actions.ts b/src/dispatcher/actions.ts index 300eed2b989..38016f2cb5a 100644 --- a/src/dispatcher/actions.ts +++ b/src/dispatcher/actions.ts @@ -106,6 +106,18 @@ export enum Action { */ DialNumber = "dial_number", + /** + * Start a call transfer to a Matrix ID + * payload: TransferCallPayload + */ + TransferCallToMatrixID = "transferCallToMatrixID", + + /** + * Start a call transfer to a phone number + * payload: TransferCallPayload + */ + TransferCallToPhoneNumber = "transferCallToPhoneNumber", + /** * Fired when CallHandler has checked for PSTN protocol support * payload: none diff --git a/src/dispatcher/payloads/TransferCallPayload.ts b/src/dispatcher/payloads/TransferCallPayload.ts new file mode 100644 index 00000000000..38431bb0d6a --- /dev/null +++ b/src/dispatcher/payloads/TransferCallPayload.ts @@ -0,0 +1,33 @@ +/* +Copyright 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. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { ActionPayload } from "../payloads"; +import { Action } from "../actions"; +import { MatrixCall } from "matrix-js-sdk/src/webrtc/call"; + +export interface TransferCallPayload extends ActionPayload { + action: Action.TransferCallToMatrixID | Action.TransferCallToPhoneNumber; + // The call to transfer + call: MatrixCall; + // Where to transfer the call. A Matrix ID if action == TransferCallToMatrixID + // and a phone number if action == TransferCallToPhoneNumber + destination: string; + // If true, puts the current call on hold and dials the transfer target, giving + // the user a button to complete the transfer when ready. + // If false, ends the call immediately and sends the user to the transfer + // destination + consultFirst: boolean; +} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 23010571c28..54414197f5b 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -65,6 +65,9 @@ "You cannot place a call with yourself.": "You cannot place a call with yourself.", "Unable to look up phone number": "Unable to look up phone number", "There was an error looking up the phone number": "There was an error looking up the phone number", + "Unable to transfer call": "Unable to transfer call", + "Transfer Failed": "Transfer Failed", + "Failed to transfer call": "Failed to transfer call", "Call in Progress": "Call in Progress", "A call is currently being placed!": "A call is currently being placed!", "Permission Required": "Permission Required", @@ -2244,7 +2247,6 @@ "Something went wrong trying to invite the users.": "Something went wrong trying to invite the users.", "We couldn't invite those users. Please check the users you want to invite and try again.": "We couldn't invite those users. Please check the users you want to invite and try again.", "A call can only be transferred to a single user.": "A call can only be transferred to a single user.", - "Failed to transfer call": "Failed to transfer call", "Failed to find the following users": "Failed to find the following users", "The following users might not exist or are invalid, and cannot be invited: %(csvNames)s": "The following users might not exist or are invalid, and cannot be invited: %(csvNames)s", "Recent Conversations": "Recent Conversations", @@ -2264,6 +2266,7 @@ "Invited people will be able to read old messages.": "Invited people will be able to read old messages.", "Transfer": "Transfer", "Consult first": "Consult first", + "Users": "Users", "a new master key signature": "a new master key signature", "a new cross-signing key signature": "a new cross-signing key signature", "a device cross-signing signature": "a device cross-signing signature", @@ -2854,7 +2857,6 @@ "Notification Autocomplete": "Notification Autocomplete", "Room Autocomplete": "Room Autocomplete", "Space Autocomplete": "Space Autocomplete", - "Users": "Users", "User Autocomplete": "User Autocomplete", "We'll store an encrypted copy of your keys on our server. Secure your backup with a Security Phrase.": "We'll store an encrypted copy of your keys on our server. Secure your backup with a Security Phrase.", "For maximum security, this should be different from your account password.": "For maximum security, this should be different from your account password.", From a85420bbac2cc392004db0d1dc691346bf96575d Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 11 Jun 2021 14:47:17 +0100 Subject: [PATCH 02/22] Re-arrange buttons as per transfer dialog design Tab style is still wrong though --- src/components/views/dialogs/InviteDialog.tsx | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index 052478c7e8b..d5f76094e4f 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -1265,11 +1265,14 @@ export default class InviteDialog extends React.PureComponent; const identityServersEnabled = SettingsStore.getValue(UIFeature.IdentityServer); + const hasSelection = this.state.targets.length > 0 + || (this.state.filterText && this.state.filterText.includes('@')); + const cli = MatrixClientPeg.get(); const userId = cli.getUserId(); if (this.props.kind === KIND_DM) { @@ -1387,33 +1390,39 @@ export default class InviteDialog extends React.PureComponent + consultConnectSection =
+ + {_t("Transfer")} +
; } else { console.error("Unknown kind of InviteDialog: " + this.props.kind); } - const hasSelection = this.state.targets.length > 0 - || (this.state.filterText && this.state.filterText.includes('@')); + const goButton = this.props.kind == KIND_CALL_TRANSFER ? null : + {buttonText} + ; + const usersSection =

{helpText}

{this.renderEditor()}
- - {buttonText} - + {goButton} {spinner}
@@ -1424,7 +1433,7 @@ export default class InviteDialog extends React.PureComponent - {consultSection} + {consultConnectSection}
; let dialogContent; @@ -1446,7 +1455,7 @@ export default class InviteDialog extends React.PureComponent - {consultSection} + {consultConnectSection} ; tabs.push(new Tab('DialPadTab', _td("Dial pad"), null, dialPadSection)); dialogContent = ; From 2cc6bcec29be908ac45bf181272e2542aadd73f9 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 18 Jun 2021 16:36:37 +0100 Subject: [PATCH 03/22] Much theming & visual of transfer window dial pad --- res/css/structures/_TabbedView.scss | 107 +++++++++++--- res/css/views/dialogs/_InviteDialog.scss | 54 ++++++- res/css/views/voip/_DialPad.scss | 2 + src/CallHandler.tsx | 2 +- src/components/structures/TabbedView.tsx | 21 ++- src/components/views/dialogs/InviteDialog.tsx | 132 +++++++++++------- src/i18n/strings/en_EN.json | 3 +- 7 files changed, 244 insertions(+), 77 deletions(-) diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index 39a8ebed32e..cd0b95e632e 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -1,6 +1,7 @@ /* Copyright 2017 Travis Ralston Copyright 2019 New Vector Ltd +Copyright 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. @@ -20,7 +21,6 @@ limitations under the License. padding: 0 0 0 16px; display: flex; flex-direction: column; - position: absolute; top: 0; bottom: 0; left: 0; @@ -28,11 +28,92 @@ limitations under the License. margin-top: 8px; } +.mx_TabbedView_tabsOnLeft { + flex-direction: column; + position: absolute; + + .mx_TabbedView_tabLabels { + width: 170px; + max-width: 170px; + position: fixed; + } + + .mx_TabbedView_tabPanel { + margin-left: 240px; // 170px sidebar + 70px padding + flex-direction: column; + } + + .mx_TabbedView_tabLabel_active { + background-color: $tab-label-active-bg-color; + color: $tab-label-active-fg-color; + } + + .mx_TabbedView_tabLabel_active .mx_TabbedView_maskedIcon::before { + background-color: $tab-label-active-icon-bg-color; + } + + .mx_TabbedView_maskedIcon { + width: 16px; + height: 16px; + margin-left: 8px; + margin-right: 16px; + } + + .mx_TabbedView_maskedIcon::before { + mask-size: 16px; + width: 16px; + height: 16px; + } +} + +.mx_TabbedView_tabsOnTop { + flex-direction: column; + + .mx_TabbedView_tabLabels { + display: flex; + } + + .mx_TabbedView_tabLabel { + padding-left: 0px; + padding-right: 52px; + + .mx_TabbedView_tabLabel_text { + font-size: 15px; + color: $tertiary-fg-color; + } + } + + .mx_TabbedView_tabPanel { + flex-direction: row; + } + + .mx_TabbedView_tabLabel_active { + color: $accent-color; + .mx_TabbedView_tabLabel_text { + color: $accent-color; + } + } + + .mx_TabbedView_tabLabel_active .mx_TabbedView_maskedIcon::before { + background-color: $accent-color; + } + + .mx_TabbedView_maskedIcon { + width: 22px; + height: 22px; + margin-left: 0px; + margin-right: 8px; + } + + .mx_TabbedView_maskedIcon::before { + mask-size: 22px; + width: 22px; + height: 22px; + } +} + .mx_TabbedView_tabLabels { - width: 170px; - max-width: 170px; color: $tab-label-fg-color; - position: fixed; } .mx_TabbedView_tabLabel { @@ -46,16 +127,7 @@ limitations under the License. position: relative; } -.mx_TabbedView_tabLabel_active { - background-color: $tab-label-active-bg-color; - color: $tab-label-active-fg-color; -} - .mx_TabbedView_maskedIcon { - margin-left: 8px; - margin-right: 16px; - width: 16px; - height: 16px; display: inline-block; } @@ -63,26 +135,17 @@ limitations under the License. display: inline-block; background-color: $tab-label-icon-bg-color; mask-repeat: no-repeat; - mask-size: 16px; - width: 16px; - height: 16px; mask-position: center; content: ''; } -.mx_TabbedView_tabLabel_active .mx_TabbedView_maskedIcon::before { - background-color: $tab-label-active-icon-bg-color; -} - .mx_TabbedView_tabLabel_text { vertical-align: middle; } .mx_TabbedView_tabPanel { - margin-left: 240px; // 170px sidebar + 70px padding flex-grow: 1; display: flex; - flex-direction: column; min-height: 0; // firefox } diff --git a/res/css/views/dialogs/_InviteDialog.scss b/res/css/views/dialogs/_InviteDialog.scss index d8ff56663ab..6b332d742b8 100644 --- a/res/css/views/dialogs/_InviteDialog.scss +++ b/res/css/views/dialogs/_InviteDialog.scss @@ -210,17 +210,37 @@ limitations under the License. } } -.mx_InviteDialog { +.mx_InviteDialog_other { // Prevent the dialog from jumping around randomly when elements change. height: 590px; padding-left: 20px; // the design wants some padding on the left + + .mx_InviteDialog_userSections { + height: 455px; // mx_InviteDialog's height minus some for the upper elements + } +} + +.mx_InviteDialog_transfer { + width: 496px; + height: 466px; + display: flex; + flex-direction: column; + + .mx_InviteDialog_content { + flex: 1; + display: flex; + flex-direction: column; + + .mx_TabbedView { + flex: 1; + } + } } .mx_InviteDialog_userSections { margin-top: 10px; overflow-y: auto; padding-right: 45px; - height: 455px; // mx_InviteDialog's height minus some for the upper elements } // Right margin for the design. We could apply this to the whole dialog, but then the scrollbar @@ -233,3 +253,33 @@ limitations under the License. .mx_InviteDialog_helpText .mx_AccessibleButton_kind_link { padding: 0; } + +.mx_InviteDialog_dialPad .mx_InviteDialog_dialPadField { + border-top: 0px; + border-left: 0px; + border-right: 0px; + border-radius: 0px; + + input { + font-size: 18px; + font-weight: 600; + } +} + +.mx_InviteDialog_dialPad { + width: 224px; + margin-left: auto; + margin-right: auto; +} + +.mx_InviteDialog_transferButton { + float: right; +} + +.mx_InviteDialog_userDirectoryIcon::before { + mask-image: url('$(res)/img/voip/tab-userdirectory.svg'); +} + +.mx_InviteDialog_dialPadIcon::before { + mask-image: url('$(res)/img/voip/tab-dialpad.svg'); +} diff --git a/res/css/views/voip/_DialPad.scss b/res/css/views/voip/_DialPad.scss index 0c7bff0ce84..724189894ce 100644 --- a/res/css/views/voip/_DialPad.scss +++ b/res/css/views/voip/_DialPad.scss @@ -30,6 +30,8 @@ limitations under the License. text-align: center; vertical-align: middle; line-height: 40px; + margin-left: auto; + margin-right: auto; } .mx_DialPad_deleteButton, .mx_DialPad_dialButton { diff --git a/src/CallHandler.tsx b/src/CallHandler.tsx index f02f50d577f..0cb2e71fdd8 100644 --- a/src/CallHandler.tsx +++ b/src/CallHandler.tsx @@ -394,7 +394,7 @@ export default class CallHandler extends EventEmitter { } private setCallListeners(call: MatrixCall) { - let mappedRoomId = CallHandler.sharedInstance().roomIdForCall(call); + let mappedRoomId = this.roomIdForCall(call); call.on(CallEvent.Error, (err: CallError) => { if (!this.matchesCallForThisRoom(call)) return; diff --git a/src/components/structures/TabbedView.tsx b/src/components/structures/TabbedView.tsx index 0097d55cf53..173d55b7eae 100644 --- a/src/components/structures/TabbedView.tsx +++ b/src/components/structures/TabbedView.tsx @@ -21,6 +21,7 @@ import {_t} from '../../languageHandler'; import * as sdk from "../../index"; import AutoHideScrollbar from './AutoHideScrollbar'; import {replaceableComponent} from "../../utils/replaceableComponent"; +import classNames from "classnames"; /** * Represents a tab for the TabbedView. @@ -37,9 +38,16 @@ export class Tab { } } +export enum TabLocation { + LEFT = 'left', + TOP = 'top', +} + interface IProps { tabs: Tab[]; initialTabId?: string; + tabLocation: TabLocation; + onChange: (tabId: string) => void; } interface IState { @@ -62,6 +70,10 @@ export default class TabbedView extends React.Component { }; } + static defaultProps = { + tabLocation: TabLocation.LEFT, + } + private _getActiveTabIndex() { if (!this.state || !this.state.activeTabIndex) return 0; return this.state.activeTabIndex; @@ -75,6 +87,7 @@ export default class TabbedView extends React.Component { private _setActiveTab(tab: Tab) { const idx = this.props.tabs.indexOf(tab); if (idx !== -1) { + if (this.props.onChange) this.props.onChange(tab.id); this.setState({activeTabIndex: idx}); } else { console.error("Could not find tab " + tab.label + " in tabs"); @@ -121,8 +134,14 @@ export default class TabbedView extends React.Component { const labels = this.props.tabs.map(tab => this._renderTabLabel(tab)); const panel = this._renderTabPanel(this.props.tabs[this._getActiveTabIndex()]); + const tabbedViewClasses = classNames({ + 'mx_TabbedView': true, + 'mx_TabbedView_tabsOnLeft': this.props.tabLocation == TabLocation.LEFT, + 'mx_TabbedView_tabsOnTop': this.props.tabLocation == TabLocation.TOP, + }); + return ( -
+
{labels}
diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index d5f76094e4f..a082a2e8ce8 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -50,7 +50,7 @@ import {getAddressType} from "../../../UserAddress"; import BaseAvatar from '../avatars/BaseAvatar'; import AccessibleButton from '../elements/AccessibleButton'; import { compare } from '../../../utils/strings'; -import TabbedView, { Tab } from '../../structures/TabbedView'; +import TabbedView, { Tab, TabLocation } from '../../structures/TabbedView'; import Dialpad from '../voip/DialPad'; import Field from '../elements/Field'; import { TransferCallPayload } from '../../../dispatcher/payloads/TransferCallPayload'; @@ -71,6 +71,11 @@ export const KIND_CALL_TRANSFER = "call_transfer"; const INITIAL_ROOMS_SHOWN = 3; // Number of rooms to show at first const INCREMENT_ROOMS_SHOWN = 5; // Number of rooms to add when 'show more' is clicked +enum TabId { + UserDirectory = 'users', + DialPad = 'dialpad', +} + // This is the interface that is expected by various components in this file. It is a bit // awkward because it also matches the RoomMember class from the js-sdk with some extra support // for 3PIDs/email addresses. @@ -343,6 +348,7 @@ interface IInviteDialogState { tryingIdentityServer: boolean; consultFirst: boolean; dialPadValue: string; + currentTabId: TabId; // These two flags are used for the 'Go' button to communicate what is going on. busy: boolean, @@ -394,6 +400,7 @@ export default class InviteDialog extends React.PureComponent { - this.convertFilter(); - const targets = this.convertFilter(); - const targetIds = targets.map(t => t.userId); - if (targetIds.length > 1) { - this.setState({ - errorText: _t("A call can only be transferred to a single user."), - }); - } + if (this.state.currentTabId == TabId.UserDirectory) { + this.convertFilter(); + const targets = this.convertFilter(); + const targetIds = targets.map(t => t.userId); + if (targetIds.length > 1) { + this.setState({ + errorText: _t("A call can only be transferred to a single user."), + }); + } - dis.dispatch({ - action: Action.TransferCallToMatrixID, - call: this.props.call, - destination: targetIds[0], - consultFirst: this.state.consultFirst, - } as TransferCallPayload); + dis.dispatch({ + action: Action.TransferCallToMatrixID, + call: this.props.call, + destination: targetIds[0], + consultFirst: this.state.consultFirst, + } as TransferCallPayload); + } else { + dis.dispatch({ + action: Action.TransferCallToPhoneNumber, + call: this.props.call, + destination: this.state.dialPadValue, + consultFirst: this.state.consultFirst, + } as TransferCallPayload); + } this.props.onFinished(); } @@ -798,6 +814,10 @@ export default class InviteDialog extends React.PureComponent { + this.props.onFinished([]); + }; + private updateSuggestions = async (term) => { MatrixClientPeg.get().searchUserDirectory({term}).then(async r => { if (term !== this.state.filterText) { @@ -933,11 +953,14 @@ export default class InviteDialog extends React.PureComponent { if (!this.state.busy) { let filterText = this.state.filterText; - const targets = this.state.targets.map(t => t); // cheap clone for mutation + let targets = this.state.targets.map(t => t); // cheap clone for mutation const idx = targets.indexOf(member); if (idx >= 0) { targets.splice(idx, 1); } else { + if (this.props.kind === KIND_CALL_TRANSFER && targets.length > 0) { + targets = []; + } targets.push(member); filterText = ""; // clear the filter when the user accepts a suggestion } @@ -1162,6 +1185,11 @@ export default class InviteDialog extends React.PureComponent ( )); @@ -1176,6 +1204,7 @@ export default class InviteDialog extends React.PureComponent ); return ( @@ -1224,7 +1253,7 @@ export default class InviteDialog extends React.PureComponent { ev.preventDefault(); - this.onDialPress(); + this.transferCall(); } private onDialChange = ev => { @@ -1235,19 +1264,8 @@ export default class InviteDialog extends React.PureComponent { - dis.dispatch({ - action: Action.TransferCallToPhoneNumber, - call: this.props.call, - destination: this.state.dialPadValue, - consultFirst: this.state.consultFirst, - } as TransferCallPayload); - this.props.onFinished(); - } - - private onDeletePress = () => { - if (this.state.dialPadValue.length === 0) return; - this.setState({dialPadValue: this.state.dialPadValue.slice(0, -1)}); + private onTabChange = (tabId: TabId) => { + this.setState({currentTabId: tabId}); } render() { @@ -1391,18 +1409,25 @@ export default class InviteDialog extends React.PureComponent - {_t("Transfer")} + + {_t("Cancel")} + +
; } else { console.error("Unknown kind of InviteDialog: " + this.props.kind); @@ -1433,39 +1458,46 @@ export default class InviteDialog extends React.PureComponent - {consultConnectSection} ; let dialogContent; if (this.props.kind === KIND_CALL_TRANSFER) { const tabs = []; - tabs.push(new Tab('UsersTab', _td("Users"), null, usersSection)); + tabs.push(new Tab( + TabId.UserDirectory, _td("User Directory"), 'mx_InviteDialog_userDirectoryIcon', usersSection, + )); - const dialPadSection = + const dialPadSection =
- -
- -
+ +
; + tabs.push(new Tab(TabId.DialPad, _td("Dial pad"), 'mx_InviteDialog_dialPadIcon', dialPadSection)); + dialogContent = + {consultConnectSection} ; - tabs.push(new Tab('DialPadTab', _td("Dial pad"), null, dialPadSection)); - dialogContent = ; } else { - dialogContent = usersSection; + dialogContent = + usersSection + {consultConnectSection} + ; } + const dialogClass = this.props.kind === KIND_CALL_TRANSFER ? + 'mx_InviteDialog_transfer' : 'mx_InviteDialog_other'; + return ( Date: Fri, 18 Jun 2021 17:14:45 +0100 Subject: [PATCH 04/22] Somehow lost react braces --- src/components/views/dialogs/InviteDialog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index 924cde81daf..30264db3d06 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -1542,7 +1542,7 @@ export default class InviteDialog extends React.PureComponent; } else { dialogContent = - usersSection + {usersSection} {consultConnectSection} ; } From 84e9ed675894d042c36cddfaa4c59c01c2566600 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 18 Jun 2021 17:15:24 +0100 Subject: [PATCH 05/22] Add tab icons --- res/img/voip/tab-dialpad.svg | 3 +++ res/img/voip/tab-userdirectory.svg | 7 +++++++ 2 files changed, 10 insertions(+) create mode 100644 res/img/voip/tab-dialpad.svg create mode 100644 res/img/voip/tab-userdirectory.svg diff --git a/res/img/voip/tab-dialpad.svg b/res/img/voip/tab-dialpad.svg new file mode 100644 index 00000000000..b7add0addb0 --- /dev/null +++ b/res/img/voip/tab-dialpad.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/voip/tab-userdirectory.svg b/res/img/voip/tab-userdirectory.svg new file mode 100644 index 00000000000..792ded7be40 --- /dev/null +++ b/res/img/voip/tab-userdirectory.svg @@ -0,0 +1,7 @@ + + + + + + + From b97a166fe605ef047a66d31743520b23ed82db8d Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 18 Jun 2021 18:46:05 +0100 Subject: [PATCH 06/22] Put delete button back, fix layout flexbox and calc()ed heights are not a winning combination, so stay consistent and use calc()ed heights throughout. Also, put the delete button back rather than implementing a different one of those too. --- res/css/views/dialogs/_InviteDialog.scss | 12 ++++++++---- src/components/views/dialogs/InviteDialog.tsx | 9 +++++++-- src/components/views/voip/DialPad.tsx | 7 +++++-- src/components/views/voip/DialPadModal.tsx | 2 +- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/res/css/views/dialogs/_InviteDialog.scss b/res/css/views/dialogs/_InviteDialog.scss index f5e3f02de04..8ab9a49d1c1 100644 --- a/res/css/views/dialogs/_InviteDialog.scss +++ b/res/css/views/dialogs/_InviteDialog.scss @@ -296,19 +296,20 @@ limitations under the License. } } +.mx_InviteDialog_content { + height: calc(100% - 36px); // full height minus the size of the header +} + .mx_InviteDialog_transfer { width: 496px; height: 466px; - display: flex; flex-direction: column; .mx_InviteDialog_content { - flex: 1; - display: flex; flex-direction: column; .mx_TabbedView { - flex: 1; + height: calc(100% - 40px); } overflow: hidden; } @@ -318,6 +319,9 @@ limitations under the License. margin-top: 4px; overflow-y: auto; padding: 0 45px 4px 0; +} + +.mx_InviteDialog_other .mx_InviteDialog_userSections { height: calc(100% - 115px); // mx_InviteDialog's height minus some for the upper and lower elements } diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index 30264db3d06..01da0ba9748 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -1275,6 +1275,11 @@ export default class InviteDialog extends React.PureComponent { + if (this.state.dialPadValue.length === 0) return; + this.setState({dialPadValue: this.state.dialPadValue.slice(0, -1)}); + } + private onTabChange = (tabId: TabId) => { this.setState({currentTabId: tabId}); } @@ -1529,8 +1534,8 @@ export default class InviteDialog extends React.PureComponent -
; tabs.push(new Tab(TabId.DialPad, _td("Dial pad"), 'mx_InviteDialog_dialPadIcon', dialPadSection)); diff --git a/src/components/views/voip/DialPad.tsx b/src/components/views/voip/DialPad.tsx index 68092fb0be1..a953818469d 100644 --- a/src/components/views/voip/DialPad.tsx +++ b/src/components/views/voip/DialPad.tsx @@ -55,7 +55,8 @@ class DialPadButton extends React.PureComponent { interface IProps { onDigitPress: (string) => void; - hasDialAndDelete: boolean; + hasDial: boolean; + hasDelete: boolean; onDeletePress?: (string) => void; onDialPress?: (string) => void; } @@ -71,10 +72,12 @@ export default class Dialpad extends React.PureComponent { />); } - if (this.props.hasDialAndDelete) { + if (this.props.hasDelete) { buttonNodes.push(); + } + if (this.props.hasDial) { buttonNodes.push(); diff --git a/src/components/views/voip/DialPadModal.tsx b/src/components/views/voip/DialPadModal.tsx index 8c0af5e81a1..8eb8eedc63e 100644 --- a/src/components/views/voip/DialPadModal.tsx +++ b/src/components/views/voip/DialPadModal.tsx @@ -89,7 +89,7 @@ export default class DialpadModal extends React.PureComponent {
- Date: Fri, 18 Jun 2021 18:52:15 +0100 Subject: [PATCH 07/22] Remove duplicated height setting --- res/css/views/dialogs/_InviteDialog.scss | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/res/css/views/dialogs/_InviteDialog.scss b/res/css/views/dialogs/_InviteDialog.scss index 8ab9a49d1c1..54e42da0996 100644 --- a/res/css/views/dialogs/_InviteDialog.scss +++ b/res/css/views/dialogs/_InviteDialog.scss @@ -292,7 +292,7 @@ limitations under the License. padding-left: 20px; // the design wants some padding on the left .mx_InviteDialog_userSections { - height: 455px; // mx_InviteDialog's height minus some for the upper elements + height: calc(100% - 115px); // mx_InviteDialog's height minus some for the upper and lower elements } } @@ -321,10 +321,6 @@ limitations under the License. padding: 0 45px 4px 0; } -.mx_InviteDialog_other .mx_InviteDialog_userSections { - height: calc(100% - 115px); // mx_InviteDialog's height minus some for the upper and lower elements -} - .mx_InviteDialog_hasFooter .mx_InviteDialog_userSections { height: calc(100% - 175px); } From ed4313ff9710d37382ea8d517eaa5b4306299d28 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 18 Jun 2021 18:57:04 +0100 Subject: [PATCH 08/22] Fix other Dialpad usage --- src/components/views/context_menus/DialpadContextMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/context_menus/DialpadContextMenu.tsx b/src/components/views/context_menus/DialpadContextMenu.tsx index 17abce0c61f..f1103afefd8 100644 --- a/src/components/views/context_menus/DialpadContextMenu.tsx +++ b/src/components/views/context_menus/DialpadContextMenu.tsx @@ -54,7 +54,7 @@ export default class DialpadContextMenu extends React.Component
- +
; } From e67a80311a1bd1d2543de03048d5cfc05908ebaa Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 21 Jun 2021 13:41:54 +0100 Subject: [PATCH 09/22] missed return --- src/components/views/dialogs/InviteDialog.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index 01da0ba9748..820d55f4716 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -787,6 +787,7 @@ export default class InviteDialog extends React.PureComponent Date: Mon, 21 Jun 2021 13:58:34 +0100 Subject: [PATCH 10/22] use currentTarget over target --- src/components/views/dialogs/InviteDialog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index 820d55f4716..730cd37bd46 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -1269,7 +1269,7 @@ export default class InviteDialog extends React.PureComponent { - this.setState({dialPadValue: ev.target.value}); + this.setState({dialPadValue: ev.currentTarget.value}); } private onDigitPress = digit => { From f4ee7bb9ba1db13e20fa588ab1882898ea74a8ee Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 21 Jun 2021 14:00:50 +0100 Subject: [PATCH 11/22] Inherit width & height Co-authored-by: Germain --- res/css/structures/_TabbedView.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index cd0b95e632e..cc95ff6cf00 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -107,8 +107,8 @@ limitations under the License. .mx_TabbedView_maskedIcon::before { mask-size: 22px; - width: 22px; - height: 22px; + width: inherit; + height: inherit; } } From 65eb9c9cf9ffc0b09d8db7f8da03b21e27c04e8f Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 21 Jun 2021 15:21:11 +0100 Subject: [PATCH 12/22] Make dispatcher actions camel case like the others --- src/dispatcher/actions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dispatcher/actions.ts b/src/dispatcher/actions.ts index ccb180dda34..6a5d0ac97a4 100644 --- a/src/dispatcher/actions.ts +++ b/src/dispatcher/actions.ts @@ -110,13 +110,13 @@ export enum Action { * Start a call transfer to a Matrix ID * payload: TransferCallPayload */ - TransferCallToMatrixID = "transferCallToMatrixID", + TransferCallToMatrixID = "transfer_call_to_matrix_id", /** * Start a call transfer to a phone number * payload: TransferCallPayload */ - TransferCallToPhoneNumber = "transferCallToPhoneNumber", + TransferCallToPhoneNumber = "transfer_call_to_phone_number", /** * Fired when CallHandler has checked for PSTN protocol support From d012e9c00b78d1298e56042a4a91af3cdf0872c5 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 21 Jun 2021 15:23:11 +0100 Subject: [PATCH 13/22] Use unit-less 0 --- res/css/views/dialogs/_InviteDialog.scss | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/res/css/views/dialogs/_InviteDialog.scss b/res/css/views/dialogs/_InviteDialog.scss index 54e42da0996..a205612e5db 100644 --- a/res/css/views/dialogs/_InviteDialog.scss +++ b/res/css/views/dialogs/_InviteDialog.scss @@ -334,10 +334,10 @@ limitations under the License. } .mx_InviteDialog_dialPad .mx_InviteDialog_dialPadField { - border-top: 0px; - border-left: 0px; - border-right: 0px; - border-radius: 0px; + border-top: 0; + border-left: 0; + border-right: 0; + border-radius: 0; input { font-size: 18px; From 284aec8a82960c514ac894d0526f21db261e751f Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 21 Jun 2021 17:24:04 +0100 Subject: [PATCH 14/22] Design feedback --- res/css/structures/_TabbedView.scss | 3 ++- res/css/views/dialogs/_InviteDialog.scss | 26 +++++++++++++++++-- src/components/views/dialogs/InviteDialog.tsx | 2 +- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index cc95ff6cf00..833450a25b8 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -71,6 +71,7 @@ limitations under the License. .mx_TabbedView_tabLabels { display: flex; + margin-bottom: 8px; } .mx_TabbedView_tabLabel { @@ -133,7 +134,7 @@ limitations under the License. .mx_TabbedView_maskedIcon::before { display: inline-block; - background-color: $tab-label-icon-bg-color; + background-color: $icon-button-color; mask-repeat: no-repeat; mask-position: center; content: ''; diff --git a/res/css/views/dialogs/_InviteDialog.scss b/res/css/views/dialogs/_InviteDialog.scss index a205612e5db..471e7031142 100644 --- a/res/css/views/dialogs/_InviteDialog.scss +++ b/res/css/views/dialogs/_InviteDialog.scss @@ -298,6 +298,7 @@ limitations under the License. .mx_InviteDialog_content { height: calc(100% - 36px); // full height minus the size of the header + overflow: hidden; } .mx_InviteDialog_transfer { @@ -309,9 +310,17 @@ limitations under the License. flex-direction: column; .mx_TabbedView { - height: calc(100% - 40px); + height: calc(100% - 60px); } - overflow: hidden; + overflow: visible; + } + + .mx_InviteDialog_addressBar { + margin-top: 8px; + } + + input[type="checkbox"] { + margin-right: 8px; } } @@ -351,6 +360,19 @@ limitations under the License. margin-right: auto; } +.mx_InviteDialog_transferConsultConnect { + padding-top: 20px; + /* This wants a drop shadow the full width of the dialog, so relative-position it + * and make it wider, then compensate with padding + */ + position: relative; + width: 496px; + left: -24px; + padding-left: 24px; + padding-right: 24px; + box-shadow: 0 -4px 4px rgba(0, 0, 0, 0.05); +} + .mx_InviteDialog_transferButton { float: right; } diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index 730cd37bd46..9b5a78cabe3 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -1466,7 +1466,7 @@ export default class InviteDialog extends React.PureComponent + consultConnectSection =
Date: Tue, 22 Jun 2021 18:58:49 +0100 Subject: [PATCH 15/22] Use flexbox to layout buttons as float: right causes them to drop out of the box model so the the alignment messes up. --- res/css/views/dialogs/_InviteDialog.scss | 8 +++++-- src/components/views/dialogs/InviteDialog.tsx | 23 +++++++++---------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/res/css/views/dialogs/_InviteDialog.scss b/res/css/views/dialogs/_InviteDialog.scss index 471e7031142..050f0a26193 100644 --- a/res/css/views/dialogs/_InviteDialog.scss +++ b/res/css/views/dialogs/_InviteDialog.scss @@ -371,10 +371,14 @@ limitations under the License. padding-left: 24px; padding-right: 24px; box-shadow: 0 -4px 4px rgba(0, 0, 0, 0.05); + + display: flex; + flex-direction: row; + align-items: center; } -.mx_InviteDialog_transferButton { - float: right; +.mx_InviteDialog_transferConsultConnect_pushRight { + margin-left: auto; } .mx_InviteDialog_userDirectoryIcon::before { diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index 9b5a78cabe3..e69cdb15340 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -1467,6 +1467,17 @@ export default class InviteDialog extends React.PureComponent + + + {_t("Cancel")} + {_t("Transfer")} - - {_t("Cancel")} - - -
; } else { console.error("Unknown kind of InviteDialog: " + this.props.kind); From fc7a3068d60c1b0607e3a57a3566349954e89a13 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 22 Jun 2021 19:11:45 +0100 Subject: [PATCH 16/22] Remove top margin on input to move dial pad up --- res/css/views/dialogs/_InviteDialog.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/views/dialogs/_InviteDialog.scss b/res/css/views/dialogs/_InviteDialog.scss index 050f0a26193..0f682c674d2 100644 --- a/res/css/views/dialogs/_InviteDialog.scss +++ b/res/css/views/dialogs/_InviteDialog.scss @@ -347,6 +347,7 @@ limitations under the License. border-left: 0; border-right: 0; border-radius: 0; + margin-top: 0; input { font-size: 18px; From a636e5e6684711c8addb82c0021783d374429d96 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 22 Jun 2021 19:46:46 +0100 Subject: [PATCH 17/22] shift the dial pad up a bit more --- res/css/views/dialogs/_InviteDialog.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/views/dialogs/_InviteDialog.scss b/res/css/views/dialogs/_InviteDialog.scss index 0f682c674d2..522e9b39052 100644 --- a/res/css/views/dialogs/_InviteDialog.scss +++ b/res/css/views/dialogs/_InviteDialog.scss @@ -352,6 +352,7 @@ limitations under the License. input { font-size: 18px; font-weight: 600; + padding-top: 0; } } From c93e97336f3ae4600900189530dd4bb97d2c6b6a Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 22 Jun 2021 19:57:21 +0100 Subject: [PATCH 18/22] Disable input once a selection is made --- src/components/views/dialogs/InviteDialog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index e69cdb15340..251360330a0 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -1214,7 +1214,7 @@ export default class InviteDialog extends React.PureComponent 0)} autoComplete="off" placeholder={hasPlaceholder ? _t("Search") : null} /> From 10a900b0f6ca5425fd19f210c6be4ac3a3a17f3c Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 23 Jun 2021 11:24:00 +0100 Subject: [PATCH 19/22] Change drop shadow to 1px border --- res/css/views/dialogs/_InviteDialog.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/dialogs/_InviteDialog.scss b/res/css/views/dialogs/_InviteDialog.scss index 522e9b39052..ef34dc904c4 100644 --- a/res/css/views/dialogs/_InviteDialog.scss +++ b/res/css/views/dialogs/_InviteDialog.scss @@ -372,7 +372,7 @@ limitations under the License. left: -24px; padding-left: 24px; padding-right: 24px; - box-shadow: 0 -4px 4px rgba(0, 0, 0, 0.05); + border-top: 1px solid $message-body-panel-bg-color; display: flex; flex-direction: row; From 0d8dd741eb53ed293a50d644793c72cc6301f5d3 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 23 Jun 2021 16:25:36 +0100 Subject: [PATCH 20/22] Reduce bottom padding by passing a special wrapper class --- res/css/views/dialogs/_InviteDialog.scss | 6 +++++- src/components/views/context_menus/CallContextMenu.tsx | 2 +- src/components/views/dialogs/InviteDialog.tsx | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/res/css/views/dialogs/_InviteDialog.scss b/res/css/views/dialogs/_InviteDialog.scss index ef34dc904c4..13be424cac3 100644 --- a/res/css/views/dialogs/_InviteDialog.scss +++ b/res/css/views/dialogs/_InviteDialog.scss @@ -14,6 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ +.mx_InviteDialog_transferWrapper .mx_Dialog { + padding-bottom: 16px; +} + .mx_InviteDialog_addressBar { display: flex; flex-direction: row; @@ -363,7 +367,7 @@ limitations under the License. } .mx_InviteDialog_transferConsultConnect { - padding-top: 20px; + padding-top: 16px; /* This wants a drop shadow the full width of the dialog, so relative-position it * and make it wider, then compensate with padding */ diff --git a/src/components/views/context_menus/CallContextMenu.tsx b/src/components/views/context_menus/CallContextMenu.tsx index 97473059a69..d5e69f78d2f 100644 --- a/src/components/views/context_menus/CallContextMenu.tsx +++ b/src/components/views/context_menus/CallContextMenu.tsx @@ -53,7 +53,7 @@ export default class CallContextMenu extends React.Component { onTransferClick = () => { Modal.createTrackedDialog( 'Transfer Call', '', InviteDialog, {kind: KIND_CALL_TRANSFER, call: this.props.call}, - /*className=*/null, /*isPriority=*/false, /*isStatic=*/true, + /*className=*/"mx_InviteDialog_transferWrapper", /*isPriority=*/false, /*isStatic=*/true, ); this.props.onFinished(); } diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index 251360330a0..0599c3c093e 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -73,6 +73,9 @@ interface IRecentUser { export const KIND_DM = "dm"; export const KIND_INVITE = "invite"; +// NB. This dialog needs the 'mx_InviteDialog_transferWrapper' wrapper class to have the correct +// padding on the bottom (because all modals have 24px padding on all sides), so this needs to +// be passed when creating the modal export const KIND_CALL_TRANSFER = "call_transfer"; const INITIAL_ROOMS_SHOWN = 3; // Number of rooms to show at first From b3afaad3b3e80beb1979add6469d31e9ee451efc Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Thu, 1 Jul 2021 17:48:34 +0100 Subject: [PATCH 21/22] lint fixes --- src/components/structures/TabbedView.tsx | 2 +- src/components/views/dialogs/InviteDialog.tsx | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/structures/TabbedView.tsx b/src/components/structures/TabbedView.tsx index b17960a0af0..ae0baf9d1b6 100644 --- a/src/components/structures/TabbedView.tsx +++ b/src/components/structures/TabbedView.tsx @@ -72,7 +72,7 @@ export default class TabbedView extends React.Component { static defaultProps = { tabLocation: TabLocation.LEFT, - } + }; private _getActiveTabIndex() { if (!this.state || !this.state.activeTabIndex) return 0; diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index 2e268a1bf91..8d19b8cd48f 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -807,7 +807,7 @@ export default class InviteDialog extends React.PureComponent { if (this.state.busy) return; @@ -1268,29 +1268,29 @@ export default class InviteDialog extends React.PureComponent { ev.preventDefault(); this.transferCall(); - } + }; private onDialChange = ev => { - this.setState({dialPadValue: ev.currentTarget.value}); - } + this.setState({ dialPadValue: ev.currentTarget.value }); + }; private onDigitPress = digit => { - this.setState({dialPadValue: this.state.dialPadValue + digit}); - } + this.setState({ dialPadValue: this.state.dialPadValue + digit }); + }; private onDeletePress = () => { if (this.state.dialPadValue.length === 0) return; - this.setState({dialPadValue: this.state.dialPadValue.slice(0, -1)}); - } + this.setState({ dialPadValue: this.state.dialPadValue.slice(0, -1) }); + }; private onTabChange = (tabId: TabId) => { - this.setState({currentTabId: tabId}); - } + this.setState({ currentTabId: tabId }); + }; private async onLinkClick(e) { e.preventDefault(); selectText(e.target); - } + }; private onCopyClick = async e => { e.preventDefault(); From 12e360b45789f41800973dbcfa97d55fc79e1989 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Thu, 1 Jul 2021 17:53:35 +0100 Subject: [PATCH 22/22] Not all semicolons are created necessary --- src/components/views/dialogs/InviteDialog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index 8d19b8cd48f..a603884758e 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -1290,7 +1290,7 @@ export default class InviteDialog extends React.PureComponent { e.preventDefault();