diff --git a/CHANGELOG.md b/CHANGELOG.md index 13c5ba81ed..5aac4e2974 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,73 @@ +Changes in [3.9.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.9.0) (2020-11-23) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.9.0-rc.1...v3.9.0) + + * Upgrade JS SDK to 9.2.0 + * [Release] Fix encrypted video playback in Chrome-based browsers + [\#5431](https://github.com/matrix-org/matrix-react-sdk/pull/5431) + +Changes in [3.9.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.9.0-rc.1) (2020-11-18) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.8.0...v3.9.0-rc.1) + + * Upgrade JS SDK to 9.2.0-rc.1 + * Translations update from Weblate + [\#5429](https://github.com/matrix-org/matrix-react-sdk/pull/5429) + * Fix message search summary text + [\#5428](https://github.com/matrix-org/matrix-react-sdk/pull/5428) + * Shrink new room intro top margin to half for encryption bubble tile + [\#5427](https://github.com/matrix-org/matrix-react-sdk/pull/5427) + * Small delight tweaks to improve rough corners in the app + [\#5418](https://github.com/matrix-org/matrix-react-sdk/pull/5418) + * Fix DM logic to always pick a more reliable DM room + [\#5424](https://github.com/matrix-org/matrix-react-sdk/pull/5424) + * Update styling of the Analytics toast + [\#5408](https://github.com/matrix-org/matrix-react-sdk/pull/5408) + * Fix vertical centering of the Homepage and button layout + [\#5420](https://github.com/matrix-org/matrix-react-sdk/pull/5420) + * Fix BaseAvatar sometimes messing up and duplicating the url + [\#5422](https://github.com/matrix-org/matrix-react-sdk/pull/5422) + * Disable buttons when required by MSC2790 + [\#5412](https://github.com/matrix-org/matrix-react-sdk/pull/5412) + * Fix drag drop file to upload for Safari + [\#5414](https://github.com/matrix-org/matrix-react-sdk/pull/5414) + * Fix poorly i18n'd string + [\#5416](https://github.com/matrix-org/matrix-react-sdk/pull/5416) + * Fix the feedback not closing without feedback/countly + [\#5417](https://github.com/matrix-org/matrix-react-sdk/pull/5417) + * Fix New Room Intro invite to this room button + [\#5419](https://github.com/matrix-org/matrix-react-sdk/pull/5419) + * Change how we expose Role in User Info and hide in DMs + [\#5413](https://github.com/matrix-org/matrix-react-sdk/pull/5413) + * Disallow sending of empty messages + [\#5390](https://github.com/matrix-org/matrix-react-sdk/pull/5390) + * hide some validation tooltips if fields are valid. + [\#5403](https://github.com/matrix-org/matrix-react-sdk/pull/5403) + * Improvements around new room empty space interactions + [\#5398](https://github.com/matrix-org/matrix-react-sdk/pull/5398) + * Implement call hold + [\#5366](https://github.com/matrix-org/matrix-react-sdk/pull/5366) + * Fix Skeleton UI showing up when not intended. + [\#5407](https://github.com/matrix-org/matrix-react-sdk/pull/5407) + * Close context menu when user clicks the Home button + [\#5406](https://github.com/matrix-org/matrix-react-sdk/pull/5406) + * Skip e2ee warn logout prompt if user has no megolm sessions to lose + [\#5410](https://github.com/matrix-org/matrix-react-sdk/pull/5410) + * Allow country names to be translated + [\#5405](https://github.com/matrix-org/matrix-react-sdk/pull/5405) + * Support thirdparty lookup for phone numbers + [\#5396](https://github.com/matrix-org/matrix-react-sdk/pull/5396) + * Change "Password" to "New Password" + [\#5371](https://github.com/matrix-org/matrix-react-sdk/pull/5371) + * Add customisation point for dehydration key + [\#5397](https://github.com/matrix-org/matrix-react-sdk/pull/5397) + * Rebrand Riot -> Element in the permalink classes + [\#5386](https://github.com/matrix-org/matrix-react-sdk/pull/5386) + * Invite / Create DM UX tweaks + [\#5387](https://github.com/matrix-org/matrix-react-sdk/pull/5387) + * Tweaks to toasts and post-registration landing + [\#5383](https://github.com/matrix-org/matrix-react-sdk/pull/5383) + Changes in [3.8.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.8.0) (2020-11-09) =================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.8.0-rc.1...v3.8.0) diff --git a/package.json b/package.json index a015728256..61adcd9807 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.8.0", + "version": "3.9.0", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { @@ -79,7 +79,7 @@ "linkifyjs": "^2.1.9", "lodash": "^4.17.19", "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", - "matrix-widget-api": "^0.1.0-beta.8", + "matrix-widget-api": "^0.1.0-beta.9", "minimist": "^1.2.5", "pako": "^1.0.11", "parse5": "^5.1.1", diff --git a/res/css/_common.scss b/res/css/_common.scss index 0317e89d20..7ab88d6f02 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -60,6 +60,10 @@ pre, code { color: $accent-color; } +.text-muted { + color: $muted-fg-color; +} + b { // On Firefox, the default weight for `` is `bolder` which results in no bold // effect since we only have specific weights of our fonts available. @@ -364,6 +368,11 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { .mx_Dialog_buttons { margin-top: 20px; text-align: right; + + .mx_Dialog_buttons_additive { + // The consumer is responsible for positioning their elements. + float: left; + } } /* XXX: Our button style are a mess: buttons that happen to appear in dialogs get special styles applied diff --git a/res/css/_components.scss b/res/css/_components.scss index 53a72c4ce8..53ca14de4a 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -92,6 +92,7 @@ @import "./views/dialogs/_TermsDialog.scss"; @import "./views/dialogs/_UploadConfirmDialog.scss"; @import "./views/dialogs/_UserSettingsDialog.scss"; +@import "./views/dialogs/_WidgetCapabilitiesPromptDialog.scss"; @import "./views/dialogs/_WidgetOpenIDPermissionsDialog.scss"; @import "./views/dialogs/security/_AccessSecretStorageDialog.scss"; @import "./views/dialogs/security/_CreateCrossSigningDialog.scss"; diff --git a/res/css/structures/_RoomStatusBar.scss b/res/css/structures/_RoomStatusBar.scss index 2d5359c0eb..5bf2aee3ae 100644 --- a/res/css/structures/_RoomStatusBar.scss +++ b/res/css/structures/_RoomStatusBar.scss @@ -19,57 +19,6 @@ limitations under the License. min-height: 50px; } -/* position the indicator in the same place horizontally as .mx_EventTile_avatar. */ -.mx_RoomStatusBar_indicator { - padding-left: 17px; - padding-right: 12px; - margin-left: -73px; - margin-top: 15px; - float: left; - width: 24px; - text-align: center; -} - -.mx_RoomStatusBar_callBar { - height: 50px; - line-height: $font-50px; -} - -.mx_RoomStatusBar_placeholderIndicator span { - color: $primary-fg-color; - opacity: 0.5; - position: relative; - top: -4px; - /* - animation-duration: 1s; - animation-name: bounce; - animation-direction: alternate; - animation-iteration-count: infinite; - */ -} - -.mx_RoomStatusBar_placeholderIndicator span:nth-child(1) { - animation-delay: 0.3s; -} -.mx_RoomStatusBar_placeholderIndicator span:nth-child(2) { - animation-delay: 0.6s; -} -.mx_RoomStatusBar_placeholderIndicator span:nth-child(3) { - animation-delay: 0.9s; -} - -@keyframes bounce { - from { - opacity: 0.5; - top: 0; - } - - to { - opacity: 0.2; - top: -3px; - } -} - .mx_RoomStatusBar_typingIndicatorAvatars { width: 52px; margin-top: -1px; @@ -162,11 +111,6 @@ limitations under the License. margin-top: 10px; } - .mx_RoomStatusBar_callBar { - height: 40px; - line-height: $font-40px; - } - .mx_RoomStatusBar_typingBar { height: 40px; line-height: $font-40px; diff --git a/res/css/views/avatars/_BaseAvatar.scss b/res/css/views/avatars/_BaseAvatar.scss index 1a1e14e7ac..cbddd97e18 100644 --- a/res/css/views/avatars/_BaseAvatar.scss +++ b/res/css/views/avatars/_BaseAvatar.scss @@ -41,7 +41,7 @@ limitations under the License. .mx_BaseAvatar_image { object-fit: cover; - border-radius: 40px; + border-radius: 125px; vertical-align: top; background-color: $avatar-bg-color; } diff --git a/res/css/views/dialogs/_WidgetCapabilitiesPromptDialog.scss b/res/css/views/dialogs/_WidgetCapabilitiesPromptDialog.scss new file mode 100644 index 0000000000..176919b84c --- /dev/null +++ b/res/css/views/dialogs/_WidgetCapabilitiesPromptDialog.scss @@ -0,0 +1,75 @@ +/* +Copyright 2020 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. +*/ + + +.mx_WidgetCapabilitiesPromptDialog { + .text-muted { + font-size: $font-12px; + } + + .mx_Dialog_content { + margin-bottom: 16px; + } + + .mx_WidgetCapabilitiesPromptDialog_cap { + margin-top: 20px; + font-size: $font-15px; + line-height: $font-15px; + + .mx_WidgetCapabilitiesPromptDialog_byline { + color: $muted-fg-color; + margin-left: 26px; + font-size: $font-12px; + line-height: $font-12px; + } + } + + .mx_Dialog_buttons { + margin-top: 40px; // double normal + } + + .mx_SettingsFlag { + line-height: calc($font-14px + 7px + 7px); // 7px top & bottom padding + color: $muted-fg-color; + font-size: $font-12px; + + .mx_ToggleSwitch { + display: inline-block; + vertical-align: middle; + margin-right: 8px; + + // downsize the switch + ball + width: $font-32px; + height: $font-15px; + + + &.mx_ToggleSwitch_on > .mx_ToggleSwitch_ball { + left: calc(100% - $font-15px); + } + + .mx_ToggleSwitch_ball { + width: $font-15px; + height: $font-15px; + border-radius: $font-15px; + } + } + + .mx_SettingsFlag_label { + display: inline-block; + vertical-align: middle; + } + } +} diff --git a/res/css/views/voip/_CallView.scss b/res/css/views/voip/_CallView.scss index 2aeaaa87dc..2b87181b1e 100644 --- a/res/css/views/voip/_CallView.scss +++ b/res/css/views/voip/_CallView.scss @@ -15,87 +15,196 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_CallView_voice { - background-color: $accent-color; - color: $accent-fg-color; - cursor: pointer; - padding: 6px; - font-weight: bold; +.mx_CallView { + border-radius: 10px; + background-color: $input-lighter-bg-color; + padding-left: 8px; + padding-right: 8px; + // XXX: CallContainer sets pointer-events: none - should probably be set back in a better place + pointer-events: initial; +} - border-radius: 8px; - min-width: 200px; +.mx_CallView_large { + padding-bottom: 10px; - display: flex; - align-items: center; - - img { - margin: 4px; - margin-right: 10px; - } - - > div { - display: flex; - flex-direction: column; - // Hacky vertical align - padding-top: 3px; - } - - > div > p, - > div > h1 { - padding: 0; - margin: 0; - font-size: $font-13px; - line-height: $font-15px; - } - - > div > p { - font-weight: bold; - } - - > * { - flex-grow: 0; - flex-shrink: 0; + .mx_CallView_voice { + height: 360px; } } -.mx_CallView_hangup { - position: absolute; +.mx_CallView_pip { + width: 320px; - right: 8px; - bottom: 10px; - - height: 35px; - width: 35px; - - border-radius: 35px; - - background-color: $notice-primary-color; - - z-index: 101; - - cursor: pointer; - - &::before { - content: ''; - position: absolute; - - height: 20px; - width: 20px; - - top: 6.5px; - left: 7.5px; - - mask: url('$(res)/img/hangup.svg'); - mask-size: contain; - background-size: contain; - - background-color: $primary-fg-color; + .mx_CallView_voice { + height: 180px; } } +.mx_CallView_voice { + position: relative; + display: flex; + align-items: center; + justify-content: center; + background-color: $inverted-bg-color; +} + .mx_CallView_video { width: 100%; position: relative; z-index: 30; } +.mx_CallView_header { + height: 44px; + display: flex; + flex-direction: row; + align-items: center; + justify-content: left; + + .mx_BaseAvatar { + margin-right: 12px; + } +} + +.mx_CallView_header_callType { + font-weight: bold; + vertical-align: middle; +} + +.mx_CallView_header_controls { + margin-left: auto; +} + +.mx_CallView_header_button { + display: inline-block; + vertical-align: middle; + cursor: pointer; + + &::before { + content: ''; + display: inline-block; + height: 20px; + width: 20px; + vertical-align: middle; + background-color: $secondary-fg-color; + mask-repeat: no-repeat; + mask-size: contain; + mask-position: center; + } +} + +.mx_CallView_header_button_fullscreen { + &::before { + mask-image: url('$(res)/img/element-icons/call/fullscreen.svg'); + } +} + +.mx_CallView_header_button_expand { + &::before { + mask-image: url('$(res)/img/element-icons/call/expand.svg'); + } +} + +.mx_CallView_header_roomName { + font-weight: bold; + font-size: 12px; + line-height: initial; +} + +.mx_CallView_header_callTypeSmall { + font-size: 12px; + color: $secondary-fg-color; + line-height: initial; +} + +.mx_CallView_header_phoneIcon { + display: inline-block; + margin-right: 6px; + height: 16px; + width: 16px; + vertical-align: middle; + + &::before { + content: ''; + display: inline-block; + vertical-align: top; + + height: 16px; + width: 16px; + background-color: $warning-color; + mask-repeat: no-repeat; + mask-size: contain; + mask-position: center; + mask-image: url('$(res)/img/element-icons/call/voice-call.svg'); + } +} + +.mx_CallView_callControls { + position: absolute; + display: flex; + justify-content: center; + bottom: 5px; + width: 100%; + opacity: 1; + transition: opacity 0.5s; +} + +.mx_CallView_callControls_hidden { + opacity: 0.001; // opacity 0 can cause a re-layout + pointer-events: none; +} + +.mx_CallView_callControls_button { + cursor: pointer; + margin-left: 8px; + margin-right: 8px; + + + &::before { + content: ''; + display: inline-block; + + height: 48px; + width: 48px; + + background-repeat: no-repeat; + background-size: contain; + background-position: center; + } +} + +.mx_CallView_callControls_button_micOn { + &::before { + background-image: url('$(res)/img/voip/mic-on.svg'); + } +} + +.mx_CallView_callControls_button_micOff { + &::before { + background-image: url('$(res)/img/voip/mic-off.svg'); + } +} + +.mx_CallView_callControls_button_vidOn { + &::before { + background-image: url('$(res)/img/voip/vid-on.svg'); + } +} + +.mx_CallView_callControls_button_vidOff { + &::before { + background-image: url('$(res)/img/voip/vid-off.svg'); + } +} + +.mx_CallView_callControls_button_hangup { + &::before { + background-image: url('$(res)/img/voip/hangup.svg'); + } +} + +.mx_CallView_callControls_button_invisible { + visibility: hidden; + pointer-events: none; + position: absolute; +} diff --git a/res/css/views/voip/_VideoFeed.scss b/res/css/views/voip/_VideoFeed.scss index e5e3587dac..931410dba3 100644 --- a/res/css/views/voip/_VideoFeed.scss +++ b/res/css/views/voip/_VideoFeed.scss @@ -14,10 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_VideoFeed video { - width: 100%; -} - .mx_VideoFeed_remote { width: 100%; background-color: #000; @@ -28,16 +24,12 @@ limitations under the License. width: 25%; height: 25%; position: absolute; - left: 10px; - bottom: 10px; + right: 10px; + top: 10px; z-index: 100; + border-radius: 4px; } -.mx_VideoFeed_local video { - width: auto; - height: 100%; -} - -.mx_VideoFeed_mirror video { +.mx_VideoFeed_mirror { transform: scale(-1, 1); } diff --git a/res/img/element-icons/call/expand.svg b/res/img/element-icons/call/expand.svg new file mode 100644 index 0000000000..91ef4d8a76 --- /dev/null +++ b/res/img/element-icons/call/expand.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/element-icons/call/video-muted.svg b/res/img/element-icons/call/video-muted.svg deleted file mode 100644 index d2aea71d11..0000000000 --- a/res/img/element-icons/call/video-muted.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/res/img/element-icons/call/voice-muted.svg b/res/img/element-icons/call/voice-muted.svg deleted file mode 100644 index 32abafb04a..0000000000 --- a/res/img/element-icons/call/voice-muted.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/res/img/element-icons/call/voice-unmuted.svg b/res/img/element-icons/call/voice-unmuted.svg deleted file mode 100644 index e664080217..0000000000 --- a/res/img/element-icons/call/voice-unmuted.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/res/img/element-icons/room/in-call.svg b/res/img/element-icons/room/in-call.svg deleted file mode 100644 index 0e574faa84..0000000000 --- a/res/img/element-icons/room/in-call.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/res/img/hangup.svg b/res/img/hangup.svg deleted file mode 100644 index be038d2b30..0000000000 --- a/res/img/hangup.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - Fill 72 + Path 98 - Created with Sketch. - - - - - - - - - - \ No newline at end of file diff --git a/res/img/voip/hangup.svg b/res/img/voip/hangup.svg new file mode 100644 index 0000000000..dfb20bd519 --- /dev/null +++ b/res/img/voip/hangup.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/res/img/voip/mic-off.svg b/res/img/voip/mic-off.svg new file mode 100644 index 0000000000..6409f1fd07 --- /dev/null +++ b/res/img/voip/mic-off.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/res/img/voip/mic-on.svg b/res/img/voip/mic-on.svg new file mode 100644 index 0000000000..3493b3c581 --- /dev/null +++ b/res/img/voip/mic-on.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/res/img/voip/vid-off.svg b/res/img/voip/vid-off.svg new file mode 100644 index 0000000000..199d97ab97 --- /dev/null +++ b/res/img/voip/vid-off.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/res/img/voip/vid-on.svg b/res/img/voip/vid-on.svg new file mode 100644 index 0000000000..d8146d01d3 --- /dev/null +++ b/res/img/voip/vid-on.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/CallHandler.tsx b/src/CallHandler.tsx index 710eded2cd..3be203ab98 100644 --- a/src/CallHandler.tsx +++ b/src/CallHandler.tsx @@ -79,6 +79,7 @@ import { ElementWidgetActions } from "./stores/widgets/ElementWidgetActions"; import { MatrixCall, CallErrorCode, CallState, CallEvent, CallParty, CallType } from "matrix-js-sdk/src/webrtc/call"; import Analytics from './Analytics'; import CountlyAnalytics from "./CountlyAnalytics"; +import {UIFeature} from "./settings/UIFeature"; enum AudioID { Ring = 'ringAudio', @@ -124,7 +125,7 @@ export default class CallHandler { return window.mxCallHandler; } - constructor() { + start() { dis.register(this.onAction); // add empty handlers for media actions, otherwise the media keys // end up causing the audio elements with our ring/ringback etc @@ -137,6 +138,27 @@ export default class CallHandler { navigator.mediaSession.setActionHandler('previoustrack', function() {}); navigator.mediaSession.setActionHandler('nexttrack', function() {}); } + + if (SettingsStore.getValue(UIFeature.Voip)) { + MatrixClientPeg.get().on('Call.incoming', this.onCallIncoming); + } + } + + stop() { + const cli = MatrixClientPeg.get(); + if (cli) { + cli.removeListener('Call.incoming', this.onCallIncoming); + } + } + + private onCallIncoming = (call) => { + // we dispatch this synchronously to make sure that the event + // handlers on the call are set up immediately (so that if + // we get an immediate hangup, we don't get a stuck call) + dis.dispatch({ + action: 'incoming_call', + call: call, + }, true); } getCallForRoom(roomId: string): MatrixCall { diff --git a/src/Lifecycle.ts b/src/Lifecycle.ts index 7469624f5c..8451568dd1 100644 --- a/src/Lifecycle.ts +++ b/src/Lifecycle.ts @@ -48,6 +48,7 @@ import {Jitsi} from "./widgets/Jitsi"; import {SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY} from "./BasePlatform"; import ThreepidInviteStore from "./stores/ThreepidInviteStore"; import CountlyAnalytics from "./CountlyAnalytics"; +import CallHandler from './CallHandler'; const HOMESERVER_URL_KEY = "mx_hs_url"; const ID_SERVER_URL_KEY = "mx_is_url"; @@ -665,6 +666,7 @@ async function startMatrixClient(startSyncing = true): Promise { DMRoomMap.makeShared().start(); IntegrationManagers.sharedInstance().startWatching(); ActiveWidgetStore.start(); + CallHandler.sharedInstance().start(); // Start Mjolnir even though we haven't checked the feature flag yet. Starting // the thing just wastes CPU cycles, but should result in no actual functionality @@ -760,6 +762,7 @@ async function clearStorage(opts?: { deleteEverything?: boolean }): Promise dis.dispatch({action: 'view_create_chat'}); -const onClickExplore = () => dis.fire(Action.ViewRoomDirectory); -const onClickNewRoom = () => dis.dispatch({action: 'view_create_room'}); +const onClickSendDm = () => { + Analytics.trackEvent('home_page', 'button', 'dm'); + CountlyAnalytics.instance.track("home_page_button", { button: "dm" }); + dis.dispatch({action: 'view_create_chat'}); +}; + +const onClickExplore = () => { + Analytics.trackEvent('home_page', 'button', 'room_directory'); + CountlyAnalytics.instance.track("home_page_button", { button: "room_directory" }); + dis.fire(Action.ViewRoomDirectory); +}; + +const onClickNewRoom = () => { + Analytics.trackEvent('home_page', 'button', 'create_room'); + CountlyAnalytics.instance.track("home_page_button", { button: "create_room" }); + dis.dispatch({action: 'view_create_room'}); +}; interface IProps { justRegistered?: boolean; diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 114347196a..9fede15aa6 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -1353,18 +1353,6 @@ export default class MatrixChat extends React.PureComponent { }); }); - if (SettingsStore.getValue(UIFeature.Voip)) { - cli.on('Call.incoming', function(call) { - // we dispatch this synchronously to make sure that the event - // handlers on the call are set up immediately (so that if - // we get an immediate hangup, we don't get a stuck call) - dis.dispatch({ - action: 'incoming_call', - call: call, - }, true); - }); - } - cli.on('Session.logged_out', function(errObj) { if (Lifecycle.isLoggingOut()) return; diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.js index e6d2985073..c1c4ad6292 100644 --- a/src/components/structures/RoomStatusBar.js +++ b/src/components/structures/RoomStatusBar.js @@ -18,13 +18,11 @@ import React from 'react'; import PropTypes from 'prop-types'; import Matrix from 'matrix-js-sdk'; import { _t, _td } from '../../languageHandler'; -import * as sdk from '../../index'; import {MatrixClientPeg} from '../../MatrixClientPeg'; import Resend from '../../Resend'; import dis from '../../dispatcher/dispatcher'; import {messageForResourceLimitError, messageForSendError} from '../../utils/ErrorUtils'; import {Action} from "../../dispatcher/actions"; -import { CallState, CallType } from 'matrix-js-sdk/lib/webrtc/call'; const STATUS_BAR_HIDDEN = 0; const STATUS_BAR_EXPANDED = 1; @@ -42,13 +40,6 @@ export default class RoomStatusBar extends React.Component { // the room this statusbar is representing. room: PropTypes.object.isRequired, - // The active call in the room, if any (means we show the call bar - // along with the status of the call) - callState: PropTypes.string, - - // The type of the call in progress, or null if no call is in progress - callType: PropTypes.string, - // true if the room is being peeked at. This affects components that shouldn't // logically be shown when peeking, such as a prompt to invite people to a room. isPeeking: PropTypes.bool, @@ -115,12 +106,6 @@ export default class RoomStatusBar extends React.Component { }); }; - _showCallBar() { - return (this.props.callState && - (this.props.callState !== CallState.Ended && this.props.callState !== CallState.Ringing) - ); - } - _onResendAllClick = () => { Resend.resendUnsentEvents(this.props.room); dis.fire(Action.FocusComposer); @@ -152,7 +137,7 @@ export default class RoomStatusBar extends React.Component { // changed - so we use '0' to indicate normal size, and other values to // indicate other sizes. _getSize() { - if (this._shouldShowConnectionError() || this._showCallBar()) { + if (this._shouldShowConnectionError()) { return STATUS_BAR_EXPANDED; } else if (this.state.unsentMessages.length > 0) { return STATUS_BAR_EXPANDED_LARGE; @@ -160,22 +145,6 @@ export default class RoomStatusBar extends React.Component { return STATUS_BAR_HIDDEN; } - // return suitable content for the image on the left of the status bar. - _getIndicator() { - if (this._showCallBar()) { - const TintableSvg = sdk.getComponent("elements.TintableSvg"); - return ( - - ); - } - - if (this._shouldShowConnectionError()) { - return null; - } - - return null; - } - _shouldShowConnectionError() { // no conn bar trumps the "some not sent" msg since you can't resend without // a connection! @@ -266,25 +235,6 @@ export default class RoomStatusBar extends React.Component { ; } - _getCallStatusText() { - switch (this.props.callState) { - case CallState.CreateOffer: - case CallState.InviteSent: - return _t('Calling...'); - case CallState.Connecting: - case CallState.CreateAnswer: - return _t('Call connecting...'); - case CallState.Connected: - return _t('Active call'); - case CallState.WaitLocalMedia: - if (this.props.callType === CallType.Video) { - return _t('Starting camera...'); - } else { - return _t('Starting microphone...'); - } - } - } - // return suitable content for the main (text) part of the status bar. _getContent() { if (this._shouldShowConnectionError()) { @@ -307,26 +257,14 @@ export default class RoomStatusBar extends React.Component { return this._getUnsentMessageContent(); } - if (this._showCallBar()) { - return ( -
- { this._getCallStatusText() } -
- ); - } - return null; } render() { const content = this._getContent(); - const indicator = this._getIndicator(); return (
-
- { indicator } -
{ content }
diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index de7ae347dd..f4f7c6ceec 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -41,7 +41,7 @@ import rateLimitedFunc from '../../ratelimitedfunc'; import * as ObjectUtils from '../../ObjectUtils'; import * as Rooms from '../../Rooms'; import eventSearch, {searchPagination} from '../../Searching'; -import {isOnlyCtrlOrCmdIgnoreShiftKeyEvent, isOnlyCtrlOrCmdKeyEvent, Key} from '../../Keyboard'; +import {isOnlyCtrlOrCmdIgnoreShiftKeyEvent, Key} from '../../Keyboard'; import MainSplit from './MainSplit'; import RightPanel from './RightPanel'; import RoomViewStore from '../../stores/RoomViewStore'; @@ -56,7 +56,6 @@ import MatrixClientContext from "../../contexts/MatrixClientContext"; import {E2EStatus, shieldStatusForRoom} from '../../utils/ShieldUtils'; import {Action} from "../../dispatcher/actions"; import {SettingLevel} from "../../settings/SettingLevel"; -import {RightPanelPhases} from "../../stores/RightPanelStorePhases"; import {IMatrixClientCreds} from "../../MatrixClientPeg"; import ScrollPanel from "./ScrollPanel"; import TimelinePanel from "./TimelinePanel"; @@ -68,10 +67,9 @@ import RoomUpgradeWarningBar from "../views/rooms/RoomUpgradeWarningBar"; import PinnedEventsPanel from "../views/rooms/PinnedEventsPanel"; import AuxPanel from "../views/rooms/AuxPanel"; import RoomHeader from "../views/rooms/RoomHeader"; -import TintableSvg from "../views/elements/TintableSvg"; import {XOR} from "../../@types/common"; import { IThreepidInvite } from "../../stores/ThreepidInviteStore"; -import { CallState, CallType, MatrixCall } from "matrix-js-sdk/src/webrtc/call"; +import { CallState, MatrixCall } from "matrix-js-sdk/src/webrtc/call"; import WidgetStore from "../../stores/WidgetStore"; import {UPDATE_EVENT} from "../../stores/AsyncStore"; import Notifier from "../../Notifier"; @@ -508,8 +506,6 @@ export default class RoomView extends React.Component { this.props.resizeNotifier.on("middlePanelResized", this.onResize); } this.onResize(); - - document.addEventListener("keydown", this.onNativeKeyDown); } shouldComponentUpdate(nextProps, nextState) { @@ -592,8 +588,6 @@ export default class RoomView extends React.Component { this.props.resizeNotifier.removeListener("middlePanelResized", this.onResize); } - document.removeEventListener("keydown", this.onNativeKeyDown); - // Remove RoomStore listener if (this.roomStoreToken) { this.roomStoreToken.remove(); @@ -642,33 +636,6 @@ export default class RoomView extends React.Component { } }; - // we register global shortcuts here, they *must not conflict* with local shortcuts elsewhere or both will fire - private onNativeKeyDown = ev => { - let handled = false; - const ctrlCmdOnly = isOnlyCtrlOrCmdKeyEvent(ev); - - switch (ev.key) { - case Key.D: - if (ctrlCmdOnly) { - this.onMuteAudioClick(); - handled = true; - } - break; - - case Key.E: - if (ctrlCmdOnly) { - this.onMuteVideoClick(); - handled = true; - } - break; - } - - if (handled) { - ev.stopPropagation(); - ev.preventDefault(); - } - }; - private onReactKeyDown = ev => { let handled = false; @@ -1324,10 +1291,7 @@ export default class RoomView extends React.Component { }; private onSettingsClick = () => { - dis.dispatch({ - action: Action.SetRightPanelPhase, - phase: RightPanelPhases.RoomSummary, - }); + dis.dispatch({ action: "open_room_settings" }); }; private onCancelClick = () => { @@ -1758,8 +1722,6 @@ export default class RoomView extends React.Component { isStatusAreaExpanded = this.state.statusBarVisible; statusBar = { }; } - if (activeCall) { - let zoomButton; let videoMuteButton; - - if (activeCall.type === CallType.Video) { - zoomButton = ( -
- -
- ); - - videoMuteButton = -
- -
; - } - const voiceMuteButton = -
- -
; - - // wrap the existing status bar into a 'callStatusBar' which adds more knobs. - statusBar = -
- { voiceMuteButton } - { videoMuteButton } - { zoomButton } - { statusBar } -
; - } - // if we have search results, we keep the messagepanel (so that it preserves its // scroll state), but hide it. let searchResultsPanel; diff --git a/src/components/views/avatars/PulsedAvatar.tsx b/src/components/views/avatars/PulsedAvatar.tsx deleted file mode 100644 index b4e876b9f6..0000000000 --- a/src/components/views/avatars/PulsedAvatar.tsx +++ /dev/null @@ -1,28 +0,0 @@ -/* -Copyright 2020 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 React from 'react'; - -interface IProps { -} - -const PulsedAvatar: React.FC = (props) => { - return
- {props.children} -
; -}; - -export default PulsedAvatar; diff --git a/src/components/views/dialogs/ModalWidgetDialog.tsx b/src/components/views/dialogs/ModalWidgetDialog.tsx index c8a736e8a6..e722374555 100644 --- a/src/components/views/dialogs/ModalWidgetDialog.tsx +++ b/src/components/views/dialogs/ModalWidgetDialog.tsx @@ -31,6 +31,7 @@ import { ModalButtonKind, Widget, WidgetApiFromWidgetAction, + WidgetKind, } from "matrix-widget-api"; import {StopGapWidgetDriver} from "../../../stores/widgets/StopGapWidgetDriver"; import {MatrixClientPeg} from "../../../MatrixClientPeg"; @@ -72,7 +73,7 @@ export default class ModalWidgetDialog extends React.PureComponent; + widget: Widget; + widgetKind: WidgetKind; // TODO: Refactor into the Widget class +} + +interface IBooleanStates { + // @ts-ignore - TS wants a string key, but we know better + [capability: Capability]: boolean; +} + +interface IState { + booleanStates: IBooleanStates; + rememberSelection: boolean; +} + +export default class WidgetCapabilitiesPromptDialog extends React.PureComponent { + private eventPermissionsMap = new Map(); + + constructor(props: IProps) { + super(props); + + const parsedEvents = WidgetEventCapability.findEventCapabilities(this.props.requestedCapabilities); + parsedEvents.forEach(e => this.eventPermissionsMap.set(e.raw, e)); + + const states: IBooleanStates = {}; + this.props.requestedCapabilities.forEach(c => states[c] = true); + + this.state = { + booleanStates: states, + rememberSelection: true, + }; + } + + private onToggle = (capability: Capability) => { + const newStates = objectShallowClone(this.state.booleanStates); + newStates[capability] = !newStates[capability]; + this.setState({booleanStates: newStates}); + }; + + private onRememberSelectionChange = (newVal: boolean) => { + this.setState({rememberSelection: newVal}); + }; + + private onSubmit = async (ev) => { + this.closeAndTryRemember(Object.entries(this.state.booleanStates) + .filter(([_, isSelected]) => isSelected) + .map(([cap]) => cap)); + }; + + private onReject = async (ev) => { + this.closeAndTryRemember([]); // nothing was approved + }; + + private closeAndTryRemember(approved: Capability[]) { + if (this.state.rememberSelection) { + setRememberedCapabilitiesForWidget(this.props.widget, approved); + } + this.props.onFinished({approved}); + } + + public render() { + const checkboxRows = Object.entries(this.state.booleanStates).map(([cap, isChecked], i) => { + const text = CapabilityText.for(cap, this.props.widgetKind); + const byline = text.byline + ? {text.byline} + : null; + + return ( +
+ this.onToggle(cap)} + >{text.primary} + {byline} +
+ ); + }); + + return ( + +
+
+
{_t("This widget would like to:")}
+ {checkboxRows} + } + /> +
+
+
+ ); + } +} diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index f3ebe24c15..b862a1e912 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -23,7 +23,6 @@ import PropTypes from 'prop-types'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; import AccessibleButton from './AccessibleButton'; import { _t } from '../../../languageHandler'; -import * as sdk from '../../../index'; import AppPermission from './AppPermission'; import AppWarning from './AppWarning'; import Spinner from './Spinner'; @@ -375,19 +374,18 @@ export default class AppTile extends React.Component { />
); - // if the widget would be allowed to remain on screen, we must put it in - // a PersistedElement from the get-go, otherwise the iframe will be - // re-mounted later when we do. - if (this.props.whitelistCapabilities.includes('m.always_on_screen')) { - const PersistedElement = sdk.getComponent("elements.PersistedElement"); - // Also wrap the PersistedElement in a div to fix the height, otherwise - // AppTile's border is in the wrong place - appTileBody =
- - {appTileBody} - -
; - } + + // all widgets can theoretically be allowed to remain on screen, so we wrap + // them all in a PersistedElement from the get-go. If we wait, the iframe will + // be re-mounted later, which means the widget has to start over, which is bad. + + // Also wrap the PersistedElement in a div to fix the height, otherwise + // AppTile's border is in the wrong place + appTileBody =
+ + {appTileBody} + +
; } } @@ -474,10 +472,6 @@ AppTile.propTypes = { handleMinimisePointerEvents: PropTypes.bool, // Optionally hide the popout widget icon showPopout: PropTypes.bool, - // Widget capabilities to allow by default (without user confirmation) - // NOTE -- Use with caution. This is intended to aid better integration / UX - // basic widget capabilities, e.g. injecting sticker message events. - whitelistCapabilities: PropTypes.array, // Is this an instance of a user widget userWidget: PropTypes.bool, }; @@ -488,7 +482,6 @@ AppTile.defaultProps = { showTitle: true, showPopout: true, handleMinimisePointerEvents: false, - whitelistCapabilities: [], userWidget: false, miniMode: false, }; diff --git a/src/components/views/elements/DialogButtons.js b/src/components/views/elements/DialogButtons.js index 001292b6b7..3417485eb8 100644 --- a/src/components/views/elements/DialogButtons.js +++ b/src/components/views/elements/DialogButtons.js @@ -54,6 +54,9 @@ export default class DialogButtons extends React.Component { // disables only the primary button primaryDisabled: PropTypes.bool, + + // something to stick next to the buttons, optionally + additive: PropTypes.element, }; static defaultProps = { @@ -85,8 +88,14 @@ export default class DialogButtons extends React.Component { ; } + let additive = null; + if (this.props.additive) { + additive =
{this.props.additive}
; + } + return (
+ { additive } { cancelButton } { this.props.children }