fix(room list): stop reading custom section settings on every room item update (#34102)
RoomListItemViewModel rebuilt the "Move to section" submenu list via getCustomSectionData() inside every snapshot rebuild - i.e. on every notification, name, tag or call update of every live room item - even though the available sections only change when the custom section settings change. Each call rebuilds the record from settings and does an O(spaces) existence check per section. Cache the available sections (tag + name) in the view model and rebuild them only from the settings watchers; per-room snapshots now only derive the cheap isSelected flag. Also watch RoomList.CustomSectionData so section renames propagate to the submenu (previously only the order setting was watched).
This commit is contained in:
@@ -42,6 +42,11 @@ import RoomListStoreV3 from "../../stores/room-list-v3/RoomListStoreV3";
|
||||
import { getCustomSectionData, isDefaultSectionTag } from "../../stores/room-list-v3/section";
|
||||
import { _t } from "../../languageHandler";
|
||||
|
||||
/**
|
||||
* View section type without `isSelected` field
|
||||
*/
|
||||
type Sections = Array<Omit<Section, "isSelected">>;
|
||||
|
||||
interface RoomItemProps {
|
||||
room: Room;
|
||||
client: MatrixClient;
|
||||
@@ -61,14 +66,29 @@ export class RoomListItemViewModel
|
||||
* Track the current call for this room to manager listeners
|
||||
*/
|
||||
private currentCall: Call | null = null;
|
||||
/**
|
||||
* The sections available in the "Move to section" submenu (tag + display name), identical
|
||||
* for every update of this item. Computing it reads the custom section settings, so it is
|
||||
* built once and rebuilt only when those settings change (see {@link onCustomSectionsChange})
|
||||
* rather than on every snapshot rebuild; only the per-room isSelected flag is derived per
|
||||
* snapshot (see {@link buildSections}).
|
||||
*/
|
||||
private availableSections: Sections;
|
||||
|
||||
public constructor(props: RoomItemProps) {
|
||||
// Get notification state first so we can generate a complete initial snapshot
|
||||
const notifState = RoomNotificationStateStore.instance.getRoomState(props.room);
|
||||
const initialItem = RoomListItemViewModel.generateItemSync(props.room, props.client, notifState);
|
||||
const availableSections = RoomListItemViewModel.computeAvailableSections();
|
||||
const initialItem = RoomListItemViewModel.generateItemSync(
|
||||
props.room,
|
||||
props.client,
|
||||
notifState,
|
||||
availableSections,
|
||||
);
|
||||
super(props, initialItem);
|
||||
|
||||
this.notifState = notifState;
|
||||
this.availableSections = availableSections;
|
||||
|
||||
// Subscribe to notification state changes for this room
|
||||
this.disposables.trackListener(this.notifState, NotificationStateEvents.Update, this.onNotificationChanged);
|
||||
@@ -99,11 +119,16 @@ export class RoomListItemViewModel
|
||||
this.disposables.trackListener(props.room, RoomEvent.Name, this.onRoomChanged);
|
||||
this.disposables.trackListener(props.room, RoomEvent.Tags, this.onRoomChanged);
|
||||
|
||||
// Rebuild the available sections when their order changes or when one is created/renamed/removed
|
||||
const orderSectionsRef = SettingsStore.watchSetting("RoomList.OrderedCustomSections", null, () =>
|
||||
this.onOrderedCustomSectionsChange(),
|
||||
this.onCustomSectionsChange(),
|
||||
);
|
||||
const sectionDataRef = SettingsStore.watchSetting("RoomList.CustomSectionData", null, () =>
|
||||
this.onCustomSectionsChange(),
|
||||
);
|
||||
this.disposables.track(() => {
|
||||
SettingsStore.unwatchSetting(orderSectionsRef);
|
||||
SettingsStore.unwatchSetting(sectionDataRef);
|
||||
});
|
||||
|
||||
// Load message preview asynchronously (sync data is already complete)
|
||||
@@ -187,7 +212,12 @@ export class RoomListItemViewModel
|
||||
* Preserves the message preview which is managed separately.
|
||||
*/
|
||||
private updateItem(): void {
|
||||
const newItem = RoomListItemViewModel.generateItemSync(this.props.room, this.props.client, this.notifState);
|
||||
const newItem = RoomListItemViewModel.generateItemSync(
|
||||
this.props.room,
|
||||
this.props.client,
|
||||
this.notifState,
|
||||
this.availableSections,
|
||||
);
|
||||
this.snapshot.merge({
|
||||
...newItem,
|
||||
notification: keepIfSame(this.snapshot.current.notification, newItem.notification),
|
||||
@@ -233,6 +263,7 @@ export class RoomListItemViewModel
|
||||
room: Room,
|
||||
client: MatrixClient,
|
||||
notifState: RoomNotificationState,
|
||||
availableSections: Sections,
|
||||
): RoomListItemViewSnapshot {
|
||||
// Get room tags for menu state
|
||||
const roomTags = room.tags;
|
||||
@@ -289,7 +320,7 @@ export class RoomListItemViewModel
|
||||
call?.callType === CallType.Voice ? "voice" : call?.callType === CallType.Video ? "video" : undefined;
|
||||
|
||||
// Build sections list for the "Move to section" submenu
|
||||
const sections: Section[] = RoomListItemViewModel.buildSections(roomTags);
|
||||
const sections: Section[] = RoomListItemViewModel.buildSections(roomTags, availableSections);
|
||||
|
||||
return {
|
||||
id: room.roomId,
|
||||
@@ -420,17 +451,20 @@ export class RoomListItemViewModel
|
||||
}
|
||||
};
|
||||
|
||||
private onOrderedCustomSectionsChange = (): void => {
|
||||
// Rebuild sections list to reflect new order
|
||||
const sections = RoomListItemViewModel.buildSections(this.props.room.tags);
|
||||
private onCustomSectionsChange = (): void => {
|
||||
// Rebuild the available sections and the sections list to reflect the new settings
|
||||
this.availableSections = RoomListItemViewModel.computeAvailableSections();
|
||||
const sections = RoomListItemViewModel.buildSections(this.props.room.tags, this.availableSections);
|
||||
this.snapshot.merge({ sections: keepIfSame(this.snapshot.current.sections, sections) });
|
||||
};
|
||||
|
||||
/**
|
||||
* Build the list of available sections for the "Move to section" submenu.
|
||||
* Compute the sections available in the "Move to section" submenu.
|
||||
* Order follows the canonical section order from RoomListStoreV3.
|
||||
* Reads the custom section settings, so callers should cache the result and recompute only
|
||||
* when those settings change.
|
||||
*/
|
||||
private static buildSections(roomTags: Room["tags"]): Section[] {
|
||||
private static computeAvailableSections(): Sections {
|
||||
const customSectionData = getCustomSectionData();
|
||||
|
||||
return (
|
||||
@@ -441,11 +475,22 @@ export class RoomListItemViewModel
|
||||
.map((tag) => ({
|
||||
tag,
|
||||
name: RoomListItemViewModel.getSectionName(tag, customSectionData),
|
||||
isSelected: Boolean(roomTags[tag]),
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the list of sections for the "Move to section" submenu by deriving the per-room
|
||||
* selected state from the room's tags.
|
||||
*/
|
||||
private static buildSections(roomTags: Room["tags"], availableSections: Sections): Section[] {
|
||||
return availableSections.map(({ tag, name }) => ({
|
||||
tag,
|
||||
name,
|
||||
isSelected: Boolean(roomTags[tag]),
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the display name for a section based on its tag.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user