Implemented virtual scrolling for notifications (helps if many notifications exist)

This commit is contained in:
Tzahi12345
2023-05-23 22:28:23 -04:00
parent a6478a50f2
commit d18fe70002
6 changed files with 61 additions and 32 deletions

View File

@@ -34,6 +34,7 @@ import { MatBadgeModule } from '@angular/material/badge';
import { DragDropModule } from '@angular/cdk/drag-drop'; import { DragDropModule } from '@angular/cdk/drag-drop';
import { ClipboardModule } from '@angular/cdk/clipboard'; import { ClipboardModule } from '@angular/cdk/clipboard';
import { TextFieldModule } from '@angular/cdk/text-field'; import { TextFieldModule } from '@angular/cdk/text-field';
import { ScrollingModule } from '@angular/cdk/scrolling';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
@@ -189,6 +190,7 @@ registerLocaleData(es, 'es');
DragDropModule, DragDropModule,
ClipboardModule, ClipboardModule,
TextFieldModule, TextFieldModule,
ScrollingModule,
NgxFileDropModule, NgxFileDropModule,
AvatarModule, AvatarModule,
ContentLoaderModule, ContentLoaderModule,

View File

@@ -1,30 +1,32 @@
<div class="card-radius mat-elevation-z2" *ngFor="let notification of notifications; let i = index;"> <cdk-virtual-scroll-viewport itemSize="50" class="viewport" minBufferPx="1200" maxBufferPx="1200">
<mat-card class="notification-card card-radius"> <div #notification_parent class="notification-card-parent card-radius mat-elevation-z2" *cdkVirtualFor="let notification of notifications; let i = index;">
<mat-card-header> <mat-card class="notification-card card-radius">
<mat-card-subtitle> <mat-card-header>
<div> <mat-card-subtitle>
<span class="notification-timestamp">{{notification.timestamp * 1000 | date:'short'}}</span> <div>
</div> <span class="notification-timestamp">{{notification.timestamp * 1000 | date:'short'}}</span>
</mat-card-subtitle> </div>
<mat-card-title> </mat-card-subtitle>
<ng-container *ngIf="NOTIFICATION_PREFIX[notification.type]"> <mat-card-title>
{{NOTIFICATION_PREFIX[notification.type]}} <ng-container *ngIf="NOTIFICATION_PREFIX[notification.type]">
{{NOTIFICATION_PREFIX[notification.type]}}
</ng-container>
</mat-card-title>
</mat-card-header>
<mat-card-content>
<ng-container *ngIf="NOTIFICATION_SUFFIX_KEY[notification.type]">
<div style="word-break: break-word">
{{notification['data'][NOTIFICATION_SUFFIX_KEY[notification.type]]}}
</div>
</ng-container> </ng-container>
</mat-card-title> </mat-card-content>
</mat-card-header> <mat-card-actions class="notification-actions" *ngIf="notification.actions?.length > 0">
<mat-card-content> <button matTooltip="Remove" i18n-matTooltip="Remove" (click)="emitDeleteNotification(notification.uid)" mat-icon-button><mat-icon>close</mat-icon></button>
<ng-container *ngIf="NOTIFICATION_SUFFIX_KEY[notification.type]"> <span *ngFor="let action of notification.actions">
<div style="word-break: break-word"> <button [matTooltip]="NOTIFICATION_ACTION_TO_STRING[action]" (click)="emitNotificationAction(notification, action)" mat-icon-button><mat-icon>{{NOTIFICATION_ICON[action]}}</mat-icon></button>
{{notification['data'][NOTIFICATION_SUFFIX_KEY[notification.type]]}} </span>
</div> </mat-card-actions>
</ng-container> <span *ngIf="!notification.read" class="dot"></span>
</mat-card-content> </mat-card>
<mat-card-actions *ngIf="notification.actions?.length > 0"> </div>
<button matTooltip="Remove" i18n-matTooltip="Remove" (click)="emitDeleteNotification(notification.uid)" mat-icon-button><mat-icon>close</mat-icon></button> </cdk-virtual-scroll-viewport>
<span *ngFor="let action of notification.actions">
<button [matTooltip]="NOTIFICATION_ACTION_TO_STRING[action]" (click)="emitNotificationAction(notification, action)" mat-icon-button><mat-icon>{{NOTIFICATION_ICON[action]}}</mat-icon></button>
</span>
</mat-card-actions>
<span *ngIf="!notification.read" class="dot"></span>
</mat-card>
</div>

View File

@@ -13,12 +13,21 @@
font-size: 14px; font-size: 14px;
} }
.notification-card-parent {
margin: 5px;
}
.notification-card { .notification-card {
margin-top: 5px; margin-top: 5px;
} }
.notification-actions {
margin-top: auto;
}
.card-radius { .card-radius {
border-radius: 12px; border-radius: 12px;
height: 166px;
} }
.dot { .dot {
@@ -30,4 +39,8 @@
position: absolute; position: absolute;
right: 8px; right: 8px;
top: 8px; top: 8px;
} }
.viewport {
height: 100%;
}

View File

@@ -4,7 +4,10 @@
} }
.notifications-list-parent { .notifications-list-parent {
max-height: 70vh;
overflow-y: auto; overflow-y: auto;
padding: 0px 10px 10px 10px; padding: 0px 10px 10px 10px;
}
.notifications-list {
display: block
} }

View File

@@ -4,7 +4,7 @@
<mat-chip-listbox [value]="selectedFilters" [multiple]="true" (change)="selectedFiltersChanged($event)"> <mat-chip-listbox [value]="selectedFilters" [multiple]="true" (change)="selectedFiltersChanged($event)">
<mat-chip-option *ngFor="let filter of notificationFilters | keyvalue: originalOrder" [value]="filter.key" [selected]="selectedFilters.includes(filter.key)" color="accent">{{filter.value.label}}</mat-chip-option> <mat-chip-option *ngFor="let filter of notificationFilters | keyvalue: originalOrder" [value]="filter.key" [selected]="selectedFilters.includes(filter.key)" color="accent">{{filter.value.label}}</mat-chip-option>
</mat-chip-listbox> </mat-chip-listbox>
<app-notifications-list (notificationAction)="notificationAction($event)" (deleteNotification)="deleteNotification($event)" [notifications]="filtered_notifications"></app-notifications-list> <app-notifications-list class="notifications-list" [style.height]="list_height" (notificationAction)="notificationAction($event)" (deleteNotification)="deleteNotification($event)" [notifications]="filtered_notifications"></app-notifications-list>
</div> </div>
<button style="margin: 10px 0px 2px 10px;" *ngIf="notifications?.length > 0" color="warn" (click)="deleteAllNotifications()" mat-stroked-button>Remove all</button> <button style="margin: 10px 0px 2px 10px;" *ngIf="notifications?.length > 0" color="warn" (click)="deleteAllNotifications()" mat-stroked-button>Remove all</button>
</div> </div>

View File

@@ -14,6 +14,7 @@ export class NotificationsComponent implements OnInit {
notifications: Notification[] = null; notifications: Notification[] = null;
filtered_notifications: Notification[] = null; filtered_notifications: Notification[] = null;
list_height = '65vh';
@Output() notificationCount = new EventEmitter<number>(); @Output() notificationCount = new EventEmitter<number>();
@@ -110,6 +111,8 @@ export class NotificationsComponent implements OnInit {
filterNotifications(): void { filterNotifications(): void {
this.filtered_notifications = this.notifications.filter(notification => this.selectedFilters.length === 0 || this.selectedFilters.includes(notification.type)); this.filtered_notifications = this.notifications.filter(notification => this.selectedFilters.length === 0 || this.selectedFilters.includes(notification.type));
// We need to do this to get the virtual scroll component to have an appropriate height
this.calculateListHeight();
} }
selectedFiltersChanged(event: MatChipListboxChange): void { selectedFiltersChanged(event: MatChipListboxChange): void {
@@ -117,6 +120,12 @@ export class NotificationsComponent implements OnInit {
this.filterNotifications(); this.filterNotifications();
} }
calculateListHeight() {
const avgHeight = 166;
const calcHeight = this.filtered_notifications.length * avgHeight;
this.list_height = calcHeight > window.innerHeight*0.65 ? '65vh' : `${calcHeight}px`;
}
originalOrder = (): number => { originalOrder = (): number => {
return 0; return 0;
} }