frontend: enhance loading state management in recordings components
This commit is contained in:
parent
f8729e4240
commit
5f289d12b8
@ -101,7 +101,7 @@ export class RecordingListsComponent implements OnInit, OnChanges {
|
|||||||
@Output() recordingAction = new EventEmitter<RecordingTableAction>();
|
@Output() recordingAction = new EventEmitter<RecordingTableAction>();
|
||||||
@Output() filterChange = new EventEmitter<{ nameFilter: string; statusFilter: string }>();
|
@Output() filterChange = new EventEmitter<{ nameFilter: string; statusFilter: string }>();
|
||||||
@Output() loadMore = new EventEmitter<{ nameFilter: string; statusFilter: string }>();
|
@Output() loadMore = new EventEmitter<{ nameFilter: string; statusFilter: string }>();
|
||||||
@Output() refresh = new EventEmitter<void>();
|
@Output() refresh = new EventEmitter<{ nameFilter: string; statusFilter: string }>();
|
||||||
|
|
||||||
// Filter controls
|
// Filter controls
|
||||||
nameFilterControl = new FormControl('');
|
nameFilterControl = new FormControl('');
|
||||||
@ -284,6 +284,12 @@ export class RecordingListsComponent implements OnInit, OnChanges {
|
|||||||
this.loadMore.emit({ nameFilter, statusFilter });
|
this.loadMore.emit({ nameFilter, statusFilter });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshRecordings() {
|
||||||
|
const nameFilter = this.nameFilterControl.value || '';
|
||||||
|
const statusFilter = this.statusFilterControl.value || '';
|
||||||
|
this.refresh.emit({ nameFilter, statusFilter });
|
||||||
|
}
|
||||||
|
|
||||||
// ===== FILTER METHODS =====
|
// ===== FILTER METHODS =====
|
||||||
|
|
||||||
hasActiveFilters(): boolean {
|
hasActiveFilters(): boolean {
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
@if (isLoading) {
|
<!-- Loading State -->
|
||||||
@if (showLoadingSpinner) {
|
@if (isInitializing && showInitialLoader) {
|
||||||
<!-- Enhanced Loading State with delay -->
|
|
||||||
<div class="ov-page-loading">
|
<div class="ov-page-loading">
|
||||||
<div class="loading-content">
|
<div class="loading-content">
|
||||||
<div class="loading-header">
|
<div class="loading-header">
|
||||||
@ -16,9 +15,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
} @else {
|
|
||||||
|
@if (!isInitializing) {
|
||||||
<div class="ov-page-container ov-mb-xxl">
|
<div class="ov-page-container ov-mb-xxl">
|
||||||
|
<!-- Recordings Header -->
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<mat-icon class="ov-recording-icon">video_library</mat-icon>
|
<mat-icon class="ov-recording-icon">video_library</mat-icon>
|
||||||
@ -27,6 +28,7 @@
|
|||||||
<p class="subtitle">Manage all your meeting recordings, play, download, and share them with others.</p>
|
<p class="subtitle">Manage all your meeting recordings, play, download, and share them with others.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Content -->
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
<ov-recording-lists
|
<ov-recording-lists
|
||||||
[recordings]="recordings()"
|
[recordings]="recordings()"
|
||||||
@ -38,7 +40,7 @@
|
|||||||
[showLoadMore]="hasMoreRecordings"
|
[showLoadMore]="hasMoreRecordings"
|
||||||
(recordingAction)="onRecordingAction($event)"
|
(recordingAction)="onRecordingAction($event)"
|
||||||
(loadMore)="loadMoreRecordings($event)"
|
(loadMore)="loadMoreRecordings($event)"
|
||||||
(refresh)="refreshRecordings()"
|
(refresh)="refreshRecordings($event)"
|
||||||
(filterChange)="refreshRecordings($event)"
|
(filterChange)="refreshRecordings($event)"
|
||||||
>
|
>
|
||||||
</ov-recording-lists>
|
</ov-recording-lists>
|
||||||
|
|||||||
@ -16,8 +16,11 @@ import { ILogger, LoggerService } from 'openvidu-components-angular';
|
|||||||
})
|
})
|
||||||
export class RecordingsComponent implements OnInit {
|
export class RecordingsComponent implements OnInit {
|
||||||
recordings = signal<MeetRecordingInfo[]>([]);
|
recordings = signal<MeetRecordingInfo[]>([]);
|
||||||
|
|
||||||
|
// Loading state
|
||||||
|
isInitializing = true;
|
||||||
|
showInitialLoader = false;
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
showLoadingSpinner = false;
|
|
||||||
|
|
||||||
// Pagination
|
// Pagination
|
||||||
hasMoreRecordings = false;
|
hasMoreRecordings = false;
|
||||||
@ -36,6 +39,10 @@ export class RecordingsComponent implements OnInit {
|
|||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
const roomId = this.route.snapshot.queryParamMap.get('room-id');
|
const roomId = this.route.snapshot.queryParamMap.get('room-id');
|
||||||
|
const delayLoader = setTimeout(() => {
|
||||||
|
this.showInitialLoader = true;
|
||||||
|
}, 200);
|
||||||
|
|
||||||
if (roomId) {
|
if (roomId) {
|
||||||
// If a specific room ID is provided, filter recordings by that room
|
// If a specific room ID is provided, filter recordings by that room
|
||||||
await this.loadRecordings({ nameFilter: roomId, statusFilter: '' });
|
await this.loadRecordings({ nameFilter: roomId, statusFilter: '' });
|
||||||
@ -43,6 +50,10 @@ export class RecordingsComponent implements OnInit {
|
|||||||
// Load all recordings if no room ID is specified
|
// Load all recordings if no room ID is specified
|
||||||
await this.loadRecordings();
|
await this.loadRecordings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearTimeout(delayLoader);
|
||||||
|
this.showInitialLoader = false;
|
||||||
|
this.isInitializing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async onRecordingAction(action: RecordingTableAction) {
|
async onRecordingAction(action: RecordingTableAction) {
|
||||||
@ -68,16 +79,15 @@ export class RecordingsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadRecordings(filters?: { nameFilter: string; statusFilter: string }) {
|
private async loadRecordings(filters?: { nameFilter: string; statusFilter: string }, refresh = false) {
|
||||||
|
const delayLoader = setTimeout(() => {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
const delaySpinner = setTimeout(() => {
|
|
||||||
this.showLoadingSpinner = true;
|
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const recordingFilters: MeetRecordingFilters = {
|
const recordingFilters: MeetRecordingFilters = {
|
||||||
maxItems: 50,
|
maxItems: 50,
|
||||||
nextPageToken: this.nextPageToken
|
nextPageToken: !refresh ? this.nextPageToken : undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
// Apply room ID filter if provided
|
// Apply room ID filter if provided
|
||||||
@ -93,9 +103,14 @@ export class RecordingsComponent implements OnInit {
|
|||||||
filteredRecordings = response.recordings.filter((r) => r.status === filters.statusFilter);
|
filteredRecordings = response.recordings.filter((r) => r.status === filters.statusFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!refresh) {
|
||||||
// Update recordings list
|
// Update recordings list
|
||||||
const currentRecordings = this.recordings();
|
const currentRecordings = this.recordings();
|
||||||
this.recordings.set([...currentRecordings, ...filteredRecordings]);
|
this.recordings.set([...currentRecordings, ...filteredRecordings]);
|
||||||
|
} else {
|
||||||
|
// Replace recordings list
|
||||||
|
this.recordings.set(filteredRecordings);
|
||||||
|
}
|
||||||
|
|
||||||
// Update pagination
|
// Update pagination
|
||||||
this.nextPageToken = response.pagination.nextPageToken;
|
this.nextPageToken = response.pagination.nextPageToken;
|
||||||
@ -104,9 +119,8 @@ export class RecordingsComponent implements OnInit {
|
|||||||
this.notificationService.showAlert('Failed to load recordings');
|
this.notificationService.showAlert('Failed to load recordings');
|
||||||
this.log.e('Error loading recordings:', error);
|
this.log.e('Error loading recordings:', error);
|
||||||
} finally {
|
} finally {
|
||||||
|
clearTimeout(delayLoader);
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
clearTimeout(delaySpinner);
|
|
||||||
this.showLoadingSpinner = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,10 +130,7 @@ export class RecordingsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async refreshRecordings(filters?: { nameFilter: string; statusFilter: string }) {
|
async refreshRecordings(filters?: { nameFilter: string; statusFilter: string }) {
|
||||||
this.recordings.set([]);
|
await this.loadRecordings(filters, true);
|
||||||
this.nextPageToken = undefined;
|
|
||||||
this.hasMoreRecordings = false;
|
|
||||||
await this.loadRecordings(filters);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async playRecording(recording: MeetRecordingInfo) {
|
private async playRecording(recording: MeetRecordingInfo) {
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Loading State -->
|
<!-- Loading State -->
|
||||||
@if (isLoading && showLoadingSpinner) {
|
@if (isInitializing && showInitialLoader) {
|
||||||
<div class="recordings-loading-container fade-in">
|
<div class="recordings-loading-container fade-in">
|
||||||
<div class="loading-card">
|
<div class="loading-card">
|
||||||
<mat-icon class="ov-recording-icon loading-icon">video_library</mat-icon>
|
<mat-icon class="ov-recording-icon loading-icon">video_library</mat-icon>
|
||||||
@ -35,7 +35,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
@if (!isLoading || recordings().length > 0) {
|
@if (!isInitializing) {
|
||||||
<div class="recordings-content fade-in-delayed">
|
<div class="recordings-content fade-in-delayed">
|
||||||
<div class="section-content">
|
<div class="section-content">
|
||||||
<ov-recording-lists
|
<ov-recording-lists
|
||||||
@ -46,7 +46,9 @@
|
|||||||
[showFilters]="false"
|
[showFilters]="false"
|
||||||
[showSelection]="true"
|
[showSelection]="true"
|
||||||
[showRoomInfo]="false"
|
[showRoomInfo]="false"
|
||||||
|
[showLoadMore]="hasMoreRecordings"
|
||||||
(recordingAction)="onRecordingAction($event)"
|
(recordingAction)="onRecordingAction($event)"
|
||||||
|
(loadMore)="loadMoreRecordings()"
|
||||||
(refresh)="refreshRecordings()"
|
(refresh)="refreshRecordings()"
|
||||||
class="recordings-list"
|
class="recordings-list"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -22,8 +22,10 @@ export class RoomRecordingsComponent implements OnInit {
|
|||||||
roomName = '';
|
roomName = '';
|
||||||
canDeleteRecordings = false;
|
canDeleteRecordings = false;
|
||||||
|
|
||||||
|
// Loading state
|
||||||
|
isInitializing = true;
|
||||||
|
showInitialLoader = false;
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
showLoadingSpinner = false;
|
|
||||||
|
|
||||||
// Pagination
|
// Pagination
|
||||||
hasMoreRecordings = false;
|
hasMoreRecordings = false;
|
||||||
@ -45,8 +47,18 @@ export class RoomRecordingsComponent implements OnInit {
|
|||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.roomId = this.route.snapshot.paramMap.get('room-id')!;
|
this.roomId = this.route.snapshot.paramMap.get('room-id')!;
|
||||||
this.canDeleteRecordings = this.recordingService.canDeleteRecordings();
|
this.canDeleteRecordings = this.recordingService.canDeleteRecordings();
|
||||||
|
|
||||||
|
// Load recordings
|
||||||
|
const delayLoader = setTimeout(() => {
|
||||||
|
this.showInitialLoader = true;
|
||||||
|
}, 200);
|
||||||
|
|
||||||
await this.loadRecordings();
|
await this.loadRecordings();
|
||||||
|
|
||||||
|
clearTimeout(delayLoader);
|
||||||
|
this.showInitialLoader = false;
|
||||||
|
this.isInitializing = false;
|
||||||
|
|
||||||
// Set room name based on recordings or roomId
|
// Set room name based on recordings or roomId
|
||||||
if (this.recordings()) {
|
if (this.recordings()) {
|
||||||
this.roomName = this.recordings()[0].roomName;
|
this.roomName = this.recordings()[0].roomName;
|
||||||
@ -89,17 +101,16 @@ export class RoomRecordingsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadRecordings(statusFilter?: string) {
|
private async loadRecordings(statusFilter?: string, refresh = false) {
|
||||||
|
const delayLoader = setTimeout(() => {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
const delaySpinner = setTimeout(() => {
|
|
||||||
this.showLoadingSpinner = true;
|
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const recordingFilters: MeetRecordingFilters = {
|
const recordingFilters: MeetRecordingFilters = {
|
||||||
roomId: this.roomId,
|
roomId: this.roomId,
|
||||||
maxItems: 50,
|
maxItems: 50,
|
||||||
nextPageToken: this.nextPageToken
|
nextPageToken: !refresh ? this.nextPageToken : undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await this.recordingService.listRecordings(recordingFilters);
|
const response = await this.recordingService.listRecordings(recordingFilters);
|
||||||
@ -110,9 +121,14 @@ export class RoomRecordingsComponent implements OnInit {
|
|||||||
filteredRecordings = response.recordings.filter((r) => r.status === statusFilter);
|
filteredRecordings = response.recordings.filter((r) => r.status === statusFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!refresh) {
|
||||||
// Update recordings list
|
// Update recordings list
|
||||||
const currentRecordings = this.recordings();
|
const currentRecordings = this.recordings();
|
||||||
this.recordings.set([...currentRecordings, ...filteredRecordings]);
|
this.recordings.set([...currentRecordings, ...filteredRecordings]);
|
||||||
|
} else {
|
||||||
|
// Replace recordings list
|
||||||
|
this.recordings.set(filteredRecordings);
|
||||||
|
}
|
||||||
|
|
||||||
// Update pagination
|
// Update pagination
|
||||||
this.nextPageToken = response.pagination.nextPageToken;
|
this.nextPageToken = response.pagination.nextPageToken;
|
||||||
@ -121,9 +137,8 @@ export class RoomRecordingsComponent implements OnInit {
|
|||||||
this.notificationService.showAlert('Failed to load recordings');
|
this.notificationService.showAlert('Failed to load recordings');
|
||||||
this.log.e('Error loading recordings:', error);
|
this.log.e('Error loading recordings:', error);
|
||||||
} finally {
|
} finally {
|
||||||
|
clearTimeout(delayLoader);
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
clearTimeout(delaySpinner);
|
|
||||||
this.showLoadingSpinner = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,10 +148,7 @@ export class RoomRecordingsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async refreshRecordings() {
|
async refreshRecordings() {
|
||||||
this.recordings.set([]);
|
await this.loadRecordings(undefined, true);
|
||||||
this.nextPageToken = undefined;
|
|
||||||
this.hasMoreRecordings = false;
|
|
||||||
await this.loadRecordings();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async playRecording(recording: MeetRecordingInfo) {
|
private async playRecording(recording: MeetRecordingInfo) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user