Merge pull request #5525 from SimonBrandner/fix-screen-sharing
Fix desktop Matrix screen sharing and add a screen/window picker
This commit is contained in:
commit
cf5ec7720c
5 changed files with 252 additions and 1 deletions
|
@ -106,6 +106,7 @@
|
||||||
@import "./views/elements/_AddressTile.scss";
|
@import "./views/elements/_AddressTile.scss";
|
||||||
@import "./views/elements/_DesktopBuildsNotice.scss";
|
@import "./views/elements/_DesktopBuildsNotice.scss";
|
||||||
@import "./views/elements/_DirectorySearchBox.scss";
|
@import "./views/elements/_DirectorySearchBox.scss";
|
||||||
|
@import "./views/elements/_DesktopCapturerSourcePicker.scss";
|
||||||
@import "./views/elements/_Dropdown.scss";
|
@import "./views/elements/_Dropdown.scss";
|
||||||
@import "./views/elements/_EditableItemList.scss";
|
@import "./views/elements/_EditableItemList.scss";
|
||||||
@import "./views/elements/_ErrorBoundary.scss";
|
@import "./views/elements/_ErrorBoundary.scss";
|
||||||
|
|
72
res/css/views/elements/_DesktopCapturerSourcePicker.scss
Normal file
72
res/css/views/elements/_DesktopCapturerSourcePicker.scss
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 Šimon Brandner <simon.bra.ag@gmail.com>
|
||||||
|
|
||||||
|
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_desktopCapturerSourcePicker {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_desktopCapturerSourcePicker_tabLabels {
|
||||||
|
display: flex;
|
||||||
|
padding: 0 0 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_desktopCapturerSourcePicker_tabLabel,
|
||||||
|
.mx_desktopCapturerSourcePicker_tabLabel_selected {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 0;
|
||||||
|
font-size: $font-13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_desktopCapturerSourcePicker_tabLabel_selected {
|
||||||
|
background-color: $tab-label-active-bg-color;
|
||||||
|
color: $tab-label-active-fg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_desktopCapturerSourcePicker_panel {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
height: 500px;
|
||||||
|
overflow: overlay;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_desktopCapturerSourcePicker_stream_button {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_desktopCapturerSourcePicker_stream_button:hover,
|
||||||
|
.mx_desktopCapturerSourcePicker_stream_button:focus {
|
||||||
|
background: $roomtile-selected-bg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_desktopCapturerSourcePicker_stream_thumbnail {
|
||||||
|
margin: 4px;
|
||||||
|
width: 312px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_desktopCapturerSourcePicker_stream_name {
|
||||||
|
margin: 0 4px;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 312px;
|
||||||
|
}
|
|
@ -82,6 +82,7 @@ import CountlyAnalytics from "./CountlyAnalytics";
|
||||||
import {UIFeature} from "./settings/UIFeature";
|
import {UIFeature} from "./settings/UIFeature";
|
||||||
import { CallError } from "matrix-js-sdk/src/webrtc/call";
|
import { CallError } from "matrix-js-sdk/src/webrtc/call";
|
||||||
import { logger } from 'matrix-js-sdk/src/logger';
|
import { logger } from 'matrix-js-sdk/src/logger';
|
||||||
|
import DesktopCapturerSourcePicker from "./components/views/elements/DesktopCapturerSourcePicker"
|
||||||
import { Action } from './dispatcher/actions';
|
import { Action } from './dispatcher/actions';
|
||||||
import { roomForVirtualRoom, getOrCreateVirtualRoomForRoom } from './VoipUserMapper';
|
import { roomForVirtualRoom, getOrCreateVirtualRoomForRoom } from './VoipUserMapper';
|
||||||
import { addManagedHybridWidget, isManagedHybridWidgetEnabled } from './widgets/ManagedHybrid';
|
import { addManagedHybridWidget, isManagedHybridWidgetEnabled } from './widgets/ManagedHybrid';
|
||||||
|
@ -572,7 +573,15 @@ export default class CallHandler {
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
call.placeScreenSharingCall(remoteElement, localElement);
|
|
||||||
|
call.placeScreenSharingCall(
|
||||||
|
remoteElement,
|
||||||
|
localElement,
|
||||||
|
async () : Promise<DesktopCapturerSource> => {
|
||||||
|
const {finished} = Modal.createDialog(DesktopCapturerSourcePicker);
|
||||||
|
const [source] = await finished;
|
||||||
|
return source;
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
console.error("Unknown conf call type: %s", type);
|
console.error("Unknown conf call type: %s", type);
|
||||||
}
|
}
|
||||||
|
|
166
src/components/views/elements/DesktopCapturerSourcePicker.tsx
Normal file
166
src/components/views/elements/DesktopCapturerSourcePicker.tsx
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 Šimon Brandner <simon.bra.ag@gmail.com>
|
||||||
|
|
||||||
|
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';
|
||||||
|
import { _t } from '../../../languageHandler';
|
||||||
|
import BaseDialog from "..//dialogs/BaseDialog"
|
||||||
|
import AccessibleButton from './AccessibleButton';
|
||||||
|
import {getDesktopCapturerSources} from "matrix-js-sdk/src/webrtc/call";
|
||||||
|
|
||||||
|
export interface DesktopCapturerSource {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
thumbnailURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Tabs {
|
||||||
|
Screens = "screens",
|
||||||
|
Windows = "windows",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DesktopCapturerSourceIProps {
|
||||||
|
source: DesktopCapturerSource;
|
||||||
|
onSelect(source: DesktopCapturerSource): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ExistingSource extends React.Component<DesktopCapturerSourceIProps> {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick = (ev) => {
|
||||||
|
this.props.onSelect(this.props.source);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<AccessibleButton
|
||||||
|
className="mx_desktopCapturerSourcePicker_stream_button"
|
||||||
|
title={this.props.source.name}
|
||||||
|
onClick={this.onClick} >
|
||||||
|
<img
|
||||||
|
className="mx_desktopCapturerSourcePicker_stream_thumbnail"
|
||||||
|
src={this.props.source.thumbnailURL}
|
||||||
|
/>
|
||||||
|
<span className="mx_desktopCapturerSourcePicker_stream_name">{this.props.source.name}</span>
|
||||||
|
</AccessibleButton>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DesktopCapturerSourcePickerIState {
|
||||||
|
selectedTab: Tabs;
|
||||||
|
sources: Array<DesktopCapturerSource>;
|
||||||
|
}
|
||||||
|
export interface DesktopCapturerSourcePickerIProps {
|
||||||
|
onFinished(source: DesktopCapturerSource): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class DesktopCapturerSourcePicker extends React.Component<
|
||||||
|
DesktopCapturerSourcePickerIProps,
|
||||||
|
DesktopCapturerSourcePickerIState
|
||||||
|
> {
|
||||||
|
interval;
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
selectedTab: Tabs.Screens,
|
||||||
|
sources: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
// We update the sources every 500ms to get newer thumbnails
|
||||||
|
this.interval = setInterval(async () => {
|
||||||
|
this.setState({
|
||||||
|
sources: await getDesktopCapturerSources(),
|
||||||
|
});
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
clearInterval(this.interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelect = (source) => {
|
||||||
|
this.props.onFinished(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
onScreensClick = (ev) => {
|
||||||
|
this.setState({selectedTab: Tabs.Screens});
|
||||||
|
}
|
||||||
|
|
||||||
|
onWindowsClick = (ev) => {
|
||||||
|
this.setState({selectedTab: Tabs.Windows});
|
||||||
|
}
|
||||||
|
|
||||||
|
onCloseClick = (ev) => {
|
||||||
|
this.props.onFinished(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let sources;
|
||||||
|
if (this.state.selectedTab === Tabs.Screens) {
|
||||||
|
sources = this.state.sources
|
||||||
|
.filter((source) => {
|
||||||
|
return source.id.startsWith("screen");
|
||||||
|
})
|
||||||
|
.map((source) => {
|
||||||
|
return <ExistingSource source={source} onSelect={this.onSelect} key={source.id} />;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
sources = this.state.sources
|
||||||
|
.filter((source) => {
|
||||||
|
return source.id.startsWith("window");
|
||||||
|
})
|
||||||
|
.map((source) => {
|
||||||
|
return <ExistingSource source={source} onSelect={this.onSelect} key={source.id} />;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const buttonStyle = "mx_desktopCapturerSourcePicker_tabLabel";
|
||||||
|
const screensButtonStyle = buttonStyle + ((this.state.selectedTab === Tabs.Screens) ? "_selected" : "");
|
||||||
|
const windowsButtonStyle = buttonStyle + ((this.state.selectedTab === Tabs.Windows) ? "_selected" : "");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseDialog
|
||||||
|
className="mx_desktopCapturerSourcePicker"
|
||||||
|
onFinished={this.onCloseClick}
|
||||||
|
title={_t("Share your screen")}
|
||||||
|
>
|
||||||
|
<div className="mx_desktopCapturerSourcePicker_tabLabels">
|
||||||
|
<AccessibleButton
|
||||||
|
className={screensButtonStyle}
|
||||||
|
onClick={this.onScreensClick}
|
||||||
|
>
|
||||||
|
{_t("Screens")}
|
||||||
|
</AccessibleButton>
|
||||||
|
<AccessibleButton
|
||||||
|
className={windowsButtonStyle}
|
||||||
|
onClick={this.onWindowsClick}
|
||||||
|
>
|
||||||
|
{_t("Windows")}
|
||||||
|
</AccessibleButton>
|
||||||
|
</div>
|
||||||
|
<div className="mx_desktopCapturerSourcePicker_panel">
|
||||||
|
{ sources }
|
||||||
|
</div>
|
||||||
|
</BaseDialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1852,6 +1852,9 @@
|
||||||
"Use the <a>Desktop app</a> to search encrypted messages": "Use the <a>Desktop app</a> to search encrypted messages",
|
"Use the <a>Desktop app</a> to search encrypted messages": "Use the <a>Desktop app</a> to search encrypted messages",
|
||||||
"This version of %(brand)s does not support viewing some encrypted files": "This version of %(brand)s does not support viewing some encrypted files",
|
"This version of %(brand)s does not support viewing some encrypted files": "This version of %(brand)s does not support viewing some encrypted files",
|
||||||
"This version of %(brand)s does not support searching encrypted messages": "This version of %(brand)s does not support searching encrypted messages",
|
"This version of %(brand)s does not support searching encrypted messages": "This version of %(brand)s does not support searching encrypted messages",
|
||||||
|
"Share your screen": "Share your screen",
|
||||||
|
"Screens": "Screens",
|
||||||
|
"Windows": "Windows",
|
||||||
"Join": "Join",
|
"Join": "Join",
|
||||||
"No results": "No results",
|
"No results": "No results",
|
||||||
"Please <newIssueLink>create a new issue</newIssueLink> on GitHub so that we can investigate this bug.": "Please <newIssueLink>create a new issue</newIssueLink> on GitHub so that we can investigate this bug.",
|
"Please <newIssueLink>create a new issue</newIssueLink> on GitHub so that we can investigate this bug.": "Please <newIssueLink>create a new issue</newIssueLink> on GitHub so that we can investigate this bug.",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue