diff --git a/src/app/hooks/useScreenshareNotices.test.ts b/src/app/hooks/useScreenshareNotices.test.ts new file mode 100644 index 000000000..e5bb2aeda --- /dev/null +++ b/src/app/hooks/useScreenshareNotices.test.ts @@ -0,0 +1,10 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { shouldShowScreenshareNotice } from './useScreenshareNotices'; + +test('shouldShowScreenshareNotice: no "window was closed" when an admin turned sharing off (#297)', () => { + assert.equal(shouldShowScreenshareNotice('ended', false), false); + assert.equal(shouldShowScreenshareNotice('ended', true), true); + assert.equal(shouldShowScreenshareNotice('no-frames', false), true); + assert.equal(shouldShowScreenshareNotice('alone', true), true); +}); diff --git a/src/app/hooks/useScreenshareNotices.ts b/src/app/hooks/useScreenshareNotices.ts index 40b52338b..4a8398a74 100644 --- a/src/app/hooks/useScreenshareNotices.ts +++ b/src/app/hooks/useScreenshareNotices.ts @@ -1,8 +1,9 @@ -import { useEffect } from 'react'; +import { useEffect, useRef } from 'react'; import { useSetAtom } from 'jotai'; import { Icons } from 'folds'; import { CallEmbed } from '../plugins/call'; import { toastQueueAtom } from '../state/toast'; +import { useRoomCallPolicy } from './useRoomCallPolicy'; export type ScreenshareNoticeKind = 'ended' | 'no-frames' | 'alone'; @@ -19,18 +20,32 @@ export const describeScreenshareNotice = (kind: ScreenshareNoticeKind): string | } }; +/** Pure: a policy-forced stop is explained by its own toast (#297). */ +export const shouldShowScreenshareNotice = ( + kind: ScreenshareNoticeKind, + allowScreenshare: boolean, +): boolean => !(kind === 'ended' && !allowScreenshare); + /** * [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); + // [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; useEffect( () => embed.listenAction<{ data?: { kind?: ScreenshareNoticeKind } }>( 'io.lotus.screenshare_notice', (evt) => { - const body = describeScreenshareNotice(evt.detail?.data?.kind as ScreenshareNoticeKind); + const kind = evt.detail?.data?.kind as ScreenshareNoticeKind; + if (!shouldShowScreenshareNotice(kind, allowRef.current)) return; + const body = describeScreenshareNotice(kind); if (!body) return; setToast({ id: `screenshare-${evt.detail?.data?.kind}-${Date.now()}`,