RED-6713 fix page refresh loop
This commit is contained in:
parent
e473db9c16
commit
72130128dc
@ -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',
|
||||
|
||||
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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());
|
||||
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
<div id="viewer"></div>
|
||||
|
||||
<redaction-compare-file-input></redaction-compare-file-input>
|
||||
|
||||
<redaction-paginator></redaction-paginator>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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 {}
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user