Skip to content

Commit

Permalink
Fix screenshare on wayland and style (#1739)
Browse files Browse the repository at this point in the history
* fix: screenshare prompts twice

* Add loading screen
  • Loading branch information
SpecialAro authored May 4, 2024
1 parent b5943d9 commit 87721c9
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 31 deletions.
147 changes: 116 additions & 31 deletions src/components/MediaSource.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,65 @@
import { ipcRenderer } from 'electron';
import { useEffect, useState } from 'react';
import { type MouseEventHandler, useEffect, useState } from 'react';
import {
type WrappedComponentProps,
defineMessages,
injectIntl,
} from 'react-intl';
import { SCREENSHARE_CANCELLED_BY_USER } from '../config';
import { isWayland } from '../environment';
import type Service from '../models/Service';
import FullscreenLoader from './ui/FullscreenLoader';

export interface IProps {
interface IProps extends WrappedComponentProps {
service: Service;
}

export default function MediaSource(props: IProps) {
const { service } = props;
const messages = defineMessages({
loading: {
id: 'service.screenshare.loading',
defaultMessage: 'Loading screens and windows',
},
cancel: {
id: 'service.screenshare.cancel',
defaultMessage: 'Cancel',
},
});

interface ICancelButtonProps extends WrappedComponentProps {
handleOnClick: MouseEventHandler<HTMLButtonElement> | undefined;
}

const CancelButton = injectIntl(
({ handleOnClick, intl }: ICancelButtonProps) => (
<li className="desktop-capturer-selection__item">
<button
type="button" // Add explicit type attribute
className="desktop-capturer-selection__btn"
data-id={SCREENSHARE_CANCELLED_BY_USER}
title="Cancel"
onClick={handleOnClick}
>
<span className="desktop-capturer-selection__name desktop-capturer-selection__name--cancel">
{intl.formatMessage(messages.cancel)}
</span>
</button>
</li>
),
);

function MediaSource(props: IProps) {
const { service, intl } = props;
const [sources, setSources] = useState<any>([]);
const [show, setShow] = useState<boolean>(false);
const [trackerId, setTrackerId] = useState<string | null>(null);
const [loadingSources, setLoadingSources] = useState<boolean>(false);

ipcRenderer.on(`select-capture-device:${service.id}`, (_event, data) => {
if (loadingSources) return;
setShow(true);
setTrackerId(data.trackerId);
});

useEffect(() => {
if (show) {
ipcRenderer
.invoke('get-desktop-capturer-sources')
.then(sources => setSources(sources));
} else {
setSources([]);
}
}, [show]);

if (sources.length === 0 || !show) {
return null;
}

const handleOnClick = (e: any) => {
const { id } = e.currentTarget.dataset;
window['ferdium'].actions.service.sendIPCMessage({
Expand All @@ -43,9 +71,76 @@ export default function MediaSource(props: IProps) {
});

setShow(false);
setSources([]);
setTrackerId(null);
};

// biome-ignore lint/correctness/useExhaustiveDependencies: This effect should only run when `show` changes
useEffect(() => {
if (show) {
setLoadingSources(true);
ipcRenderer
.invoke('get-desktop-capturer-sources')
.then(sources => {
if (isWayland) {
// On Linux, we do not need to prompt the user again for the source
handleOnClick({
currentTarget: { dataset: { id: sources[0].id } },
});
return;
}

setSources(sources);
setLoadingSources(false);
})
// silence the error
.catch(() => {
setShow(false);
setSources([]);
setLoadingSources(false);
});
} else {
setSources([]);
setLoadingSources(false);
}
}, [show]);

if (!show) {
return null;
}

if (loadingSources) {
return (
<div className="desktop-capturer-selection">
<ul
className="desktop-capturer-selection__list"
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<FullscreenLoader title={intl.formatMessage(messages.loading)}>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<CancelButton handleOnClick={handleOnClick} />
</div>
</FullscreenLoader>
</ul>
</div>
);
}

if (sources.length === 0) {
return (
<div className="desktop-capturer-selection">
<ul className="desktop-capturer-selection__list">
<li>No available sources.</li>
<CancelButton handleOnClick={handleOnClick} />
</ul>
</div>
);
}

return (
<div className="desktop-capturer-selection">
<ul className="desktop-capturer-selection__list">
Expand All @@ -67,20 +162,10 @@ export default function MediaSource(props: IProps) {
</button>
</li>
))}
<li className="desktop-capturer-selection__item">
<button
type="button" // Add explicit type attribute
className="desktop-capturer-selection__btn"
data-id={SCREENSHARE_CANCELLED_BY_USER}
title="Cancel"
onClick={handleOnClick}
>
<span className="desktop-capturer-selection__name desktop-capturer-selection__name--cancel">
Cancel
</span>
</button>
</li>
</ul>
<CancelButton handleOnClick={handleOnClick} />
</div>
);
}

export default injectIntl(MediaSource);
2 changes: 2 additions & 0 deletions src/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export const isWindows = process.platform === 'win32';
export const isLinux = process.platform === 'linux';
export const isWinPortable = process.env.PORTABLE_EXECUTABLE_FILE != null;

export const isWayland = isLinux && process.env.XDG_SESSION_TYPE === 'wayland';

export const electronVersion: string = process.versions.electron ?? '';
export const chromeVersion: string = process.versions.chrome ?? '';
export const nodeVersion: string = process.versions.node;
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@
"service.errorHandler.headline": "Oh no!",
"service.errorHandler.message": "Error",
"service.errorHandler.text": "{name} has failed to load.",
"service.screenshare.cancel": "Cancel",
"service.screenshare.loading": "Loading screens and windows",
"service.webviewLoader.loading": "Loading {service}",
"services.getStarted": "Get started",
"services.login": "Please login to use Ferdium.",
Expand Down

0 comments on commit 87721c9

Please sign in to comment.