diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/components/MediaSource.tsx | 147 | ||||
-rw-r--r-- | src/environment.ts | 2 | ||||
-rw-r--r-- | src/i18n/locales/en-US.json | 2 |
3 files changed, 120 insertions, 31 deletions
diff --git a/src/components/MediaSource.tsx b/src/components/MediaSource.tsx index 02e617800..34fbda227 100644 --- a/src/components/MediaSource.tsx +++ b/src/components/MediaSource.tsx | |||
@@ -1,37 +1,65 @@ | |||
1 | import { ipcRenderer } from 'electron'; | 1 | import { ipcRenderer } from 'electron'; |
2 | import { useEffect, useState } from 'react'; | 2 | import { type MouseEventHandler, useEffect, useState } from 'react'; |
3 | import { | ||
4 | type WrappedComponentProps, | ||
5 | defineMessages, | ||
6 | injectIntl, | ||
7 | } from 'react-intl'; | ||
3 | import { SCREENSHARE_CANCELLED_BY_USER } from '../config'; | 8 | import { SCREENSHARE_CANCELLED_BY_USER } from '../config'; |
9 | import { isWayland } from '../environment'; | ||
4 | import type Service from '../models/Service'; | 10 | import type Service from '../models/Service'; |
11 | import FullscreenLoader from './ui/FullscreenLoader'; | ||
5 | 12 | ||
6 | export interface IProps { | 13 | interface IProps extends WrappedComponentProps { |
7 | service: Service; | 14 | service: Service; |
8 | } | 15 | } |
9 | 16 | ||
10 | export default function MediaSource(props: IProps) { | 17 | const messages = defineMessages({ |
11 | const { service } = props; | 18 | loading: { |
19 | id: 'service.screenshare.loading', | ||
20 | defaultMessage: 'Loading screens and windows', | ||
21 | }, | ||
22 | cancel: { | ||
23 | id: 'service.screenshare.cancel', | ||
24 | defaultMessage: 'Cancel', | ||
25 | }, | ||
26 | }); | ||
27 | |||
28 | interface ICancelButtonProps extends WrappedComponentProps { | ||
29 | handleOnClick: MouseEventHandler<HTMLButtonElement> | undefined; | ||
30 | } | ||
31 | |||
32 | const CancelButton = injectIntl( | ||
33 | ({ handleOnClick, intl }: ICancelButtonProps) => ( | ||
34 | <li className="desktop-capturer-selection__item"> | ||
35 | <button | ||
36 | type="button" // Add explicit type attribute | ||
37 | className="desktop-capturer-selection__btn" | ||
38 | data-id={SCREENSHARE_CANCELLED_BY_USER} | ||
39 | title="Cancel" | ||
40 | onClick={handleOnClick} | ||
41 | > | ||
42 | <span className="desktop-capturer-selection__name desktop-capturer-selection__name--cancel"> | ||
43 | {intl.formatMessage(messages.cancel)} | ||
44 | </span> | ||
45 | </button> | ||
46 | </li> | ||
47 | ), | ||
48 | ); | ||
49 | |||
50 | function MediaSource(props: IProps) { | ||
51 | const { service, intl } = props; | ||
12 | const [sources, setSources] = useState<any>([]); | 52 | const [sources, setSources] = useState<any>([]); |
13 | const [show, setShow] = useState<boolean>(false); | 53 | const [show, setShow] = useState<boolean>(false); |
14 | const [trackerId, setTrackerId] = useState<string | null>(null); | 54 | const [trackerId, setTrackerId] = useState<string | null>(null); |
55 | const [loadingSources, setLoadingSources] = useState<boolean>(false); | ||
15 | 56 | ||
16 | ipcRenderer.on(`select-capture-device:${service.id}`, (_event, data) => { | 57 | ipcRenderer.on(`select-capture-device:${service.id}`, (_event, data) => { |
58 | if (loadingSources) return; | ||
17 | setShow(true); | 59 | setShow(true); |
18 | setTrackerId(data.trackerId); | 60 | setTrackerId(data.trackerId); |
19 | }); | 61 | }); |
20 | 62 | ||
21 | useEffect(() => { | ||
22 | if (show) { | ||
23 | ipcRenderer | ||
24 | .invoke('get-desktop-capturer-sources') | ||
25 | .then(sources => setSources(sources)); | ||
26 | } else { | ||
27 | setSources([]); | ||
28 | } | ||
29 | }, [show]); | ||
30 | |||
31 | if (sources.length === 0 || !show) { | ||
32 | return null; | ||
33 | } | ||
34 | |||
35 | const handleOnClick = (e: any) => { | 63 | const handleOnClick = (e: any) => { |
36 | const { id } = e.currentTarget.dataset; | 64 | const { id } = e.currentTarget.dataset; |
37 | window['ferdium'].actions.service.sendIPCMessage({ | 65 | window['ferdium'].actions.service.sendIPCMessage({ |
@@ -43,9 +71,76 @@ export default function MediaSource(props: IProps) { | |||
43 | }); | 71 | }); |
44 | 72 | ||
45 | setShow(false); | 73 | setShow(false); |
74 | setSources([]); | ||
46 | setTrackerId(null); | 75 | setTrackerId(null); |
47 | }; | 76 | }; |
48 | 77 | ||
78 | // biome-ignore lint/correctness/useExhaustiveDependencies: This effect should only run when `show` changes | ||
79 | useEffect(() => { | ||
80 | if (show) { | ||
81 | setLoadingSources(true); | ||
82 | ipcRenderer | ||
83 | .invoke('get-desktop-capturer-sources') | ||
84 | .then(sources => { | ||
85 | if (isWayland) { | ||
86 | // On Linux, we do not need to prompt the user again for the source | ||
87 | handleOnClick({ | ||
88 | currentTarget: { dataset: { id: sources[0].id } }, | ||
89 | }); | ||
90 | return; | ||
91 | } | ||
92 | |||
93 | setSources(sources); | ||
94 | setLoadingSources(false); | ||
95 | }) | ||
96 | // silence the error | ||
97 | .catch(() => { | ||
98 | setShow(false); | ||
99 | setSources([]); | ||
100 | setLoadingSources(false); | ||
101 | }); | ||
102 | } else { | ||
103 | setSources([]); | ||
104 | setLoadingSources(false); | ||
105 | } | ||
106 | }, [show]); | ||
107 | |||
108 | if (!show) { | ||
109 | return null; | ||
110 | } | ||
111 | |||
112 | if (loadingSources) { | ||
113 | return ( | ||
114 | <div className="desktop-capturer-selection"> | ||
115 | <ul | ||
116 | className="desktop-capturer-selection__list" | ||
117 | style={{ | ||
118 | display: 'flex', | ||
119 | flexDirection: 'column', | ||
120 | alignItems: 'center', | ||
121 | }} | ||
122 | > | ||
123 | <FullscreenLoader title={intl.formatMessage(messages.loading)}> | ||
124 | <div style={{ display: 'flex', justifyContent: 'center' }}> | ||
125 | <CancelButton handleOnClick={handleOnClick} /> | ||
126 | </div> | ||
127 | </FullscreenLoader> | ||
128 | </ul> | ||
129 | </div> | ||
130 | ); | ||
131 | } | ||
132 | |||
133 | if (sources.length === 0) { | ||
134 | return ( | ||
135 | <div className="desktop-capturer-selection"> | ||
136 | <ul className="desktop-capturer-selection__list"> | ||
137 | <li>No available sources.</li> | ||
138 | <CancelButton handleOnClick={handleOnClick} /> | ||
139 | </ul> | ||
140 | </div> | ||
141 | ); | ||
142 | } | ||
143 | |||
49 | return ( | 144 | return ( |
50 | <div className="desktop-capturer-selection"> | 145 | <div className="desktop-capturer-selection"> |
51 | <ul className="desktop-capturer-selection__list"> | 146 | <ul className="desktop-capturer-selection__list"> |
@@ -67,20 +162,10 @@ export default function MediaSource(props: IProps) { | |||
67 | </button> | 162 | </button> |
68 | </li> | 163 | </li> |
69 | ))} | 164 | ))} |
70 | <li className="desktop-capturer-selection__item"> | ||
71 | <button | ||
72 | type="button" // Add explicit type attribute | ||
73 | className="desktop-capturer-selection__btn" | ||
74 | data-id={SCREENSHARE_CANCELLED_BY_USER} | ||
75 | title="Cancel" | ||
76 | onClick={handleOnClick} | ||
77 | > | ||
78 | <span className="desktop-capturer-selection__name desktop-capturer-selection__name--cancel"> | ||
79 | Cancel | ||
80 | </span> | ||
81 | </button> | ||
82 | </li> | ||
83 | </ul> | 165 | </ul> |
166 | <CancelButton handleOnClick={handleOnClick} /> | ||
84 | </div> | 167 | </div> |
85 | ); | 168 | ); |
86 | } | 169 | } |
170 | |||
171 | export default injectIntl(MediaSource); | ||
diff --git a/src/environment.ts b/src/environment.ts index 87e2f4f66..4a0399876 100644 --- a/src/environment.ts +++ b/src/environment.ts | |||
@@ -7,6 +7,8 @@ export const isWindows = process.platform === 'win32'; | |||
7 | export const isLinux = process.platform === 'linux'; | 7 | export const isLinux = process.platform === 'linux'; |
8 | export const isWinPortable = process.env.PORTABLE_EXECUTABLE_FILE != null; | 8 | export const isWinPortable = process.env.PORTABLE_EXECUTABLE_FILE != null; |
9 | 9 | ||
10 | export const isWayland = isLinux && process.env.XDG_SESSION_TYPE === 'wayland'; | ||
11 | |||
10 | export const electronVersion: string = process.versions.electron ?? ''; | 12 | export const electronVersion: string = process.versions.electron ?? ''; |
11 | export const chromeVersion: string = process.versions.chrome ?? ''; | 13 | export const chromeVersion: string = process.versions.chrome ?? ''; |
12 | export const nodeVersion: string = process.versions.node; | 14 | export const nodeVersion: string = process.versions.node; |
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index 1835854e1..3947b2e5f 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json | |||
@@ -164,6 +164,8 @@ | |||
164 | "service.errorHandler.headline": "Oh no!", | 164 | "service.errorHandler.headline": "Oh no!", |
165 | "service.errorHandler.message": "Error", | 165 | "service.errorHandler.message": "Error", |
166 | "service.errorHandler.text": "{name} has failed to load.", | 166 | "service.errorHandler.text": "{name} has failed to load.", |
167 | "service.screenshare.cancel": "Cancel", | ||
168 | "service.screenshare.loading": "Loading screens and windows", | ||
167 | "service.webviewLoader.loading": "Loading {service}", | 169 | "service.webviewLoader.loading": "Loading {service}", |
168 | "services.getStarted": "Get started", | 170 | "services.getStarted": "Get started", |
169 | "services.login": "Please login to use Ferdium.", | 171 | "services.login": "Please login to use Ferdium.", |