Skip to content

Commit 7eb5570

Browse files
committed
feat(vm): wire up vm and stores
1 parent ebbc382 commit 7eb5570

2 files changed

Lines changed: 130 additions & 1 deletion

File tree

apps/web/src/viewmodels/room-list/RoomListSectionHeaderViewModel.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import {
1515
import { RoomNotificationStateStore } from "../../stores/notifications/RoomNotificationStateStore";
1616
import { NotificationStateEvents } from "../../stores/notifications/NotificationState";
1717
import { type RoomNotificationState } from "../../stores/notifications/RoomNotificationState";
18+
import SettingsStore from "../../settings/SettingsStore";
19+
import { DefaultTagID } from "../../stores/room-list-v3/skip-list/tag";
20+
import RoomListStoreV3 from "../../stores/room-list-v3/RoomListStoreV3";
1821

1922
interface RoomListSectionHeaderViewModelProps {
2023
tag: string;
@@ -42,7 +45,18 @@ export class RoomListSectionHeaderViewModel
4245
private readonly expandedBySpace = new Map<string, boolean>();
4346

4447
public constructor(props: RoomListSectionHeaderViewModelProps) {
45-
super(props, { id: props.tag, title: props.title, isExpanded: true, isUnread: false });
48+
const isDefaultSection = props.tag === DefaultTagID.Favourite || props.tag === DefaultTagID.LowPriority;
49+
super(props, {
50+
id: props.tag,
51+
title: props.title,
52+
isExpanded: true,
53+
isUnread: false,
54+
displaySectionMenu: !isDefaultSection,
55+
});
56+
const sectionWatherRef = SettingsStore.watchSetting("RoomList.CustomSectionData", null, () =>
57+
this.onCustomSectionDataChange(),
58+
);
59+
this.disposables.track(() => SettingsStore.unwatchSetting(sectionWatherRef));
4660
}
4761

4862
public onClick = (): void => {
@@ -120,4 +134,23 @@ export class RoomListSectionHeaderViewModel
120134
this.roomNotificationStates.clear();
121135
super.dispose();
122136
}
137+
138+
/**
139+
* Handle changes to custom section data.
140+
*/
141+
private onCustomSectionDataChange(): void {
142+
const customSectionData = SettingsStore.getValue("RoomList.CustomSectionData") || {};
143+
const sectionData = customSectionData[this.props.tag];
144+
if (sectionData) {
145+
this.snapshot.merge({ title: sectionData.name });
146+
}
147+
}
148+
149+
public editSection = async (): Promise<void> => {
150+
await RoomListStoreV3.instance.editSection(this.props.tag);
151+
};
152+
153+
public removeSection = async (): Promise<void> => {
154+
await RoomListStoreV3.instance.removeSection(this.props.tag);
155+
};
123156
}

apps/web/test/viewmodels/room-list/RoomListSectionHeaderViewModel-test.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import { RoomNotificationState } from "../../../src/stores/notifications/RoomNot
1212
import { RoomNotificationStateStore } from "../../../src/stores/notifications/RoomNotificationStateStore";
1313
import { NotificationStateEvents } from "../../../src/stores/notifications/NotificationState";
1414
import { createTestClient, mkRoom } from "../../test-utils";
15+
import SettingsStore from "../../../src/settings/SettingsStore";
16+
import RoomListStoreV3 from "../../../src/stores/room-list-v3/RoomListStoreV3";
17+
import { DefaultTagID } from "../../../src/stores/room-list-v3/skip-list/tag";
1518

1619
describe("RoomListSectionHeaderViewModel", () => {
1720
let onToggleExpanded: jest.Mock;
@@ -20,6 +23,9 @@ describe("RoomListSectionHeaderViewModel", () => {
2023
beforeEach(() => {
2124
onToggleExpanded = jest.fn();
2225
matrixClient = createTestClient();
26+
jest.spyOn(SettingsStore, "watchSetting").mockReturnValue("watcher-id");
27+
jest.spyOn(SettingsStore, "unwatchSetting").mockReturnValue(undefined);
28+
jest.spyOn(SettingsStore, "getValue").mockReturnValue(null);
2329
});
2430

2531
afterEach(() => {
@@ -87,6 +93,96 @@ describe("RoomListSectionHeaderViewModel", () => {
8793
expect(vm.isExpanded).toBe(false);
8894
});
8995

96+
describe("displaySectionMenu", () => {
97+
it.each([
98+
[DefaultTagID.Favourite, false],
99+
[DefaultTagID.LowPriority, false],
100+
["element.io.section.custom", true],
101+
])("should be %s for tag %s", (tag, expected) => {
102+
const vm = new RoomListSectionHeaderViewModel({
103+
tag,
104+
title: "Section",
105+
spaceId: "!space:server",
106+
onToggleExpanded,
107+
});
108+
expect(vm.getSnapshot().displaySectionMenu).toBe(expected);
109+
});
110+
});
111+
112+
describe("onCustomSectionDataChange", () => {
113+
let watchCallback: () => void;
114+
115+
beforeEach(() => {
116+
jest.spyOn(SettingsStore, "watchSetting").mockImplementation((settingName, _roomId, callback) => {
117+
if (settingName === "RoomList.CustomSectionData") watchCallback = callback as () => void;
118+
return "watcher-id";
119+
});
120+
});
121+
122+
it("should update title when custom section data changes", () => {
123+
const tag = "element.io.section.custom";
124+
const vm = new RoomListSectionHeaderViewModel({
125+
tag,
126+
title: "Old Title",
127+
spaceId: "!space:server",
128+
onToggleExpanded,
129+
});
130+
expect(vm.getSnapshot().title).toBe("Old Title");
131+
132+
jest.spyOn(SettingsStore, "getValue").mockReturnValue({ [tag]: { tag, name: "New Title" } });
133+
watchCallback();
134+
135+
expect(vm.getSnapshot().title).toBe("New Title");
136+
});
137+
138+
it("should not update title when section data is missing", () => {
139+
const tag = "element.io.section.custom";
140+
const vm = new RoomListSectionHeaderViewModel({
141+
tag,
142+
title: "My Section",
143+
spaceId: "!space:server",
144+
onToggleExpanded,
145+
});
146+
147+
jest.spyOn(SettingsStore, "getValue").mockReturnValue({});
148+
watchCallback();
149+
150+
expect(vm.getSnapshot().title).toBe("My Section");
151+
});
152+
});
153+
154+
describe("editSection", () => {
155+
it("should delegate to RoomListStoreV3.instance.editSection", async () => {
156+
const editSectionSpy = jest.spyOn(RoomListStoreV3.instance, "editSection").mockResolvedValue(undefined);
157+
const tag = "element.io.section.custom";
158+
const vm = new RoomListSectionHeaderViewModel({
159+
tag,
160+
title: "Section",
161+
spaceId: "!space:server",
162+
onToggleExpanded,
163+
});
164+
165+
await vm.editSection();
166+
expect(editSectionSpy).toHaveBeenCalledWith(tag);
167+
});
168+
});
169+
170+
describe("removeSection", () => {
171+
it("should delegate to RoomListStoreV3.instance.removeSection", async () => {
172+
const removeSectionSpy = jest.spyOn(RoomListStoreV3.instance, "removeSection").mockResolvedValue(undefined);
173+
const tag = "element.io.section.custom";
174+
const vm = new RoomListSectionHeaderViewModel({
175+
tag,
176+
title: "Section",
177+
spaceId: "!space:server",
178+
onToggleExpanded,
179+
});
180+
181+
await vm.removeSection();
182+
expect(removeSectionSpy).toHaveBeenCalledWith(tag);
183+
});
184+
});
185+
90186
describe("unread status", () => {
91187
let room: Room;
92188
let notificationState: RoomNotificationState;

0 commit comments

Comments
 (0)