Room list: assign room to custom section (#33238)
* feat(sc): add new toast type for room list * feat(sc): add section entries in room list item menu * feat(rls): expose util functions * feat: allows to tag room with custom sections * feat(vm): add new Chat moved toast to room list vm * feat(vm): add section selection to room list item vm * feat(e2e): add tests for adding room in a custom section * test(e2e): update existing screenshots * chore: fix lint after merge * chore: remove outline in test
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
RoomNotifState,
|
||||
type RoomListItemViewSnapshot,
|
||||
type RoomListItemViewActions,
|
||||
type Section,
|
||||
} from "@element-hq/web-shared-components";
|
||||
import { RoomEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { CallType } from "matrix-js-sdk/src/webrtc/call";
|
||||
@@ -37,7 +38,8 @@ import { Action } from "../../dispatcher/actions";
|
||||
import type { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
|
||||
import PosthogTrackers from "../../PosthogTrackers";
|
||||
import { type Call, CallEvent } from "../../models/Call";
|
||||
import RoomListStoreV3 from "../../stores/room-list-v3/RoomListStoreV3";
|
||||
import RoomListStoreV3, { CHATS_TAG } from "../../stores/room-list-v3/RoomListStoreV3";
|
||||
import { _t } from "../../languageHandler";
|
||||
|
||||
interface RoomItemProps {
|
||||
room: Room;
|
||||
@@ -96,6 +98,13 @@ export class RoomListItemViewModel
|
||||
this.disposables.trackListener(props.room, RoomEvent.Name, this.onRoomChanged);
|
||||
this.disposables.trackListener(props.room, RoomEvent.Tags, this.onRoomChanged);
|
||||
|
||||
const orderSectionsRef = SettingsStore.watchSetting("RoomList.OrderedCustomSections", null, () =>
|
||||
this.onOrderedCustomSectionsChange(),
|
||||
);
|
||||
this.disposables.track(() => {
|
||||
SettingsStore.unwatchSetting(orderSectionsRef);
|
||||
});
|
||||
|
||||
// Load message preview asynchronously (sync data is already complete)
|
||||
void this.loadAndSetMessagePreview();
|
||||
}
|
||||
@@ -181,6 +190,7 @@ export class RoomListItemViewModel
|
||||
this.snapshot.merge({
|
||||
...newItem,
|
||||
notification: keepIfSame(this.snapshot.current.notification, newItem.notification),
|
||||
sections: keepIfSame(this.snapshot.current.sections, newItem.sections),
|
||||
// Preserve message preview - it's managed separately by loadAndSetMessagePreview
|
||||
messagePreview: this.snapshot.current.messagePreview,
|
||||
});
|
||||
@@ -279,6 +289,9 @@ export class RoomListItemViewModel
|
||||
|
||||
const canMoveToSection = SettingsStore.getValue("feature_room_list_sections");
|
||||
|
||||
// Build sections list for the "Move to section" submenu
|
||||
const sections: Section[] = canMoveToSection ? RoomListItemViewModel.buildSections(roomTags) : [];
|
||||
|
||||
return {
|
||||
id: room.roomId,
|
||||
room,
|
||||
@@ -307,6 +320,7 @@ export class RoomListItemViewModel
|
||||
canMarkAsUnread,
|
||||
roomNotifState,
|
||||
canMoveToSection,
|
||||
sections,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -389,4 +403,42 @@ export class RoomListItemViewModel
|
||||
public onCreateSection = (): void => {
|
||||
RoomListStoreV3.instance.createSection();
|
||||
};
|
||||
|
||||
public onToggleSection = (tag: string): void => {
|
||||
tagRoom(this.props.room, tag);
|
||||
};
|
||||
|
||||
private onOrderedCustomSectionsChange = (): void => {
|
||||
// Rebuild sections list to reflect new order
|
||||
const sections = RoomListItemViewModel.buildSections(this.props.room.tags);
|
||||
this.snapshot.merge({ sections: keepIfSame(this.snapshot.current.sections, sections) });
|
||||
};
|
||||
|
||||
/**
|
||||
* Build the list of available sections for the "Move to section" submenu.
|
||||
* Order follows the canonical section order from RoomListStoreV3.
|
||||
*/
|
||||
private static buildSections(roomTags: Room["tags"]): Section[] {
|
||||
const customSectionData = SettingsStore.getValue("RoomList.CustomSectionData") || {};
|
||||
|
||||
return (
|
||||
RoomListStoreV3.instance.orderedSectionTags
|
||||
// Exclude the Chats section because the user toggle the other sections to move rooms in and out of the Chats section.
|
||||
.filter((tag) => tag !== CHATS_TAG)
|
||||
.map((tag) => ({
|
||||
tag,
|
||||
name: RoomListItemViewModel.getSectionName(tag, customSectionData),
|
||||
isSelected: Boolean(roomTags[tag]),
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the display name for a section based on its tag.
|
||||
*/
|
||||
private static getSectionName(tag: string, customSectionData: Record<string, { name: string }>): string {
|
||||
if (tag === DefaultTagID.Favourite) return _t("room_list|section|favourites");
|
||||
if (tag === DefaultTagID.LowPriority) return _t("room_list|section|low_priority");
|
||||
return customSectionData[tag]?.name || tag;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
type RoomListViewState,
|
||||
type RoomListSection,
|
||||
_t,
|
||||
type ToastType,
|
||||
} from "@element-hq/web-shared-components";
|
||||
import { type MatrixClient, type Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
@@ -156,6 +157,13 @@ export class RoomListViewModel
|
||||
this.onSectionCreated as (...args: unknown[]) => void,
|
||||
);
|
||||
|
||||
// Subscribe to room tagging
|
||||
this.disposables.trackListener(
|
||||
RoomListStoreV3.instance,
|
||||
RoomListStoreV3Event.RoomTagged as any,
|
||||
this.onRoomTagged,
|
||||
);
|
||||
|
||||
// Subscribe to active room changes to update selected room
|
||||
const dispatcherRef = dispatcher.register(this.onDispatch);
|
||||
this.disposables.track(() => {
|
||||
@@ -595,15 +603,11 @@ export class RoomListViewModel
|
||||
|
||||
public onSectionCreated = (tag: string): void => {
|
||||
this.updateRoomListData(false, null, tag);
|
||||
this.showToast("section_created");
|
||||
};
|
||||
|
||||
clearTimeout(this.toastRef);
|
||||
this.snapshot.merge({
|
||||
toast: "section_created",
|
||||
});
|
||||
// Automatically close the toast after 15 seconds
|
||||
this.toastRef = setTimeout(() => {
|
||||
this.closeToast();
|
||||
}, 15 * 1000);
|
||||
public onRoomTagged = (): void => {
|
||||
this.showToast("chat_moved");
|
||||
};
|
||||
|
||||
public closeToast: () => void = () => {
|
||||
@@ -612,6 +616,15 @@ export class RoomListViewModel
|
||||
toast: undefined,
|
||||
});
|
||||
};
|
||||
|
||||
private showToast(toast: ToastType): void {
|
||||
clearTimeout(this.toastRef);
|
||||
this.snapshot.merge({ toast });
|
||||
// Automatically close the toast after 15 seconds
|
||||
this.toastRef = setTimeout(() => {
|
||||
this.closeToast();
|
||||
}, 15 * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user