diff --git a/ts/components/lightbox/Lightbox.tsx b/ts/components/lightbox/Lightbox.tsx
index 20f55228a8..39a529ce9f 100644
--- a/ts/components/lightbox/Lightbox.tsx
+++ b/ts/components/lightbox/Lightbox.tsx
@@ -23,6 +23,7 @@ import { AriaLabels } from '../../util/hardcodedAriaLabels';
import { LUCIDE_ICONS_UNICODE } from '../icon/lucide';
import { SessionLucideIconButton } from '../icon/SessionIconButton';
import { SessionFocusTrap } from '../SessionFocusTrap';
+import { LightboxVideo } from './LightboxVideo';
type Props = {
contentType: MIME.MIMEType | undefined;
@@ -443,9 +444,12 @@ const LightboxObject = ({
}
return (
-
+
);
}
diff --git a/ts/components/lightbox/LightboxVideo.tsx b/ts/components/lightbox/LightboxVideo.tsx
new file mode 100644
index 0000000000..d5fd49effc
--- /dev/null
+++ b/ts/components/lightbox/LightboxVideo.tsx
@@ -0,0 +1,39 @@
+import type { CSSProperties, Ref, SyntheticEvent } from 'react';
+
+import { SettingsKey } from '../../data/settings-key';
+
+type Props = {
+ renderedRef: Ref;
+ style: CSSProperties;
+ urlToLoad: string | undefined;
+};
+
+const isValidVolume = (volume: unknown): volume is number =>
+ typeof volume === 'number' && Number.isFinite(volume) && volume >= 0 && volume <= 1;
+
+export const LightboxVideo = ({ renderedRef, style, urlToLoad }: Props) => {
+ const handleLoadedMetadata = (event: SyntheticEvent) => {
+ const savedVolume = window.getSettingValue(SettingsKey.lightboxVideoVolume);
+ if (isValidVolume(savedVolume)) {
+ const video = event.currentTarget;
+ video.volume = savedVolume;
+ }
+ };
+
+ const handleVolumeChange = (event: SyntheticEvent) => {
+ void window.setSettingValue(SettingsKey.lightboxVideoVolume, event.currentTarget.volume);
+ };
+
+ return (
+
+ );
+};
diff --git a/ts/data/settings-key.ts b/ts/data/settings-key.ts
index df01d4e9b7..21aba9238f 100644
--- a/ts/data/settings-key.ts
+++ b/ts/data/settings-key.ts
@@ -63,6 +63,7 @@ export const SettingsKey = {
proLongerMessagesSent,
proBadgesSent,
audioAutoplay: 'audioAutoplay',
+ lightboxVideoVolume: 'lightboxVideoVolume',
showRecoveryPhrasePrompt: 'showRecoveryPhrasePrompt',
dismissedRecoveryPhrasePrompt: 'dismissedRecoveryPhrasePrompt',
hideMessageRequests: 'hideMessageRequests',
diff --git a/ts/test/components/LightboxVideo_test.tsx b/ts/test/components/LightboxVideo_test.tsx
new file mode 100644
index 0000000000..2b163a6c67
--- /dev/null
+++ b/ts/test/components/LightboxVideo_test.tsx
@@ -0,0 +1,66 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { fireEvent } from '@testing-library/react';
+import { expect } from 'chai';
+import { createRef } from 'react';
+import Sinon from 'sinon';
+
+import { LightboxVideo } from '../../components/lightbox/LightboxVideo';
+import { SettingsKey } from '../../data/settings-key';
+import { findAllByTagName, renderComponent } from './renderComponent';
+
+describe('LightboxVideo', () => {
+ const getSettingValue = Sinon.stub();
+ const setSettingValue = Sinon.stub().resolves();
+ const originalGetSettingValue = window.getSettingValue;
+ const originalSetSettingValue = window.setSettingValue;
+
+ beforeEach(() => {
+ getSettingValue.reset();
+ setSettingValue.resetHistory();
+ window.getSettingValue = getSettingValue;
+ window.setSettingValue = setSettingValue;
+ });
+
+ afterEach(() => {
+ window.getSettingValue = originalGetSettingValue;
+ window.setSettingValue = originalSetSettingValue;
+ });
+
+ const renderVideo = () => {
+ const result = renderComponent(
+
+ );
+ const [video] = findAllByTagName(result, 'video');
+ return { result, video };
+ };
+
+ it('restores the saved volume when the video metadata loads', () => {
+ getSettingValue.withArgs(SettingsKey.lightboxVideoVolume).returns(0.35);
+ const { result, video } = renderVideo();
+
+ fireEvent.loadedMetadata(video);
+
+ expect(video.volume).to.equal(0.35);
+ result.unmount();
+ });
+
+ it('ignores an invalid saved volume', () => {
+ getSettingValue.withArgs(SettingsKey.lightboxVideoVolume).returns(2);
+ const { result, video } = renderVideo();
+
+ fireEvent.loadedMetadata(video);
+
+ expect(video.volume).to.equal(1);
+ result.unmount();
+ });
+
+ it('saves volume changes', () => {
+ const { result, video } = renderVideo();
+ video.volume = 0.42;
+
+ fireEvent.volumeChange(video);
+
+ expect(setSettingValue.calledWithExactly(SettingsKey.lightboxVideoVolume, 0.42)).to.equal(true);
+ result.unmount();
+ });
+});