Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit cf5ec77

Browse files
authored
Merge pull request #5525 from SimonBrandner/fix-screen-sharing
Fix desktop Matrix screen sharing and add a screen/window picker
2 parents 91f2e4b + 022781e commit cf5ec77

File tree

5 files changed

+252
-1
lines changed

5 files changed

+252
-1
lines changed

res/css/_components.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
@import "./views/elements/_AddressTile.scss";
107107
@import "./views/elements/_DesktopBuildsNotice.scss";
108108
@import "./views/elements/_DirectorySearchBox.scss";
109+
@import "./views/elements/_DesktopCapturerSourcePicker.scss";
109110
@import "./views/elements/_Dropdown.scss";
110111
@import "./views/elements/_EditableItemList.scss";
111112
@import "./views/elements/_ErrorBoundary.scss";
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
Copyright 2021 Šimon Brandner <[email protected]>
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
.mx_desktopCapturerSourcePicker {
18+
overflow: hidden;
19+
}
20+
21+
.mx_desktopCapturerSourcePicker_tabLabels {
22+
display: flex;
23+
padding: 0 0 8px 0;
24+
}
25+
26+
.mx_desktopCapturerSourcePicker_tabLabel,
27+
.mx_desktopCapturerSourcePicker_tabLabel_selected {
28+
width: 100%;
29+
text-align: center;
30+
border-radius: 8px;
31+
padding: 8px 0;
32+
font-size: $font-13px;
33+
}
34+
35+
.mx_desktopCapturerSourcePicker_tabLabel_selected {
36+
background-color: $tab-label-active-bg-color;
37+
color: $tab-label-active-fg-color;
38+
}
39+
40+
.mx_desktopCapturerSourcePicker_panel {
41+
display: flex;
42+
flex-wrap: wrap;
43+
justify-content: center;
44+
align-items: flex-start;
45+
height: 500px;
46+
overflow: overlay;
47+
}
48+
49+
.mx_desktopCapturerSourcePicker_stream_button {
50+
display: flex;
51+
flex-direction: column;
52+
margin: 8px;
53+
border-radius: 4px;
54+
}
55+
56+
.mx_desktopCapturerSourcePicker_stream_button:hover,
57+
.mx_desktopCapturerSourcePicker_stream_button:focus {
58+
background: $roomtile-selected-bg-color;
59+
}
60+
61+
.mx_desktopCapturerSourcePicker_stream_thumbnail {
62+
margin: 4px;
63+
width: 312px;
64+
}
65+
66+
.mx_desktopCapturerSourcePicker_stream_name {
67+
margin: 0 4px;
68+
white-space: nowrap;
69+
text-overflow: ellipsis;
70+
overflow: hidden;
71+
width: 312px;
72+
}

src/CallHandler.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ import CountlyAnalytics from "./CountlyAnalytics";
8282
import {UIFeature} from "./settings/UIFeature";
8383
import { CallError } from "matrix-js-sdk/src/webrtc/call";
8484
import { logger } from 'matrix-js-sdk/src/logger';
85+
import DesktopCapturerSourcePicker from "./components/views/elements/DesktopCapturerSourcePicker"
8586
import { Action } from './dispatcher/actions';
8687
import { roomForVirtualRoom, getOrCreateVirtualRoomForRoom } from './VoipUserMapper';
8788
import { addManagedHybridWidget, isManagedHybridWidgetEnabled } from './widgets/ManagedHybrid';
@@ -572,7 +573,15 @@ export default class CallHandler {
572573
});
573574
return;
574575
}
575-
call.placeScreenSharingCall(remoteElement, localElement);
576+
577+
call.placeScreenSharingCall(
578+
remoteElement,
579+
localElement,
580+
async () : Promise<DesktopCapturerSource> => {
581+
const {finished} = Modal.createDialog(DesktopCapturerSourcePicker);
582+
const [source] = await finished;
583+
return source;
584+
});
576585
} else {
577586
console.error("Unknown conf call type: %s", type);
578587
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
Copyright 2021 Šimon Brandner <[email protected]>
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import React from 'react';
18+
import { _t } from '../../../languageHandler';
19+
import BaseDialog from "..//dialogs/BaseDialog"
20+
import AccessibleButton from './AccessibleButton';
21+
import {getDesktopCapturerSources} from "matrix-js-sdk/src/webrtc/call";
22+
23+
export interface DesktopCapturerSource {
24+
id: string;
25+
name: string;
26+
thumbnailURL;
27+
}
28+
29+
export enum Tabs {
30+
Screens = "screens",
31+
Windows = "windows",
32+
}
33+
34+
export interface DesktopCapturerSourceIProps {
35+
source: DesktopCapturerSource;
36+
onSelect(source: DesktopCapturerSource): void;
37+
}
38+
39+
export class ExistingSource extends React.Component<DesktopCapturerSourceIProps> {
40+
constructor(props) {
41+
super(props);
42+
}
43+
44+
onClick = (ev) => {
45+
this.props.onSelect(this.props.source);
46+
}
47+
48+
render() {
49+
return (
50+
<AccessibleButton
51+
className="mx_desktopCapturerSourcePicker_stream_button"
52+
title={this.props.source.name}
53+
onClick={this.onClick} >
54+
<img
55+
className="mx_desktopCapturerSourcePicker_stream_thumbnail"
56+
src={this.props.source.thumbnailURL}
57+
/>
58+
<span className="mx_desktopCapturerSourcePicker_stream_name">{this.props.source.name}</span>
59+
</AccessibleButton>
60+
);
61+
}
62+
}
63+
64+
export interface DesktopCapturerSourcePickerIState {
65+
selectedTab: Tabs;
66+
sources: Array<DesktopCapturerSource>;
67+
}
68+
export interface DesktopCapturerSourcePickerIProps {
69+
onFinished(source: DesktopCapturerSource): void;
70+
}
71+
72+
export default class DesktopCapturerSourcePicker extends React.Component<
73+
DesktopCapturerSourcePickerIProps,
74+
DesktopCapturerSourcePickerIState
75+
> {
76+
interval;
77+
78+
constructor(props) {
79+
super(props);
80+
81+
this.state = {
82+
selectedTab: Tabs.Screens,
83+
sources: [],
84+
};
85+
}
86+
87+
componentDidMount() {
88+
// We update the sources every 500ms to get newer thumbnails
89+
this.interval = setInterval(async () => {
90+
this.setState({
91+
sources: await getDesktopCapturerSources(),
92+
});
93+
}, 500);
94+
}
95+
96+
componentWillUnmount() {
97+
clearInterval(this.interval);
98+
}
99+
100+
onSelect = (source) => {
101+
this.props.onFinished(source);
102+
}
103+
104+
onScreensClick = (ev) => {
105+
this.setState({selectedTab: Tabs.Screens});
106+
}
107+
108+
onWindowsClick = (ev) => {
109+
this.setState({selectedTab: Tabs.Windows});
110+
}
111+
112+
onCloseClick = (ev) => {
113+
this.props.onFinished(null);
114+
}
115+
116+
render() {
117+
let sources;
118+
if (this.state.selectedTab === Tabs.Screens) {
119+
sources = this.state.sources
120+
.filter((source) => {
121+
return source.id.startsWith("screen");
122+
})
123+
.map((source) => {
124+
return <ExistingSource source={source} onSelect={this.onSelect} key={source.id} />;
125+
});
126+
} else {
127+
sources = this.state.sources
128+
.filter((source) => {
129+
return source.id.startsWith("window");
130+
})
131+
.map((source) => {
132+
return <ExistingSource source={source} onSelect={this.onSelect} key={source.id} />;
133+
});
134+
}
135+
136+
const buttonStyle = "mx_desktopCapturerSourcePicker_tabLabel";
137+
const screensButtonStyle = buttonStyle + ((this.state.selectedTab === Tabs.Screens) ? "_selected" : "");
138+
const windowsButtonStyle = buttonStyle + ((this.state.selectedTab === Tabs.Windows) ? "_selected" : "");
139+
140+
return (
141+
<BaseDialog
142+
className="mx_desktopCapturerSourcePicker"
143+
onFinished={this.onCloseClick}
144+
title={_t("Share your screen")}
145+
>
146+
<div className="mx_desktopCapturerSourcePicker_tabLabels">
147+
<AccessibleButton
148+
className={screensButtonStyle}
149+
onClick={this.onScreensClick}
150+
>
151+
{_t("Screens")}
152+
</AccessibleButton>
153+
<AccessibleButton
154+
className={windowsButtonStyle}
155+
onClick={this.onWindowsClick}
156+
>
157+
{_t("Windows")}
158+
</AccessibleButton>
159+
</div>
160+
<div className="mx_desktopCapturerSourcePicker_panel">
161+
{ sources }
162+
</div>
163+
</BaseDialog>
164+
);
165+
}
166+
}

src/i18n/strings/en_EN.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1852,6 +1852,9 @@
18521852
"Use the <a>Desktop app</a> to search encrypted messages": "Use the <a>Desktop app</a> to search encrypted messages",
18531853
"This version of %(brand)s does not support viewing some encrypted files": "This version of %(brand)s does not support viewing some encrypted files",
18541854
"This version of %(brand)s does not support searching encrypted messages": "This version of %(brand)s does not support searching encrypted messages",
1855+
"Share your screen": "Share your screen",
1856+
"Screens": "Screens",
1857+
"Windows": "Windows",
18551858
"Join": "Join",
18561859
"No results": "No results",
18571860
"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.",

0 commit comments

Comments
 (0)