From 4dbf48b3f9d6618110be1aee70263bcad1cbece3 Mon Sep 17 00:00:00 2001 From: Alois Date: Thu, 30 Apr 2026 23:37:26 +0200 Subject: [PATCH] (fix): joining a call while already being in a call wouldn't reset the call state (qol): format --- .../call/src/components/buttons/invite.tsx | 7 +-- packages/call/src/components/modals/base.tsx | 3 +- packages/call/src/components/top.tsx | 15 +++--- packages/call/src/screenshare.ts | 23 ++++---- packages/call/src/store.tsx | 52 ++++++++++++------- packages/call/src/views/main/focused.tsx | 12 +++-- packages/call/src/views/main/grid.tsx | 22 +++++--- packages/call/todo.md | 1 + 8 files changed, 85 insertions(+), 50 deletions(-) diff --git a/packages/call/src/components/buttons/invite.tsx b/packages/call/src/components/buttons/invite.tsx index 8ab8e24..2e1538e 100644 --- a/packages/call/src/components/buttons/invite.tsx +++ b/packages/call/src/components/buttons/invite.tsx @@ -1,10 +1,5 @@ import { useTTP } from "@tensamin/ttp"; -import { - Button, - Popover, - PopoverContent, - PopoverTrigger, -} from "@tensamin/ui"; +import { Button, Popover, PopoverContent, PopoverTrigger } from "@tensamin/ui"; import Wrapper from "@tensamin/user/wrapper"; import { Mail } from "lucide-react"; import { sendCallInvite } from "../../store"; diff --git a/packages/call/src/components/modals/base.tsx b/packages/call/src/components/modals/base.tsx index 8a0c2c1..03e1883 100644 --- a/packages/call/src/components/modals/base.tsx +++ b/packages/call/src/components/modals/base.tsx @@ -142,7 +142,8 @@ export default function Base({ {/* Detect video / user and place here */} {type === "stream" && - (screenSharePublication?.isSubscribed && screenSharePublication.track ? ( + (screenSharePublication?.isSubscribed && + screenSharePublication.track ? ( state.room); const view = useCall((state) => state.view); - const userIds = Array.from(room.remoteParticipants.values(), (participant) => { - const participantId = Number(participant.identity); - return Number.isInteger(participantId) && participantId > 0 - ? participantId - : null; - }).filter((participantId): participantId is number => participantId != null); + const userIds = Array.from( + room.remoteParticipants.values(), + (participant) => { + const participantId = Number(participant.identity); + return Number.isInteger(participantId) && participantId > 0 + ? participantId + : null; + }, + ).filter((participantId): participantId is number => participantId != null); const userIdsKey = userIds.join(","); const [users, setUsers] = useState([]); diff --git a/packages/call/src/screenshare.ts b/packages/call/src/screenshare.ts index 7d5971c..21b8b71 100644 --- a/packages/call/src/screenshare.ts +++ b/packages/call/src/screenshare.ts @@ -51,7 +51,13 @@ export function createScreenShareController({ await Promise.all( screenShareSession.tracks.map((track) => room.localParticipant.unpublishTrack(track, true).catch((error) => { - log(1, "call", "red", "Failed to unpublish screen share track", error); + log( + 1, + "call", + "red", + "Failed to unpublish screen share track", + error, + ); }), ), ); @@ -194,15 +200,12 @@ export function createScreenShareController({ }); }, 125); - await publishScreenShareTracks( - [videoTrack], - () => { - stopped = true; - window.clearInterval(interval); - stream.getTracks().forEach((track) => track.stop()); - canvas.remove(); - }, - ); + await publishScreenShareTracks([videoTrack], () => { + stopped = true; + window.clearInterval(interval); + stream.getTracks().forEach((track) => track.stop()); + canvas.remove(); + }); } async function stopScreenShare() { diff --git a/packages/call/src/store.tsx b/packages/call/src/store.tsx index 9bf9ba7..31fe425 100644 --- a/packages/call/src/store.tsx +++ b/packages/call/src/store.tsx @@ -157,7 +157,8 @@ function getActiveScreenShareParticipantIds(): number[] { .map((participant) => ({ participantId: getParticipantId(participant.identity), hasScreenShare: - participant.getTrackPublication(Track.Source.ScreenShare)?.track != null, + participant.getTrackPublication(Track.Source.ScreenShare)?.track != + null, })) .filter( (entry): entry is { participantId: number; hasScreenShare: true } => @@ -290,18 +291,21 @@ export async function sendCallInvite(userId: number) { const ownUserId = (await runtime.load("user_id")) as number; const privateKey = await runtime.load("private_key"); - const ownPublicKey = await runtime.getUser(ownUserId).then( - (data) => data.public_key, - ); - const remotePublicKey = await runtime.getUser(userId).then( - (data) => data.public_key, - ); + const ownPublicKey = await runtime + .getUser(ownUserId) + .then((data) => data.public_key); + const remotePublicKey = await runtime + .getUser(userId) + .then((data) => data.public_key); const sharedSecret = await runtime.getSharedSecret( privateKey, ownPublicKey, remotePublicKey, ); - const encryptedCallSecret = await runtime.encryptText(sharedSecret, callSecret); + const encryptedCallSecret = await runtime.encryptText( + sharedSecret, + callSecret, + ); await runtime.send("call_invite", { receiver_id: userId, @@ -373,8 +377,9 @@ export function stopWatchingFocusedStream() { stopWatchingStream(focusedParticipantId); } -let screenShareController: ReturnType | null = - null; +let screenShareController: ReturnType< + typeof createScreenShareController +> | null = null; function getScreenShareController() { if (!screenShareController) { @@ -390,7 +395,8 @@ function getScreenShareController() { : updater, ); }, - getLocalParticipantId: () => getParticipantId(room.localParticipant.identity), + getLocalParticipantId: () => + getParticipantId(room.localParticipant.identity), startWatching: startWatchingStream, stopWatching: stopWatchingStream, syncParticipantState, @@ -439,7 +445,13 @@ export async function disconnect() { try { await getScreenShareController().clearPublishedScreenShare(); } catch (error) { - log(1, "call", "red", "Failed to clear screen share during disconnect", error); + log( + 1, + "call", + "red", + "Failed to clear screen share during disconnect", + error, + ); } useCall.setState({ @@ -463,7 +475,7 @@ export async function disconnect() { }); try { - room.disconnect(); + await room.disconnect(); } catch (error) { log(1, "call", "red", "Failed to disconnect from room", error); } finally { @@ -480,6 +492,11 @@ export async function joinCall( sendInvite = true, ) { const runtime = requireRuntime(useCall.getState().runtime); + const state = useCall.getState().state; + + if (state !== "closed") { + await disconnect(); + } log(2, "call", "purple", "Call creation initialised"); useCall.setState({ @@ -754,11 +771,10 @@ export function useInitializeCall() { if (invitedUserId != null) { setTimeout(async () => { - void sendCallInvite(invitedUserId) - .catch((error) => { - toast("error", "Failed to send call invite."); - log(1, "call", "red", "Failed to send call invite", error); - }); + void sendCallInvite(invitedUserId).catch((error) => { + toast("error", "Failed to send call invite."); + log(1, "call", "red", "Failed to send call invite", error); + }); }, 1000); } diff --git a/packages/call/src/views/main/focused.tsx b/packages/call/src/views/main/focused.tsx index 3bcb276..0fc0b5a 100644 --- a/packages/call/src/views/main/focused.tsx +++ b/packages/call/src/views/main/focused.tsx @@ -15,7 +15,10 @@ export default function View() { (state) => state.activeScreenShareParticipantIds, ); const containerRef = useRef(null); - const [focusedTileSize, setFocusedTileSize] = useState({ width: 0, height: 0 }); + const [focusedTileSize, setFocusedTileSize] = useState({ + width: 0, + height: 0, + }); const [isFocusedTileFlush, setIsFocusedTileFlush] = useState(false); const [participantVersion, setParticipantVersion] = useState(0); @@ -42,7 +45,7 @@ export default function View() { const participantId = Number(participant.identity); return Number.isInteger(participantId) && participantId > 0; }); - // eslint-disable-next-line + // eslint-disable-next-line }, [participantVersion, room]); const userIds = useMemo( @@ -141,7 +144,10 @@ export default function View() { activeScreenShareParticipantIdSet.has(focusedParticipantId); return ( -
+
0 ? STACK_GAP_PX : 0 }} diff --git a/packages/call/src/views/main/grid.tsx b/packages/call/src/views/main/grid.tsx index 882d83a..beae0f1 100644 --- a/packages/call/src/views/main/grid.tsx +++ b/packages/call/src/views/main/grid.tsx @@ -129,13 +129,16 @@ export default function View() { }, [room]); const users = useMemo(() => { - const participants = [...room.remoteParticipants.values(), room.localParticipant]; + const participants = [ + ...room.remoteParticipants.values(), + room.localParticipant, + ]; return participants.filter((participant) => { const participantId = Number(participant.identity); return Number.isInteger(participantId) && participantId > 0; }); - // eslint-disable-next-line + // eslint-disable-next-line }, [participantVersion, room]); const userIds = useMemo( @@ -195,9 +198,13 @@ export default function View() { } return ( -
+
{rows.map((row) => (
@@ -217,7 +224,10 @@ export default function View() { height: layout.tileHeight, }} > - +
); })} diff --git a/packages/call/todo.md b/packages/call/todo.md index 7b8ddb5..2534699 100644 --- a/packages/call/todo.md +++ b/packages/call/todo.md @@ -7,3 +7,4 @@ - Buttons - Preview image - Mobile +- Deaf status is not synced