diff --git a/frontend/angular/src/app/features/player/player-shell.component.spec.ts b/frontend/angular/src/app/features/player/player-shell.component.spec.ts index df15bf6..a97fe0c 100644 --- a/frontend/angular/src/app/features/player/player-shell.component.spec.ts +++ b/frontend/angular/src/app/features/player/player-shell.component.spec.ts @@ -400,7 +400,14 @@ describe('PlayerShellComponent gameplay wiring', () => { const component = new PlayerShellComponent(); component.ngOnInit(); - await expect(mediaPrototype.play()).resolves.toBeUndefined(); + const pause = vi.fn(); + const audioElement = { muted: false, defaultMuted: false, volume: 1, pause }; + + await expect(mediaPrototype.play.call(audioElement)).resolves.toBeUndefined(); + expect(audioElement.muted).toBe(true); + expect(audioElement.defaultMuted).toBe(true); + expect(audioElement.volume).toBe(0); + expect(pause).toHaveBeenCalledTimes(1); component.ngOnDestroy(); diff --git a/frontend/angular/src/app/features/player/player-shell.component.ts b/frontend/angular/src/app/features/player/player-shell.component.ts index aa5fdcb..6542e4b 100644 --- a/frontend/angular/src/app/features/player/player-shell.component.ts +++ b/frontend/angular/src/app/features/player/player-shell.component.ts @@ -16,18 +16,21 @@ interface SessionDetail { type ConnectionState = 'online' | 'reconnecting' | 'offline'; type LoadingTransition = 'refresh' | 'join' | 'submit-lie' | 'submit-guess' | null; +type GuardableMediaElement = { + muted?: boolean; + defaultMuted?: boolean; + volume?: number; + pause?: () => void; +}; + type MediaPrototypeWithGuardState = { - play?: () => Promise; + play?: (this: GuardableMediaElement) => Promise; __wppSecondaryDeviceAudioGuard__?: { - originalPlay: () => Promise; + originalPlay: (this: GuardableMediaElement) => Promise; installs: number; }; }; -type GuardableMediaElement = { - muted?: boolean; - pause?: () => void; -}; function resolveLocalStorage(): Storage | undefined { if (typeof window === 'undefined') { @@ -221,7 +224,15 @@ export class PlayerShellComponent implements OnInit, OnDestroy { guardState.installs += 1; } else { const originalPlay = mediaPrototype.play; - mediaPrototype.play = () => Promise.resolve(); + mediaPrototype.play = function mediaGuardedPlay(this: GuardableMediaElement): Promise { + this.muted = true; + this.defaultMuted = true; + if (typeof this.volume === 'number') { + this.volume = 0; + } + this.pause?.(); + return Promise.resolve(); + }; mediaPrototype.__wppSecondaryDeviceAudioGuard__ = { originalPlay, installs: 1, @@ -242,6 +253,7 @@ export class PlayerShellComponent implements OnInit, OnDestroy { }; } + private silenceExistingMediaElements(): void { if (typeof document === 'undefined' || typeof document.querySelectorAll !== 'function') { return; @@ -258,6 +270,10 @@ export class PlayerShellComponent implements OnInit, OnDestroy { activeElements.forEach((element) => { element.muted = true; + element.defaultMuted = true; + if (typeof element.volume === 'number') { + element.volume = 0; + } element.pause?.(); }); }