From 2bed70d3354409b2cd457fb524f2af0773bc93cb Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Sun, 20 Sep 2026 15:51:59 -0400 Subject: [PATCH] feat(calls): toast the fork's screenshare reminders (EC #39) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit io.lotus.screenshare_notice → "Screen sharing stopped — the shared window was closed.", "Your screen share is showing nothing — the shared window may be minimised or hidden.", and a sticky "Still sharing?" after 30 min with nobody else in the call (fork ≥ 0.25.0-lotus.12). Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA --- src/app/components/CallEmbedProvider.tsx | 2 + src/app/hooks/useScreenshareNotices.ts | 48 ++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 src/app/hooks/useScreenshareNotices.ts diff --git a/src/app/components/CallEmbedProvider.tsx b/src/app/components/CallEmbedProvider.tsx index 12159eb07..5c8eb085d 100644 --- a/src/app/components/CallEmbedProvider.tsx +++ b/src/app/components/CallEmbedProvider.tsx @@ -50,6 +50,7 @@ import { useCallPolicyRevokedToast } from '../hooks/useCallPolicyRevokedToast'; import { useCallEndedToast } from '../hooks/useCallEndedToast'; import { useCallRejoin } from '../hooks/useCallRejoin'; import { usePttHaptics } from '../hooks/usePttHaptics'; +import { useScreenshareNotices } from '../hooks/useScreenshareNotices'; import { useCallAnnouncements } from '../hooks/useCallAnnouncements'; import { useMutedTalkWarning } from '../hooks/useMutedTalkWarning'; import { callAnnouncementAtom } from '../state/callAnnouncement'; @@ -748,6 +749,7 @@ function CallUtils({ embed, joined }: { embed: CallEmbed; joined: boolean }) { useCallJoinLeaveSounds(embed); useCallPolicyRevokedToast(embed, joined); useCallEndedToast(embed); + useScreenshareNotices(embed); usePttHaptics(); useCallAnnouncements(embed, joined); useMutedTalkWarning(embed, joined); diff --git a/src/app/hooks/useScreenshareNotices.ts b/src/app/hooks/useScreenshareNotices.ts new file mode 100644 index 000000000..40b52338b --- /dev/null +++ b/src/app/hooks/useScreenshareNotices.ts @@ -0,0 +1,48 @@ +import { useEffect } from 'react'; +import { useSetAtom } from 'jotai'; +import { Icons } from 'folds'; +import { CallEmbed } from '../plugins/call'; +import { toastQueueAtom } from '../state/toast'; + +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; + } +}; + +/** + * [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); + useEffect( + () => + embed.listenAction<{ data?: { kind?: ScreenshareNoticeKind } }>( + 'io.lotus.screenshare_notice', + (evt) => { + const body = describeScreenshareNotice(evt.detail?.data?.kind as ScreenshareNoticeKind); + 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], + ); +}