Skip to content

Commit 532a290

Browse files
committed
feat(vm): wire up vm and stores
1 parent a279708 commit 532a290

2 files changed

Lines changed: 133 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: 99 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,12 @@ 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").mockImplementation((setting) => {
29+
if (setting === "RoomList.OrderedCustomSections") return [];
30+
return null;
31+
});
2332
});
2433

2534
afterEach(() => {
@@ -87,6 +96,96 @@ describe("RoomListSectionHeaderViewModel", () => {
8796
expect(vm.isExpanded).toBe(false);
8897
});
8998

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

0 commit comments

Comments
 (0)