Pull request #161: RED-1307
Merge in RED/ui from RED-1307 to master * commit '0913e12cfa16036115ff041f022e297a45d30cfb': resolve conflicts fix fileId and projectId add route reuse for projects listing and project overview throws undefined error activate current file in appStateService loading component works, wip
This commit is contained in:
commit
ff1c79f1a4
@ -79,7 +79,7 @@ const routes = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forRoot(routes)],
|
imports: [RouterModule.forRoot(routes, { scrollPositionRestoration: 'enabled' })],
|
||||||
exports: [RouterModule]
|
exports: [RouterModule]
|
||||||
})
|
})
|
||||||
export class AppRoutingModule {}
|
export class AppRoutingModule {}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router, RouteReuseStrategy } from '@angular/router';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from '@angular/common/http';
|
import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from '@angular/common/http';
|
||||||
import { BaseScreenComponent } from './components/base-screen/base-screen.component';
|
import { BaseScreenComponent } from './components/base-screen/base-screen.component';
|
||||||
@ -30,6 +30,7 @@ import { AppRoutingModule } from './app-routing.module';
|
|||||||
import { SharedModule } from './modules/shared/shared.module';
|
import { SharedModule } from './modules/shared/shared.module';
|
||||||
import { FileUploadDownloadModule } from './modules/upload-download/file-upload-download.module';
|
import { FileUploadDownloadModule } from './modules/upload-download/file-upload-download.module';
|
||||||
import { UserProfileScreenComponent } from './components/user-profile/user-profile-screen.component';
|
import { UserProfileScreenComponent } from './components/user-profile/user-profile-screen.component';
|
||||||
|
import { CustomRouteReuseStrategy } from './utils/custom-route-reuse.strategy';
|
||||||
|
|
||||||
export function HttpLoaderFactory(httpClient: HttpClient) {
|
export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||||
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
|
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
|
||||||
@ -92,7 +93,8 @@ const components = [AppComponent, LogoComponent, AuthErrorComponent, ToastCompon
|
|||||||
monthYearA11yLabel: 'YYYY'
|
monthYearA11yLabel: 'YYYY'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
{ provide: RouteReuseStrategy, useClass: CustomRouteReuseStrategy }
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
import { ProjectListingScreenComponent } from './screens/project-listing-screen/project-listing-screen.component';
|
import { ProjectListingScreenComponent } from './screens/project-listing-screen/project-listing-screen.component';
|
||||||
import { CompositeRouteGuard } from '../../guards/composite-route.guard';
|
import { CompositeRouteGuard } from '../../guards/composite-route.guard';
|
||||||
@ -15,7 +14,8 @@ const routes = [
|
|||||||
component: ProjectListingScreenComponent,
|
component: ProjectListingScreenComponent,
|
||||||
canActivate: [CompositeRouteGuard],
|
canActivate: [CompositeRouteGuard],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
|
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard],
|
||||||
|
reuse: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -23,7 +23,8 @@ const routes = [
|
|||||||
component: ProjectOverviewScreenComponent,
|
component: ProjectOverviewScreenComponent,
|
||||||
canActivate: [CompositeRouteGuard],
|
canActivate: [CompositeRouteGuard],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
|
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard],
|
||||||
|
reuse: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -32,6 +33,7 @@ const routes = [
|
|||||||
canActivate: [CompositeRouteGuard],
|
canActivate: [CompositeRouteGuard],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
|
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
|
||||||
|
// reuse: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import { debounce } from '../../../../utils/debounce';
|
|||||||
import { MatDialogRef, MatDialogState } from '@angular/material/dialog';
|
import { MatDialogRef, MatDialogState } from '@angular/material/dialog';
|
||||||
import { ManualRedactionEntryWrapper } from '../../../../models/file/manual-redaction-entry.wrapper';
|
import { ManualRedactionEntryWrapper } from '../../../../models/file/manual-redaction-entry.wrapper';
|
||||||
import { AnnotationWrapper } from '../../../../models/file/annotation.wrapper';
|
import { AnnotationWrapper } from '../../../../models/file/annotation.wrapper';
|
||||||
import { ManualAnnotationService } from '../../services/manual-annotation.service';
|
|
||||||
import { ManualAnnotationResponse } from '../../../../models/file/manual-annotation-response';
|
import { ManualAnnotationResponse } from '../../../../models/file/manual-annotation-response';
|
||||||
import { AnnotationData, FileDataModel } from '../../../../models/file/file-data.model';
|
import { AnnotationData, FileDataModel } from '../../../../models/file/file-data.model';
|
||||||
import { FileActionService } from '../../services/file-action.service';
|
import { FileActionService } from '../../services/file-action.service';
|
||||||
@ -16,7 +15,6 @@ import { AnnotationProcessingService } from '../../services/annotation-processin
|
|||||||
import { FilterModel } from '../../../shared/components/filter/model/filter.model';
|
import { FilterModel } from '../../../shared/components/filter/model/filter.model';
|
||||||
import { tap } from 'rxjs/operators';
|
import { tap } from 'rxjs/operators';
|
||||||
import { NotificationService } from '../../../../services/notification.service';
|
import { NotificationService } from '../../../../services/notification.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
import { FileStatusWrapper } from '../../../../models/file/file-status.wrapper';
|
import { FileStatusWrapper } from '../../../../models/file/file-status.wrapper';
|
||||||
import { PermissionsService } from '../../../../services/permissions.service';
|
import { PermissionsService } from '../../../../services/permissions.service';
|
||||||
import { Subscription, timer } from 'rxjs';
|
import { Subscription, timer } from 'rxjs';
|
||||||
@ -49,7 +47,6 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
|||||||
public analysisProgress: number;
|
public analysisProgress: number;
|
||||||
public analysisInterval: number;
|
public analysisInterval: number;
|
||||||
fileData: FileDataModel;
|
fileData: FileDataModel;
|
||||||
fileId: string;
|
|
||||||
annotationData: AnnotationData;
|
annotationData: AnnotationData;
|
||||||
selectedAnnotations: AnnotationWrapper[];
|
selectedAnnotations: AnnotationWrapper[];
|
||||||
viewReady = false;
|
viewReady = false;
|
||||||
@ -60,7 +57,6 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
|||||||
fileReanalysedSubscription: Subscription;
|
fileReanalysedSubscription: Subscription;
|
||||||
hideSkipped = false;
|
hideSkipped = false;
|
||||||
public viewDocumentInfo = false;
|
public viewDocumentInfo = false;
|
||||||
private projectId: string;
|
|
||||||
private _instance: WebViewerInstance;
|
private _instance: WebViewerInstance;
|
||||||
|
|
||||||
@ViewChild('fileWorkloadComponent') private _workloadComponent: FileWorkloadComponent;
|
@ViewChild('fileWorkloadComponent') private _workloadComponent: FileWorkloadComponent;
|
||||||
@ -77,25 +73,17 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
|||||||
private readonly _dialogService: ProjectsDialogService,
|
private readonly _dialogService: ProjectsDialogService,
|
||||||
private readonly _router: Router,
|
private readonly _router: Router,
|
||||||
private readonly _notificationService: NotificationService,
|
private readonly _notificationService: NotificationService,
|
||||||
private readonly _translateService: TranslateService,
|
|
||||||
private readonly _annotationProcessingService: AnnotationProcessingService,
|
private readonly _annotationProcessingService: AnnotationProcessingService,
|
||||||
private readonly _annotationDrawService: AnnotationDrawService,
|
private readonly _annotationDrawService: AnnotationDrawService,
|
||||||
private readonly _fileActionService: FileActionService,
|
private readonly _fileActionService: FileActionService,
|
||||||
private readonly _manualAnnotationService: ManualAnnotationService,
|
|
||||||
private readonly _fileDownloadService: PdfViewerDataService,
|
private readonly _fileDownloadService: PdfViewerDataService,
|
||||||
private readonly _formBuilder: FormBuilder,
|
private readonly _formBuilder: FormBuilder,
|
||||||
private readonly _statusControllerService: StatusControllerService,
|
private readonly _statusControllerService: StatusControllerService,
|
||||||
private readonly ngZone: NgZone,
|
private readonly _ngZone: NgZone,
|
||||||
private readonly _fileManagementControllerService: FileManagementControllerService
|
private readonly _fileManagementControllerService: FileManagementControllerService
|
||||||
) {
|
) {
|
||||||
this._activatedRoute.params.subscribe((params) => {
|
this.reviewerForm = this._formBuilder.group({
|
||||||
this.projectId = params.projectId;
|
reviewer: [this.appStateService.activeFile.currentReviewer]
|
||||||
this.fileId = params.fileId;
|
|
||||||
this.appStateService.activateFile(this.projectId, this.fileId);
|
|
||||||
|
|
||||||
this.reviewerForm = this._formBuilder.group({
|
|
||||||
reviewer: [this.appStateService.activeFile.currentReviewer]
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,6 +117,14 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
|||||||
); // on less than 3 seconds show indeterminate
|
); // on less than 3 seconds show indeterminate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get projectId() {
|
||||||
|
return this.appStateService.activeProjectId;
|
||||||
|
}
|
||||||
|
|
||||||
|
get fileId() {
|
||||||
|
return this.appStateService.activeFileId;
|
||||||
|
}
|
||||||
|
|
||||||
updateViewMode() {
|
updateViewMode() {
|
||||||
const allAnnotations = this._instance.annotManager.getAnnotationsList();
|
const allAnnotations = this._instance.annotManager.getAnnotationsList();
|
||||||
|
|
||||||
@ -262,7 +258,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
openManualAnnotationDialog($event: ManualRedactionEntryWrapper) {
|
openManualAnnotationDialog($event: ManualRedactionEntryWrapper) {
|
||||||
this.ngZone.run(() => {
|
this._ngZone.run(() => {
|
||||||
this.dialogRef = this._dialogService.openManualAnnotationDialog($event, async (response: ManualAnnotationResponse) => {
|
this.dialogRef = this._dialogService.openManualAnnotationDialog($event, async (response: ManualAnnotationResponse) => {
|
||||||
if (response?.annotationId) {
|
if (response?.annotationId) {
|
||||||
const annotation = this.activeViewer.annotManager.getAnnotationById(response.manualRedactionEntryWrapper.rectId);
|
const annotation = this.activeViewer.annotManager.getAnnotationById(response.manualRedactionEntryWrapper.rectId);
|
||||||
@ -545,7 +541,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
|||||||
private _openFullScreen() {
|
private _openFullScreen() {
|
||||||
const documentElement = document.documentElement;
|
const documentElement = document.documentElement;
|
||||||
if (documentElement.requestFullscreen) {
|
if (documentElement.requestFullscreen) {
|
||||||
documentElement.requestFullscreen();
|
documentElement.requestFullscreen().then();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Component, Injector, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
import { Component, Injector, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||||
import { FileManagementControllerService, Project, RuleSetModel } from '@redaction/red-ui-http';
|
import { Project, RuleSetModel } from '@redaction/red-ui-http';
|
||||||
import { AppStateService } from '../../../../state/app-state.service';
|
import { AppStateService } from '../../../../state/app-state.service';
|
||||||
import { UserService } from '../../../../services/user.service';
|
import { UserService } from '../../../../services/user.service';
|
||||||
import { DoughnutChartConfig } from '../../../shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
|
import { DoughnutChartConfig } from '../../../shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||||
@ -16,13 +16,14 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { PermissionsService } from '../../../../services/permissions.service';
|
import { PermissionsService } from '../../../../services/permissions.service';
|
||||||
import { ProjectWrapper } from '../../../../state/model/project.wrapper';
|
import { ProjectWrapper } from '../../../../state/model/project.wrapper';
|
||||||
import { Subscription, timer } from 'rxjs';
|
import { Subscription, timer } from 'rxjs';
|
||||||
import { tap } from 'rxjs/operators';
|
import { filter, tap } from 'rxjs/operators';
|
||||||
import { TranslateChartService } from '../../../../services/translate-chart.service';
|
import { TranslateChartService } from '../../../../services/translate-chart.service';
|
||||||
import { RedactionFilterSorter } from '../../../../utils/sorters/redaction-filter-sorter';
|
import { RedactionFilterSorter } from '../../../../utils/sorters/redaction-filter-sorter';
|
||||||
import { StatusSorter } from '../../../../utils/sorters/status-sorter';
|
import { StatusSorter } from '../../../../utils/sorters/status-sorter';
|
||||||
import { Router } from '@angular/router';
|
import { NavigationEnd, NavigationStart, Router } from '@angular/router';
|
||||||
import { FilterComponent } from '../../../shared/components/filter/filter.component';
|
import { FilterComponent } from '../../../shared/components/filter/filter.component';
|
||||||
import { ProjectsDialogService } from '../../services/projects-dialog.service';
|
import { ProjectsDialogService } from '../../services/projects-dialog.service';
|
||||||
|
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||||
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
|
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -37,8 +38,6 @@ export class ProjectListingScreenComponent extends BaseListingComponent<ProjectW
|
|||||||
public projectsChartData: DoughnutChartConfig[] = [];
|
public projectsChartData: DoughnutChartConfig[] = [];
|
||||||
public documentsChartData: DoughnutChartConfig[] = [];
|
public documentsChartData: DoughnutChartConfig[] = [];
|
||||||
|
|
||||||
public actionMenuOpen: boolean;
|
|
||||||
|
|
||||||
public statusFilters: FilterModel[];
|
public statusFilters: FilterModel[];
|
||||||
public peopleFilters: FilterModel[];
|
public peopleFilters: FilterModel[];
|
||||||
public needsWorkFilters: FilterModel[];
|
public needsWorkFilters: FilterModel[];
|
||||||
@ -50,7 +49,10 @@ export class ProjectListingScreenComponent extends BaseListingComponent<ProjectW
|
|||||||
statusFilters: []
|
statusFilters: []
|
||||||
};
|
};
|
||||||
|
|
||||||
private projectAutoUpdateTimer: Subscription;
|
private _projectAutoUpdateTimer: Subscription;
|
||||||
|
private _lastScrollPosition: number;
|
||||||
|
|
||||||
|
@ViewChild(CdkVirtualScrollViewport) private _scrollBar: CdkVirtualScrollViewport;
|
||||||
|
|
||||||
@ViewChild('statusFilter') private _statusFilterComponent: FilterComponent;
|
@ViewChild('statusFilter') private _statusFilterComponent: FilterComponent;
|
||||||
@ViewChild('peopleFilter') private _peopleFilterComponent: FilterComponent;
|
@ViewChild('peopleFilter') private _peopleFilterComponent: FilterComponent;
|
||||||
@ -58,14 +60,13 @@ export class ProjectListingScreenComponent extends BaseListingComponent<ProjectW
|
|||||||
@ViewChild('ruleSetFilter') private _ruleSetFilterComponent: FilterComponent;
|
@ViewChild('ruleSetFilter') private _ruleSetFilterComponent: FilterComponent;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _appStateService: AppStateService,
|
|
||||||
public readonly userService: UserService,
|
public readonly userService: UserService,
|
||||||
|
public readonly translateChartService: TranslateChartService,
|
||||||
public readonly permissionsService: PermissionsService,
|
public readonly permissionsService: PermissionsService,
|
||||||
private readonly _dialogService: ProjectsDialogService,
|
private readonly _dialogService: ProjectsDialogService,
|
||||||
private readonly _translateService: TranslateService,
|
private readonly _translateService: TranslateService,
|
||||||
private readonly _router: Router,
|
private readonly _router: Router,
|
||||||
public readonly translateChartService: TranslateChartService,
|
private readonly _appStateService: AppStateService,
|
||||||
private readonly _fileManagementControllerService: FileManagementControllerService,
|
|
||||||
protected readonly _injector: Injector
|
protected readonly _injector: Injector
|
||||||
) {
|
) {
|
||||||
super(_injector);
|
super(_injector);
|
||||||
@ -74,7 +75,7 @@ export class ProjectListingScreenComponent extends BaseListingComponent<ProjectW
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.projectAutoUpdateTimer = timer(0, 10000)
|
this._projectAutoUpdateTimer = timer(0, 10000)
|
||||||
.pipe(
|
.pipe(
|
||||||
tap(async () => {
|
tap(async () => {
|
||||||
await this._appStateService.loadAllProjects();
|
await this._appStateService.loadAllProjects();
|
||||||
@ -86,10 +87,19 @@ export class ProjectListingScreenComponent extends BaseListingComponent<ProjectW
|
|||||||
this._appStateService.fileChanged.subscribe(() => {
|
this._appStateService.fileChanged.subscribe(() => {
|
||||||
this._calculateData();
|
this._calculateData();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._router.events.pipe(filter((events) => events instanceof NavigationStart || events instanceof NavigationEnd)).subscribe((event) => {
|
||||||
|
if (event instanceof NavigationStart && event.url !== '/ui/projects') {
|
||||||
|
this._lastScrollPosition = this._scrollBar.getOffsetToRenderedContentStart() + this._scrollBar.getRenderedRange().end;
|
||||||
|
}
|
||||||
|
if (event instanceof NavigationEnd && event.url === '/ui/projects') {
|
||||||
|
this._scrollBar.scrollTo({ top: this._lastScrollPosition });
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.projectAutoUpdateTimer.unsubscribe();
|
this._projectAutoUpdateTimer.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _loadEntitiesFromState() {
|
private _loadEntitiesFromState() {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Component, HostListener, Injector, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
import { Component, HostListener, Injector, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { NavigationEnd, NavigationStart, Router } from '@angular/router';
|
||||||
import { NotificationService, NotificationType } from '../../../../services/notification.service';
|
import { NotificationService, NotificationType } from '../../../../services/notification.service';
|
||||||
import { AppStateService } from '../../../../state/app-state.service';
|
import { AppStateService } from '../../../../state/app-state.service';
|
||||||
import { FileDropOverlayService } from '../../../upload-download/services/file-drop-overlay.service';
|
import { FileDropOverlayService } from '../../../upload-download/services/file-drop-overlay.service';
|
||||||
@ -7,7 +7,6 @@ import { FileUploadModel } from '../../../upload-download/model/file-upload.mode
|
|||||||
import { FileUploadService } from '../../../upload-download/services/file-upload.service';
|
import { FileUploadService } from '../../../upload-download/services/file-upload.service';
|
||||||
import { StatusOverlayService } from '../../../upload-download/services/status-overlay.service';
|
import { StatusOverlayService } from '../../../upload-download/services/status-overlay.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { FileActionService } from '../../services/file-action.service';
|
|
||||||
import { FilterModel } from '../../../shared/components/filter/model/filter.model';
|
import { FilterModel } from '../../../shared/components/filter/model/filter.model';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import { ProjectDetailsComponent } from '../../components/project-details/project-details.component';
|
import { ProjectDetailsComponent } from '../../components/project-details/project-details.component';
|
||||||
@ -15,15 +14,16 @@ import { FileStatusWrapper } from '../../../../models/file/file-status.wrapper';
|
|||||||
import { annotationFilterChecker, keyChecker, processFilters } from '../../../shared/components/filter/utils/filter-utils';
|
import { annotationFilterChecker, keyChecker, processFilters } from '../../../shared/components/filter/utils/filter-utils';
|
||||||
import { PermissionsService } from '../../../../services/permissions.service';
|
import { PermissionsService } from '../../../../services/permissions.service';
|
||||||
import { UserService } from '../../../../services/user.service';
|
import { UserService } from '../../../../services/user.service';
|
||||||
import { FileManagementControllerService, FileStatus } from '@redaction/red-ui-http';
|
import { FileStatus } from '@redaction/red-ui-http';
|
||||||
import { Subscription, timer } from 'rxjs';
|
import { Subscription, timer } from 'rxjs';
|
||||||
import { tap } from 'rxjs/operators';
|
import { filter, tap } from 'rxjs/operators';
|
||||||
import { RedactionFilterSorter } from '../../../../utils/sorters/redaction-filter-sorter';
|
import { RedactionFilterSorter } from '../../../../utils/sorters/redaction-filter-sorter';
|
||||||
import { StatusSorter } from '../../../../utils/sorters/status-sorter';
|
import { StatusSorter } from '../../../../utils/sorters/status-sorter';
|
||||||
import { FormGroup } from '@angular/forms';
|
import { FormGroup } from '@angular/forms';
|
||||||
import { convertFiles, handleFileDrop } from '../../../../utils/file-drop-utils';
|
import { convertFiles, handleFileDrop } from '../../../../utils/file-drop-utils';
|
||||||
import { FilterComponent } from '../../../shared/components/filter/filter.component';
|
import { FilterComponent } from '../../../shared/components/filter/filter.component';
|
||||||
import { ProjectsDialogService } from '../../services/projects-dialog.service';
|
import { ProjectsDialogService } from '../../services/projects-dialog.service';
|
||||||
|
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||||
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
|
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
|
||||||
import { ProjectWrapper } from '../../../../state/model/project.wrapper';
|
import { ProjectWrapper } from '../../../../state/model/project.wrapper';
|
||||||
|
|
||||||
@ -50,34 +50,30 @@ export class ProjectOverviewScreenComponent extends BaseListingComponent<FileSta
|
|||||||
|
|
||||||
@ViewChild('projectDetailsComponent', { static: false })
|
@ViewChild('projectDetailsComponent', { static: false })
|
||||||
private _projectDetailsComponent: ProjectDetailsComponent;
|
private _projectDetailsComponent: ProjectDetailsComponent;
|
||||||
private filesAutoUpdateTimer: Subscription;
|
private _filesAutoUpdateTimer: Subscription;
|
||||||
|
private _lastScrollPosition: number;
|
||||||
|
|
||||||
|
@ViewChild(CdkVirtualScrollViewport) private _scrollBar: CdkVirtualScrollViewport;
|
||||||
|
|
||||||
@ViewChild('statusFilter') private _statusFilterComponent: FilterComponent;
|
@ViewChild('statusFilter') private _statusFilterComponent: FilterComponent;
|
||||||
@ViewChild('peopleFilter') private _peopleFilterComponent: FilterComponent;
|
@ViewChild('peopleFilter') private _peopleFilterComponent: FilterComponent;
|
||||||
@ViewChild('needsWorkFilter') private _needsWorkFilterComponent: FilterComponent;
|
@ViewChild('needsWorkFilter') private _needsWorkFilterComponent: FilterComponent;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _appStateService: AppStateService,
|
|
||||||
public readonly userService: UserService,
|
public readonly userService: UserService,
|
||||||
public readonly permissionsService: PermissionsService,
|
public readonly permissionsService: PermissionsService,
|
||||||
private readonly _activatedRoute: ActivatedRoute,
|
|
||||||
private readonly _notificationService: NotificationService,
|
private readonly _notificationService: NotificationService,
|
||||||
private readonly _dialogService: ProjectsDialogService,
|
private readonly _dialogService: ProjectsDialogService,
|
||||||
private readonly _fileActionService: FileActionService,
|
|
||||||
private readonly _fileUploadService: FileUploadService,
|
private readonly _fileUploadService: FileUploadService,
|
||||||
private readonly _statusOverlayService: StatusOverlayService,
|
private readonly _statusOverlayService: StatusOverlayService,
|
||||||
private readonly _router: Router,
|
private readonly _router: Router,
|
||||||
private readonly _translateService: TranslateService,
|
private readonly _translateService: TranslateService,
|
||||||
private readonly _fileDropOverlayService: FileDropOverlayService,
|
private readonly _fileDropOverlayService: FileDropOverlayService,
|
||||||
private readonly _fileManagementControllerService: FileManagementControllerService,
|
private readonly _appStateService: AppStateService,
|
||||||
protected readonly _injector: Injector
|
protected readonly _injector: Injector
|
||||||
) {
|
) {
|
||||||
super(_injector);
|
super(_injector);
|
||||||
|
this._loadEntitiesFromState();
|
||||||
this._activatedRoute.params.subscribe((params) => {
|
|
||||||
this._appStateService.activateProject(params.projectId);
|
|
||||||
this._loadEntitiesFromState();
|
|
||||||
});
|
|
||||||
|
|
||||||
this._appStateService.fileChanged.subscribe(() => {
|
this._appStateService.fileChanged.subscribe(() => {
|
||||||
this.calculateData();
|
this.calculateData();
|
||||||
@ -85,7 +81,7 @@ export class ProjectOverviewScreenComponent extends BaseListingComponent<FileSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.filesAutoUpdateTimer = timer(0, 7500)
|
this._filesAutoUpdateTimer = timer(0, 7500)
|
||||||
.pipe(
|
.pipe(
|
||||||
tap(async () => {
|
tap(async () => {
|
||||||
await this._appStateService.reloadActiveProjectFilesIfNecessary();
|
await this._appStateService.reloadActiveProjectFilesIfNecessary();
|
||||||
@ -95,11 +91,21 @@ export class ProjectOverviewScreenComponent extends BaseListingComponent<FileSta
|
|||||||
.subscribe();
|
.subscribe();
|
||||||
this._fileDropOverlayService.initFileDropHandling();
|
this._fileDropOverlayService.initFileDropHandling();
|
||||||
this.calculateData();
|
this.calculateData();
|
||||||
|
|
||||||
|
this._router.events.pipe(filter((events) => events instanceof NavigationStart || events instanceof NavigationEnd)).subscribe((event) => {
|
||||||
|
if (event instanceof NavigationStart && !event.url.endsWith(this._appStateService.activeProjectId)) {
|
||||||
|
this._lastScrollPosition = this._scrollBar.getOffsetToRenderedContentStart() + this._scrollBar.getRenderedRange().end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event instanceof NavigationEnd && event.url.endsWith(this._appStateService.activeProjectId)) {
|
||||||
|
this._scrollBar.scrollTo({ top: this._lastScrollPosition });
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this._fileDropOverlayService.cleanupFileDropHandling();
|
this._fileDropOverlayService.cleanupFileDropHandling();
|
||||||
this.filesAutoUpdateTimer.unsubscribe();
|
this._filesAutoUpdateTimer.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
public get activeProject(): ProjectWrapper {
|
public get activeProject(): ProjectWrapper {
|
||||||
@ -143,7 +149,7 @@ export class ProjectOverviewScreenComponent extends BaseListingComponent<FileSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _loadEntitiesFromState() {
|
private _loadEntitiesFromState() {
|
||||||
this.allEntities = this._appStateService.activeProject.files;
|
if (this.activeProject) this.allEntities = this.activeProject.files;
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadProjects() {
|
reloadProjects() {
|
||||||
|
|||||||
@ -38,12 +38,16 @@ export class SyncWidthDirective implements AfterViewInit, OnDestroy {
|
|||||||
const hasExtraColumns = headerItems.length !== length ? 1 : 0;
|
const hasExtraColumns = headerItems.length !== length ? 1 : 0;
|
||||||
|
|
||||||
for (let idx = 0; idx < length - hasExtraColumns - 1; ++idx) {
|
for (let idx = 0; idx < length - hasExtraColumns - 1; ++idx) {
|
||||||
headerItems[idx].style.width = `${tableRow.children[idx].getBoundingClientRect().width}px`;
|
if (headerItems[idx]) {
|
||||||
headerItems[idx].style.minWidth = `${tableRow.children[idx].getBoundingClientRect().width}px`;
|
headerItems[idx].style.width = `${tableRow.children[idx].getBoundingClientRect().width}px`;
|
||||||
|
headerItems[idx].style.minWidth = `${tableRow.children[idx].getBoundingClientRect().width}px`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let idx = length - hasExtraColumns - 1; idx < headerItems.length; ++idx) {
|
for (let idx = length - hasExtraColumns - 1; idx < headerItems.length; ++idx) {
|
||||||
headerItems[idx].style.minWidth = `0`;
|
if (headerItems[idx]) {
|
||||||
|
headerItems[idx].style.minWidth = `0`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import {
|
|||||||
DictionaryControllerService,
|
DictionaryControllerService,
|
||||||
FileAttributeConfig,
|
FileAttributeConfig,
|
||||||
FileAttributesControllerService,
|
FileAttributesControllerService,
|
||||||
FileManagementControllerService,
|
|
||||||
FileStatus,
|
FileStatus,
|
||||||
Project,
|
Project,
|
||||||
ProjectControllerService,
|
ProjectControllerService,
|
||||||
@ -16,7 +15,7 @@ import {
|
|||||||
} from '@redaction/red-ui-http';
|
} from '@redaction/red-ui-http';
|
||||||
import { NotificationService, NotificationType } from '../services/notification.service';
|
import { NotificationService, NotificationType } from '../services/notification.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { Router } from '@angular/router';
|
import { Event, NavigationEnd, ResolveStart, Router } from '@angular/router';
|
||||||
import { UserService } from '../services/user.service';
|
import { UserService } from '../services/user.service';
|
||||||
import { forkJoin, Observable } from 'rxjs';
|
import { forkJoin, Observable } from 'rxjs';
|
||||||
import { tap } from 'rxjs/operators';
|
import { tap } from 'rxjs/operators';
|
||||||
@ -50,7 +49,6 @@ export class AppStateService {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly _router: Router,
|
private readonly _router: Router,
|
||||||
private readonly _userService: UserService,
|
private readonly _userService: UserService,
|
||||||
private readonly _fileManagementControllerService: FileManagementControllerService,
|
|
||||||
private readonly _projectControllerService: ProjectControllerService,
|
private readonly _projectControllerService: ProjectControllerService,
|
||||||
private readonly _notificationService: NotificationService,
|
private readonly _notificationService: NotificationService,
|
||||||
private readonly _reanalysisControllerService: ReanalysisControllerService,
|
private readonly _reanalysisControllerService: ReanalysisControllerService,
|
||||||
@ -71,6 +69,33 @@ export class AppStateService {
|
|||||||
activeDictionaryType: null,
|
activeDictionaryType: null,
|
||||||
versions: {}
|
versions: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this._router.events.subscribe((event: Event) => {
|
||||||
|
if (AppStateService._isFileOverviewRoute(event)) {
|
||||||
|
const url = (event as ResolveStart).url.replace('/ui/projects/', '');
|
||||||
|
const [projectId, , fileId] = url.split('/');
|
||||||
|
this.activateFile(projectId, fileId);
|
||||||
|
}
|
||||||
|
if (AppStateService._isProjectOverviewRoute(event)) {
|
||||||
|
const projectId = (event as ResolveStart).url.replace('/ui/projects/', '');
|
||||||
|
this.activateProject(projectId);
|
||||||
|
}
|
||||||
|
if (AppStateService._isRandomRoute(event)) {
|
||||||
|
this._appState.activeProjectId = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _isFileOverviewRoute(event: Event) {
|
||||||
|
return event instanceof ResolveStart && event.url.includes('/ui/projects/') && event.url.includes('/file/');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _isProjectOverviewRoute(event: Event) {
|
||||||
|
return event instanceof ResolveStart && event.url.includes('/ui/projects/') && !event.url.includes('/file/');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _isRandomRoute(event: Event) {
|
||||||
|
return event instanceof NavigationEnd && !event.url.includes('/ui/projects/') && !event.url.includes('/file/');
|
||||||
}
|
}
|
||||||
|
|
||||||
async reloadActiveProjectFilesIfNecessary() {
|
async reloadActiveProjectFilesIfNecessary() {
|
||||||
@ -327,6 +352,7 @@ export class AppStateService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
activateFile(projectId: string, fileId: string) {
|
activateFile(projectId: string, fileId: string) {
|
||||||
|
if (this._appState.activeProjectId === projectId && this._appState.activeFileId === fileId) return;
|
||||||
this.activateProject(projectId);
|
this.activateProject(projectId);
|
||||||
if (this.activeProject) {
|
if (this.activeProject) {
|
||||||
this._appState.activeFileId = fileId;
|
this._appState.activeFileId = fileId;
|
||||||
|
|||||||
26
apps/red-ui/src/app/utils/custom-route-reuse.strategy.ts
Normal file
26
apps/red-ui/src/app/utils/custom-route-reuse.strategy.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router';
|
||||||
|
|
||||||
|
export class CustomRouteReuseStrategy implements RouteReuseStrategy {
|
||||||
|
private _handlers: { [key: string]: DetachedRouteHandle } = {};
|
||||||
|
|
||||||
|
shouldDetach(route: ActivatedRouteSnapshot): boolean {
|
||||||
|
return !!route.routeConfig.data?.reuse;
|
||||||
|
}
|
||||||
|
|
||||||
|
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
|
||||||
|
if (handle === null) return;
|
||||||
|
this._handlers[route.toString()] = handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldAttach(route: ActivatedRouteSnapshot): boolean {
|
||||||
|
return !!this._handlers[route.toString()];
|
||||||
|
}
|
||||||
|
|
||||||
|
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
|
||||||
|
return this._handlers[route.toString()] as DetachedRouteHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldReuseRoute(future: ActivatedRouteSnapshot, current: ActivatedRouteSnapshot): boolean {
|
||||||
|
return future.routeConfig === current.routeConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user