MVVM - Introduce some helpers for snapshot management (#30398)

* Introduce snapshot class to track snapshot updates

This avoids the hassle of having to manually call emit.

* Better viewmodel ergonomics

- Rename `SubscriptionViewModel` to `BaseViewModel`. I feel this is
  appropriate since that class does more than just manage subscriptions.
- `getSnapshot` is no longer an abstract method. It's simply a method
  that returns the current snapshot state. This ensures that getSnapshot
result is cached by default which is required by `useSyncExternalStore`.
- `props` are a property of the base vm class so that actual VMs don't
  have to keep creating this property.

* Update `TextualEventViewModel`

* Fix test

* Rename `TextualEvent` to `TextualEventView`

* Fix snapshot object not being merged

* Rename directory to `EventTileView`

* Fix broken snapshot

* Add test for snapshot class
This commit is contained in:
R Midhun Suresh
2025-08-06 17:59:32 +05:30
committed by GitHub
parent 15f1291cbc
commit ee37734cfc
12 changed files with 130 additions and 34 deletions

View File

@@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
import React from "react";
import { type Meta, type StoryFn } from "@storybook/react-vite";
import { TextualEvent as TextualEventComponent } from "./TextualEvent";
import { TextualEventView as TextualEventComponent } from "./TextualEventView";
import { MockViewModel } from "../../MockViewModel";
export default {
@@ -16,7 +16,7 @@ export default {
component: TextualEventComponent,
tags: ["autodocs"],
args: {
vm: new MockViewModel("Dummy textual event text"),
vm: new MockViewModel({ content: "Dummy textual event text" }),
},
} as Meta<typeof TextualEventComponent>;

View File

@@ -9,11 +9,11 @@ import { composeStories } from "@storybook/react-vite";
import { render } from "jest-matrix-react";
import React from "react";
import * as stories from "./TextualEvent.stories.tsx";
import * as stories from "./TextualEventView.stories.tsx";
const { Default } = composeStories(stories);
describe("TextualEvent", () => {
describe("TextualEventView", () => {
it("renders a textual event", () => {
const { container } = render(<Default />);
expect(container).toMatchSnapshot();

View File

@@ -10,14 +10,15 @@ import React, { type ReactNode, type JSX } from "react";
import { type ViewModel } from "../../ViewModel";
import { useViewModel } from "../../useViewModel";
export type TextualEventViewSnapshot = string | ReactNode;
export type TextualEventViewSnapshot = {
content: string | ReactNode;
};
export interface Props {
vm: ViewModel<TextualEventViewSnapshot>;
}
export function TextualEvent({ vm }: Props): JSX.Element {
const contents = useViewModel(vm);
return <div className="mx_TextualEvent">{contents}</div>;
export function TextualEventView({ vm }: Props): JSX.Element {
const snapshot = useViewModel(vm);
return <div className="mx_TextualEvent">{snapshot.content}</div>;
}

View File

@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`TextualEvent renders a textual event 1`] = `
exports[`TextualEventView renders a textual event 1`] = `
<div>
<div
class="mx_TextualEvent"

View File

@@ -5,4 +5,4 @@ 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.
*/
export { TextualEvent } from "./TextualEvent";
export { TextualEventView } from "./TextualEventView";