RED-6713 fix page refresh loop

This commit is contained in:
Dan Percic 2023-07-07 15:13:42 +03:00
parent e473db9c16
commit 72130128dc
12 changed files with 114 additions and 98 deletions

View File

@ -5,8 +5,8 @@ import { BaseScreenComponent } from '@components/base-screen/base-screen.compone
import { RouteReuseStrategy, RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
import { DownloadsListScreenComponent } from '@components/downloads-list-screen/downloads-list-screen.component';
import { DossiersGuard } from '@guards/dossiers.guard';
import { ACTIVE_DOSSIERS_SERVICE, ARCHIVED_DOSSIERS_SERVICE } from './tokens';
import { loadActiveDossiersGuard, loadAllDossiersGuard, loadArchivedDossiersGuard } from '@guards/dossiers.guard';
import { ACTIVE_DOSSIERS_SERVICE } from './tokens';
import { FeaturesGuard } from '@guards/features-guard.service';
import { DossierTemplatesGuard } from '@guards/dossier-templates.guard';
import { templateExistsWhenEnteringDossierList } from '@guards/dossier-template-exists.guard';
@ -14,7 +14,7 @@ import { DashboardGuard } from '@guards/dashboard-guard.service';
import { TrashGuard } from '@guards/trash.guard';
import { ARCHIVE_ROUTE, BreadcrumbTypes, DOSSIER_ID, DOSSIER_TEMPLATE_ID, DOSSIERS_ARCHIVE, DOSSIERS_ROUTE, FILE_ID } from '@red/domain';
import { DossierFilesGuard } from '@guards/dossier-files-guard';
import { WebViewerLoadedGuard } from './modules/pdf-viewer/services/webviewer-loaded.guard';
import { webViewerLoadedGuard } from './modules/pdf-viewer/services/webviewer-loaded.guard';
import { Roles } from '@users/roles';
import { mainResolver } from '@utils/main.resolver';
import { hasAnyRoleGuard, IqserAuthGuard } from '@iqser/common-ui/lib/users';
@ -26,10 +26,8 @@ import { TenantSelectComponent } from '@iqser/common-ui/lib/tenants';
const dossierTemplateIdRoutes: IqserRoutes = [
{
path: `${DOSSIERS_ROUTE}`,
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
canActivate: [loadActiveDossiersGuard(), IqserPermissionsGuard],
data: {
routeGuards: [DossiersGuard],
dossiersService: ACTIVE_DOSSIERS_SERVICE,
permissions: {
allow: [Roles.files.readStatus],
redirectTo: '/auth-error',
@ -53,9 +51,9 @@ const dossierTemplateIdRoutes: IqserRoutes = [
},
{
path: `:${DOSSIER_ID}/file/:${FILE_ID}`,
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
canActivate: [CompositeRouteGuard, IqserPermissionsGuard, webViewerLoadedGuard()],
data: {
routeGuards: [DossierFilesGuard, WebViewerLoadedGuard],
routeGuards: [DossierFilesGuard],
breadcrumbs: [BreadcrumbTypes.dossierTemplate, BreadcrumbTypes.dossier, BreadcrumbTypes.file],
dossiersService: ACTIVE_DOSSIERS_SERVICE,
permissions: {
@ -78,10 +76,9 @@ const dossierTemplateIdRoutes: IqserRoutes = [
{
path: `${ARCHIVE_ROUTE}`,
loadChildren: () => import('./modules/archive/archive.module').then(m => m.ArchiveModule),
canActivate: [CompositeRouteGuard, WebViewerLoadedGuard],
canActivate: [CompositeRouteGuard, webViewerLoadedGuard(), loadArchivedDossiersGuard()],
data: {
routeGuards: [FeaturesGuard, DossiersGuard],
dossiersService: ARCHIVED_DOSSIERS_SERVICE,
routeGuards: [FeaturesGuard],
features: [DOSSIERS_ARCHIVE],
},
},
@ -150,9 +147,9 @@ const mainRoutes: IqserRoutes = [
{
path: 'search',
loadChildren: () => import('./modules/search/search.module').then(m => m.SearchModule),
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
canActivate: [CompositeRouteGuard, IqserPermissionsGuard, loadAllDossiersGuard()],
data: {
routeGuards: [IqserAuthGuard, RedRoleGuard, DossiersGuard],
routeGuards: [IqserAuthGuard, RedRoleGuard],
permissions: {
allow: [Roles.search],
redirectTo: '/auth-error',
@ -162,10 +159,9 @@ const mainRoutes: IqserRoutes = [
{
path: 'trash',
loadChildren: () => import('./modules/trash/trash.module').then(m => m.TrashModule),
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
canActivate: [CompositeRouteGuard, IqserPermissionsGuard, loadActiveDossiersGuard()],
data: {
routeGuards: [IqserAuthGuard, RedRoleGuard, DossiersGuard, TrashGuard],
dossiersService: ACTIVE_DOSSIERS_SERVICE,
routeGuards: [IqserAuthGuard, RedRoleGuard, TrashGuard],
permissions: {
allow: [Roles.dossiers.read, Roles.files.readStatus],
redirectTo: '/auth-error',

View File

@ -1,45 +1,57 @@
import { Injectable, Injector, ProviderToken } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
import { firstValueFrom, forkJoin } from 'rxjs';
import { take } from 'rxjs/operators';
import { inject } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivateFn, Router } from '@angular/router';
import { firstValueFrom } from 'rxjs';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { ArchivedDossiersService } from '@services/dossiers/archived-dossiers.service';
import { DossiersService } from '@services/dossiers/dossiers.service';
import { ARCHIVE_ROUTE, DOSSIER_TEMPLATE_ID } from '@red/domain';
import { DOSSIER_TEMPLATE_ID } from '@red/domain';
import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service';
import { TenantsService } from '@iqser/common-ui/lib/tenants';
import { NGXLogger } from 'ngx-logger';
import { ACTIVE_DOSSIERS_SERVICE, ARCHIVED_DOSSIERS_SERVICE } from '../tokens';
@Injectable({ providedIn: 'root' })
export class DossiersGuard implements CanActivate {
constructor(
private readonly _injector: Injector,
private readonly _router: Router,
private readonly _tenantsService: TenantsService,
private readonly _dashboardStatsService: DashboardStatsService,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _archivedDossiersService: ArchivedDossiersService,
) {}
export function loadAllDossiersGuard(): CanActivateFn {
return async () => {
const logger = inject(NGXLogger);
logger.info('[GUARDS] loadAllDossiersGuard start');
async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
const token: ProviderToken<DossiersService> = route.data.dossiersService;
if (!token) {
const services = [this._archivedDossiersService, this._activeDossiersService];
const loading$ = forkJoin(services.map(service => service.loadAll().pipe(take(1))));
await firstValueFrom(loading$);
return true;
}
const services = [inject(ArchivedDossiersService), inject(ActiveDossiersService)];
const requests = services.map(service => firstValueFrom(service.loadAll()));
await Promise.all(requests);
const dossiersService: DossiersService = this._injector.get<DossiersService>(token);
const isArchive = dossiersService.routerPath === ARCHIVE_ROUTE;
logger.info('[GUARDS] loadAllDossiersGuard end');
return true;
};
}
export function loadActiveDossiersGuard(): CanActivateFn {
return async () => {
const logger = inject(NGXLogger);
logger.info('[GUARDS] loadDossiersGuard start');
await firstValueFrom(inject<ActiveDossiersService>(ACTIVE_DOSSIERS_SERVICE).loadAll());
logger.info('[GUARDS] loadDossiersGuard end');
return true;
};
}
export function loadArchivedDossiersGuard(): CanActivateFn {
return async (route: ActivatedRouteSnapshot) => {
const logger = inject(NGXLogger);
logger.info('[GUARDS] loadArchivedDossiersGuard start');
const dossiersService = inject<ArchivedDossiersService>(ARCHIVED_DOSSIERS_SERVICE);
const dossierTemplateId = route.paramMap.get(DOSSIER_TEMPLATE_ID);
const dossierTemplateStats = this._dashboardStatsService.find(dossierTemplateId);
const dossierTemplateStats = inject(DashboardStatsService).find(dossierTemplateId);
if (isArchive && dossierTemplateStats?.numberOfArchivedDossiers === 0) {
await this._router.navigate([this._tenantsService.activeTenantId, 'main', dossierTemplateId, 'dossiers']);
if (dossierTemplateStats?.numberOfArchivedDossiers === 0) {
logger.info('[GUARDS] loadArchivedDossiersGuard no archived dossiers, redirect to active dossiers page');
await inject(Router).navigate([inject(TenantsService).activeTenantId, 'main', dossierTemplateId, 'dossiers']);
return false;
}
await firstValueFrom(dossiersService.loadAll());
logger.info('[GUARDS] loadArchivedDossiersGuard end');
return true;
}
};
}

View File

@ -9,7 +9,7 @@ import { LicenseService } from '@services/license.service';
export function ifLoggedIn(): CanActivateFn {
return async (route: ActivatedRouteSnapshot) => {
const logger = inject(NGXLogger);
logger.info('[ROUTES] Check if can activate main');
logger.info('[ROUTES] Check if can activate route', route);
const tenantsService = inject(TenantsService);
const keycloakService = inject(KeycloakService);

View File

@ -1,5 +1,5 @@
<div class="content-container">
<div #viewer class="viewer" id="viewer"></div>
<div #viewer class="viewer"></div>
<redaction-paginator (changePage)="navigateTo($event)" *ngIf="loaded$ | async"></redaction-paginator>
@ -54,30 +54,30 @@
<div class="iqser-input-group">
<label [translate]="'watermark-screen.form.alignment'" class="all-caps-label mb-8"></label>
<div class="flex">
<div class="alignment-buttons" [class.disabled]="form.controls.horizontalTextAlignment.disabled">
<div [class.disabled]="form.controls.horizontalTextAlignment.disabled" class="alignment-buttons">
<div
(click)="alignHorizontally(alignment)"
*ngFor="let alignment of watermarkHorizontalAlignments"
[class.active]="currentAlignment.horizontal === alignment"
[class.disabled]="form.controls.horizontalTextAlignment.disabled"
[matTooltipPosition]="'above'"
[matTooltip]="translations.HORIZONTAL[alignment] | translate"
[ngClass]="'horizontal-' + alignment.toLowerCase()"
[class.disabled]="form.controls.horizontalTextAlignment.disabled"
class="alignment"
(click)="alignHorizontally(alignment)"
>
<mat-icon [svgIcon]="'red:align-horizontal-' + alignment.toLowerCase()"></mat-icon>
</div>
</div>
<div class="alignment-buttons" [class.disabled]="form.controls.verticalTextAlignment.disabled">
<div [class.disabled]="form.controls.verticalTextAlignment.disabled" class="alignment-buttons">
<div
(click)="alignVertically(alignment)"
*ngFor="let alignment of watermarkVerticalAlignments"
[class.active]="currentAlignment.vertical === alignment"
[class.disabled]="form.controls.verticalTextAlignment.disabled"
[matTooltipPosition]="'above'"
[matTooltip]="translations.VERTICAL[alignment] | translate"
[ngClass]="'vertical-' + alignment.toLowerCase()"
[class.disabled]="form.controls.verticalTextAlignment.disabled"
class="alignment"
(click)="alignVertically(alignment)"
>
<mat-icon [svgIcon]="'red:align-vertical-' + alignment.toLowerCase()"></mat-icon>
</div>

View File

@ -11,7 +11,7 @@ import {
TemplateRef,
ViewChild,
} from '@angular/core';
import { ActivatedRoute, ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router';
import { ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router';
import {
CircleButtonTypes,
ConfirmOption,
@ -20,6 +20,7 @@ import {
ErrorService,
HelpModeService,
IConfirmationDialogData,
IqserDialog,
LoadingService,
Toaster,
} from '@iqser/common-ui';
@ -63,7 +64,6 @@ import { ConfigService } from '@services/config.service';
import { ReadableRedactionsService } from '../pdf-viewer/services/readable-redactions.service';
import { Roles } from '@users/roles';
import { SuggestionsService } from './services/suggestions.service';
import { IqserDialog } from '../../../../../../libs/common-ui/src/lib/dialog/iqser-dialog.service';
import { RedactTextDialogComponent } from './dialogs/redact-text-dialog/redact-text-dialog.component';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { copyLocalStorageFiltersValues, FilterService, NestedFilter, processFilters } from '@iqser/common-ui/lib/filtering';
@ -108,7 +108,6 @@ export class FilePreviewScreenComponent
private readonly _annotationManager: REDAnnotationManager,
private readonly _errorService: ErrorService,
private readonly _filterService: FilterService,
private readonly _activatedRoute: ActivatedRoute,
private readonly _loadingService: LoadingService,
private readonly _filesMapService: FilesMapService,
private readonly _dossiersService: DossiersService,
@ -161,6 +160,7 @@ export class FilePreviewScreenComponent
effect(() => {
const selectedText = this._documentViewer.selectedText();
console.log('selectedText', selectedText);
const canPerformActions = this.pdfProxyService.canPerformActions();
const isCurrentPageExcluded = this.state.file().isPageExcluded(this.pdf.currentPage());

View File

@ -1,5 +1,3 @@
<div id="viewer"></div>
<redaction-compare-file-input></redaction-compare-file-input>
<redaction-paginator></redaction-paginator>

View File

@ -1,12 +0,0 @@
:host {
--workload-width: 350px;
--header-height: calc(var(--iqser-top-bar-height) + 50px);
}
div {
width: calc(100% - var(--workload-width));
height: calc(100% - var(--header-height));
bottom: 0;
left: 0;
position: absolute;
}

View File

@ -11,7 +11,6 @@ import { PaginatorComponent } from './components/paginator/paginator.component';
import { MatIconModule } from '@angular/material/icon';
import { AnnotationDrawService } from './services/annotation-draw.service';
import { REDDocumentViewer } from './services/document-viewer.service';
import { WebViewerLoadedGuard } from './services/webviewer-loaded.guard';
import { ReadableRedactionsService } from './services/readable-redactions.service';
@NgModule({
@ -27,7 +26,6 @@ import { ReadableRedactionsService } from './services/readable-redactions.servic
ReadableRedactionsService,
ViewerHeaderService,
AnnotationDrawService,
WebViewerLoadedGuard,
],
})
export class PdfViewerModule {}

View File

@ -1,4 +1,4 @@
import { inject, Injectable, NgZone, Signal, signal } from '@angular/core';
import { effect, inject, Injectable, Signal, signal } from '@angular/core';
import { Core } from '@pdftron/webviewer';
import { NGXLogger } from 'ngx-logger';
import { fromEvent, Observable } from 'rxjs';
@ -25,7 +25,6 @@ export class REDDocumentViewer {
readonly #userPreferenceService = inject(UserPreferenceService);
readonly #pdf = inject(PdfViewer);
readonly #activatedRoute = inject(ActivatedRoute);
readonly #ngZone = inject(NgZone);
readonly loaded$: Observable<boolean>;
keyUp$: Observable<KeyboardEvent>;
readonly selectedText: Signal<string>;
@ -37,6 +36,21 @@ export class REDDocumentViewer {
this.loaded$ = toObservable(this.#loaded);
this.pageComplete = this.#pageComplete.asReadonly();
this.selectedText = this.#selectedText.asReadonly();
effect(() => {
const viewerElement = document.getElementById('viewer');
if (!viewerElement) {
return;
}
if (this.loaded()) {
this.#logger.info('[PDF] Show viewer');
viewerElement.style.visibility = 'visible';
} else {
this.#logger.info('[PDF] Hide viewer');
viewerElement.style.visibility = 'hidden';
}
});
}
get PDFDoc() {

View File

@ -1,41 +1,42 @@
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { inject } from '@angular/core';
import { CanActivateFn, ResolveFn, Router } from '@angular/router';
import { REDDocumentViewer } from './document-viewer.service';
import { PdfViewer } from './pdf-viewer.service';
import { REDAnnotationManager } from './annotation-manager.service';
import { ViewerHeaderService } from './viewer-header.service';
import { LoadingService } from '@iqser/common-ui';
import { WebViewerInstance } from '@pdftron/webviewer';
import { NGXLogger } from 'ngx-logger';
import { DOCUMENT } from '@angular/common';
@Injectable()
export class WebViewerLoadedGuard implements CanActivate {
constructor(
private readonly _documentViewer: REDDocumentViewer,
private readonly _pdf: PdfViewer,
private readonly _router: Router,
private readonly _annotationManager: REDAnnotationManager,
private readonly _viewerHeaderService: ViewerHeaderService,
private readonly _loadingService: LoadingService,
) {}
export function webViewerLoadedGuard(): CanActivateFn | ResolveFn<boolean> {
return async (_, state) => {
const pdf = inject(PdfViewer);
const logger = inject(NGXLogger);
async canActivate(_, state) {
if (this._pdf.instance) {
if (pdf.instance) {
logger.info('[PDF] WebViewerGuard already loaded.');
return true;
}
this._loadingService.start();
inject(LoadingService).start();
const router = inject(Router);
const annotationManager = inject(REDAnnotationManager);
const documentViewer = inject(REDDocumentViewer);
const viewerHeaderService = inject(ViewerHeaderService);
let instance: WebViewerInstance | undefined;
try {
instance = await this._pdf.init(document.getElementById('viewer'));
} catch {
return this._router.navigateByUrl(state.url);
instance = await pdf.init(inject(DOCUMENT).getElementById('viewer'));
} catch (e) {
logger.warn('[PDF] WebViewerGuard error: ', e, 'redirecting to', state.url);
return router.navigateByUrl(state.url);
}
this._annotationManager.init(instance.Core.annotationManager);
this._documentViewer.init(instance.Core.documentViewer);
this._viewerHeaderService.init();
annotationManager.init(instance.Core.annotationManager);
documentViewer.init(instance.Core.documentViewer);
viewerHeaderService.init();
return !!this._pdf.instance;
}
return !!pdf.instance;
};
}

View File

@ -10,6 +10,7 @@
</head>
<body>
<redaction-root></redaction-root>
<div id="viewer"></div>
<noscript>Please enable JavaScript to continue using this application.</noscript>
</body>
</html>

View File

@ -143,3 +143,11 @@ $dark-accent-10: darken(vars.$accent, 10%);
$iqser-app-name-font-size: 13px,
$iqser-app-name-color: vars.$white
);
#viewer {
width: calc(100% - 350px);
height: calc(100% - calc(var(--iqser-top-bar-height) + 50px));
bottom: 0;
left: 0;
position: absolute;
}