From 166b9620e8b963209e5701dcb1a30e7e6c7455a5 Mon Sep 17 00:00:00 2001 From: Bernard Gawor Date: Wed, 23 Sep 2026 13:29:47 +0200 Subject: [PATCH 1/4] FCE-3775 Let useSandbox choose the video codec --- packages/mobile-client/src/index.ts | 1 + packages/react-client/src/hooks/useSandbox.ts | 8 +++-- packages/react-client/src/index.ts | 2 +- .../react-client/src/tests/sandbox.spec.ts | 36 +++++++++++++++++++ 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/packages/mobile-client/src/index.ts b/packages/mobile-client/src/index.ts index 88a1005b6..79651eb50 100644 --- a/packages/mobile-client/src/index.ts +++ b/packages/mobile-client/src/index.ts @@ -123,6 +123,7 @@ export type { ConnectViewerConfig, RoomType, UseSandboxProps, + VideoCodec, BandwidthLimits, Brand, DeviceError, diff --git a/packages/react-client/src/hooks/useSandbox.ts b/packages/react-client/src/hooks/useSandbox.ts index 57d54572d..8b7079420 100644 --- a/packages/react-client/src/hooks/useSandbox.ts +++ b/packages/react-client/src/hooks/useSandbox.ts @@ -26,17 +26,20 @@ export type UseSandboxProps = { export type RoomType = "conference" | "livestream" | "audio_only"; +export type VideoCodec = "vp8" | "h264"; + export const useSandbox = (props: UseSandboxProps) => { const sandboxApiUrl = props?.sandboxApiUrl; const getSandboxPeerToken = useCallback( - async (roomName: string, peerName: string, roomType: RoomType = "conference") => { + async (roomName: string, peerName: string, roomType: RoomType = "conference", videoCodec?: VideoCodec) => { if (!sandboxApiUrl) throw new MissingSandboxApiUrlError(); const url = new URL(sandboxApiUrl); url.searchParams.set("roomName", roomName); url.searchParams.set("peerName", peerName); url.searchParams.set("roomType", roomType); + if (videoCodec) url.searchParams.set("videoCodec", videoCodec); const res = await fetch(url); @@ -73,12 +76,13 @@ export const useSandbox = (props: UseSandboxProps) => { ); const getSandboxLivestream = useCallback( - async (roomName: string, isPublic: boolean = false) => { + async (roomName: string, isPublic: boolean = false, videoCodec?: VideoCodec) => { if (!sandboxApiUrl) throw new MissingSandboxApiUrlError(); const url = new URL(`${sandboxApiUrl}/livestream`); url.searchParams.set("roomName", roomName); url.searchParams.set("public", isPublic.toString()); + if (videoCodec) url.searchParams.set("videoCodec", videoCodec); const res = await fetch(url); if (!res.ok) throw new Error(`Failed to retrieve streamer token for '${roomName}' livestream room.`); diff --git a/packages/react-client/src/index.ts b/packages/react-client/src/index.ts index 5f404ec44..6c8acb7c2 100644 --- a/packages/react-client/src/index.ts +++ b/packages/react-client/src/index.ts @@ -23,7 +23,7 @@ export { type UseLivestreamViewerResult, } from "./hooks/useLivestreamViewer"; export { type PeerWithTracks, usePeers } from "./hooks/usePeers"; -export { type MoqAccess, type RoomType, useSandbox, type UseSandboxProps } from "./hooks/useSandbox"; +export { type MoqAccess, type RoomType, useSandbox, type UseSandboxProps, type VideoCodec } from "./hooks/useSandbox"; export { useScreenShare } from "./hooks/useScreenShare"; export { useUpdatePeerMetadata } from "./hooks/useUpdatePeerMetadata"; export { useVAD } from "./hooks/useVAD"; diff --git a/packages/react-client/src/tests/sandbox.spec.ts b/packages/react-client/src/tests/sandbox.spec.ts index 7b613a851..a63f21fd6 100644 --- a/packages/react-client/src/tests/sandbox.spec.ts +++ b/packages/react-client/src/tests/sandbox.spec.ts @@ -41,6 +41,24 @@ describe("useSandbox", () => { expect(calledUrl.searchParams.get("roomType")).toBe("conference"); }); + it("getSandboxPeerToken passes the requested video codec", async ({ renderHook }) => { + const fetchSpy = mockFetch({ ok: true, json: async () => ({ peerToken: "pt" }) }); + const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); + + await result.current.getSandboxPeerToken("room", "bob", "conference", "h264"); + const calledUrl = new URL(fetchSpy.mock.calls[0][0]); + expect(calledUrl.searchParams.get("videoCodec")).toBe("h264"); + }); + + it("getSandboxPeerToken omits the video codec by default", async ({ renderHook }) => { + const fetchSpy = mockFetch({ ok: true, json: async () => ({ peerToken: "pt" }) }); + const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); + + await result.current.getSandboxPeerToken("room", "bob"); + const calledUrl = new URL(fetchSpy.mock.calls[0][0]); + expect(calledUrl.searchParams.has("videoCodec")).toBe(false); + }); + it("getSandboxViewerToken gives a friendly error when the room is missing", async ({ renderHook }) => { mockFetch({ ok: false, status: 404 }); const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); @@ -53,4 +71,22 @@ describe("useSandbox", () => { const data = await result.current.getSandboxLivestream("room", true); expect(data.streamerToken).toBe("st"); }); + + it("getSandboxLivestream passes the requested video codec", async ({ renderHook }) => { + const fetchSpy = mockFetch({ ok: true, json: async () => ({ streamerToken: "st", room: { id: "r", name: "n" } }) }); + const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); + + await result.current.getSandboxLivestream("room", false, "h264"); + const calledUrl = new URL(fetchSpy.mock.calls[0][0]); + expect(calledUrl.searchParams.get("videoCodec")).toBe("h264"); + }); + + it("getSandboxLivestream omits the video codec by default", async ({ renderHook }) => { + const fetchSpy = mockFetch({ ok: true, json: async () => ({ streamerToken: "st", room: { id: "r", name: "n" } }) }); + const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); + + await result.current.getSandboxLivestream("room"); + const calledUrl = new URL(fetchSpy.mock.calls[0][0]); + expect(calledUrl.searchParams.has("videoCodec")).toBe(false); + }); }); From 703cf998c7dab22c1216211cee5947ae57f6eac3 Mon Sep 17 00:00:00 2001 From: Bernard Gawor Date: Wed, 23 Sep 2026 13:31:53 +0200 Subject: [PATCH 2/4] Explain sandbox 409 conflicts and rename type to SandboxVideoCodec --- packages/mobile-client/src/index.ts | 2 +- packages/react-client/src/hooks/useSandbox.ts | 19 ++++++++++++++----- packages/react-client/src/index.ts | 8 +++++++- .../react-client/src/tests/sandbox.spec.ts | 16 ++++++++++++++++ 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/packages/mobile-client/src/index.ts b/packages/mobile-client/src/index.ts index 79651eb50..cd21ce725 100644 --- a/packages/mobile-client/src/index.ts +++ b/packages/mobile-client/src/index.ts @@ -123,7 +123,7 @@ export type { ConnectViewerConfig, RoomType, UseSandboxProps, - VideoCodec, + SandboxVideoCodec, BandwidthLimits, Brand, DeviceError, diff --git a/packages/react-client/src/hooks/useSandbox.ts b/packages/react-client/src/hooks/useSandbox.ts index 8b7079420..f5a9c9e4c 100644 --- a/packages/react-client/src/hooks/useSandbox.ts +++ b/packages/react-client/src/hooks/useSandbox.ts @@ -26,13 +26,13 @@ export type UseSandboxProps = { export type RoomType = "conference" | "livestream" | "audio_only"; -export type VideoCodec = "vp8" | "h264"; +export type SandboxVideoCodec = "vp8" | "h264"; export const useSandbox = (props: UseSandboxProps) => { const sandboxApiUrl = props?.sandboxApiUrl; const getSandboxPeerToken = useCallback( - async (roomName: string, peerName: string, roomType: RoomType = "conference", videoCodec?: VideoCodec) => { + async (roomName: string, peerName: string, roomType: RoomType = "conference", videoCodec?: SandboxVideoCodec) => { if (!sandboxApiUrl) throw new MissingSandboxApiUrlError(); const url = new URL(sandboxApiUrl); @@ -44,7 +44,10 @@ export const useSandbox = (props: UseSandboxProps) => { const res = await fetch(url); if (!res.ok) { - const message = `Failed to retrieve peer token for peer '${peerName}' in ${roomType} room '${roomName}'.`; + let message = `Failed to retrieve peer token for peer '${peerName}' in ${roomType} room '${roomName}'.`; + if (res.status === 409) { + message = `Room '${roomName}' already exists with a different room type or video codec.`; + } throw new Error(message); } @@ -76,7 +79,7 @@ export const useSandbox = (props: UseSandboxProps) => { ); const getSandboxLivestream = useCallback( - async (roomName: string, isPublic: boolean = false, videoCodec?: VideoCodec) => { + async (roomName: string, isPublic: boolean = false, videoCodec?: SandboxVideoCodec) => { if (!sandboxApiUrl) throw new MissingSandboxApiUrlError(); const url = new URL(`${sandboxApiUrl}/livestream`); @@ -85,7 +88,13 @@ export const useSandbox = (props: UseSandboxProps) => { if (videoCodec) url.searchParams.set("videoCodec", videoCodec); const res = await fetch(url); - if (!res.ok) throw new Error(`Failed to retrieve streamer token for '${roomName}' livestream room.`); + if (!res.ok) { + let message = `Failed to retrieve streamer token for '${roomName}' livestream room.`; + if (res.status === 409) { + message = `Room '${roomName}' already exists with a different room type or video codec.`; + } + throw new Error(message); + } const data: { streamerToken: string; room: { id: string; name: string } } = await res.json(); return data; diff --git a/packages/react-client/src/index.ts b/packages/react-client/src/index.ts index 6c8acb7c2..2a6527a96 100644 --- a/packages/react-client/src/index.ts +++ b/packages/react-client/src/index.ts @@ -23,7 +23,13 @@ export { type UseLivestreamViewerResult, } from "./hooks/useLivestreamViewer"; export { type PeerWithTracks, usePeers } from "./hooks/usePeers"; -export { type MoqAccess, type RoomType, useSandbox, type UseSandboxProps, type VideoCodec } from "./hooks/useSandbox"; +export { + type MoqAccess, + type RoomType, + type SandboxVideoCodec, + useSandbox, + type UseSandboxProps, +} from "./hooks/useSandbox"; export { useScreenShare } from "./hooks/useScreenShare"; export { useUpdatePeerMetadata } from "./hooks/useUpdatePeerMetadata"; export { useVAD } from "./hooks/useVAD"; diff --git a/packages/react-client/src/tests/sandbox.spec.ts b/packages/react-client/src/tests/sandbox.spec.ts index a63f21fd6..ae1a46cbc 100644 --- a/packages/react-client/src/tests/sandbox.spec.ts +++ b/packages/react-client/src/tests/sandbox.spec.ts @@ -89,4 +89,20 @@ describe("useSandbox", () => { const calledUrl = new URL(fetchSpy.mock.calls[0][0]); expect(calledUrl.searchParams.has("videoCodec")).toBe(false); }); + + it("getSandboxPeerToken explains a conflicting room configuration", async ({ renderHook }) => { + mockFetch({ ok: false, status: 409 }); + const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); + await expect(result.current.getSandboxPeerToken("room", "bob", "conference", "h264")).rejects.toThrow( + /already exists with a different/, + ); + }); + + it("getSandboxLivestream explains a conflicting room configuration", async ({ renderHook }) => { + mockFetch({ ok: false, status: 409 }); + const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); + await expect(result.current.getSandboxLivestream("room", false, "h264")).rejects.toThrow( + /already exists with a different/, + ); + }); }); From f93b47f14cc5975903dd3719331d71612faf67b3 Mon Sep 17 00:00:00 2001 From: Bernard Gawor Date: Fri, 25 Sep 2026 11:50:05 +0200 Subject: [PATCH 3/4] Accept sandbox options objects and move VideoCodec to public types --- packages/mobile-client/src/index.ts | 4 ++- packages/react-client/src/hooks/useSandbox.ts | 19 ++++++++-- packages/react-client/src/index.ts | 4 ++- .../react-client/src/tests/sandbox.spec.ts | 35 ++++++++++++++++--- packages/react-client/src/types/public.ts | 2 ++ 5 files changed, 55 insertions(+), 9 deletions(-) diff --git a/packages/mobile-client/src/index.ts b/packages/mobile-client/src/index.ts index cd21ce725..0b02c75a9 100644 --- a/packages/mobile-client/src/index.ts +++ b/packages/mobile-client/src/index.ts @@ -123,7 +123,9 @@ export type { ConnectViewerConfig, RoomType, UseSandboxProps, - SandboxVideoCodec, + SandboxLivestreamOptions, + SandboxPeerOptions, + VideoCodec, BandwidthLimits, Brand, DeviceError, diff --git a/packages/react-client/src/hooks/useSandbox.ts b/packages/react-client/src/hooks/useSandbox.ts index f5a9c9e4c..2e3e7431e 100644 --- a/packages/react-client/src/hooks/useSandbox.ts +++ b/packages/react-client/src/hooks/useSandbox.ts @@ -1,5 +1,6 @@ import { useCallback } from "react"; +import type { VideoCodec } from "../types/public"; import { MissingSandboxApiUrlError } from "../utils/errors"; type BasicInfo = { id: string; name: string }; @@ -26,13 +27,23 @@ export type UseSandboxProps = { export type RoomType = "conference" | "livestream" | "audio_only"; -export type SandboxVideoCodec = "vp8" | "h264"; +export type SandboxPeerOptions = { + roomType?: RoomType; + videoCodec?: VideoCodec; +}; + +export type SandboxLivestreamOptions = { + isPublic?: boolean; + videoCodec?: VideoCodec; +}; export const useSandbox = (props: UseSandboxProps) => { const sandboxApiUrl = props?.sandboxApiUrl; const getSandboxPeerToken = useCallback( - async (roomName: string, peerName: string, roomType: RoomType = "conference", videoCodec?: SandboxVideoCodec) => { + async (roomName: string, peerName: string, roomTypeOrOptions: RoomType | SandboxPeerOptions = {}) => { + const { roomType = "conference", videoCodec } = + typeof roomTypeOrOptions === "string" ? { roomType: roomTypeOrOptions } : roomTypeOrOptions; if (!sandboxApiUrl) throw new MissingSandboxApiUrlError(); const url = new URL(sandboxApiUrl); @@ -79,7 +90,9 @@ export const useSandbox = (props: UseSandboxProps) => { ); const getSandboxLivestream = useCallback( - async (roomName: string, isPublic: boolean = false, videoCodec?: SandboxVideoCodec) => { + async (roomName: string, isPublicOrOptions: boolean | SandboxLivestreamOptions = {}) => { + const { isPublic = false, videoCodec } = + typeof isPublicOrOptions === "boolean" ? { isPublic: isPublicOrOptions } : isPublicOrOptions; if (!sandboxApiUrl) throw new MissingSandboxApiUrlError(); const url = new URL(`${sandboxApiUrl}/livestream`); diff --git a/packages/react-client/src/index.ts b/packages/react-client/src/index.ts index 2a6527a96..05b4e7c1c 100644 --- a/packages/react-client/src/index.ts +++ b/packages/react-client/src/index.ts @@ -26,7 +26,8 @@ export { type PeerWithTracks, usePeers } from "./hooks/usePeers"; export { type MoqAccess, type RoomType, - type SandboxVideoCodec, + type SandboxLivestreamOptions, + type SandboxPeerOptions, useSandbox, type UseSandboxProps, } from "./hooks/useSandbox"; @@ -54,6 +55,7 @@ export type { TracksMiddleware, TracksMiddlewareResult, UseDataChannelResult, + VideoCodec, } from "./types/public"; export type { AuthErrorReason, diff --git a/packages/react-client/src/tests/sandbox.spec.ts b/packages/react-client/src/tests/sandbox.spec.ts index ae1a46cbc..23ba1de36 100644 --- a/packages/react-client/src/tests/sandbox.spec.ts +++ b/packages/react-client/src/tests/sandbox.spec.ts @@ -45,7 +45,7 @@ describe("useSandbox", () => { const fetchSpy = mockFetch({ ok: true, json: async () => ({ peerToken: "pt" }) }); const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); - await result.current.getSandboxPeerToken("room", "bob", "conference", "h264"); + await result.current.getSandboxPeerToken("room", "bob", { videoCodec: "h264" }); const calledUrl = new URL(fetchSpy.mock.calls[0][0]); expect(calledUrl.searchParams.get("videoCodec")).toBe("h264"); }); @@ -72,11 +72,38 @@ describe("useSandbox", () => { expect(data.streamerToken).toBe("st"); }); + it("getSandboxPeerToken accepts the room type in options", async ({ renderHook }) => { + const fetchSpy = mockFetch({ ok: true, json: async () => ({ peerToken: "pt" }) }); + const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); + + await result.current.getSandboxPeerToken("room", "bob", { roomType: "audio_only" }); + const calledUrl = new URL(fetchSpy.mock.calls[0][0]); + expect(calledUrl.searchParams.get("roomType")).toBe("audio_only"); + }); + + it("getSandboxLivestream accepts visibility as a positional argument", async ({ renderHook }) => { + const fetchSpy = mockFetch({ ok: true, json: async () => ({ streamerToken: "st", room: { id: "r", name: "n" } }) }); + const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); + + await result.current.getSandboxLivestream("room", true); + const calledUrl = new URL(fetchSpy.mock.calls[0][0]); + expect(calledUrl.searchParams.get("public")).toBe("true"); + }); + + it("getSandboxLivestream accepts visibility in options", async ({ renderHook }) => { + const fetchSpy = mockFetch({ ok: true, json: async () => ({ streamerToken: "st", room: { id: "r", name: "n" } }) }); + const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); + + await result.current.getSandboxLivestream("room", { isPublic: true }); + const calledUrl = new URL(fetchSpy.mock.calls[0][0]); + expect(calledUrl.searchParams.get("public")).toBe("true"); + }); + it("getSandboxLivestream passes the requested video codec", async ({ renderHook }) => { const fetchSpy = mockFetch({ ok: true, json: async () => ({ streamerToken: "st", room: { id: "r", name: "n" } }) }); const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); - await result.current.getSandboxLivestream("room", false, "h264"); + await result.current.getSandboxLivestream("room", { videoCodec: "h264" }); const calledUrl = new URL(fetchSpy.mock.calls[0][0]); expect(calledUrl.searchParams.get("videoCodec")).toBe("h264"); }); @@ -93,7 +120,7 @@ describe("useSandbox", () => { it("getSandboxPeerToken explains a conflicting room configuration", async ({ renderHook }) => { mockFetch({ ok: false, status: 409 }); const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); - await expect(result.current.getSandboxPeerToken("room", "bob", "conference", "h264")).rejects.toThrow( + await expect(result.current.getSandboxPeerToken("room", "bob", { videoCodec: "h264" })).rejects.toThrow( /already exists with a different/, ); }); @@ -101,7 +128,7 @@ describe("useSandbox", () => { it("getSandboxLivestream explains a conflicting room configuration", async ({ renderHook }) => { mockFetch({ ok: false, status: 409 }); const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); - await expect(result.current.getSandboxLivestream("room", false, "h264")).rejects.toThrow( + await expect(result.current.getSandboxLivestream("room", { videoCodec: "h264" })).rejects.toThrow( /already exists with a different/, ); }); diff --git a/packages/react-client/src/types/public.ts b/packages/react-client/src/types/public.ts index 576558475..5ba0690af 100644 --- a/packages/react-client/src/types/public.ts +++ b/packages/react-client/src/types/public.ts @@ -15,6 +15,8 @@ export type InitializeDevicesResult = { errors: { audio: DeviceError | null; video: DeviceError | null } | null; }; +export type VideoCodec = "vp8" | "h264"; + export type TrackId = Brand; export type PeerId = Brand; From 40788dc08e8cc09a783f1f786992fa5844b78af6 Mon Sep 17 00:00:00 2001 From: Bernard Gawor Date: Fri, 25 Sep 2026 12:05:30 +0200 Subject: [PATCH 4/4] Keep roomType and isPublic positional with a separate options argument --- packages/mobile-client/src/index.ts | 3 +- packages/react-client/src/hooks/useSandbox.ts | 20 +++--------- packages/react-client/src/index.ts | 3 +- .../react-client/src/tests/sandbox.spec.ts | 32 ++++--------------- 4 files changed, 14 insertions(+), 44 deletions(-) diff --git a/packages/mobile-client/src/index.ts b/packages/mobile-client/src/index.ts index 0b02c75a9..0059d118e 100644 --- a/packages/mobile-client/src/index.ts +++ b/packages/mobile-client/src/index.ts @@ -123,8 +123,7 @@ export type { ConnectViewerConfig, RoomType, UseSandboxProps, - SandboxLivestreamOptions, - SandboxPeerOptions, + SandboxOptions, VideoCodec, BandwidthLimits, Brand, diff --git a/packages/react-client/src/hooks/useSandbox.ts b/packages/react-client/src/hooks/useSandbox.ts index 2e3e7431e..7a082812f 100644 --- a/packages/react-client/src/hooks/useSandbox.ts +++ b/packages/react-client/src/hooks/useSandbox.ts @@ -27,13 +27,7 @@ export type UseSandboxProps = { export type RoomType = "conference" | "livestream" | "audio_only"; -export type SandboxPeerOptions = { - roomType?: RoomType; - videoCodec?: VideoCodec; -}; - -export type SandboxLivestreamOptions = { - isPublic?: boolean; +export type SandboxOptions = { videoCodec?: VideoCodec; }; @@ -41,16 +35,14 @@ export const useSandbox = (props: UseSandboxProps) => { const sandboxApiUrl = props?.sandboxApiUrl; const getSandboxPeerToken = useCallback( - async (roomName: string, peerName: string, roomTypeOrOptions: RoomType | SandboxPeerOptions = {}) => { - const { roomType = "conference", videoCodec } = - typeof roomTypeOrOptions === "string" ? { roomType: roomTypeOrOptions } : roomTypeOrOptions; + async (roomName: string, peerName: string, roomType: RoomType = "conference", options: SandboxOptions = {}) => { if (!sandboxApiUrl) throw new MissingSandboxApiUrlError(); const url = new URL(sandboxApiUrl); url.searchParams.set("roomName", roomName); url.searchParams.set("peerName", peerName); url.searchParams.set("roomType", roomType); - if (videoCodec) url.searchParams.set("videoCodec", videoCodec); + if (options.videoCodec) url.searchParams.set("videoCodec", options.videoCodec); const res = await fetch(url); @@ -90,15 +82,13 @@ export const useSandbox = (props: UseSandboxProps) => { ); const getSandboxLivestream = useCallback( - async (roomName: string, isPublicOrOptions: boolean | SandboxLivestreamOptions = {}) => { - const { isPublic = false, videoCodec } = - typeof isPublicOrOptions === "boolean" ? { isPublic: isPublicOrOptions } : isPublicOrOptions; + async (roomName: string, isPublic: boolean = false, options: SandboxOptions = {}) => { if (!sandboxApiUrl) throw new MissingSandboxApiUrlError(); const url = new URL(`${sandboxApiUrl}/livestream`); url.searchParams.set("roomName", roomName); url.searchParams.set("public", isPublic.toString()); - if (videoCodec) url.searchParams.set("videoCodec", videoCodec); + if (options.videoCodec) url.searchParams.set("videoCodec", options.videoCodec); const res = await fetch(url); if (!res.ok) { diff --git a/packages/react-client/src/index.ts b/packages/react-client/src/index.ts index 05b4e7c1c..8d953a519 100644 --- a/packages/react-client/src/index.ts +++ b/packages/react-client/src/index.ts @@ -26,8 +26,7 @@ export { type PeerWithTracks, usePeers } from "./hooks/usePeers"; export { type MoqAccess, type RoomType, - type SandboxLivestreamOptions, - type SandboxPeerOptions, + type SandboxOptions, useSandbox, type UseSandboxProps, } from "./hooks/useSandbox"; diff --git a/packages/react-client/src/tests/sandbox.spec.ts b/packages/react-client/src/tests/sandbox.spec.ts index 23ba1de36..e8a3c61fd 100644 --- a/packages/react-client/src/tests/sandbox.spec.ts +++ b/packages/react-client/src/tests/sandbox.spec.ts @@ -45,7 +45,7 @@ describe("useSandbox", () => { const fetchSpy = mockFetch({ ok: true, json: async () => ({ peerToken: "pt" }) }); const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); - await result.current.getSandboxPeerToken("room", "bob", { videoCodec: "h264" }); + await result.current.getSandboxPeerToken("room", "bob", "conference", { videoCodec: "h264" }); const calledUrl = new URL(fetchSpy.mock.calls[0][0]); expect(calledUrl.searchParams.get("videoCodec")).toBe("h264"); }); @@ -72,16 +72,7 @@ describe("useSandbox", () => { expect(data.streamerToken).toBe("st"); }); - it("getSandboxPeerToken accepts the room type in options", async ({ renderHook }) => { - const fetchSpy = mockFetch({ ok: true, json: async () => ({ peerToken: "pt" }) }); - const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); - - await result.current.getSandboxPeerToken("room", "bob", { roomType: "audio_only" }); - const calledUrl = new URL(fetchSpy.mock.calls[0][0]); - expect(calledUrl.searchParams.get("roomType")).toBe("audio_only"); - }); - - it("getSandboxLivestream accepts visibility as a positional argument", async ({ renderHook }) => { + it("getSandboxLivestream passes the visibility", async ({ renderHook }) => { const fetchSpy = mockFetch({ ok: true, json: async () => ({ streamerToken: "st", room: { id: "r", name: "n" } }) }); const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); @@ -90,20 +81,11 @@ describe("useSandbox", () => { expect(calledUrl.searchParams.get("public")).toBe("true"); }); - it("getSandboxLivestream accepts visibility in options", async ({ renderHook }) => { - const fetchSpy = mockFetch({ ok: true, json: async () => ({ streamerToken: "st", room: { id: "r", name: "n" } }) }); - const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); - - await result.current.getSandboxLivestream("room", { isPublic: true }); - const calledUrl = new URL(fetchSpy.mock.calls[0][0]); - expect(calledUrl.searchParams.get("public")).toBe("true"); - }); - it("getSandboxLivestream passes the requested video codec", async ({ renderHook }) => { const fetchSpy = mockFetch({ ok: true, json: async () => ({ streamerToken: "st", room: { id: "r", name: "n" } }) }); const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); - await result.current.getSandboxLivestream("room", { videoCodec: "h264" }); + await result.current.getSandboxLivestream("room", false, { videoCodec: "h264" }); const calledUrl = new URL(fetchSpy.mock.calls[0][0]); expect(calledUrl.searchParams.get("videoCodec")).toBe("h264"); }); @@ -120,15 +102,15 @@ describe("useSandbox", () => { it("getSandboxPeerToken explains a conflicting room configuration", async ({ renderHook }) => { mockFetch({ ok: false, status: 409 }); const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); - await expect(result.current.getSandboxPeerToken("room", "bob", { videoCodec: "h264" })).rejects.toThrow( - /already exists with a different/, - ); + await expect( + result.current.getSandboxPeerToken("room", "bob", "conference", { videoCodec: "h264" }), + ).rejects.toThrow(/already exists with a different/); }); it("getSandboxLivestream explains a conflicting room configuration", async ({ renderHook }) => { mockFetch({ ok: false, status: 409 }); const { result } = renderHook(() => useSandbox({ sandboxApiUrl: "https://sandbox.test/api" })); - await expect(result.current.getSandboxLivestream("room", { videoCodec: "h264" })).rejects.toThrow( + await expect(result.current.getSandboxLivestream("room", false, { videoCodec: "h264" })).rejects.toThrow( /already exists with a different/, ); });