2026-10-17 20:55:05 -04:00
|
|
|
import { useEffect, useRef } from 'react';
|
2026-09-20 15:51:59 -04:00
|
|
|
import { useSetAtom } from 'jotai';
|
|
|
|
|
import { Icons } from 'folds';
|
|
|
|
|
import { CallEmbed } from '../plugins/call';
|
|
|
|
|
import { toastQueueAtom } from '../state/toast';
|
2026-10-17 20:55:05 -04:00
|
|
|
import { useRoomCallPolicy } from './useRoomCallPolicy';
|
2026-09-20 15:51:59 -04:00
|
|
|
|
|
|
|
|
export type ScreenshareNoticeKind = 'ended' | 'no-frames' | 'alone';
|
|
|
|
|
|
|
|
|
|
export const describeScreenshareNotice = (kind: ScreenshareNoticeKind): string | undefined => {
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case 'ended':
|
|
|
|
|
return 'Screen sharing stopped — the shared window was closed.';
|
|
|
|
|
case 'no-frames':
|
|
|
|
|
return 'Your screen share is showing nothing — the shared window may be minimised or hidden.';
|
|
|
|
|
case 'alone':
|
|
|
|
|
return "Still sharing? You've been sharing your screen for 30 minutes with nobody else in the call.";
|
|
|
|
|
default:
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-10-17 20:55:05 -04:00
|
|
|
/** Pure: a policy-forced stop is explained by its own toast (#297). */
|
|
|
|
|
export const shouldShowScreenshareNotice = (
|
|
|
|
|
kind: ScreenshareNoticeKind,
|
|
|
|
|
allowScreenshare: boolean,
|
|
|
|
|
): boolean => !(kind === 'ended' && !allowScreenshare);
|
|
|
|
|
|
2026-09-20 15:51:59 -04:00
|
|
|
/**
|
|
|
|
|
* [Gitea EC#39] Toast the fork's io.lotus.screenshare_notice: shared window
|
|
|
|
|
* closed, no frames for a while, or a long share with nobody watching.
|
|
|
|
|
*/
|
|
|
|
|
export function useScreenshareNotices(embed: CallEmbed): void {
|
|
|
|
|
const setToast = useSetAtom(toastQueueAtom);
|
2026-10-17 20:55:05 -04:00
|
|
|
// [Gitea #297] When a room admin turns screen sharing off, the share ends
|
|
|
|
|
// and the fork reports `ended` ("the shared window was closed"), which is
|
|
|
|
|
// wrong; useCallPolicyRevokedToast already says what happened.
|
|
|
|
|
const { allowScreenshare } = useRoomCallPolicy(embed.room);
|
|
|
|
|
const allowRef = useRef(allowScreenshare);
|
|
|
|
|
allowRef.current = allowScreenshare;
|
2026-09-20 15:51:59 -04:00
|
|
|
useEffect(
|
|
|
|
|
() =>
|
|
|
|
|
embed.listenAction<{ data?: { kind?: ScreenshareNoticeKind } }>(
|
|
|
|
|
'io.lotus.screenshare_notice',
|
|
|
|
|
(evt) => {
|
2026-10-17 20:55:05 -04:00
|
|
|
const kind = evt.detail?.data?.kind as ScreenshareNoticeKind;
|
|
|
|
|
if (!shouldShowScreenshareNotice(kind, allowRef.current)) return;
|
|
|
|
|
const body = describeScreenshareNotice(kind);
|
2026-09-20 15:51:59 -04:00
|
|
|
if (!body) return;
|
|
|
|
|
setToast({
|
|
|
|
|
id: `screenshare-${evt.detail?.data?.kind}-${Date.now()}`,
|
|
|
|
|
iconSrc: Icons.ScreenShare,
|
|
|
|
|
displayName: 'Screen share',
|
|
|
|
|
body,
|
|
|
|
|
roomName: embed.room.name ?? 'Voice call',
|
|
|
|
|
roomId: embed.roomId,
|
|
|
|
|
sticky: evt.detail?.data?.kind === 'alone',
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
[embed, setToast],
|
|
|
|
|
);
|
|
|
|
|
}
|