RED-3796: Dossiers cache for notifications

This commit is contained in:
Adina Țeudan 2022-05-05 18:01:47 +03:00
parent e1be225c17
commit 3739e7a468
6 changed files with 73 additions and 24 deletions

View File

@ -12,6 +12,7 @@ import { FeaturesGuard } from '@guards/features-guard.service';
import { ARCHIVE_ROUTE, DOSSIER_TEMPLATE_ID, DOSSIERS_ARCHIVE, DOSSIERS_ROUTE } from '@utils/constants';
import { DossierTemplatesGuard } from '@guards/dossier-templates.guard';
import { DossierTemplateExistsGuard } from '@guards/dossier-template-exists.guard';
import { DashboardGuard } from '@guards/dashboard-guard.service';
const routes: Routes = [
{
@ -41,7 +42,7 @@ const routes: Routes = [
loadChildren: () => import('./modules/dashboard/dashboard.module').then(m => m.DashboardModule),
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [AuthGuard, RedRoleGuard],
routeGuards: [AuthGuard, RedRoleGuard, DossierTemplatesGuard, DashboardGuard],
requiredRoles: ['RED_USER', 'RED_MANAGER'],
},
},
@ -75,7 +76,7 @@ const routes: Routes = [
],
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [AuthGuard, RedRoleGuard, DossierTemplatesGuard, DossierTemplateExistsGuard],
routeGuards: [AuthGuard, RedRoleGuard, DossierTemplatesGuard, DashboardGuard, DossierTemplateExistsGuard],
requiredRoles: ['RED_USER', 'RED_MANAGER'],
},
},

View File

@ -5,6 +5,7 @@ import { Notification } from '@red/domain';
import { distinctUntilChanged, map } from 'rxjs/operators';
import { firstValueFrom, Observable } from 'rxjs';
import { shareLast } from '@iqser/common-ui';
import { DossiersCacheService } from '@services/dossiers/dossiers-cache.service';
interface NotificationsGroup {
date: string;
@ -21,7 +22,11 @@ export class NotificationsComponent {
readonly hasUnreadNotifications$: Observable<boolean>;
readonly groupedNotifications$: Observable<NotificationsGroup[]>;
constructor(private readonly _notificationsService: NotificationsService, private readonly _datePipe: DatePipe) {
constructor(
private readonly _notificationsService: NotificationsService,
private readonly _datePipe: DatePipe,
private readonly _dossiersCacheService: DossiersCacheService,
) {
this.groupedNotifications$ = this._notificationsService.all$.pipe(map(notifications => this._groupNotifications(notifications)));
this.hasUnreadNotifications$ = this._hasUnreadNotifications$;
}

View File

@ -4,20 +4,15 @@ import { DashboardScreenComponent } from './dashboard-screen/dashboard-screen.co
import { RouterModule } from '@angular/router';
import { SharedModule } from '../shared/shared.module';
import { TemplateStatsComponent } from './components/template-stats/template-stats.component';
import { CompositeRouteGuard } from '@iqser/common-ui';
import { SharedDossiersModule } from '../dossier/shared/shared-dossiers.module';
import { BreadcrumbTypes } from '@red/domain';
import { DossierTemplatesGuard } from '../../guards/dossier-templates.guard';
import { DashboardGuard } from '../../guards/dashboard-guard.service';
const routes = [
{
path: '',
component: DashboardScreenComponent,
canActivate: [CompositeRouteGuard],
data: {
breadcrumbs: [BreadcrumbTypes.dashboard],
routeGuards: [DossierTemplatesGuard, DashboardGuard],
},
},
];

View File

@ -0,0 +1,49 @@
import { EventEmitter, Injectable } from '@angular/core';
import { ActiveDossiersService } from './active-dossiers.service';
import { ArchivedDossiersService } from './archived-dossiers.service';
import { firstValueFrom, forkJoin, merge } from 'rxjs';
import { map, skip, take } from 'rxjs/operators';
import { flatten } from 'lodash-es';
import { Dossier } from '@red/domain';
@Injectable({
providedIn: 'root',
})
export class DossiersCacheService {
readonly changed$ = new EventEmitter<void>();
private _dossiers: Dossier[] = JSON.parse(localStorage.getItem('dossiers')) || [];
constructor(
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _archivedDossiersService: ArchivedDossiersService,
) {
// Skip 1 to avoid clearing the cache when the dossier services are initialized
merge(_activeDossiersService.all$.pipe(skip(1)), _archivedDossiersService.all$.pipe(skip(1))).subscribe(() => {
this.set();
});
}
get empty(): boolean {
return !localStorage.getItem('dossiers');
}
async load(): Promise<void> {
await firstValueFrom(
forkJoin([this._activeDossiersService.loadAll().pipe(take(1)), this._archivedDossiersService.loadAll().pipe(take(1))]).pipe(
map(list => flatten(list)),
),
);
this.set();
}
set(): void {
const dossiers = flatten([this._activeDossiersService.all, this._archivedDossiersService.all]);
this._dossiers = dossiers;
localStorage.setItem('dossiers', JSON.stringify(dossiers));
this.changed$.emit();
}
get(dossierId: string) {
return this._dossiers.find(dossier => dossier.dossierId === dossierId);
}
}

View File

@ -6,12 +6,11 @@ import { Dossier, INotification, Notification, NotificationTypes } from '@red/do
import { map, switchMap, tap } from 'rxjs/operators';
import { notificationsTranslations } from '../translations/notifications-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { ActiveDossiersService } from './dossiers/active-dossiers.service';
import { UserService } from '@services/user.service';
import dayjs from 'dayjs';
import { CHANGED_CHECK_INTERVAL } from '@utils/constants';
import { BASE_HREF } from '../tokens';
import { ArchivedDossiersService } from '@services/dossiers/archived-dossiers.service';
import { DossiersCacheService } from '@services/dossiers/dossiers-cache.service';
const INCLUDE_SEEN = false;
@ -23,19 +22,22 @@ export class NotificationsService extends EntitiesService<Notification, INotific
@Inject(BASE_HREF) private readonly _baseHref: string,
protected readonly _injector: Injector,
private readonly _translateService: TranslateService,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _archivedDossiersService: ArchivedDossiersService,
private readonly _userService: UserService,
private readonly _dossiersCacheService: DossiersCacheService,
) {
super(_injector, Notification, 'notification');
timer(0, CHANGED_CHECK_INTERVAL)
.pipe(
switchMap(() => (this._activeDossiersService.all.length ? of(null) : this._activeDossiersService.loadAll())),
switchMap(() => (this._archivedDossiersService.all.length ? of(null) : this._archivedDossiersService.loadAll())),
switchMap(() => (this._dossiersCacheService.empty ? this._dossiersCacheService.load() : of(null))),
switchMap(() => this.#loadNotificationsIfChanged()),
)
.subscribe();
// Rebuild notifications when cached dossiers are updated
this._dossiersCacheService.changed$.subscribe(() => {
this.setEntities(this.all.map(e => this._new(e)));
});
}
@Validate()
@ -75,7 +77,7 @@ export class NotificationsService extends EntitiesService<Notification, INotific
private _translate(notification: INotification, translation: string): string {
const fileId = notification.target.fileId;
const dossierId = notification.target.dossierId;
const dossier = this._activeDossiersService.find(dossierId) || this._archivedDossiersService.find(dossierId);
const dossier = this._dossiersCacheService.get(dossierId);
const fileName = notification.target.fileName;
return this._translateService.instant(translation, {

View File

@ -23,6 +23,8 @@ export class Dossier implements IDossier, IListable {
readonly watermarkPreviewEnabled: boolean;
readonly archivedTime: string;
readonly hasReviewers: boolean;
readonly routerLink: string;
readonly dossiersListRouterLink: string;
constructor(dossier: IDossier) {
this.dossierId = dossier.dossierId;
@ -44,21 +46,16 @@ export class Dossier implements IDossier, IListable {
this.watermarkPreviewEnabled = dossier.watermarkPreviewEnabled;
this.archivedTime = dossier.archivedTime;
this.hasReviewers = !!this.memberIds && this.memberIds.length > 1;
const routerPath = (this.isArchived ? ARCHIVE_ROUTE : DOSSIERS_ROUTE) as string;
this.dossiersListRouterLink = `/main/${this.dossierTemplateId}/${routerPath}`;
this.routerLink = `${this.dossiersListRouterLink}/${this.dossierId}`;
}
get id(): string {
return this.dossierId;
}
get routerLink(): string {
return `${this.dossiersListRouterLink}/${this.dossierId}`;
}
get dossiersListRouterLink(): string {
const routerPath = this.isArchived ? ARCHIVE_ROUTE : DOSSIERS_ROUTE;
return `/main/${this.dossierTemplateId}/${routerPath}`;
}
get searchKey(): string {
return this.dossierName;
}