From dcc96dc4bd144c13e4355439d45e7dadf0b87043 Mon Sep 17 00:00:00 2001 From: Dan Percic Date: Wed, 21 Apr 2021 23:32:00 +0300 Subject: [PATCH] reuse file preview routes --- .../file-actions/file-actions.component.ts | 4 +- .../projects/projects-routing.module.ts | 2 +- .../file-preview-screen.component.ts | 76 +++++++++++-------- .../app/utils/custom-route-reuse.strategy.ts | 45 ++++++++--- 4 files changed, 79 insertions(+), 48 deletions(-) diff --git a/apps/red-ui/src/app/modules/projects/components/file-actions/file-actions.component.ts b/apps/red-ui/src/app/modules/projects/components/file-actions/file-actions.component.ts index ef9edb584..029dee8b5 100644 --- a/apps/red-ui/src/app/modules/projects/components/file-actions/file-actions.component.ts +++ b/apps/red-ui/src/app/modules/projects/components/file-actions/file-actions.component.ts @@ -108,7 +108,7 @@ export class FileActionsComponent implements OnInit { $event.stopPropagation(); await this._fileActionService.toggleAnalysis(this.fileStatus).toPromise(); await this.appStateService.getFiles(); - this.actionPerformed.emit(this.fileStatus.isExcluded ? 'enable-analysis' : 'disable-analysis'); + this.actionPerformed.emit(this.fileStatus?.isExcluded ? 'enable-analysis' : 'disable-analysis'); } get toggleTooltip(): string { @@ -116,6 +116,6 @@ export class FileActionsComponent implements OnInit { return 'file-preview.toggle-analysis.only-managers'; } - return this.fileStatus.isExcluded ? 'file-preview.toggle-analysis.enable' : 'file-preview.toggle-analysis.disable'; + return this.fileStatus?.isExcluded ? 'file-preview.toggle-analysis.enable' : 'file-preview.toggle-analysis.disable'; } } diff --git a/apps/red-ui/src/app/modules/projects/projects-routing.module.ts b/apps/red-ui/src/app/modules/projects/projects-routing.module.ts index f43d3f6c8..eb656c35a 100644 --- a/apps/red-ui/src/app/modules/projects/projects-routing.module.ts +++ b/apps/red-ui/src/app/modules/projects/projects-routing.module.ts @@ -33,7 +33,7 @@ const routes = [ canActivate: [CompositeRouteGuard], data: { routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], - reuse: false // TODO ROUTE REUSE FOR file + reuse: true } } ]; diff --git a/apps/red-ui/src/app/modules/projects/screens/file-preview-screen/file-preview-screen.component.ts b/apps/red-ui/src/app/modules/projects/screens/file-preview-screen/file-preview-screen.component.ts index f577cb902..f72a45127 100644 --- a/apps/red-ui/src/app/modules/projects/screens/file-preview-screen/file-preview-screen.component.ts +++ b/apps/red-ui/src/app/modules/projects/screens/file-preview-screen/file-preview-screen.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectorRef, Component, HostListener, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, ActivatedRouteSnapshot, Router } from '@angular/router'; import { AppStateService } from '../../../../state/app-state.service'; import { WebViewerInstance } from '@pdftron/webviewer'; import { PdfViewerComponent } from '../../components/pdf-viewer/pdf-viewer.component'; @@ -28,7 +28,7 @@ import { download } from '../../../../utils/file-download-utils'; import { ViewMode } from '../../../../models/file/view-mode'; import { FileWorkloadComponent } from '../../components/file-workload/file-workload.component'; import { ProjectsDialogService } from '../../services/projects-dialog.service'; -import { ReuseAwareRoute } from '../../../../utils/custom-route-reuse.strategy'; +import { OnAttach, OnDetach } from '../../../../utils/custom-route-reuse.strategy'; const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f']; @@ -37,7 +37,7 @@ const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f']; templateUrl: './file-preview-screen.component.html', styleUrls: ['./file-preview-screen.component.scss'] }) -export class FilePreviewScreenComponent implements OnInit, OnDestroy, ReuseAwareRoute { +export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach, OnDetach { public dialogRef: MatDialogRef; public viewMode: ViewMode = 'STANDARD'; public fullScreen = false; @@ -60,6 +60,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, ReuseAware displayPDFViewer = false; public viewDocumentInfo = false; private _instance: WebViewerInstance; + private _lastPage: string; @ViewChild('fileWorkloadComponent') private _workloadComponent: FileWorkloadComponent; @ViewChild(PdfViewerComponent) private _viewerComponent: PdfViewerComponent; @@ -167,16 +168,16 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, ReuseAware this._updateCanPerformActions(); } - // TODO POC - onRouteReuse() { - // primitive route reuse mechanic to trigger code on reuse, in this case reload PDF Viewer, - // we would still need to first store some state-related data and the restore it to the new instance - // for example page - console.log('testing route reuse ... '); + ngOnDetach() { this.displayPDFViewer = false; - setTimeout(() => { - this.displayPDFViewer = true; - }); + this.viewReady = false; + this._unsubscribeFromFileUpdates(); + } + + ngOnAttach(previousRoute: ActivatedRouteSnapshot) { + this.displayPDFViewer = true; + this._lastPage = previousRoute.queryParams.page; + this._subscribeToFileUpdates(); } ngOnInit(): void { @@ -188,32 +189,15 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, ReuseAware } }); - this.filesAutoUpdateTimer = timer(0, 5000) - .pipe( - tap(async () => { - await this.appStateService.reloadActiveFile(); - }) - ) - .subscribe(); this._loadFileData().subscribe(() => { this._updateCanPerformActions(); }); - this.fileReanalysedSubscription = this.appStateService.fileReanalysed.subscribe((fileStatus: FileStatusWrapper) => { - if (fileStatus.fileId === this.fileId) { - this._loadFileData(true).subscribe(() => { - this.viewReady = true; - this.loadingMessage = null; - this._stopAnalysisTimer(); - this._updateCanPerformActions(); - this._cleanupAndRedrawManualAnnotations(); - }); - } - }); + + this._subscribeToFileUpdates(); } ngOnDestroy(): void { - this.filesAutoUpdateTimer.unsubscribe(); - this.fileReanalysedSubscription.unsubscribe(); + this._unsubscribeFromFileUpdates(); } public rebuildFilters(deletePreviousAnnotations: boolean = false) { @@ -344,7 +328,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, ReuseAware this._cleanupAndRedrawManualAnnotations(); this.viewReady = true; // Go to initial page from query params - const pageNumber = this._activatedRoute.snapshot.queryParams.page; + const pageNumber = this._lastPage || this._activatedRoute.snapshot.queryParams.page; if (pageNumber) { setTimeout(() => { this.selectPage(parseInt(pageNumber, 10)); @@ -447,6 +431,32 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, ReuseAware window.open(`/html-debug/${this.projectId}/${this.fileId}`, '_blank'); } + private _subscribeToFileUpdates(): void { + this.filesAutoUpdateTimer = timer(0, 5000) + .pipe( + tap(async () => { + await this.appStateService.reloadActiveFile(); + }) + ) + .subscribe(); + this.fileReanalysedSubscription = this.appStateService.fileReanalysed.subscribe((fileStatus: FileStatusWrapper) => { + if (fileStatus.fileId === this.fileId) { + this._loadFileData(true).subscribe(() => { + this.viewReady = true; + this.loadingMessage = null; + this._stopAnalysisTimer(); + this._updateCanPerformActions(); + this._cleanupAndRedrawManualAnnotations(); + }); + } + }); + } + + private _unsubscribeFromFileUpdates(): void { + this.filesAutoUpdateTimer.unsubscribe(); + this.fileReanalysedSubscription.unsubscribe(); + } + private _updateCanPerformActions() { this.canPerformAnnotationActions = this.permissionsService.canPerformAnnotationActions() && this.viewMode === 'STANDARD'; } diff --git a/apps/red-ui/src/app/utils/custom-route-reuse.strategy.ts b/apps/red-ui/src/app/utils/custom-route-reuse.strategy.ts index d9177f68c..4ea02ce79 100644 --- a/apps/red-ui/src/app/utils/custom-route-reuse.strategy.ts +++ b/apps/red-ui/src/app/utils/custom-route-reuse.strategy.ts @@ -1,8 +1,21 @@ import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router'; import { debounce } from './debounce'; +export interface OnAttach { + ngOnAttach(previousRoute: ActivatedRouteSnapshot); +} + +export interface OnDetach { + ngOnDetach(); +} + +interface RouteStorageObject { + handle: DetachedRouteHandle; + previousRoute: ActivatedRouteSnapshot; +} + export class CustomRouteReuseStrategy implements RouteReuseStrategy { - private _handlers: { [key: string]: DetachedRouteHandle } = {}; + private _storedRoutes: { [key: string]: RouteStorageObject } = {}; shouldDetach(route: ActivatedRouteSnapshot): boolean { return !!route.routeConfig.data?.reuse && !!this._getKey(route); @@ -10,19 +23,26 @@ export class CustomRouteReuseStrategy implements RouteReuseStrategy { store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void { if (handle === null) return; - this._handlers[this._getKey(route)] = handle; + + const element: any = handle; + + if (element?.componentRef?.instance?.ngOnDetach) { + this._onDetach(element.componentRef?.instance); + } + + this._storedRoutes[this._getKey(route)] = { handle: element as DetachedRouteHandle, previousRoute: route }; } shouldAttach(route: ActivatedRouteSnapshot): boolean { - return !!this._handlers[this._getKey(route)]; + return !!this._storedRoutes[this._getKey(route)]; } retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle { const key = this._getKey(route); - const element: any = this._handlers[key]; + const element: any = this._storedRoutes[key]?.handle; - if (element?.componentRef?.instance?.onRouteReuse) { - this._reuseRoute(element.componentRef?.instance); + if (element?.componentRef?.instance?.ngOnAttach) { + this._onAttach(element.componentRef?.instance, this._storedRoutes[key].previousRoute); } return element as DetachedRouteHandle; @@ -40,11 +60,12 @@ export class CustomRouteReuseStrategy implements RouteReuseStrategy { } @debounce() - private _reuseRoute(instance: any) { - instance.onRouteReuse(); + private _onAttach(instance: OnAttach, previousRoute?: ActivatedRouteSnapshot) { + instance.ngOnAttach(previousRoute); + } + + @debounce() + private _onDetach(instance: OnDetach) { + instance.ngOnDetach(); } } - -export interface ReuseAwareRoute { - onRouteReuse(); -}