diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/components/recording-lists/recording-lists.component.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/components/recording-lists/recording-lists.component.ts index e20148ad..951bb44e 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/components/recording-lists/recording-lists.component.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/recordings/components/recording-lists/recording-lists.component.ts @@ -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 ===== diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/components/rooms-lists/rooms-lists.component.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/components/rooms-lists/rooms-lists.component.ts index c58f1636..afc70448 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/components/rooms-lists/rooms-lists.component.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/components/rooms-lists/rooms-lists.component.ts @@ -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(); diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/utils/array.utils.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/utils/array.utils.ts new file mode 100644 index 00000000..48e18d21 --- /dev/null +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/utils/array.utils.ts @@ -0,0 +1,14 @@ +export const setsAreEqual = (setA: Set, setB: Set): boolean => { + if (setA.size !== setB.size) return false; + for (const item of setA) { + if (!setB.has(item)) return false; + } + return true; +}; +export const arraysAreEqual = (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; +}; diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/utils/index.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/utils/index.ts index e71e1cb7..89fe6278 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/utils/index.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/utils/index.ts @@ -1,2 +1,4 @@ +export * from './array.utils'; export * from './format.utils'; export * from './token.utils'; +