frontend: Refactors selection logic in lists
Ensures the selected items in lists are correctly updated when the underlying data changes by using `untracked` to avoid circular dependencies. Introduces a utility function to compare sets for equality, preventing unnecessary updates and improving performance.
This commit is contained in:
parent
eabb559a82
commit
68a10ff901
@ -1,14 +1,5 @@
|
||||
import { CommonModule, DatePipe } from '@angular/common';
|
||||
import {
|
||||
Component,
|
||||
computed,
|
||||
effect,
|
||||
EventEmitter,
|
||||
input,
|
||||
OnInit,
|
||||
Output,
|
||||
signal
|
||||
} from '@angular/core';
|
||||
import { Component, computed, effect, EventEmitter, input, OnInit, Output, signal, untracked } from '@angular/core';
|
||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatBadgeModule } from '@angular/material/badge';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
@ -26,6 +17,7 @@ import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { MeetRecordingInfo, MeetRecordingStatus } from '@openvidu-meet/typings';
|
||||
import { ViewportService } from 'openvidu-components-angular';
|
||||
import { setsAreEqual } from '../../../../shared/utils/array.utils';
|
||||
import { formatBytes, formatDurationToHMS } from '../../../../shared/utils/format.utils';
|
||||
import { RecordingTableAction, RecordingTableFilter } from '../../models/recording-list.model';
|
||||
|
||||
@ -155,10 +147,17 @@ export class RecordingListsComponent implements OnInit {
|
||||
effect(() => {
|
||||
// Update selected recordings based on current recordings
|
||||
const recordings = this.recordings();
|
||||
const validIds = new Set(recordings.map((r) => r.recordingId));
|
||||
const filteredSelection = new Set([...this.selectedRecordings()].filter((id) => validIds.has(id)));
|
||||
this.selectedRecordings.set(filteredSelection);
|
||||
this.updateSelectionState();
|
||||
const validRecordingIds = new Set(recordings.map((r) => r.recordingId));
|
||||
|
||||
// Use untracked to avoid circular dependency in effect
|
||||
const currentSelection = untracked(() => this.selectedRecordings());
|
||||
const filteredSelection = new Set([...currentSelection].filter((id) => validRecordingIds.has(id)));
|
||||
|
||||
// Only update if the selection has actually changed
|
||||
if (!setsAreEqual(filteredSelection, currentSelection)) {
|
||||
this.selectedRecordings.set(filteredSelection);
|
||||
this.updateSelectionState();
|
||||
}
|
||||
|
||||
// Show message when no recordings match filters
|
||||
this.showEmptyFilterMessage = recordings.length === 0 && this.hasActiveFilters();
|
||||
@ -170,7 +169,7 @@ export class RecordingListsComponent implements OnInit {
|
||||
this.updateDisplayedColumns();
|
||||
|
||||
// Calculate showEmptyFilterMessage based on initial state
|
||||
this.showEmptyFilterMessage = this.recordings.length === 0 && this.hasActiveFilters();
|
||||
this.showEmptyFilterMessage = this.recordings().length === 0 && this.hasActiveFilters();
|
||||
}
|
||||
|
||||
// ===== INITIALIZATION METHODS =====
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { CommonModule, DatePipe } from '@angular/common';
|
||||
import { Component, effect, EventEmitter, HostBinding, input, OnInit, Output, signal } from '@angular/core';
|
||||
import { Component, effect, EventEmitter, HostBinding, input, OnInit, Output, signal, untracked } from '@angular/core';
|
||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatBadgeModule } from '@angular/material/badge';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
@ -16,6 +16,7 @@ import { MatTableModule } from '@angular/material/table';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { MeetingEndAction, MeetRoom, MeetRoomStatus } from '@openvidu-meet/typings';
|
||||
import { setsAreEqual } from '../../../../shared/utils/array.utils';
|
||||
|
||||
export interface RoomTableAction {
|
||||
rooms: MeetRoom[];
|
||||
@ -144,10 +145,18 @@ export class RoomsListsComponent implements OnInit {
|
||||
effect(() => {
|
||||
// Update selected rooms based on current rooms
|
||||
const rooms = this.rooms();
|
||||
const validIds = new Set(rooms.map((r) => r.roomId));
|
||||
const filteredSelection = new Set([...this.selectedRooms()].filter((id) => validIds.has(id)));
|
||||
this.selectedRooms.set(filteredSelection);
|
||||
this.updateSelectionState();
|
||||
const validRoomIds = new Set(rooms.map((r) => r.roomId));
|
||||
|
||||
// Use untracked to avoid creating a reactive dependency on selectedRooms
|
||||
const currentSelection = untracked(() => this.selectedRooms());
|
||||
const filteredSelection = new Set([...currentSelection].filter((id) => validRoomIds.has(id)));
|
||||
|
||||
|
||||
// Only update if the selection has actually changed
|
||||
if (!setsAreEqual(filteredSelection, currentSelection)) {
|
||||
this.selectedRooms.set(filteredSelection);
|
||||
this.updateSelectionState();
|
||||
}
|
||||
|
||||
// Show message when no rooms match filters
|
||||
this.showEmptyFilterMessage = rooms.length === 0 && this.hasActiveFilters();
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
export const setsAreEqual = <T>(setA: Set<T>, setB: Set<T>): boolean => {
|
||||
if (setA.size !== setB.size) return false;
|
||||
for (const item of setA) {
|
||||
if (!setB.has(item)) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
export const arraysAreEqual = <T>(arrayA: T[], arrayB: T[]): boolean => {
|
||||
if (arrayA.length !== arrayB.length) return false;
|
||||
for (let i = 0; i < arrayA.length; i++) {
|
||||
if (arrayA[i] !== arrayB[i]) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
@ -1,2 +1,4 @@
|
||||
export * from './array.utils';
|
||||
export * from './format.utils';
|
||||
export * from './token.utils';
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user