From 136bb78c152e9b45aa0daf9b158cdd9572a26c7e Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Tue, 17 Mar 2026 15:20:52 +0100 Subject: [PATCH] Tidy up snapshot, actions and vm names according to MVVM doc (#32819) * chore: rename snapshot, actions and vm according to MVVM doc * doc: add naming conventions * chore: fix UrlGroupView naming, folders and children * chore: remove `Example` column * refactor: rename `UrlPreviewGroupViewPreview` into `UrlPreview` --- .../components/views/messages/TextualBody.tsx | 10 +-- .../rooms/RoomListPanel/RoomListView.tsx | 4 +- ...ewModel.ts => UrlPreviewGroupViewModel.ts} | 36 +++++------ .../WidgetContextMenuViewModel.tsx | 6 +- .../room-list/RoomListItemViewModel.ts | 12 ++-- ...tViewViewModel.ts => RoomListViewModel.ts} | 10 +-- ...st.ts => UrlPreviewGroupViewModel-test.ts} | 14 ++-- ... => UrlPreviewGroupViewModel-test.ts.snap} | 28 ++++---- ...el-test.tsx => RoomListViewModel-test.tsx} | 60 +++++++++--------- docs/MVVM.md | 13 ++++ .../LinkPreview.stories.tsx/default-auto.png | Bin .../title-and-description-auto.png | Bin .../LinkPreview.stories.tsx/title-auto.png | Bin .../with-tooltip-auto.png | Bin .../with-very-long-text-auto.png | Bin .../default-auto.png | Bin .../multiple-previews-hidden-auto.png | Bin .../multiple-previews-visible-auto.png | Bin .../with-compact-view-auto.png | Bin .../LinkPreview}/LinkPreview.module.css | 0 .../LinkPreview}/LinkPreview.stories.tsx | 6 +- .../LinkPreview}/LinkPreview.test.tsx | 0 .../LinkPreview}/LinkPreview.tsx | 8 +-- .../__snapshots__/LinkPreview.test.tsx.snap | 0 .../UrlPreviewGroupView/LinkPreview/index.ts | 8 +++ .../UrlPreviewGroupView.module.css | 0 .../UrlPreviewGroupView.stories.tsx | 0 .../UrlPreviewGroupView.test.tsx | 0 .../UrlPreviewGroupView.tsx | 8 ++- .../UrlPreviewGroupView.test.tsx.snap | 0 .../index.ts | 3 +- .../types.ts | 3 +- packages/shared-components/src/index.ts | 2 +- .../WidgetContextMenuView.stories.tsx | 6 +- .../WidgetContextMenuView.test.tsx | 10 +-- .../WidgetContextMenuView.tsx | 6 +- .../right-panel/WidgetContextMenu/index.ts | 2 +- .../RoomListItemContextMenu.tsx | 4 +- .../RoomListItemHoverMenu.tsx | 4 +- .../RoomListItemMoreOptionsMenu.test.tsx | 6 +- .../RoomListItemMoreOptionsMenu.tsx | 8 +-- .../RoomListItemNotificationMenu.test.tsx | 4 +- .../RoomListItemNotificationMenu.tsx | 6 +- .../RoomListItemView.stories.tsx | 6 +- .../RoomListItemView/RoomListItemView.tsx | 8 +-- .../RoomListItemView/default-snapshot.ts | 4 +- .../src/room-list/RoomListItemView/index.ts | 6 +- .../RoomListItemView/mocked-actions.ts | 4 +- .../RoomListView/RoomListView.stories.tsx | 5 +- .../room-list/RoomListView/RoomListView.tsx | 8 +-- .../src/room-list/RoomListView/index.tsx | 2 +- .../VirtualizedRoomListView.stories.tsx | 5 +- .../VirtualizedRoomListView.tsx | 6 +- .../src/room-list/story-mocks.tsx | 15 +++-- 54 files changed, 194 insertions(+), 162 deletions(-) rename apps/web/src/viewmodels/message-body/{UrlPreviewViewModel.ts => UrlPreviewGroupViewModel.ts} (92%) rename apps/web/src/viewmodels/room-list/{RoomListViewViewModel.ts => RoomListViewModel.ts} (98%) rename apps/web/test/viewmodels/message-body/{UrlPreviewViewModel-test.ts => UrlPreviewGroupViewModel-test.ts} (95%) rename apps/web/test/viewmodels/message-body/__snapshots__/{UrlPreviewViewModel-test.ts.snap => UrlPreviewGroupViewModel-test.ts.snap} (65%) rename apps/web/test/viewmodels/room-list/{RoomListViewViewModel-test.tsx => RoomListViewModel-test.tsx} (89%) rename packages/shared-components/__vis__/linux/__baselines__/event-tiles/{UrlPreviewView => UrlPreviewGroupView/LinkPreview}/LinkPreview.stories.tsx/default-auto.png (100%) rename packages/shared-components/__vis__/linux/__baselines__/event-tiles/{UrlPreviewView => UrlPreviewGroupView/LinkPreview}/LinkPreview.stories.tsx/title-and-description-auto.png (100%) rename packages/shared-components/__vis__/linux/__baselines__/event-tiles/{UrlPreviewView => UrlPreviewGroupView/LinkPreview}/LinkPreview.stories.tsx/title-auto.png (100%) rename packages/shared-components/__vis__/linux/__baselines__/event-tiles/{UrlPreviewView => UrlPreviewGroupView/LinkPreview}/LinkPreview.stories.tsx/with-tooltip-auto.png (100%) rename packages/shared-components/__vis__/linux/__baselines__/event-tiles/{UrlPreviewView => UrlPreviewGroupView/LinkPreview}/LinkPreview.stories.tsx/with-very-long-text-auto.png (100%) rename packages/shared-components/__vis__/linux/__baselines__/event-tiles/{UrlPreviewView => UrlPreviewGroupView}/UrlPreviewGroupView.stories.tsx/default-auto.png (100%) rename packages/shared-components/__vis__/linux/__baselines__/event-tiles/{UrlPreviewView => UrlPreviewGroupView}/UrlPreviewGroupView.stories.tsx/multiple-previews-hidden-auto.png (100%) rename packages/shared-components/__vis__/linux/__baselines__/event-tiles/{UrlPreviewView => UrlPreviewGroupView}/UrlPreviewGroupView.stories.tsx/multiple-previews-visible-auto.png (100%) rename packages/shared-components/__vis__/linux/__baselines__/event-tiles/{UrlPreviewView => UrlPreviewGroupView}/UrlPreviewGroupView.stories.tsx/with-compact-view-auto.png (100%) rename packages/shared-components/src/event-tiles/{UrlPreviewView => UrlPreviewGroupView/LinkPreview}/LinkPreview.module.css (100%) rename packages/shared-components/src/event-tiles/{UrlPreviewView => UrlPreviewGroupView/LinkPreview}/LinkPreview.stories.tsx (92%) rename packages/shared-components/src/event-tiles/{UrlPreviewView => UrlPreviewGroupView/LinkPreview}/LinkPreview.test.tsx (100%) rename packages/shared-components/src/event-tiles/{UrlPreviewView => UrlPreviewGroupView/LinkPreview}/LinkPreview.tsx (92%) rename packages/shared-components/src/event-tiles/{UrlPreviewView => UrlPreviewGroupView/LinkPreview}/__snapshots__/LinkPreview.test.tsx.snap (100%) create mode 100644 packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/index.ts rename packages/shared-components/src/event-tiles/{UrlPreviewView => UrlPreviewGroupView}/UrlPreviewGroupView.module.css (100%) rename packages/shared-components/src/event-tiles/{UrlPreviewView => UrlPreviewGroupView}/UrlPreviewGroupView.stories.tsx (100%) rename packages/shared-components/src/event-tiles/{UrlPreviewView => UrlPreviewGroupView}/UrlPreviewGroupView.test.tsx (100%) rename packages/shared-components/src/event-tiles/{UrlPreviewView => UrlPreviewGroupView}/UrlPreviewGroupView.tsx (91%) rename packages/shared-components/src/event-tiles/{UrlPreviewView => UrlPreviewGroupView}/__snapshots__/UrlPreviewGroupView.test.tsx.snap (100%) rename packages/shared-components/src/event-tiles/{UrlPreviewView => UrlPreviewGroupView}/index.ts (83%) rename packages/shared-components/src/event-tiles/{UrlPreviewView => UrlPreviewGroupView}/types.ts (95%) diff --git a/apps/web/src/components/views/messages/TextualBody.tsx b/apps/web/src/components/views/messages/TextualBody.tsx index fe9ed8e910..31fff6b780 100644 --- a/apps/web/src/components/views/messages/TextualBody.tsx +++ b/apps/web/src/components/views/messages/TextualBody.tsx @@ -10,7 +10,7 @@ import React, { type JSX, createRef, type SyntheticEvent, type MouseEvent, useCa import { MsgType } from "matrix-js-sdk/src/matrix"; import { UrlPreviewGroupView, - type UrlPreviewViewSnapshotPreview, + type UrlPreview, useCreateAutoDisposedViewModel, EventContentBodyView, LINKIFIED_DATA_ATTRIBUTE, @@ -35,14 +35,14 @@ import AccessibleButton from "../elements/AccessibleButton"; import { getParentEventId } from "../../../utils/Reply"; import { EditWysiwygComposer } from "../rooms/wysiwyg_composer"; import { type IEventTileOps } from "../rooms/EventTile"; -import { UrlPreviewViewModel } from "../../../viewmodels/message-body/UrlPreviewViewModel"; +import { UrlPreviewGroupViewModel } from "../../../viewmodels/message-body/UrlPreviewGroupViewModel.ts"; import { useMediaVisible } from "../../../hooks/useMediaVisible.ts"; import ImageView from "../elements/ImageView.tsx"; import { useMatrixClientContext } from "../../../contexts/MatrixClientContext.tsx"; const logger = rootLogger.getChild("TextualBody"); -type Props = IBodyProps & { urlPreviewViewModel: UrlPreviewViewModel }; +type Props = IBodyProps & { urlPreviewViewModel: UrlPreviewGroupViewModel }; class InnerTextualBody extends React.Component { private readonly contentRef = createRef(); @@ -390,7 +390,7 @@ export default function TextualBody(props: IBodyProps): React.ReactElement { const [mediaVisible] = useMediaVisible(props.mxEvent); const client = useMatrixClientContext(); - const onUrlPreviewImageClicked = useCallback((preview: UrlPreviewViewSnapshotPreview): void => { + const onUrlPreviewImageClicked = useCallback((preview: UrlPreview): void => { if (!preview.image?.imageFull) { // Should never get this far, but doesn't hurt to check. return; @@ -408,7 +408,7 @@ export default function TextualBody(props: IBodyProps): React.ReactElement { const vm = useCreateAutoDisposedViewModel( () => - new UrlPreviewViewModel({ + new UrlPreviewGroupViewModel({ client, mxEvent: props.mxEvent, mediaVisible: mediaVisible, diff --git a/apps/web/src/components/views/rooms/RoomListPanel/RoomListView.tsx b/apps/web/src/components/views/rooms/RoomListPanel/RoomListView.tsx index 50dd83e505..6c4cad44ec 100644 --- a/apps/web/src/components/views/rooms/RoomListPanel/RoomListView.tsx +++ b/apps/web/src/components/views/rooms/RoomListPanel/RoomListView.tsx @@ -18,7 +18,7 @@ import { RoomAvatarView } from "../../avatars/RoomAvatarView"; import { getKeyBindingsManager } from "../../../../KeyBindingsManager"; import { KeyBindingAction } from "../../../../accessibility/KeyboardShortcuts"; import { Landmark, LandmarkNavigation } from "../../../../accessibility/LandmarkNavigation"; -import { RoomListViewViewModel } from "../../../../viewmodels/room-list/RoomListViewViewModel"; +import { RoomListViewModel } from "../../../../viewmodels/room-list/RoomListViewModel"; /** * RoomListView component using shared components with proper MVVM pattern. @@ -27,7 +27,7 @@ export function RoomListView(): JSX.Element { const matrixClient = useMatrixClientContext(); // Create and auto-dispose ViewModel instance - const vm = useCreateAutoDisposedViewModel(() => new RoomListViewViewModel({ client: matrixClient })); + const vm = useCreateAutoDisposedViewModel(() => new RoomListViewModel({ client: matrixClient })); // Render avatar for each room - memoized to prevent re-renders const renderAvatar = useCallback((room: SharedRoom): ReactNode => { diff --git a/apps/web/src/viewmodels/message-body/UrlPreviewViewModel.ts b/apps/web/src/viewmodels/message-body/UrlPreviewGroupViewModel.ts similarity index 92% rename from apps/web/src/viewmodels/message-body/UrlPreviewViewModel.ts rename to apps/web/src/viewmodels/message-body/UrlPreviewGroupViewModel.ts index 0ec667a6c6..b67ca74c27 100644 --- a/apps/web/src/viewmodels/message-body/UrlPreviewViewModel.ts +++ b/apps/web/src/viewmodels/message-body/UrlPreviewGroupViewModel.ts @@ -9,7 +9,7 @@ import { BaseViewModel, type UrlPreviewGroupViewSnapshot, type UrlPreviewGroupViewActions, - type UrlPreviewViewSnapshotPreview, + type UrlPreview, } from "@element-hq/web-shared-components"; import { logger as rootLogger } from "matrix-js-sdk/src/logger"; import { type IPreviewUrlResponse, type MatrixClient, MatrixError, type MatrixEvent } from "matrix-js-sdk/src/matrix"; @@ -21,14 +21,14 @@ import PlatformPeg from "../../PlatformPeg"; import { thumbHeight } from "../../ImageUtils"; import SettingsStore from "../../settings/SettingsStore"; -const logger = rootLogger.getChild("UrlPreviewViewModel"); +const logger = rootLogger.getChild("UrlPreviewGroupViewModel"); -export interface UrlPreviewViewModelProps { +export interface UrlPreviewGroupViewModelProps { client: MatrixClient; mxEvent: MatrixEvent; mediaVisible: boolean; visible: boolean; - onImageClicked: (preview: UrlPreviewViewSnapshotPreview) => void; + onImageClicked: (preview: UrlPreview) => void; } export const MAX_PREVIEWS_WHEN_LIMITED = 2; @@ -57,8 +57,8 @@ export enum PreviewVisibility { /** * ViewModel for fetching and rendering URL previews for an individual event. */ -export class UrlPreviewViewModel - extends BaseViewModel +export class UrlPreviewGroupViewModel + extends BaseViewModel implements UrlPreviewGroupViewActions { /** @@ -89,7 +89,7 @@ export class UrlPreviewViewModel private static getBaseMetadataFromResponse( response: IPreviewUrlResponse, link: string, - ): Pick { + ): Pick { let title = typeof response["og:title"] === "string" && response["og:title"].trim() ? response["og:title"].trim() @@ -215,14 +215,14 @@ export class UrlPreviewViewModel /** * A cache containing all previously calculated previews. */ - private readonly previewCache = new Map(); + private readonly previewCache = new Map(); /** * Called when the user clicks on the preview thumbnail. */ - public readonly onImageClick: (preview: UrlPreviewViewSnapshotPreview) => void; + public readonly onImageClick: (preview: UrlPreview) => void; - public constructor(props: UrlPreviewViewModelProps) { + public constructor(props: UrlPreviewGroupViewModelProps) { const storageKey = `hide_preview_${props.mxEvent.getId()}`; super(props, { previews: [], @@ -256,7 +256,7 @@ export class UrlPreviewViewModel * @returns A Promise that returns the snapshot needed to render the preview, or null * if the resource could not be previewed. */ - private async fetchPreview(link: string): Promise { + private async fetchPreview(link: string): Promise { const cached = this.previewCache.get(link); if (cached) { return cached; @@ -275,18 +275,18 @@ export class UrlPreviewViewModel return null; } - const { title, description, siteName } = UrlPreviewViewModel.getBaseMetadataFromResponse(preview, link); + const { title, description, siteName } = UrlPreviewGroupViewModel.getBaseMetadataFromResponse(preview, link); const hasImage = preview["og:image"] && typeof preview?.["og:image"] === "string"; // Ensure we have something relevant to render. // The title must not just be the link, or we must have an image. if (title === link && !hasImage) { return null; } - let image: UrlPreviewViewSnapshotPreview["image"]; + let image: UrlPreview["image"]; if (typeof preview["og:image"] === "string" && this.visibility > PreviewVisibility.MediaHidden) { const media = mediaFromMxc(preview["og:image"], this.client); - const declaredHeight = UrlPreviewViewModel.getNumberFromOpenGraph(preview["og:image:height"]); - const declaredWidth = UrlPreviewViewModel.getNumberFromOpenGraph(preview["og:image:width"]); + const declaredHeight = UrlPreviewGroupViewModel.getNumberFromOpenGraph(preview["og:image:height"]); + const declaredWidth = UrlPreviewGroupViewModel.getNumberFromOpenGraph(preview["og:image:width"]); const width = Math.min(declaredWidth ?? PREVIEW_WIDTH, PREVIEW_WIDTH); const height = thumbHeight(width, declaredHeight, PREVIEW_WIDTH, PREVIEW_WIDTH) ?? PREVIEW_WIDTH; const thumb = media.getThumbnailOfSourceHttp(PREVIEW_WIDTH, PREVIEW_HEIGHT, "scale"); @@ -297,7 +297,7 @@ export class UrlPreviewViewModel imageFull: media.srcHttp ?? thumb, width, height, - fileSize: UrlPreviewViewModel.getNumberFromOpenGraph(preview["matrix:image:size"]), + fileSize: UrlPreviewGroupViewModel.getNumberFromOpenGraph(preview["matrix:image:size"]), }; } } @@ -309,7 +309,7 @@ export class UrlPreviewViewModel siteName, showTooltipOnLink: link !== title && PlatformPeg.get()?.needsUrlTooltips(), image, - } satisfies UrlPreviewViewSnapshotPreview; + } satisfies UrlPreview; this.previewCache.set(link, result); return result; } @@ -356,7 +356,7 @@ export class UrlPreviewViewModel * @param eventElement */ public async updateEventElement(eventElement: HTMLDivElement): Promise { - const newLinks = UrlPreviewViewModel.findLinks([eventElement]); + const newLinks = UrlPreviewGroupViewModel.findLinks([eventElement]); // Only recalculate if the set of links has changed. if (newLinks.some((x) => !this.links.includes(x)) || this.links.some((x) => !newLinks.includes(x))) { this.links = newLinks; diff --git a/apps/web/src/viewmodels/right-panel/WidgetContextMenuViewModel.tsx b/apps/web/src/viewmodels/right-panel/WidgetContextMenuViewModel.tsx index 092c78bbe4..83f02a1c05 100644 --- a/apps/web/src/viewmodels/right-panel/WidgetContextMenuViewModel.tsx +++ b/apps/web/src/viewmodels/right-panel/WidgetContextMenuViewModel.tsx @@ -11,7 +11,7 @@ import { type Room, type MatrixClient } from "matrix-js-sdk/src/matrix"; import { type IWidget, MatrixCapabilities } from "matrix-widget-api"; import { BaseViewModel, - type WidgetContextMenuSnapshot, + type WidgetContextMenuViewSnapshot, WidgetContextMenuView, type WidgetContextMenuViewModel as WidgetContextMenuViewModelInterface, } from "@element-hq/web-shared-components"; @@ -56,7 +56,7 @@ const checkRevokeButtonState = ( }; export class WidgetContextMenuViewModel - extends BaseViewModel + extends BaseViewModel implements WidgetContextMenuViewModelInterface { private _app: IWidget; @@ -96,7 +96,7 @@ export class WidgetContextMenuViewModel menuDisplayed: boolean, trigger: ReactNode, onDeleteClick?: () => void, - ): WidgetContextMenuSnapshot => { + ): WidgetContextMenuViewSnapshot => { const showStreamAudioStreamButton = !!getConfigLivestreamUrl() && WidgetType.JITSI.matches(app.type); const canModify = userWidget || WidgetUtils.canUserModifyWidgets(cli, room?.roomId); const widgetMessaging = WidgetMessagingStore.instance.getMessagingForUid(WidgetUtils.getWidgetUid(app)); diff --git a/apps/web/src/viewmodels/room-list/RoomListItemViewModel.ts b/apps/web/src/viewmodels/room-list/RoomListItemViewModel.ts index 938a9d37f3..73e154d778 100644 --- a/apps/web/src/viewmodels/room-list/RoomListItemViewModel.ts +++ b/apps/web/src/viewmodels/room-list/RoomListItemViewModel.ts @@ -8,8 +8,8 @@ Please see LICENSE files in the repository root for full details. import { BaseViewModel, RoomNotifState, - type RoomListItemSnapshot, - type RoomListItemActions, + type RoomListItemViewSnapshot, + type RoomListItemViewActions, } from "@element-hq/web-shared-components"; import { RoomEvent } from "matrix-js-sdk/src/matrix"; import { CallType } from "matrix-js-sdk/src/webrtc/call"; @@ -46,11 +46,11 @@ interface RoomItemProps { /** * View model for an individual room list item. * Manages per-room subscriptions and updates only when this specific room's data changes. - * Implements RoomListItemActions to provide interaction callbacks. + * Implements RoomListItemViewActions to provide interaction callbacks. */ export class RoomListItemViewModel - extends BaseViewModel - implements RoomListItemActions + extends BaseViewModel + implements RoomListItemViewActions { private notifState: RoomNotificationState; /** @@ -205,7 +205,7 @@ export class RoomListItemViewModel room: Room, client: MatrixClient, notifState: RoomNotificationState, - ): RoomListItemSnapshot { + ): RoomListItemViewSnapshot { // Get room tags for menu state const roomTags = room.tags; const isDm = Boolean(DMRoomMap.shared().getUserIdForRoomId(room.roomId)); diff --git a/apps/web/src/viewmodels/room-list/RoomListViewViewModel.ts b/apps/web/src/viewmodels/room-list/RoomListViewModel.ts similarity index 98% rename from apps/web/src/viewmodels/room-list/RoomListViewViewModel.ts rename to apps/web/src/viewmodels/room-list/RoomListViewModel.ts index 2eaea3d246..5d5fecfefe 100644 --- a/apps/web/src/viewmodels/room-list/RoomListViewViewModel.ts +++ b/apps/web/src/viewmodels/room-list/RoomListViewModel.ts @@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details. import { BaseViewModel, - type RoomListSnapshot, + type RoomListViewSnapshot, type FilterId, type RoomListViewActions, type RoomListViewState, @@ -27,7 +27,7 @@ import { SdkContextClass } from "../../contexts/SDKContext"; import { hasCreateRoomRights } from "./utils"; import { keepIfSame } from "../../utils/keepIfSame"; -interface RoomListViewViewModelProps { +interface RoomListViewModelProps { client: MatrixClient; } @@ -41,8 +41,8 @@ const filterKeyToIdMap: Map = new Map([ [FilterKey.LowPriorityFilter, "low_priority"], ]); -export class RoomListViewViewModel - extends BaseViewModel +export class RoomListViewModel + extends BaseViewModel implements RoomListViewActions { // State tracking @@ -54,7 +54,7 @@ export class RoomListViewViewModel private roomItemViewModels = new Map(); private roomsMap = new Map(); - public constructor(props: RoomListViewViewModelProps) { + public constructor(props: RoomListViewModelProps) { const activeSpace = SpaceStore.instance.activeSpaceRoom; // Get initial rooms diff --git a/apps/web/test/viewmodels/message-body/UrlPreviewViewModel-test.ts b/apps/web/test/viewmodels/message-body/UrlPreviewGroupViewModel-test.ts similarity index 95% rename from apps/web/test/viewmodels/message-body/UrlPreviewViewModel-test.ts rename to apps/web/test/viewmodels/message-body/UrlPreviewGroupViewModel-test.ts index 7b86057df2..782cc8d7ee 100644 --- a/apps/web/test/viewmodels/message-body/UrlPreviewViewModel-test.ts +++ b/apps/web/test/viewmodels/message-body/UrlPreviewGroupViewModel-test.ts @@ -9,8 +9,8 @@ import { expect } from "@jest/globals"; import type { MockedObject } from "jest-mock"; import type { MatrixClient, IPreviewUrlResponse } from "matrix-js-sdk/src/matrix"; -import { UrlPreviewViewModel } from "../../../src/viewmodels/message-body/UrlPreviewViewModel"; -import type { UrlPreviewViewSnapshotPreview } from "@element-hq/web-shared-components"; +import { UrlPreviewGroupViewModel } from "../../../src/viewmodels/message-body/UrlPreviewGroupViewModel"; +import type { UrlPreview } from "@element-hq/web-shared-components"; import { getMockClientWithEventEmitter, mkEvent } from "../../test-utils"; const IMAGE_MXC = "mxc://example.org/abc"; @@ -23,16 +23,16 @@ const BASIC_PREVIEW_OGDATA = { }; function getViewModel({ mediaVisible, visible } = { mediaVisible: true, visible: true }): { - vm: UrlPreviewViewModel; + vm: UrlPreviewGroupViewModel; client: MockedObject; - onImageClicked: jest.Mock; + onImageClicked: jest.Mock; } { const client = getMockClientWithEventEmitter({ getUrlPreview: jest.fn(), mxcUrlToHttp: jest.fn(), }); - const onImageClicked = jest.fn(); - const vm = new UrlPreviewViewModel({ + const onImageClicked = jest.fn(); + const vm = new UrlPreviewGroupViewModel({ client, mediaVisible, visible, @@ -48,7 +48,7 @@ function getViewModel({ mediaVisible, visible } = { mediaVisible: true, visible: return { vm, client, onImageClicked }; } -describe("UrlPreviewViewModel", () => { +describe("UrlPreviewGroupViewModel", () => { it("should return no previews by default", () => { expect(getViewModel().vm.getSnapshot()).toMatchSnapshot(); }); diff --git a/apps/web/test/viewmodels/message-body/__snapshots__/UrlPreviewViewModel-test.ts.snap b/apps/web/test/viewmodels/message-body/__snapshots__/UrlPreviewGroupViewModel-test.ts.snap similarity index 65% rename from apps/web/test/viewmodels/message-body/__snapshots__/UrlPreviewViewModel-test.ts.snap rename to apps/web/test/viewmodels/message-body/__snapshots__/UrlPreviewGroupViewModel-test.ts.snap index 29c607dd0a..1f412d86cb 100644 --- a/apps/web/test/viewmodels/message-body/__snapshots__/UrlPreviewViewModel-test.ts.snap +++ b/apps/web/test/viewmodels/message-body/__snapshots__/UrlPreviewGroupViewModel-test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`UrlPreviewViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:description': 'A description',\\n 'og:title': ''\\n} 1`] = ` +exports[`UrlPreviewGroupViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:description': 'A description',\\n 'og:title': ''\\n} 1`] = ` { "description": undefined, "image": undefined, @@ -11,7 +11,7 @@ exports[`UrlPreviewViewModel handles different kinds of opengraph responses {\\n } `; -exports[`UrlPreviewViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:site_name': 'Site name',\\n 'og:title': ''\\n} 1`] = ` +exports[`UrlPreviewGroupViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:site_name': 'Site name',\\n 'og:title': ''\\n} 1`] = ` { "description": undefined, "image": undefined, @@ -22,7 +22,7 @@ exports[`UrlPreviewViewModel handles different kinds of opengraph responses {\\n } `; -exports[`UrlPreviewViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:title': 'Basic title'\\n} 1`] = ` +exports[`UrlPreviewGroupViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:title': 'Basic title'\\n} 1`] = ` { "description": undefined, "image": undefined, @@ -33,7 +33,7 @@ exports[`UrlPreviewViewModel handles different kinds of opengraph responses {\\n } `; -exports[`UrlPreviewViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:title': 'Cool blog',\\n 'og:site_name': 'Cool site'\\n} 1`] = ` +exports[`UrlPreviewGroupViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:title': 'Cool blog',\\n 'og:site_name': 'Cool site'\\n} 1`] = ` { "description": undefined, "image": undefined, @@ -44,7 +44,7 @@ exports[`UrlPreviewViewModel handles different kinds of opengraph responses {\\n } `; -exports[`UrlPreviewViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:title': 'Media test',\\n 'og:image:height': '500',\\n 'og:image:width': 500,\\n 'matrix:image:size': 1024,\\n 'og:image': 'mxc://example.org/abc'\\n} 1`] = ` +exports[`UrlPreviewGroupViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:title': 'Media test',\\n 'og:image:height': '500',\\n 'og:image:width': 500,\\n 'matrix:image:size': 1024,\\n 'og:image': 'mxc://example.org/abc'\\n} 1`] = ` { "description": undefined, "image": { @@ -61,7 +61,7 @@ exports[`UrlPreviewViewModel handles different kinds of opengraph responses {\\n } `; -exports[`UrlPreviewViewModel should deduplicate multiple versions of the same URL 1`] = ` +exports[`UrlPreviewGroupViewModel should deduplicate multiple versions of the same URL 1`] = ` { "compactLayout": false, "overPreviewLimit": false, @@ -80,7 +80,7 @@ exports[`UrlPreviewViewModel should deduplicate multiple versions of the same UR } `; -exports[`UrlPreviewViewModel should handle being hidden and shown by the user 1`] = ` +exports[`UrlPreviewGroupViewModel should handle being hidden and shown by the user 1`] = ` { "compactLayout": false, "overPreviewLimit": false, @@ -90,7 +90,7 @@ exports[`UrlPreviewViewModel should handle being hidden and shown by the user 1` } `; -exports[`UrlPreviewViewModel should handle being hidden and shown by the user 2`] = ` +exports[`UrlPreviewGroupViewModel should handle being hidden and shown by the user 2`] = ` { "compactLayout": false, "overPreviewLimit": false, @@ -109,7 +109,7 @@ exports[`UrlPreviewViewModel should handle being hidden and shown by the user 2` } `; -exports[`UrlPreviewViewModel should hide preview when invisible 1`] = ` +exports[`UrlPreviewGroupViewModel should hide preview when invisible 1`] = ` { "compactLayout": false, "overPreviewLimit": false, @@ -119,7 +119,7 @@ exports[`UrlPreviewViewModel should hide preview when invisible 1`] = ` } `; -exports[`UrlPreviewViewModel should ignore failed previews 1`] = ` +exports[`UrlPreviewGroupViewModel should ignore failed previews 1`] = ` { "compactLayout": false, "overPreviewLimit": false, @@ -129,7 +129,7 @@ exports[`UrlPreviewViewModel should ignore failed previews 1`] = ` } `; -exports[`UrlPreviewViewModel should ignore media when mediaVisible is false 1`] = ` +exports[`UrlPreviewGroupViewModel should ignore media when mediaVisible is false 1`] = ` { "compactLayout": false, "overPreviewLimit": false, @@ -148,7 +148,7 @@ exports[`UrlPreviewViewModel should ignore media when mediaVisible is false 1`] } `; -exports[`UrlPreviewViewModel should preview a URL with media 1`] = ` +exports[`UrlPreviewGroupViewModel should preview a URL with media 1`] = ` { "compactLayout": false, "overPreviewLimit": false, @@ -173,7 +173,7 @@ exports[`UrlPreviewViewModel should preview a URL with media 1`] = ` } `; -exports[`UrlPreviewViewModel should preview a single valid URL 1`] = ` +exports[`UrlPreviewGroupViewModel should preview a single valid URL 1`] = ` { "compactLayout": false, "overPreviewLimit": false, @@ -192,7 +192,7 @@ exports[`UrlPreviewViewModel should preview a single valid URL 1`] = ` } `; -exports[`UrlPreviewViewModel should return no previews by default 1`] = ` +exports[`UrlPreviewGroupViewModel should return no previews by default 1`] = ` { "compactLayout": false, "overPreviewLimit": false, diff --git a/apps/web/test/viewmodels/room-list/RoomListViewViewModel-test.tsx b/apps/web/test/viewmodels/room-list/RoomListViewModel-test.tsx similarity index 89% rename from apps/web/test/viewmodels/room-list/RoomListViewViewModel-test.tsx rename to apps/web/test/viewmodels/room-list/RoomListViewModel-test.tsx index ceeb940669..2f95795f36 100644 --- a/apps/web/test/viewmodels/room-list/RoomListViewViewModel-test.tsx +++ b/apps/web/test/viewmodels/room-list/RoomListViewModel-test.tsx @@ -16,7 +16,7 @@ import dispatcher from "../../../src/dispatcher/dispatcher"; import { Action } from "../../../src/dispatcher/actions"; import { SdkContextClass } from "../../../src/contexts/SDKContext"; import DMRoomMap from "../../../src/utils/DMRoomMap"; -import { RoomListViewViewModel } from "../../../src/viewmodels/room-list/RoomListViewViewModel"; +import { RoomListViewModel } from "../../../src/viewmodels/room-list/RoomListViewModel"; import { hasCreateRoomRights } from "../../../src/viewmodels/room-list/utils"; jest.mock("../../../src/viewmodels/room-list/utils", () => ({ @@ -25,12 +25,12 @@ jest.mock("../../../src/viewmodels/room-list/utils", () => ({ hasAccessToNotificationMenu: jest.fn().mockReturnValue(true), })); -describe("RoomListViewViewModel", () => { +describe("RoomListViewModel", () => { let matrixClient: MatrixClient; let room1: Room; let room2: Room; let room3: Room; - let viewModel: RoomListViewViewModel; + let viewModel: RoomListViewModel; beforeEach(() => { matrixClient = createTestClient(); @@ -63,7 +63,7 @@ describe("RoomListViewViewModel", () => { describe("Initialization", () => { it("should initialize with correct snapshot", () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); const snapshot = viewModel.getSnapshot(); expect(snapshot.sections[0].roomIds).toEqual(["!room1:server", "!room2:server", "!room3:server"]); @@ -80,7 +80,7 @@ describe("RoomListViewViewModel", () => { rooms: [], }); - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); expect(viewModel.getSnapshot().sections[0].roomIds).toEqual([]); expect(viewModel.getSnapshot().isRoomListEmpty).toBe(true); @@ -88,7 +88,7 @@ describe("RoomListViewViewModel", () => { it("should set canCreateRoom based on user rights", () => { mocked(hasCreateRoomRights).mockReturnValue(true); - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); expect(viewModel.getSnapshot().canCreateRoom).toBe(true); }); @@ -96,7 +96,7 @@ describe("RoomListViewViewModel", () => { describe("Room list updates", () => { it("should update room list when ListsUpdate event fires", () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); const newRoom = mkStubRoom("!room4:server", "Room 4", matrixClient); jest.spyOn(RoomListStoreV3.instance, "getSortedRoomsInActiveSpace").mockReturnValue({ @@ -116,7 +116,7 @@ describe("RoomListViewViewModel", () => { it("should update loading state when ListsLoaded event fires", () => { jest.spyOn(RoomListStoreV3.instance, "isLoadingRooms", "get").mockReturnValue(true); - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); expect(viewModel.getSnapshot().isLoadingRooms).toBe(true); @@ -127,7 +127,7 @@ describe("RoomListViewViewModel", () => { // This test ensures that the room list item vms are preserved when the room list is changing it("should keep existing view model when ListsUpdate event fires", () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); // Create view model for room1 const room1VM = viewModel.getRoomItemViewModel("!room1:server"); @@ -142,7 +142,7 @@ describe("RoomListViewViewModel", () => { describe("Space switching", () => { it("should update room list when space changes", () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); const spaceRoomList = [room1, room2]; @@ -160,7 +160,7 @@ describe("RoomListViewViewModel", () => { }); it("should clear view models when space changes", () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); // Get view models for visible rooms const vm1 = viewModel.getRoomItemViewModel("!room1:server"); @@ -184,7 +184,7 @@ describe("RoomListViewViewModel", () => { describe("Active room tracking", () => { it("should update active room index when room is selected", async () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue("!room2:server"); @@ -200,7 +200,7 @@ describe("RoomListViewViewModel", () => { }); it("should return undefined active room index when no room is selected", async () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue(null); @@ -218,7 +218,7 @@ describe("RoomListViewViewModel", () => { describe("Sticky room behavior", () => { it("should keep selected room at same index when room list updates", async () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); // Select room at index 1 jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue("!room2:server"); @@ -244,7 +244,7 @@ describe("RoomListViewViewModel", () => { }); it("should not apply sticky behavior when user changes rooms", async () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); // Select room at index 1 jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue("!room2:server"); @@ -270,7 +270,7 @@ describe("RoomListViewViewModel", () => { describe("Filters", () => { it("should toggle filter on", () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); expect(viewModel.getSnapshot().activeFilterId).toBeUndefined(); @@ -287,7 +287,7 @@ describe("RoomListViewViewModel", () => { }); it("should toggle filter off", () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); // Turn filter on jest.spyOn(RoomListStoreV3.instance, "getSortedRoomsInActiveSpace").mockReturnValue({ @@ -317,7 +317,7 @@ describe("RoomListViewViewModel", () => { describe("Room item view models", () => { it("should create room item view model on demand", () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); const itemViewModel = viewModel.getRoomItemViewModel("!room1:server"); @@ -326,7 +326,7 @@ describe("RoomListViewViewModel", () => { }); it("should reuse existing room item view model", () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); const itemViewModel1 = viewModel.getRoomItemViewModel("!room1:server"); const itemViewModel2 = viewModel.getRoomItemViewModel("!room1:server"); @@ -335,7 +335,7 @@ describe("RoomListViewViewModel", () => { }); it("should throw error when requesting view model for non-existent room", () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); expect(() => { viewModel.getRoomItemViewModel("!nonexistent:server"); @@ -343,7 +343,7 @@ describe("RoomListViewViewModel", () => { }); it("should dispose view models for rooms no longer visible", () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); const vm1 = viewModel.getRoomItemViewModel("!room1:server"); const vm2 = viewModel.getRoomItemViewModel("!room2:server"); @@ -366,7 +366,7 @@ describe("RoomListViewViewModel", () => { describe("Room creation", () => { it("should dispatch CreateChat action when createChatRoom is called", () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); const dispatchSpy = jest.spyOn(dispatcher, "fire"); @@ -376,7 +376,7 @@ describe("RoomListViewViewModel", () => { }); it("should dispatch CreateRoom action without parent space", () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); const dispatchSpy = jest.spyOn(dispatcher, "dispatch"); @@ -391,7 +391,7 @@ describe("RoomListViewViewModel", () => { const spaceRoom = mkStubRoom("!space:server", "Space", matrixClient); jest.spyOn(SpaceStore.instance, "activeSpaceRoom", "get").mockReturnValue(spaceRoom); - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); const dispatchSpy = jest.spyOn(dispatcher, "dispatch"); @@ -411,7 +411,7 @@ describe("RoomListViewViewModel", () => { }); it("should navigate to next room when delta is 1", async () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue("!room1:server"); @@ -434,7 +434,7 @@ describe("RoomListViewViewModel", () => { }); it("should navigate to previous room when delta is -1", async () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue("!room2:server"); @@ -457,7 +457,7 @@ describe("RoomListViewViewModel", () => { }); it("should wrap around to last room when navigating backwards from first room", async () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue("!room1:server"); @@ -480,7 +480,7 @@ describe("RoomListViewViewModel", () => { }); it("should not navigate when current room is not found", async () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue("!unknown:server"); @@ -504,7 +504,7 @@ describe("RoomListViewViewModel", () => { }); it("should not navigate when no room is selected", async () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue(null); @@ -529,7 +529,7 @@ describe("RoomListViewViewModel", () => { describe("Cleanup", () => { it("should dispose all room item view models on dispose", () => { - viewModel = new RoomListViewViewModel({ client: matrixClient }); + viewModel = new RoomListViewModel({ client: matrixClient }); const vm1 = viewModel.getRoomItemViewModel("!room1:server"); const vm2 = viewModel.getRoomItemViewModel("!room2:server"); diff --git a/docs/MVVM.md b/docs/MVVM.md index 01efc71904..81d7c36a50 100644 --- a/docs/MVVM.md +++ b/docs/MVVM.md @@ -18,6 +18,19 @@ If you do MVVM right, your view should be dumb i.e it gets data from the view mo A first documentation and implementation of MVVM was done in [MVVM-v1.md](MVVM-v1.md). This v1 version is now deprecated and this document describes the current implementation. +#### Naming conventions + +Given a feature named `Foo`, the naming convention for each MVVM artifact is: + +| Artifact | Name | +| ----------------------------------- | ----------------- | +| View component | `FooView` | +| Snapshot interface | `FooViewSnapshot` | +| Actions interface | `FooViewActions` | +| ViewModel type alias (in view file) | `FooViewModel` | +| ViewModel class (in `apps/web`) | `FooViewModel` | +| ViewModel class file | `FooViewModel.ts` | + #### Model This is anywhere your data or business logic comes from. If your view model is accessing something simple exposed from `matrix-js-sdk`, then the sdk is your model. If you're using something more high level in element-web to get your data/logic (eg: `MemberListStore`), then that becomes your model. diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewView/LinkPreview.stories.tsx/default-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/default-auto.png similarity index 100% rename from packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewView/LinkPreview.stories.tsx/default-auto.png rename to packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/default-auto.png diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewView/LinkPreview.stories.tsx/title-and-description-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/title-and-description-auto.png similarity index 100% rename from packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewView/LinkPreview.stories.tsx/title-and-description-auto.png rename to packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/title-and-description-auto.png diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewView/LinkPreview.stories.tsx/title-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/title-auto.png similarity index 100% rename from packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewView/LinkPreview.stories.tsx/title-auto.png rename to packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/title-auto.png diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewView/LinkPreview.stories.tsx/with-tooltip-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-tooltip-auto.png similarity index 100% rename from packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewView/LinkPreview.stories.tsx/with-tooltip-auto.png rename to packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-tooltip-auto.png diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewView/LinkPreview.stories.tsx/with-very-long-text-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-very-long-text-auto.png similarity index 100% rename from packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewView/LinkPreview.stories.tsx/with-very-long-text-auto.png rename to packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-very-long-text-auto.png diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewView/UrlPreviewGroupView.stories.tsx/default-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/default-auto.png similarity index 100% rename from packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewView/UrlPreviewGroupView.stories.tsx/default-auto.png rename to packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/default-auto.png diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewView/UrlPreviewGroupView.stories.tsx/multiple-previews-hidden-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/multiple-previews-hidden-auto.png similarity index 100% rename from packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewView/UrlPreviewGroupView.stories.tsx/multiple-previews-hidden-auto.png rename to packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/multiple-previews-hidden-auto.png diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewView/UrlPreviewGroupView.stories.tsx/multiple-previews-visible-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/multiple-previews-visible-auto.png similarity index 100% rename from packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewView/UrlPreviewGroupView.stories.tsx/multiple-previews-visible-auto.png rename to packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/multiple-previews-visible-auto.png diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewView/UrlPreviewGroupView.stories.tsx/with-compact-view-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/with-compact-view-auto.png similarity index 100% rename from packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewView/UrlPreviewGroupView.stories.tsx/with-compact-view-auto.png rename to packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/with-compact-view-auto.png diff --git a/packages/shared-components/src/event-tiles/UrlPreviewView/LinkPreview.module.css b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.module.css similarity index 100% rename from packages/shared-components/src/event-tiles/UrlPreviewView/LinkPreview.module.css rename to packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.module.css diff --git a/packages/shared-components/src/event-tiles/UrlPreviewView/LinkPreview.stories.tsx b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx similarity index 92% rename from packages/shared-components/src/event-tiles/UrlPreviewView/LinkPreview.stories.tsx rename to packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx index b9ea7e05bb..3d428add00 100644 --- a/packages/shared-components/src/event-tiles/UrlPreviewView/LinkPreview.stories.tsx +++ b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx @@ -9,12 +9,12 @@ import React from "react"; import { fn } from "storybook/test"; import type { Meta, StoryFn } from "@storybook/react-vite"; -import imageFile from "../../../static/element.png"; +import imageFile from "../../../../static/element.png"; import { LinkPreview } from "./LinkPreview"; -import { LinkedTextContext } from "../../utils/LinkedText"; +import { LinkedTextContext } from "../../../utils/LinkedText"; export default { - title: "Event/UrlPreviewView", + title: "Event/UrlPreviewGroupView/LinkPreview", component: LinkPreview, tags: ["autodocs"], args: { diff --git a/packages/shared-components/src/event-tiles/UrlPreviewView/LinkPreview.test.tsx b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.test.tsx similarity index 100% rename from packages/shared-components/src/event-tiles/UrlPreviewView/LinkPreview.test.tsx rename to packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.test.tsx diff --git a/packages/shared-components/src/event-tiles/UrlPreviewView/LinkPreview.tsx b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.tsx similarity index 92% rename from packages/shared-components/src/event-tiles/UrlPreviewView/LinkPreview.tsx rename to packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.tsx index 2e39cdeab6..33bbd71646 100644 --- a/packages/shared-components/src/event-tiles/UrlPreviewView/LinkPreview.tsx +++ b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.tsx @@ -9,16 +9,16 @@ import React, { type MouseEventHandler, type JSX, useCallback, useMemo } from "r import { Tooltip, Text } from "@vector-im/compound-web"; import classNames from "classnames"; -import { useI18n } from "../../utils/i18nContext"; +import { useI18n } from "../../../utils/i18nContext"; import styles from "./LinkPreview.module.css"; -import type { UrlPreviewViewSnapshotPreview } from "./types"; -import { LinkedText } from "../../utils/LinkedText"; +import type { UrlPreview } from "../types"; +import { LinkedText } from "../../../utils/LinkedText"; export interface LinkPreviewActions { onImageClick: () => void; } -export type LinkPreviewProps = UrlPreviewViewSnapshotPreview & LinkPreviewActions; +export type LinkPreviewProps = UrlPreview & LinkPreviewActions; /** * LinkPreview renders a single preview component for a single link on an event. It is usually rendered as part of diff --git a/packages/shared-components/src/event-tiles/UrlPreviewView/__snapshots__/LinkPreview.test.tsx.snap b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/__snapshots__/LinkPreview.test.tsx.snap similarity index 100% rename from packages/shared-components/src/event-tiles/UrlPreviewView/__snapshots__/LinkPreview.test.tsx.snap rename to packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/__snapshots__/LinkPreview.test.tsx.snap diff --git a/packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/index.ts b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/index.ts new file mode 100644 index 0000000000..be300783a8 --- /dev/null +++ b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +export { LinkPreview } from "./LinkPreview"; diff --git a/packages/shared-components/src/event-tiles/UrlPreviewView/UrlPreviewGroupView.module.css b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.module.css similarity index 100% rename from packages/shared-components/src/event-tiles/UrlPreviewView/UrlPreviewGroupView.module.css rename to packages/shared-components/src/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.module.css diff --git a/packages/shared-components/src/event-tiles/UrlPreviewView/UrlPreviewGroupView.stories.tsx b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx similarity index 100% rename from packages/shared-components/src/event-tiles/UrlPreviewView/UrlPreviewGroupView.stories.tsx rename to packages/shared-components/src/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx diff --git a/packages/shared-components/src/event-tiles/UrlPreviewView/UrlPreviewGroupView.test.tsx b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.test.tsx similarity index 100% rename from packages/shared-components/src/event-tiles/UrlPreviewView/UrlPreviewGroupView.test.tsx rename to packages/shared-components/src/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.test.tsx diff --git a/packages/shared-components/src/event-tiles/UrlPreviewView/UrlPreviewGroupView.tsx b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.tsx similarity index 91% rename from packages/shared-components/src/event-tiles/UrlPreviewView/UrlPreviewGroupView.tsx rename to packages/shared-components/src/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.tsx index 0c54103f53..682c0717a3 100644 --- a/packages/shared-components/src/event-tiles/UrlPreviewView/UrlPreviewGroupView.tsx +++ b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.tsx @@ -12,12 +12,12 @@ import classNames from "classnames"; import { useViewModel, type ViewModel } from "../../viewmodel"; import { useI18n } from "../../utils/i18nContext"; -import type { UrlPreviewViewSnapshotPreview } from "./types"; +import type { UrlPreview } from "./types"; import { LinkPreview } from "./LinkPreview"; import styles from "./UrlPreviewGroupView.module.css"; export interface UrlPreviewGroupViewSnapshot { - previews: Array; + previews: Array; totalPreviewCount: number; previewsLimited: boolean; overPreviewLimit: boolean; @@ -31,9 +31,11 @@ export interface UrlPreviewGroupViewProps { export interface UrlPreviewGroupViewActions { onTogglePreviewLimit: () => void; onHideClick: () => Promise; - onImageClick: (preview: UrlPreviewViewSnapshotPreview) => void; + onImageClick: (preview: UrlPreview) => void; } +export type UrlPreviewGroupViewModel = ViewModel; + /** * UrlPreviewGroupView renders a list of URL previews for a single event. */ diff --git a/packages/shared-components/src/event-tiles/UrlPreviewView/__snapshots__/UrlPreviewGroupView.test.tsx.snap b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/__snapshots__/UrlPreviewGroupView.test.tsx.snap similarity index 100% rename from packages/shared-components/src/event-tiles/UrlPreviewView/__snapshots__/UrlPreviewGroupView.test.tsx.snap rename to packages/shared-components/src/event-tiles/UrlPreviewGroupView/__snapshots__/UrlPreviewGroupView.test.tsx.snap diff --git a/packages/shared-components/src/event-tiles/UrlPreviewView/index.ts b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/index.ts similarity index 83% rename from packages/shared-components/src/event-tiles/UrlPreviewView/index.ts rename to packages/shared-components/src/event-tiles/UrlPreviewGroupView/index.ts index 7f732494e9..2b59dac466 100644 --- a/packages/shared-components/src/event-tiles/UrlPreviewView/index.ts +++ b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/index.ts @@ -10,6 +10,7 @@ export { type UrlPreviewGroupViewSnapshot, type UrlPreviewGroupViewProps, type UrlPreviewGroupViewActions, + type UrlPreviewGroupViewModel, } from "./UrlPreviewGroupView"; -export { type UrlPreviewViewSnapshotPreview } from "./types"; +export { type UrlPreview } from "./types"; diff --git a/packages/shared-components/src/event-tiles/UrlPreviewView/types.ts b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/types.ts similarity index 95% rename from packages/shared-components/src/event-tiles/UrlPreviewView/types.ts rename to packages/shared-components/src/event-tiles/UrlPreviewGroupView/types.ts index fb5a32145c..4e9da4d4d6 100644 --- a/packages/shared-components/src/event-tiles/UrlPreviewView/types.ts +++ b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/types.ts @@ -5,7 +5,8 @@ * Please see LICENSE files in the repository root for full details. */ -export interface UrlPreviewViewSnapshotPreview { +/** Represents a URL preview. */ +export interface UrlPreview { /** * The URL for the preview. */ diff --git a/packages/shared-components/src/index.ts b/packages/shared-components/src/index.ts index 8f39375632..96b2a8acba 100644 --- a/packages/shared-components/src/index.ts +++ b/packages/shared-components/src/index.ts @@ -16,7 +16,7 @@ export * from "./crypto/SasEmoji"; export * from "./event-tiles/EncryptionEventView"; export * from "./event-tiles/EventTileBubble"; export * from "./event-tiles/TextualEventView"; -export * from "./event-tiles/UrlPreviewView"; +export * from "./event-tiles/UrlPreviewGroupView"; export * from "./message-body/EventContentBody"; export * from "./message-body/MediaBody"; export * from "./message-body/MessageTimestampView"; diff --git a/packages/shared-components/src/right-panel/WidgetContextMenu/WidgetContextMenuView.stories.tsx b/packages/shared-components/src/right-panel/WidgetContextMenu/WidgetContextMenuView.stories.tsx index 3e3c2daeeb..b397fd1200 100644 --- a/packages/shared-components/src/right-panel/WidgetContextMenu/WidgetContextMenuView.stories.tsx +++ b/packages/shared-components/src/right-panel/WidgetContextMenu/WidgetContextMenuView.stories.tsx @@ -12,14 +12,14 @@ import TriggerIcon from "@vector-im/compound-design-tokens/assets/web/icons/over import type { Meta, StoryObj } from "@storybook/react-vite"; import { - type WidgetContextMenuAction, - type WidgetContextMenuSnapshot, + type WidgetContextMenuViewActions, + type WidgetContextMenuViewSnapshot, WidgetContextMenuView, } from "./WidgetContextMenuView"; import { useMockedViewModel } from "../../viewmodel/useMockedViewModel"; import { withViewDocs } from "../../../.storybook/withViewDocs"; -type WidgetContextMenuViewModelProps = WidgetContextMenuSnapshot & WidgetContextMenuAction; +type WidgetContextMenuViewModelProps = WidgetContextMenuViewSnapshot & WidgetContextMenuViewActions; const WidgetContextMenuViewWrapperImpl = ({ onStreamAudioClick, diff --git a/packages/shared-components/src/right-panel/WidgetContextMenu/WidgetContextMenuView.test.tsx b/packages/shared-components/src/right-panel/WidgetContextMenu/WidgetContextMenuView.test.tsx index e590e8d8d3..78fbc2e9e1 100644 --- a/packages/shared-components/src/right-panel/WidgetContextMenu/WidgetContextMenuView.test.tsx +++ b/packages/shared-components/src/right-panel/WidgetContextMenu/WidgetContextMenuView.test.tsx @@ -14,8 +14,8 @@ import TriggerIcon from "@vector-im/compound-design-tokens/assets/web/icons/over import { describe, vi, expect, it, afterEach } from "vitest"; import { - type WidgetContextMenuAction, - type WidgetContextMenuSnapshot, + type WidgetContextMenuViewActions, + type WidgetContextMenuViewSnapshot, WidgetContextMenuView, } from "./WidgetContextMenuView"; import * as stories from "./WidgetContextMenuView.stories.tsx"; @@ -52,8 +52,8 @@ describe("", () => { const onFinished = vi.fn(); const onMoveButton = vi.fn(); class WidgetContextMenuViewModel - extends MockViewModel - implements WidgetContextMenuAction + extends MockViewModel + implements WidgetContextMenuViewActions { public onKeyDown = onKeyDown; public togglePlay = togglePlay; @@ -68,7 +68,7 @@ describe("", () => { public onMoveButton = onMoveButton; } - const defaultValue: WidgetContextMenuSnapshot = { + const defaultValue: WidgetContextMenuViewSnapshot = { showStreamAudioStreamButton: true, showEditButton: true, showRevokeButton: true, diff --git a/packages/shared-components/src/right-panel/WidgetContextMenu/WidgetContextMenuView.tsx b/packages/shared-components/src/right-panel/WidgetContextMenu/WidgetContextMenuView.tsx index 9143ec6691..410bb6d7d9 100644 --- a/packages/shared-components/src/right-panel/WidgetContextMenu/WidgetContextMenuView.tsx +++ b/packages/shared-components/src/right-panel/WidgetContextMenu/WidgetContextMenuView.tsx @@ -13,7 +13,7 @@ import { type ViewModel } from "../../viewmodel/ViewModel.ts"; import { useI18n } from "../../utils/i18nContext.ts"; import { useViewModel } from "../../viewmodel/useViewModel.ts"; -export interface WidgetContextMenuSnapshot { +export interface WidgetContextMenuViewSnapshot { /** * Indicates if the audio stream button needs to be shown or not * depending on the config value audio_stream_url and widget type jitsi @@ -57,7 +57,7 @@ export interface WidgetContextMenuSnapshot { userWidget: boolean; } -export interface WidgetContextMenuAction { +export interface WidgetContextMenuViewActions { /** * Function triggered when stream audio is clicked */ @@ -89,7 +89,7 @@ export interface WidgetContextMenuAction { onMoveButton: (direction: number) => void; } -export type WidgetContextMenuViewModel = ViewModel; +export type WidgetContextMenuViewModel = ViewModel; interface WidgetContextMenuViewProps { vm: WidgetContextMenuViewModel; diff --git a/packages/shared-components/src/right-panel/WidgetContextMenu/index.ts b/packages/shared-components/src/right-panel/WidgetContextMenu/index.ts index fadbd317e5..57b5a911b7 100644 --- a/packages/shared-components/src/right-panel/WidgetContextMenu/index.ts +++ b/packages/shared-components/src/right-panel/WidgetContextMenu/index.ts @@ -5,5 +5,5 @@ * Please see LICENSE files in the repository root for full details. */ -export type { WidgetContextMenuSnapshot, WidgetContextMenuViewModel } from "./WidgetContextMenuView"; +export type { WidgetContextMenuViewSnapshot, WidgetContextMenuViewModel } from "./WidgetContextMenuView"; export { WidgetContextMenuView } from "./WidgetContextMenuView"; diff --git a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemContextMenu.tsx b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemContextMenu.tsx index 0d202474f8..17c44e4a0c 100644 --- a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemContextMenu.tsx +++ b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemContextMenu.tsx @@ -9,14 +9,14 @@ import React, { type JSX, type PropsWithChildren } from "react"; import { ContextMenu } from "@vector-im/compound-web"; import { _t } from "../../utils/i18n"; -import { MoreOptionContent, type RoomItemViewModel } from "./RoomListItemMoreOptionsMenu"; +import { MoreOptionContent, type RoomListItemViewModel } from "./RoomListItemMoreOptionsMenu"; /** * Props for RoomListItemContextMenu component */ export interface RoomListItemContextMenuProps { /** The room item view model */ - vm: RoomItemViewModel; + vm: RoomListItemViewModel; } /** diff --git a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemHoverMenu.tsx b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemHoverMenu.tsx index ea2b2ef9d0..b46f72a6d6 100644 --- a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemHoverMenu.tsx +++ b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemHoverMenu.tsx @@ -8,7 +8,7 @@ import React, { type JSX } from "react"; import { Flex } from "../../utils/Flex"; -import { RoomListItemMoreOptionsMenu, type RoomItemViewModel } from "./RoomListItemMoreOptionsMenu"; +import { RoomListItemMoreOptionsMenu, type RoomListItemViewModel } from "./RoomListItemMoreOptionsMenu"; import { RoomListItemNotificationMenu } from "./RoomListItemNotificationMenu"; import styles from "./RoomListItemView.module.css"; @@ -21,7 +21,7 @@ export interface RoomListItemHoverMenuProps { /** Whether the notification menu should be shown */ showNotificationMenu: boolean; /** The room item view model */ - vm: RoomItemViewModel; + vm: RoomListItemViewModel; } /** diff --git a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemMoreOptionsMenu.test.tsx b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemMoreOptionsMenu.test.tsx index 2885706cd1..c3f491d261 100644 --- a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemMoreOptionsMenu.test.tsx +++ b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemMoreOptionsMenu.test.tsx @@ -12,7 +12,7 @@ import { describe, it, expect, vi } from "vitest"; import { RoomListItemMoreOptionsMenu } from "./RoomListItemMoreOptionsMenu"; import { useMockedViewModel } from "../../viewmodel"; -import type { RoomListItemSnapshot } from "./RoomListItemView"; +import type { RoomListItemViewSnapshot } from "./RoomListItemView"; import { defaultSnapshot } from "./default-snapshot"; describe("", () => { @@ -28,7 +28,7 @@ describe("", () => { onSetRoomNotifState: vi.fn(), }; - const renderMenu = (overrides: Partial = {}): ReturnType => { + const renderMenu = (overrides: Partial = {}): ReturnType => { const TestComponent = (): JSX.Element => { const vm = useMockedViewModel( { @@ -36,7 +36,7 @@ describe("", () => { showMoreOptionsMenu: true, showNotificationMenu: false, ...overrides, - } as RoomListItemSnapshot, + } as RoomListItemViewSnapshot, mockCallbacks, ); return ; diff --git a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemMoreOptionsMenu.tsx b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemMoreOptionsMenu.tsx index 62571baf4b..b5557e8834 100644 --- a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemMoreOptionsMenu.tsx +++ b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemMoreOptionsMenu.tsx @@ -20,19 +20,19 @@ import { import { _t } from "../../utils/i18n"; import { useViewModel, type ViewModel } from "../../viewmodel"; -import type { RoomListItemSnapshot, RoomListItemActions } from "./RoomListItemView"; +import type { RoomListItemViewSnapshot, RoomListItemViewActions } from "./RoomListItemView"; /** * View model type for room list item */ -export type RoomItemViewModel = ViewModel; +export type RoomListItemViewModel = ViewModel; /** * Props for RoomListItemMoreOptionsMenu component */ export interface RoomListItemMoreOptionsMenuProps { /** The room item view model */ - vm: RoomItemViewModel; + vm: RoomListItemViewModel; } /** @@ -66,7 +66,7 @@ export function RoomListItemMoreOptionsMenu({ vm }: RoomListItemMoreOptionsMenuP } interface MoreOptionContentProps { - vm: RoomItemViewModel; + vm: RoomListItemViewModel; } export function MoreOptionContent({ vm }: MoreOptionContentProps): JSX.Element { diff --git a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemNotificationMenu.test.tsx b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemNotificationMenu.test.tsx index e2a0036ef3..4a47b6f436 100644 --- a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemNotificationMenu.test.tsx +++ b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemNotificationMenu.test.tsx @@ -13,7 +13,7 @@ import { describe, it, expect, vi } from "vitest"; import { RoomListItemNotificationMenu } from "./RoomListItemNotificationMenu"; import { RoomNotifState } from "./RoomNotifs"; import { useMockedViewModel } from "../../viewmodel"; -import type { RoomListItemSnapshot } from "./RoomListItemView"; +import type { RoomListItemViewSnapshot } from "./RoomListItemView"; import { defaultSnapshot } from "./default-snapshot"; describe("", () => { @@ -37,7 +37,7 @@ describe("", () => { showMoreOptionsMenu: false, showNotificationMenu: true, roomNotifState, - } as RoomListItemSnapshot, + } as RoomListItemViewSnapshot, mockCallbacks, ); return ; diff --git a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemNotificationMenu.tsx b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemNotificationMenu.tsx index db5ae2cf63..545993f3ee 100644 --- a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemNotificationMenu.tsx +++ b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemNotificationMenu.tsx @@ -16,19 +16,19 @@ import { import { _t } from "../../utils/i18n"; import { RoomNotifState } from "./RoomNotifs"; import { useViewModel, type ViewModel } from "../../viewmodel"; -import type { RoomListItemSnapshot, RoomListItemActions } from "./RoomListItemView"; +import type { RoomListItemViewSnapshot, RoomListItemViewActions } from "./RoomListItemView"; /** * View model type for room list item */ -export type RoomItemViewModel = ViewModel; +export type RoomListItemViewModel = ViewModel; /** * Props for RoomListItemNotificationMenu component */ export interface RoomListItemNotificationMenuProps { /** The room item view model */ - vm: RoomItemViewModel; + vm: RoomListItemViewModel; } /** diff --git a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemView.stories.tsx b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemView.stories.tsx index b371205ba7..7279231d20 100644 --- a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemView.stories.tsx +++ b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemView.stories.tsx @@ -10,15 +10,15 @@ import { fn } from "storybook/test"; import type { Meta, StoryObj } from "@storybook/react-vite"; import type { Room } from "./RoomListItemView"; -import { RoomListItemView, type RoomListItemSnapshot, type RoomListItemActions } from "./RoomListItemView"; +import { RoomListItemView, type RoomListItemViewSnapshot, type RoomListItemViewActions } from "./RoomListItemView"; import { useMockedViewModel } from "../../viewmodel"; import { withViewDocs } from "../../../.storybook/withViewDocs"; import { defaultSnapshot } from "./default-snapshot"; import { renderAvatar } from "../story-mocks"; import { mockedActions } from "./mocked-actions"; -type RoomListItemProps = RoomListItemSnapshot & - RoomListItemActions & { +type RoomListItemProps = RoomListItemViewSnapshot & + RoomListItemViewActions & { isSelected: boolean; isFocused: boolean; onFocus: (room: Room, e: React.FocusEvent) => void; diff --git a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemView.tsx b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemView.tsx index 729f7b4ef5..2ba83f11a9 100644 --- a/packages/shared-components/src/room-list/RoomListItemView/RoomListItemView.tsx +++ b/packages/shared-components/src/room-list/RoomListItemView/RoomListItemView.tsx @@ -44,7 +44,7 @@ function getA11yLabel(roomName: string, notification: NotificationDecorationData * Snapshot for a room list item. * Contains all the data needed to render a room in the list. */ -export interface RoomListItemSnapshot { +export interface RoomListItemViewSnapshot { /** Unique identifier for the room (used for list keying) */ id: string; /** The opaque Room object from the client (e.g., matrix-js-sdk Room) */ @@ -81,7 +81,7 @@ export interface RoomListItemSnapshot { * Actions interface for room list item operations. * Implemented by the room item view model. */ -export interface RoomListItemActions { +export interface RoomListItemViewActions { /** Called when the room should be opened */ onOpenRoom: () => void; /** Called when the room should be marked as read */ @@ -105,14 +105,14 @@ export interface RoomListItemActions { /** * The view model type for a room list item */ -export type RoomItemViewModel = ViewModel; +export type RoomListItemViewModel = ViewModel; /** * Props for RoomListItemView component */ export interface RoomListItemViewProps extends Omit, "onFocus"> { /** The room item view model */ - vm: RoomItemViewModel; + vm: RoomListItemViewModel; /** Whether the room is selected */ isSelected: boolean; /** Whether the room should be focused */ diff --git a/packages/shared-components/src/room-list/RoomListItemView/default-snapshot.ts b/packages/shared-components/src/room-list/RoomListItemView/default-snapshot.ts index 2a4338795d..2ec961ff98 100644 --- a/packages/shared-components/src/room-list/RoomListItemView/default-snapshot.ts +++ b/packages/shared-components/src/room-list/RoomListItemView/default-snapshot.ts @@ -5,12 +5,12 @@ * Please see LICENSE files in the repository root for full details. */ -import { type RoomListItemSnapshot } from "./RoomListItemView"; +import { type RoomListItemViewSnapshot } from "./RoomListItemView"; import { RoomNotifState } from "./RoomNotifs"; export const mockRoom = { name: "General" }; -export const defaultSnapshot: RoomListItemSnapshot = { +export const defaultSnapshot: RoomListItemViewSnapshot = { id: "!room:server", room: mockRoom, name: "General", diff --git a/packages/shared-components/src/room-list/RoomListItemView/index.ts b/packages/shared-components/src/room-list/RoomListItemView/index.ts index 1a5e5ae45b..13db309fd0 100644 --- a/packages/shared-components/src/room-list/RoomListItemView/index.ts +++ b/packages/shared-components/src/room-list/RoomListItemView/index.ts @@ -8,9 +8,9 @@ export { RoomListItemView } from "./RoomListItemView"; export type { Room, - RoomListItemSnapshot, - RoomItemViewModel, - RoomListItemActions, + RoomListItemViewSnapshot, + RoomListItemViewModel, + RoomListItemViewActions, RoomListItemViewProps, } from "./RoomListItemView"; export { RoomListItemNotificationMenu } from "./RoomListItemNotificationMenu"; diff --git a/packages/shared-components/src/room-list/RoomListItemView/mocked-actions.ts b/packages/shared-components/src/room-list/RoomListItemView/mocked-actions.ts index 600674d7ef..bda12ac114 100644 --- a/packages/shared-components/src/room-list/RoomListItemView/mocked-actions.ts +++ b/packages/shared-components/src/room-list/RoomListItemView/mocked-actions.ts @@ -7,9 +7,9 @@ import { fn } from "storybook/test"; -import { type RoomListItemActions } from "./RoomListItemView"; +import { type RoomListItemViewActions } from "./RoomListItemView"; -export const mockedActions: RoomListItemActions = { +export const mockedActions: RoomListItemViewActions = { onOpenRoom: fn(), onMarkAsRead: fn(), onMarkAsUnread: fn(), diff --git a/packages/shared-components/src/room-list/RoomListView/RoomListView.stories.tsx b/packages/shared-components/src/room-list/RoomListView/RoomListView.stories.tsx index 4f50062116..7ac3439b87 100644 --- a/packages/shared-components/src/room-list/RoomListView/RoomListView.stories.tsx +++ b/packages/shared-components/src/room-list/RoomListView/RoomListView.stories.tsx @@ -11,7 +11,7 @@ import { fn } from "storybook/test"; import type { Meta, StoryObj } from "@storybook/react-vite"; import type { Room } from "../RoomListItemView"; import type { FilterId } from "../RoomListPrimaryFilters"; -import { RoomListView, type RoomListSnapshot, type RoomListViewActions } from "./RoomListView"; +import { RoomListView, type RoomListViewSnapshot, type RoomListViewActions } from "./RoomListView"; import { useMockedViewModel } from "../../viewmodel"; import { withViewDocs } from "../../../.storybook/withViewDocs"; import { @@ -25,7 +25,8 @@ import { mockLargeListRoomIds, } from "../story-mocks"; -type RoomListViewProps = RoomListSnapshot & RoomListViewActions & { renderAvatar: (room: Room) => React.ReactElement }; +type RoomListViewProps = RoomListViewSnapshot & + RoomListViewActions & { renderAvatar: (room: Room) => React.ReactElement }; const mockFilterIds: FilterId[] = ["unread", "people", "rooms", "favourite"]; diff --git a/packages/shared-components/src/room-list/RoomListView/RoomListView.tsx b/packages/shared-components/src/room-list/RoomListView/RoomListView.tsx index 3fd4450d0b..eec6cc3aa3 100644 --- a/packages/shared-components/src/room-list/RoomListView/RoomListView.tsx +++ b/packages/shared-components/src/room-list/RoomListView/RoomListView.tsx @@ -12,7 +12,7 @@ import { RoomListPrimaryFilters, type FilterId } from "../RoomListPrimaryFilters import { RoomListLoadingSkeleton } from "./RoomListLoadingSkeleton"; import { RoomListEmptyStateView } from "./RoomListEmptyStateView"; import { VirtualizedRoomListView, type RoomListViewState } from "../VirtualizedRoomListView"; -import { type Room, type RoomItemViewModel } from "../RoomListItemView"; +import { type Room, type RoomListItemViewModel } from "../RoomListItemView"; import { type RoomListSectionHeaderViewModel } from "../RoomListSectionHeaderView"; export type RoomListSection = { @@ -25,7 +25,7 @@ export type RoomListSection = { /** * Snapshot for the room list view */ -export type RoomListSnapshot = { +export type RoomListViewSnapshot = { /** Whether the rooms are currently loading */ isLoadingRooms: boolean; /** Whether the room list is empty */ @@ -59,7 +59,7 @@ export interface RoomListViewActions { /** Called to create a new room */ createRoom: () => void; /** Get view model for a specific room (virtualization API) */ - getRoomItemViewModel: (roomId: string) => RoomItemViewModel; + getRoomItemViewModel: (roomId: string) => RoomListItemViewModel; /** Called when the visible range changes (virtualization API) */ updateVisibleRooms: (startIndex: number, endIndex: number) => void; /** Get view model for a specific section header (virtualization API) */ @@ -69,7 +69,7 @@ export interface RoomListViewActions { /** * The view model type for the room list view */ -export type RoomListViewModel = ViewModel; +export type RoomListViewModel = ViewModel; /** * Props for RoomListView component diff --git a/packages/shared-components/src/room-list/RoomListView/index.tsx b/packages/shared-components/src/room-list/RoomListView/index.tsx index a620a6f23d..b35793e1a1 100644 --- a/packages/shared-components/src/room-list/RoomListView/index.tsx +++ b/packages/shared-components/src/room-list/RoomListView/index.tsx @@ -9,7 +9,7 @@ export { RoomListView } from "./RoomListView"; export type { RoomListViewProps, RoomListViewModel, - RoomListSnapshot, + RoomListViewSnapshot, RoomListViewActions, RoomListSection, } from "./RoomListView"; diff --git a/packages/shared-components/src/room-list/VirtualizedRoomListView/VirtualizedRoomListView.stories.tsx b/packages/shared-components/src/room-list/VirtualizedRoomListView/VirtualizedRoomListView.stories.tsx index ce8d3f1915..c4b62480cc 100644 --- a/packages/shared-components/src/room-list/VirtualizedRoomListView/VirtualizedRoomListView.stories.tsx +++ b/packages/shared-components/src/room-list/VirtualizedRoomListView/VirtualizedRoomListView.stories.tsx @@ -11,7 +11,7 @@ import { fn } from "storybook/test"; import type { Meta, StoryObj } from "@storybook/react-vite"; import type { Room } from "../RoomListItemView"; import { VirtualizedRoomListView, type RoomListViewState } from "./VirtualizedRoomListView"; -import type { RoomListSnapshot, RoomListViewActions } from "../RoomListView"; +import type { RoomListViewSnapshot, RoomListViewActions } from "../RoomListView"; import { useMockedViewModel } from "../../viewmodel"; import { withViewDocs } from "../../../.storybook/withViewDocs"; import type { FilterId } from "../RoomListPrimaryFilters"; @@ -23,7 +23,8 @@ import { mock10RoomsSections, } from "../story-mocks"; -type RoomListStoryProps = RoomListSnapshot & RoomListViewActions & { renderAvatar: (room: Room) => React.ReactElement }; +type RoomListStoryProps = RoomListViewSnapshot & + RoomListViewActions & { renderAvatar: (room: Room) => React.ReactElement }; // Wrapper component that creates a mocked ViewModel const RoomListWrapperImpl = ({ diff --git a/packages/shared-components/src/room-list/VirtualizedRoomListView/VirtualizedRoomListView.tsx b/packages/shared-components/src/room-list/VirtualizedRoomListView/VirtualizedRoomListView.tsx index 63f7761d95..686018c137 100644 --- a/packages/shared-components/src/room-list/VirtualizedRoomListView/VirtualizedRoomListView.tsx +++ b/packages/shared-components/src/room-list/VirtualizedRoomListView/VirtualizedRoomListView.tsx @@ -17,7 +17,7 @@ import { getContainerAccessibleProps, type VirtualizedListContext, } from "../../utils/VirtualizedList"; -import type { RoomListSnapshot, RoomListViewModel } from "../RoomListView"; +import type { RoomListViewSnapshot, RoomListViewModel } from "../RoomListView"; import { GroupedVirtualizedList } from "../../utils/VirtualizedList"; import { RoomListSectionHeaderView } from "../RoomListSectionHeaderView"; import { RoomListItemAccessibilityWrapper } from "../RoomListItemAccessibilityWrapper"; @@ -28,7 +28,7 @@ import { RoomListItemAccessibilityWrapper } from "../RoomListItemAccessibilityWr export type FilterKey = string; /** - * State for the room list data (nested within RoomListSnapshot) + * State for the room list data (nested within RoomListViewSnapshot) */ export interface RoomListViewState { /** Optional active room index for keyboard navigation */ @@ -74,7 +74,7 @@ type Context = { /** Active room index for keyboard navigation */ activeRoomIndex: number | undefined; /** Sections of the room list */ - sections: RoomListSnapshot["sections"]; + sections: RoomListViewSnapshot["sections"]; /** Total number of rooms in the list */ roomCount: number; /** Number of sections in the list */ diff --git a/packages/shared-components/src/room-list/story-mocks.tsx b/packages/shared-components/src/room-list/story-mocks.tsx index 156858edcd..4ef1b64fb9 100644 --- a/packages/shared-components/src/room-list/story-mocks.tsx +++ b/packages/shared-components/src/room-list/story-mocks.tsx @@ -8,7 +8,12 @@ import React from "react"; import { fn } from "storybook/test"; -import { type Room, type RoomItemViewModel, type RoomListItemSnapshot, RoomNotifState } from "./RoomListItemView"; +import { + type Room, + type RoomListItemViewModel, + type RoomListItemViewSnapshot, + RoomNotifState, +} from "./RoomListItemView"; import { type RoomListSectionHeaderViewModel } from "./RoomListSectionHeaderView"; import { MockViewModel } from "../viewmodel"; @@ -74,7 +79,7 @@ const roomNames = [ /** * Create a mock room item snapshot for stories */ -export const createMockRoomSnapshot = (id: string, name: string, index: number): RoomListItemSnapshot => ({ +export const createMockRoomSnapshot = (id: string, name: string, index: number): RoomListItemViewSnapshot => ({ id, room: { name }, name, @@ -102,7 +107,7 @@ export const createMockRoomSnapshot = (id: string, name: string, index: number): roomNotifState: RoomNotifState.AllMessages, }); -export function createMockRoomItemViewModel(roomId: string, name: string, index: number): RoomItemViewModel { +export function createMockRoomItemViewModel(roomId: string, name: string, index: number): RoomListItemViewModel { const snapshot = createMockRoomSnapshot(roomId, name, index); return { getSnapshot: () => snapshot, @@ -122,8 +127,8 @@ export function createMockRoomItemViewModel(roomId: string, name: string, index: /** * Create a mock getRoomItemViewModel function for stories */ -export const createGetRoomItemViewModel = (roomIds: string[]): ((roomId: string) => RoomItemViewModel) => { - const viewModels = new Map(); +export const createGetRoomItemViewModel = (roomIds: string[]): ((roomId: string) => RoomListItemViewModel) => { + const viewModels = new Map(); roomIds.forEach((roomId, index) => { const name = roomNames[index % roomNames.length]; viewModels.set(roomId, createMockRoomItemViewModel(roomId, name, index));