From 7d3047bcfdfb972c1dda5ab2ff3f2d941557a457 Mon Sep 17 00:00:00 2001 From: Claude Agent Date: Fri, 2 Oct 2026 19:44:39 +0000 Subject: [PATCH] fix: render volume sliders on mobile/touch devices (iOS) (#621) The volume sliders for lines and PGM were hidden on iOS phones and iPads because UserControls gated the behind `!isIOSMobile && !isIpad`. Volume is applied to audio elements independently of this UI guard, so the slider is functional on these devices; the guard only hid the control. Remove the mobile/touch guard (keeping the program-user-on-program-output-line guard) so the sliders render everywhere, and add a regression test. Co-Authored-By: Claude Opus 4.8 --- .../production-line/user-controls.test.tsx | 78 +++++++++++++++++++ .../production-line/user-controls.tsx | 9 +-- 2 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 src/components/production-line/user-controls.test.tsx diff --git a/src/components/production-line/user-controls.test.tsx b/src/components/production-line/user-controls.test.tsx new file mode 100644 index 00000000..702106a0 --- /dev/null +++ b/src/components/production-line/user-controls.test.tsx @@ -0,0 +1,78 @@ +import { describe, expect, it, vi } from "vitest"; +import { render, screen } from "@testing-library/react"; +import { UserControls } from "./user-controls"; +import { TJoinProductionOptions, TLine } from "./types"; + +// Simulate an iOS mobile / iPad device. The volume slider must still render in +// this case (regression test for #621 — sliders were previously hidden on +// mobile/touch devices). +vi.mock("../../bowser", () => ({ + isIOSMobile: true, + isIpad: true, + isMobile: true, +})); + +const baseJoinOptions: TJoinProductionOptions = { + productionId: "p1", + lineId: "l1", + username: "tester", + lineUsedForProgramOutput: false, + isProgramUser: false, +}; + +const renderControls = (overrides?: { + line?: TLine | null; + joinProductionOptions?: Partial; +}) => + render( + + ); + +describe("UserControls volume slider", () => { + it("renders the volume slider on mobile/touch devices for a regular line", () => { + renderControls(); + + expect(screen.getByRole("slider")).toBeInTheDocument(); + }); + + it("renders the volume slider on mobile/touch devices for PGM (non-program user)", () => { + renderControls({ + line: { + name: "PGM", + id: "pgm1", + participants: [], + programOutputLine: true, + }, + joinProductionOptions: { isProgramUser: false }, + }); + + expect(screen.getByRole("slider")).toBeInTheDocument(); + }); + + it("still hides the volume slider for a program user on the program output line", () => { + renderControls({ + line: { + name: "PGM", + id: "pgm1", + participants: [], + programOutputLine: true, + }, + joinProductionOptions: { isProgramUser: true }, + }); + + expect(screen.queryByRole("slider")).toBeNull(); + }); +}); diff --git a/src/components/production-line/user-controls.tsx b/src/components/production-line/user-controls.tsx index 313f30f8..c310bda5 100644 --- a/src/components/production-line/user-controls.tsx +++ b/src/components/production-line/user-controls.tsx @@ -5,7 +5,6 @@ import { SpeakerOff, SpeakerOn, } from "../../assets/icons/icon"; -import { isIOSMobile, isIpad } from "../../bowser"; import { VolumeSlider } from "../volume-slider/volume-slider"; import { ButtonIcon, UserControlBtn } from "./production-line-components"; import { TJoinProductionOptions, TLine } from "./types"; @@ -46,11 +45,9 @@ export const UserControls = ({ }) => { return ( <> - {!isIOSMobile && - !isIpad && - !(line?.programOutputLine && joinProductionOptions.isProgramUser) && ( - - )} + {!(line?.programOutputLine && joinProductionOptions.isProgramUser) && ( + + )} {!(line?.programOutputLine && joinProductionOptions.isProgramUser) && (