From 23fbe9cef69edadff3770e1b3cf4770638ea2a5c Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Thu, 11 Dec 2025 16:41:27 +0100 Subject: [PATCH] UseCreateAutoDisposedViewModel for audio player (#31503) * refactor: useCreateAutoDisposedViewModel for audio player * Update src/viewmodels/audio/AudioPlayerViewModel.ts Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/messages/MAudioBody.tsx | 13 +++++-------- src/viewmodels/audio/AudioPlayerViewModel.ts | 9 +++++++++ test/viewmodels/audio/AudioPlayerViewModel-test.tsx | 8 ++++++++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/components/views/messages/MAudioBody.tsx b/src/components/views/messages/MAudioBody.tsx index db61672c9c..967e0d5fc7 100644 --- a/src/components/views/messages/MAudioBody.tsx +++ b/src/components/views/messages/MAudioBody.tsx @@ -6,11 +6,11 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com Please see LICENSE files in the repository root for full details. */ -import React, { type JSX, useEffect, useMemo } from "react"; +import React, { type JSX, useEffect } from "react"; import { logger } from "matrix-js-sdk/src/logger"; import { type IContent } from "matrix-js-sdk/src/matrix"; import { type MediaEventContent } from "matrix-js-sdk/src/types"; -import { AudioPlayerView } from "@element-hq/web-shared-components"; +import { AudioPlayerView, useCreateAutoDisposedViewModel } from "@element-hq/web-shared-components"; import { type Playback } from "../../../audio/Playback"; import InlineSpinner from "../elements/InlineSpinner"; @@ -132,13 +132,10 @@ interface AudioPlayerProps { * AudioPlayer component that initializes the AudioPlayerViewModel and renders the AudioPlayerView. */ function AudioPlayer({ playback, mediaName }: AudioPlayerProps): JSX.Element { - const vm = useMemo(() => new AudioPlayerViewModel({ playback, mediaName }), [playback, mediaName]); - + const vm = useCreateAutoDisposedViewModel(() => new AudioPlayerViewModel({ playback, mediaName })); useEffect(() => { - return () => { - vm.dispose(); - }; - }, [vm]); + vm.setProps({ playback, mediaName }); + }, [playback, mediaName, vm]); return ; } diff --git a/src/viewmodels/audio/AudioPlayerViewModel.ts b/src/viewmodels/audio/AudioPlayerViewModel.ts index f0f5f87989..37588c5afd 100644 --- a/src/viewmodels/audio/AudioPlayerViewModel.ts +++ b/src/viewmodels/audio/AudioPlayerViewModel.ts @@ -141,4 +141,13 @@ export class AudioPlayerViewModel public onSeekbarChange = async (ev: ChangeEvent): Promise => { await this.props.playback.skipTo((Number(ev.target.value) / 100) * this.props.playback.durationSeconds); }; + + /** + * Updates the properties of the view model and recomputes the snapshot. + * @param newProps + */ + public setProps(newProps: Partial): void { + this.props = { ...this.props, ...newProps }; + this.setSnapshot(); + } } diff --git a/test/viewmodels/audio/AudioPlayerViewModel-test.tsx b/test/viewmodels/audio/AudioPlayerViewModel-test.tsx index a6c4a04166..4fc10fe596 100644 --- a/test/viewmodels/audio/AudioPlayerViewModel-test.tsx +++ b/test/viewmodels/audio/AudioPlayerViewModel-test.tsx @@ -64,4 +64,12 @@ describe("AudioPlayerViewModel", () => { vm.onKeyDown(event); expect(playback.skipTo).toHaveBeenCalledWith(10 + 5); // 5 seconds forward }); + + it("should update snapshot when setProps is called with new mediaName", () => { + const vm = new AudioPlayerViewModel({ playback, mediaName: "oldName" }); + expect(vm.getSnapshot().mediaName).toBe("oldName"); + + vm.setProps({ mediaName: "newName" }); + expect(vm.getSnapshot().mediaName).toBe("newName"); + }); });