Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions src/components/viewmodels/roomlist/MessagePreviewViewModel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2025 New Vector 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.
*/

import { useCallback, useEffect, useState } from "react";

import type { Room } from "matrix-js-sdk/src/matrix";
import { type MessagePreview, MessagePreviewStore } from "../../../stores/room-list/MessagePreviewStore";
import { useEventEmitter } from "../../../hooks/useEventEmitter";

interface MessagePreviewViewState {
/**
* A string representation of the message preview if available.
*/
message?: string;
}

/**
* View model for rendering a message preview for a given room list item.
* @param room The room for which we're rendering the message preview.
* @see {@link MessagePreviewViewState} for what this view model returns.
*/
export function useMessagePreviewViewModel(room: Room): MessagePreviewViewState {
const [messagePreview, setMessagePreview] = useState<MessagePreview | null>(null);

const updatePreview = useCallback(async (): Promise<void> => {
/**
* The second argument to getPreviewForRoom is a tag id which doesn't really make
* much sense within the context of the new room list. We can pass an empty string
* to match all tags for now but we should remember to actually change the implementation
* in the store once we remove the legacy room list.
*/
const newPreview = await MessagePreviewStore.instance.getPreviewForRoom(room, "");
setMessagePreview(newPreview);
}, [room]);

/**
* Update when the message preview has changed for this room.
*/
useEventEmitter(MessagePreviewStore.instance, MessagePreviewStore.getPreviewChangedEventName(room), () => {
updatePreview();
});

/**
* Do an initial fetch of the message preview.
*/
useEffect(() => {
updatePreview();
}, [updatePreview]);

return {
message: messagePreview?.text,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2025 New Vector 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.
*/

import { renderHook, waitFor } from "jest-matrix-react";
import { type Room } from "matrix-js-sdk/src/matrix";

import { createTestClient, mkStubRoom } from "../../../../test-utils";
import { type MessagePreview, MessagePreviewStore } from "../../../../../src/stores/room-list/MessagePreviewStore";
import { useMessagePreviewViewModel } from "../../../../../src/components/viewmodels/roomlist/MessagePreviewViewModel";

describe("MessagePreviewViewModel", () => {
let room: Room;

beforeEach(() => {
const matrixClient = createTestClient();
room = mkStubRoom("roomId", "roomName", matrixClient);
});

it("should do an initial fetch of the message preview", async () => {
// Mock the store to return some text.
jest.spyOn(MessagePreviewStore.instance, "getPreviewForRoom").mockImplementation(async (room) => {
return { text: "Hello world!" } as MessagePreview;
});

const { result: vm } = renderHook(() => useMessagePreviewViewModel(room));

// Eventually, vm.message should have the text from the store.
await waitFor(() => {
expect(vm.current.message).toEqual("Hello world!");
});
});

it("should fetch message preview again on update from store", async () => {
// Mock the store to return the text in variable message.
let message = "Hello World!";
jest.spyOn(MessagePreviewStore.instance, "getPreviewForRoom").mockImplementation(async (room) => {
return { text: message } as MessagePreview;
});
jest.spyOn(MessagePreviewStore, "getPreviewChangedEventName").mockImplementation((room) => {
return "UPDATE";
});

const { result: vm } = renderHook(() => useMessagePreviewViewModel(room));

// Let's assume the message changed.
message = "New message!";
MessagePreviewStore.instance.emit("UPDATE");

/// vm.message should be the updated message.
await waitFor(() => {
expect(vm.current.message).toEqual(message);
});
});
});
Loading