diff --git a/apps/red-ui/src/app/app-routing.module.ts b/apps/red-ui/src/app/app-routing.module.ts index 01ba8ff2e..ccf478b4b 100644 --- a/apps/red-ui/src/app/app-routing.module.ts +++ b/apps/red-ui/src/app/app-routing.module.ts @@ -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', diff --git a/apps/red-ui/src/app/guards/dossiers.guard.ts b/apps/red-ui/src/app/guards/dossiers.guard.ts index 35632190f..01be634ee 100644 --- a/apps/red-ui/src/app/guards/dossiers.guard.ts +++ b/apps/red-ui/src/app/guards/dossiers.guard.ts @@ -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 { - const token: ProviderToken = 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(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(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(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; - } + }; } diff --git a/apps/red-ui/src/app/guards/if-logged-in.guard.ts b/apps/red-ui/src/app/guards/if-logged-in.guard.ts index dc2631dbd..91a51e440 100644 --- a/apps/red-ui/src/app/guards/if-logged-in.guard.ts +++ b/apps/red-ui/src/app/guards/if-logged-in.guard.ts @@ -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); diff --git a/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen/watermark-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen/watermark-screen.component.html index c0a2d0a50..defe51978 100644 --- a/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen/watermark-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen/watermark-screen.component.html @@ -1,5 +1,5 @@
-
+
@@ -54,30 +54,30 @@
-
+
-
+
diff --git a/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts b/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts index da2646e18..fb61214bb 100644 --- a/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts @@ -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()); diff --git a/apps/red-ui/src/app/modules/pdf-viewer/pdf-viewer.component.html b/apps/red-ui/src/app/modules/pdf-viewer/pdf-viewer.component.html index 0df39e49d..3fd4bcd55 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/pdf-viewer.component.html +++ b/apps/red-ui/src/app/modules/pdf-viewer/pdf-viewer.component.html @@ -1,5 +1,3 @@ -
- diff --git a/apps/red-ui/src/app/modules/pdf-viewer/pdf-viewer.component.scss b/apps/red-ui/src/app/modules/pdf-viewer/pdf-viewer.component.scss index 37792961d..e69de29bb 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/pdf-viewer.component.scss +++ b/apps/red-ui/src/app/modules/pdf-viewer/pdf-viewer.component.scss @@ -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; -} diff --git a/apps/red-ui/src/app/modules/pdf-viewer/pdf-viewer.module.ts b/apps/red-ui/src/app/modules/pdf-viewer/pdf-viewer.module.ts index d49e497f6..7af623aba 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/pdf-viewer.module.ts +++ b/apps/red-ui/src/app/modules/pdf-viewer/pdf-viewer.module.ts @@ -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 {} diff --git a/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts b/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts index 65724b262..7561fc1ce 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts +++ b/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts @@ -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; keyUp$: Observable; readonly selectedText: Signal; @@ -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() { diff --git a/apps/red-ui/src/app/modules/pdf-viewer/services/webviewer-loaded.guard.ts b/apps/red-ui/src/app/modules/pdf-viewer/services/webviewer-loaded.guard.ts index 62feb6842..6278b91af 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/services/webviewer-loaded.guard.ts +++ b/apps/red-ui/src/app/modules/pdf-viewer/services/webviewer-loaded.guard.ts @@ -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 { + 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; + }; } diff --git a/apps/red-ui/src/index.html b/apps/red-ui/src/index.html index e54d15cff..52dbffae2 100644 --- a/apps/red-ui/src/index.html +++ b/apps/red-ui/src/index.html @@ -10,6 +10,7 @@ +
diff --git a/apps/red-ui/src/styles.scss b/apps/red-ui/src/styles.scss index c05edc9ac..716f736f0 100644 --- a/apps/red-ui/src/styles.scss +++ b/apps/red-ui/src/styles.scss @@ -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; +}