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`
This commit is contained in:
Florian Duros
2026-03-17 14:20:52 +00:00
committed by GitHub
parent 6339bcda15
commit 136bb78c15
54 changed files with 194 additions and 162 deletions
@@ -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: {
@@ -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
@@ -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";
@@ -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<UrlPreviewViewSnapshotPreview>;
previews: Array<UrlPreview>;
totalPreviewCount: number;
previewsLimited: boolean;
overPreviewLimit: boolean;
@@ -31,9 +31,11 @@ export interface UrlPreviewGroupViewProps {
export interface UrlPreviewGroupViewActions {
onTogglePreviewLimit: () => void;
onHideClick: () => Promise<void>;
onImageClick: (preview: UrlPreviewViewSnapshotPreview) => void;
onImageClick: (preview: UrlPreview) => void;
}
export type UrlPreviewGroupViewModel = ViewModel<UrlPreviewGroupViewSnapshot, UrlPreviewGroupViewActions>;
/**
* UrlPreviewGroupView renders a list of URL previews for a single event.
*/
@@ -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";
@@ -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.
*/
+1 -1
View File
@@ -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";
@@ -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,
@@ -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("<WidgetContextMenuView />", () => {
const onFinished = vi.fn();
const onMoveButton = vi.fn();
class WidgetContextMenuViewModel
extends MockViewModel<WidgetContextMenuSnapshot>
implements WidgetContextMenuAction
extends MockViewModel<WidgetContextMenuViewSnapshot>
implements WidgetContextMenuViewActions
{
public onKeyDown = onKeyDown;
public togglePlay = togglePlay;
@@ -68,7 +68,7 @@ describe("<WidgetContextMenuView />", () => {
public onMoveButton = onMoveButton;
}
const defaultValue: WidgetContextMenuSnapshot = {
const defaultValue: WidgetContextMenuViewSnapshot = {
showStreamAudioStreamButton: true,
showEditButton: true,
showRevokeButton: true,
@@ -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<WidgetContextMenuSnapshot, WidgetContextMenuAction>;
export type WidgetContextMenuViewModel = ViewModel<WidgetContextMenuViewSnapshot, WidgetContextMenuViewActions>;
interface WidgetContextMenuViewProps {
vm: WidgetContextMenuViewModel;
@@ -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";
@@ -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;
}
/**
@@ -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;
}
/**
@@ -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("<RoomListItemMoreOptionsMenu />", () => {
@@ -28,7 +28,7 @@ describe("<RoomListItemMoreOptionsMenu />", () => {
onSetRoomNotifState: vi.fn(),
};
const renderMenu = (overrides: Partial<RoomListItemSnapshot> = {}): ReturnType<typeof render> => {
const renderMenu = (overrides: Partial<RoomListItemViewSnapshot> = {}): ReturnType<typeof render> => {
const TestComponent = (): JSX.Element => {
const vm = useMockedViewModel(
{
@@ -36,7 +36,7 @@ describe("<RoomListItemMoreOptionsMenu />", () => {
showMoreOptionsMenu: true,
showNotificationMenu: false,
...overrides,
} as RoomListItemSnapshot,
} as RoomListItemViewSnapshot,
mockCallbacks,
);
return <RoomListItemMoreOptionsMenu vm={vm} />;
@@ -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<RoomListItemSnapshot, RoomListItemActions>;
export type RoomListItemViewModel = ViewModel<RoomListItemViewSnapshot, RoomListItemViewActions>;
/**
* 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 {
@@ -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("<RoomListItemNotificationMenu />", () => {
@@ -37,7 +37,7 @@ describe("<RoomListItemNotificationMenu />", () => {
showMoreOptionsMenu: false,
showNotificationMenu: true,
roomNotifState,
} as RoomListItemSnapshot,
} as RoomListItemViewSnapshot,
mockCallbacks,
);
return <RoomListItemNotificationMenu vm={vm} />;
@@ -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<RoomListItemSnapshot, RoomListItemActions>;
export type RoomListItemViewModel = ViewModel<RoomListItemViewSnapshot, RoomListItemViewActions>;
/**
* Props for RoomListItemNotificationMenu component
*/
export interface RoomListItemNotificationMenuProps {
/** The room item view model */
vm: RoomItemViewModel;
vm: RoomListItemViewModel;
}
/**
@@ -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;
@@ -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<RoomListItemSnapshot, RoomListItemActions>;
export type RoomListItemViewModel = ViewModel<RoomListItemViewSnapshot, RoomListItemViewActions>;
/**
* Props for RoomListItemView component
*/
export interface RoomListItemViewProps extends Omit<React.HTMLAttributes<HTMLButtonElement>, "onFocus"> {
/** The room item view model */
vm: RoomItemViewModel;
vm: RoomListItemViewModel;
/** Whether the room is selected */
isSelected: boolean;
/** Whether the room should be focused */
@@ -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",
@@ -8,9 +8,9 @@
export { RoomListItemView } from "./RoomListItemView";
export type {
Room,
RoomListItemSnapshot,
RoomItemViewModel,
RoomListItemActions,
RoomListItemViewSnapshot,
RoomListItemViewModel,
RoomListItemViewActions,
RoomListItemViewProps,
} from "./RoomListItemView";
export { RoomListItemNotificationMenu } from "./RoomListItemNotificationMenu";
@@ -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(),
@@ -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"];
@@ -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<RoomListSnapshot, RoomListViewActions>;
export type RoomListViewModel = ViewModel<RoomListViewSnapshot, RoomListViewActions>;
/**
* Props for RoomListView component
@@ -9,7 +9,7 @@ export { RoomListView } from "./RoomListView";
export type {
RoomListViewProps,
RoomListViewModel,
RoomListSnapshot,
RoomListViewSnapshot,
RoomListViewActions,
RoomListSection,
} from "./RoomListView";
@@ -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 = ({
@@ -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 */
@@ -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<string, RoomItemViewModel>();
export const createGetRoomItemViewModel = (roomIds: string[]): ((roomId: string) => RoomListItemViewModel) => {
const viewModels = new Map<string, RoomListItemViewModel>();
roomIds.forEach((roomId, index) => {
const name = roomNames[index % roomNames.length];
viewModels.set(roomId, createMockRoomItemViewModel(roomId, name, index));