diff options
author | Kristóf Marussy <kristof@marussy.com> | 2021-05-28 19:51:37 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-28 19:51:37 +0200 |
commit | 97408875613c39ea0acc33af41c5caf6a82e7ba1 (patch) | |
tree | 48e9411543fbde7fad0af62b402fadc32bbd4e92 /src/webview/screenshare.js | |
parent | Read CSS assets in recipes as utf8 (#1459) (diff) | |
download | ferdium-app-97408875613c39ea0acc33af41c5caf6a82e7ba1.tar.gz ferdium-app-97408875613c39ea0acc33af41c5caf6a82e7ba1.tar.zst ferdium-app-97408875613c39ea0acc33af41c5caf6a82e7ba1.zip |
Screen share refactoring (#1460)
* Extract screenshare into a separate file
Extracted from 240c3a72363e7388779c9ed3c6467ec63bb64d94 according to
https://github.com/getferdi/ferdi/pull/1456#discussion_r641194876
* Cosmetic screenshare changes and cancellation
* Makes the screen/window selector more readable
* Adds a Cancel button to close the selector
Diffstat (limited to 'src/webview/screenshare.js')
-rw-r--r-- | src/webview/screenshare.js | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/src/webview/screenshare.js b/src/webview/screenshare.js new file mode 100644 index 000000000..2715f2e3e --- /dev/null +++ b/src/webview/screenshare.js | |||
@@ -0,0 +1,139 @@ | |||
1 | import { desktopCapturer } from 'electron'; | ||
2 | |||
3 | const CANCEL_ID = 'desktop-capturer-selection__cancel'; | ||
4 | |||
5 | export const screenShareCss = ` | ||
6 | .desktop-capturer-selection { | ||
7 | position: fixed; | ||
8 | top: 0; | ||
9 | left: 0; | ||
10 | width: 100%; | ||
11 | height: 100vh; | ||
12 | background: rgba(30,30,30,.75); | ||
13 | color: #fff; | ||
14 | z-index: 10000000; | ||
15 | display: flex; | ||
16 | align-items: center; | ||
17 | justify-content: center; | ||
18 | } | ||
19 | .desktop-capturer-selection__scroller { | ||
20 | width: 100%; | ||
21 | max-height: 100vh; | ||
22 | overflow-y: auto; | ||
23 | } | ||
24 | .desktop-capturer-selection__list { | ||
25 | max-width: calc(100% - 100px); | ||
26 | margin: 50px; | ||
27 | padding: 0; | ||
28 | display: flex; | ||
29 | flex-wrap: wrap; | ||
30 | list-style: none; | ||
31 | overflow: hidden; | ||
32 | justify-content: center; | ||
33 | } | ||
34 | .desktop-capturer-selection__item { | ||
35 | display: flex; | ||
36 | margin: 4px; | ||
37 | } | ||
38 | .desktop-capturer-selection__btn { | ||
39 | display: flex; | ||
40 | flex-direction: column; | ||
41 | align-items: stretch; | ||
42 | width: 145px; | ||
43 | margin: 0; | ||
44 | border: 0; | ||
45 | border-radius: 3px; | ||
46 | padding: 4px; | ||
47 | background: #252626; | ||
48 | text-align: left; | ||
49 | transition: background-color .15s, box-shadow .15s, color .15s; | ||
50 | color: #dedede; | ||
51 | } | ||
52 | .desktop-capturer-selection__btn:hover, | ||
53 | .desktop-capturer-selection__btn:focus { | ||
54 | background: rgba(98,100,167,.8); | ||
55 | box-shadow: 0 0 4px rgba(0,0,0,0.45), 0 0 2px rgba(0,0,0,0.25); | ||
56 | color: #fff; | ||
57 | } | ||
58 | .desktop-capturer-selection__thumbnail { | ||
59 | width: 100%; | ||
60 | height: 81px; | ||
61 | object-fit: cover; | ||
62 | } | ||
63 | .desktop-capturer-selection__name { | ||
64 | margin: 6px 0; | ||
65 | white-space: nowrap; | ||
66 | text-overflow: ellipsis; | ||
67 | text-align: center; | ||
68 | overflow: hidden; | ||
69 | } | ||
70 | .desktop-capturer-selection__name--cancel { | ||
71 | margin: auto 0; | ||
72 | } | ||
73 | `; | ||
74 | |||
75 | // Patch getDisplayMedia for screen sharing | ||
76 | window.navigator.mediaDevices.getDisplayMedia = () => new Promise(async (resolve, reject) => { | ||
77 | try { | ||
78 | const sources = await desktopCapturer.getSources({ types: ['screen', 'window'] }); | ||
79 | |||
80 | const selectionElem = document.createElement('div'); | ||
81 | selectionElem.classList = 'desktop-capturer-selection'; | ||
82 | selectionElem.innerHTML = ` | ||
83 | <div class="desktop-capturer-selection__scroller"> | ||
84 | <ul class="desktop-capturer-selection__list"> | ||
85 | ${sources.map(({ | ||
86 | id, name, thumbnail, | ||
87 | }) => ` | ||
88 | <li class="desktop-capturer-selection__item"> | ||
89 | <button class="desktop-capturer-selection__btn" data-id="${id}" title="${name}"> | ||
90 | <img class="desktop-capturer-selection__thumbnail" src="${thumbnail.toDataURL()}" /> | ||
91 | <span class="desktop-capturer-selection__name">${name}</span> | ||
92 | </button> | ||
93 | </li> | ||
94 | `).join('')} | ||
95 | <li class="desktop-capturer-selection__item"> | ||
96 | <button class="desktop-capturer-selection__btn" data-id="${CANCEL_ID}" title="Cancel"> | ||
97 | <span class="desktop-capturer-selection__name desktop-capturer-selection__name--cancel">Cancel</span> | ||
98 | </button> | ||
99 | </li> | ||
100 | </ul> | ||
101 | </div> | ||
102 | `; | ||
103 | document.body.appendChild(selectionElem); | ||
104 | |||
105 | document.querySelectorAll('.desktop-capturer-selection__btn') | ||
106 | .forEach((button) => { | ||
107 | button.addEventListener('click', async () => { | ||
108 | try { | ||
109 | const id = button.getAttribute('data-id'); | ||
110 | if (id === CANCEL_ID) { | ||
111 | reject(new Error('Cancelled by user')); | ||
112 | } else { | ||
113 | const mediaSource = sources.find(source => source.id === id); | ||
114 | if (!mediaSource) { | ||
115 | throw new Error(`Source with id ${id} does not exist`); | ||
116 | } | ||
117 | |||
118 | const stream = await window.navigator.mediaDevices.getUserMedia({ | ||
119 | audio: false, | ||
120 | video: { | ||
121 | mandatory: { | ||
122 | chromeMediaSource: 'desktop', | ||
123 | chromeMediaSourceId: mediaSource.id, | ||
124 | }, | ||
125 | }, | ||
126 | }); | ||
127 | resolve(stream); | ||
128 | } | ||
129 | } catch (err) { | ||
130 | reject(err); | ||
131 | } finally { | ||
132 | selectionElem.remove(); | ||
133 | } | ||
134 | }); | ||
135 | }); | ||
136 | } catch (err) { | ||
137 | reject(err); | ||
138 | } | ||
139 | }); | ||