Replaced file actions with file assign service, move some services
This commit is contained in:
parent
ea0eaab218
commit
b99f9f4470
@ -1,10 +1,10 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, CanDeactivate, Router, RouterStateSnapshot } from '@angular/router';
|
||||
import { DossiersService } from '../services/entity-services/dossiers.service';
|
||||
import { BreadcrumbsService } from '../services/breadcrumbs.service';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
import { BreadcrumbsService } from '@services/breadcrumbs.service';
|
||||
import { pluck } from 'rxjs/operators';
|
||||
import { AppStateService } from '../state/app-state.service';
|
||||
import { FilesMapService } from '../services/entity-services/files-map.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { FilesMapService } from '@services/entity-services/files-map.service';
|
||||
import { FilesService } from '@services/entity-services/files.service';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, CanDeactivate, Router, RouterStateSnapshot } from '@angular/router';
|
||||
import { FilesMapService } from '../services/entity-services/files-map.service';
|
||||
import { AppStateService } from '../state/app-state.service';
|
||||
import { DossiersService } from '../services/entity-services/dossiers.service';
|
||||
import { BreadcrumbsService } from '../services/breadcrumbs.service';
|
||||
import { FilesMapService } from '@services/entity-services/files-map.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
import { BreadcrumbsService } from '@services/breadcrumbs.service';
|
||||
import { pluck } from 'rxjs/operators';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SharedModule } from '../../../shared/shared.module';
|
||||
import { SharedModule } from '@shared/shared.module';
|
||||
import { NotificationsScreenComponent } from './notifications-screen/notifications-screen.component';
|
||||
|
||||
const routes = [{ path: '', component: NotificationsScreenComponent }];
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SharedModule } from '../../../shared/shared.module';
|
||||
import { SharedModule } from '@shared/shared.module';
|
||||
import { UserProfileScreenComponent } from './user-profile-screen/user-profile-screen.component';
|
||||
|
||||
const routes = [{ path: '', component: UserProfileScreenComponent }];
|
||||
|
||||
@ -10,7 +10,7 @@ import { BASE_HREF } from '../../../../tokens';
|
||||
import { stampPDFPage } from '@utils/page-stamper';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
||||
import { WatermarkService } from '../../../shared/services/watermark.service';
|
||||
import { WatermarkService } from '@shared/services/watermark.service';
|
||||
|
||||
export const DEFAULT_WATERMARK: IWatermark = {
|
||||
text: null,
|
||||
|
||||
@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, OnD
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { ConfigService } from '@services/config.service';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
import { ViewedPagesService } from '../../shared/services/viewed-pages.service';
|
||||
import { ViewedPagesService } from '@services/entity-services/viewed-pages.service';
|
||||
import { File, IViewedPage } from '@red/domain';
|
||||
import { AutoUnsubscribe } from '@iqser/common-ui';
|
||||
import { FilesMapService } from '@services/entity-services/files-map.service';
|
||||
|
||||
@ -20,7 +20,7 @@ import { Observable } from 'rxjs';
|
||||
import { distinctUntilChanged, map } from 'rxjs/operators';
|
||||
import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
|
||||
import { FilesService } from '@services/entity-services/files.service';
|
||||
import { FileManagementService } from '../../../shared/services/file-management.service';
|
||||
import { FileManagementService } from '@services/entity-services/file-management.service';
|
||||
|
||||
interface FileListItem extends IFile, IListable {
|
||||
readonly canRestore: boolean;
|
||||
|
||||
@ -38,7 +38,6 @@ import { AnnotationsListComponent } from './components/file-workload/components/
|
||||
import { AnnotationSourceComponent } from './components/file-workload/components/annotation-source/annotation-source.component';
|
||||
import { OverlayModule } from '@angular/cdk/overlay';
|
||||
import { SharedDossiersModule } from './shared/shared-dossiers.module';
|
||||
import { PlatformSearchService } from './shared/services/platform-search.service';
|
||||
import { ResizeAnnotationDialogComponent } from './dialogs/resize-annotation-dialog/resize-annotation-dialog.component';
|
||||
import { BreadcrumbsService } from '@services/breadcrumbs.service';
|
||||
import { of } from 'rxjs';
|
||||
@ -88,7 +87,6 @@ const services = [
|
||||
PdfViewerDataService,
|
||||
AnnotationDrawService,
|
||||
AnnotationProcessingService,
|
||||
PlatformSearchService,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
||||
@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { Dossier, File } from '@red/domain';
|
||||
import { FileActionService } from '../../../../shared/services/file-action.service';
|
||||
import { FileAssignService } from '../../../../shared/services/file-assign.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { DossiersDialogService } from '../../../../services/dossiers-dialog.service';
|
||||
import { CircleButtonTypes, ConfirmationDialogInput, ListingService, LoadingService } from '@iqser/common-ui';
|
||||
@ -10,7 +10,7 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { LongPressEvent } from '@shared/directives/long-press.directive';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { FileManagementService } from '../../../../shared/services/file-management.service';
|
||||
import { FileManagementService } from '@services/entity-services/file-management.service';
|
||||
import { ReanalysisService } from '@services/reanalysis.service';
|
||||
import { FilesService } from '@services/entity-services/files.service';
|
||||
|
||||
@ -34,7 +34,7 @@ export class DossierOverviewBulkActionsComponent {
|
||||
private readonly _fileManagementService: FileManagementService,
|
||||
private readonly _reanalysisService: ReanalysisService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _fileActionService: FileActionService,
|
||||
private readonly _fileAssignService: FileAssignService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _translateService: TranslateService,
|
||||
readonly listingService: ListingService<File>,
|
||||
@ -142,7 +142,13 @@ export class DossierOverviewBulkActionsComponent {
|
||||
if (this.dossier.approverIds.length > 1) {
|
||||
this._assignFiles('approver', true);
|
||||
} else {
|
||||
this._performBulkAction(this._fileActionService.setFilesUnderApproval(this.selectedFiles, this.dossier.approverIds[0]));
|
||||
this._performBulkAction(
|
||||
this._filesService.setUnderApprovalFor(
|
||||
this.selectedFiles.map(f => f.id),
|
||||
this.dossier.id,
|
||||
this.dossier.approverIds[0],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,7 +205,7 @@ export class DossierOverviewBulkActionsComponent {
|
||||
}
|
||||
|
||||
assignToMe() {
|
||||
this._fileActionService.assignToMe(this.selectedFiles).then(() => {
|
||||
this._fileAssignService.assignToMe(this.selectedFiles).then(() => {
|
||||
this._loadingService.start();
|
||||
this.reload.emit();
|
||||
this._loadingService.stop();
|
||||
|
||||
@ -14,7 +14,7 @@ import {
|
||||
} from '@iqser/common-ui';
|
||||
import { File, IFileAttributeConfig, StatusSorter, WorkflowFileStatus, WorkflowFileStatuses } from '@red/domain';
|
||||
import { workflowFileStatusTranslations } from '../../translations/file-status-translations';
|
||||
import { FileActionService } from '../../shared/services/file-action.service';
|
||||
import { FileAssignService } from '../../shared/services/file-assign.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
@ -35,7 +35,7 @@ export class ConfigService {
|
||||
private readonly _listingMode$ = new BehaviorSubject<ListingMode>(ListingModes.table);
|
||||
|
||||
constructor(
|
||||
private readonly _fileActionService: FileActionService,
|
||||
private readonly _fileAssignService: FileAssignService,
|
||||
private readonly _filesService: FilesService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
@ -374,16 +374,16 @@ export class ConfigService {
|
||||
};
|
||||
|
||||
private _underReviewFn = (reloadDossiers: () => Promise<void>) => (file: File) => {
|
||||
this._fileActionService.assignFile('reviewer', null, file, () => this._loadingService.loadWhile(reloadDossiers()), true);
|
||||
this._fileAssignService.assignReviewer(null, file, () => this._loadingService.loadWhile(reloadDossiers()), true);
|
||||
};
|
||||
|
||||
private _underApprovalFn = (reloadDossiers: () => Promise<void>) => async (file: File) => {
|
||||
const dossier = this._dossiersService.find(file.dossierId);
|
||||
if (dossier.approverIds.length > 1) {
|
||||
this._fileActionService.assignFile('approver', null, file, () => this._loadingService.loadWhile(reloadDossiers()), true);
|
||||
this._fileAssignService.assignApprover(null, file, () => this._loadingService.loadWhile(reloadDossiers()), true);
|
||||
} else {
|
||||
this._loadingService.start();
|
||||
await this._fileActionService.setFilesUnderApproval([file]).toPromise();
|
||||
await this._filesService.setUnderApprovalFor([file.id], dossier.dossierId, dossier.approverIds[0]).toPromise();
|
||||
await reloadDossiers();
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { ManualAnnotationResponse } from '@models/file/manual-annotation-response';
|
||||
import { AnnotationData, FileDataModel } from '@models/file/file-data.model';
|
||||
import { FileActionService } from '../../shared/services/file-action.service';
|
||||
import { FileAssignService } from '../../shared/services/file-assign.service';
|
||||
import { AnnotationDrawService } from '../../services/annotation-draw.service';
|
||||
import { AnnotationProcessingService } from '../../services/annotation-processing.service';
|
||||
import { Dossier, File, User, ViewMode, WorkflowFileStatus } from '@red/domain';
|
||||
@ -50,10 +50,10 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { FileActionsComponent } from '../../shared/components/file-actions/file-actions.component';
|
||||
import { FilesService } from '@services/entity-services/files.service';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
import { FileManagementService } from '../../shared/services/file-management.service';
|
||||
import { FileManagementService } from '@services/entity-services/file-management.service';
|
||||
import { filter, switchMap, switchMapTo, tap } from 'rxjs/operators';
|
||||
import { FilesMapService } from '@services/entity-services/files-map.service';
|
||||
import { WatermarkService } from '../../../shared/services/watermark.service';
|
||||
import { WatermarkService } from '@shared/services/watermark.service';
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
import PDFNet = Core.PDFNet;
|
||||
|
||||
@ -112,7 +112,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
private readonly _toaster: Toaster,
|
||||
private readonly _annotationProcessingService: AnnotationProcessingService,
|
||||
private readonly _annotationDrawService: AnnotationDrawService,
|
||||
private readonly _fileActionService: FileActionService,
|
||||
private readonly _fileAssignService: FileAssignService,
|
||||
private readonly _fileDownloadService: PdfViewerDataService,
|
||||
private readonly _filesService: FilesService,
|
||||
private readonly _ngZone: NgZone,
|
||||
@ -507,7 +507,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
async assignToMe(file: File) {
|
||||
await this._fileActionService.assignToMe([file], async () => {
|
||||
await this._fileAssignService.assignToMe([file], async () => {
|
||||
await this._filesService.reload(this.dossierId, this.fileId);
|
||||
this._updateCanPerformActions();
|
||||
});
|
||||
|
||||
@ -19,9 +19,9 @@ import { workflowFileStatusTranslations } from '../../translations/file-status-t
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { RouterHistoryService } from '@services/router-history.service';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
import { PlatformSearchService } from '../../shared/services/platform-search.service';
|
||||
import { Dossier, IMatchedDocument, ISearchInput, ISearchListItem, ISearchResponse } from '@red/domain';
|
||||
import { FilesMapService } from '@services/entity-services/files-map.service';
|
||||
import { PlatformSearchService } from '@services/entity-services/platform-search.service';
|
||||
|
||||
function toSearchInput(query: string, dossierIds: List | string): ISearchInput {
|
||||
return {
|
||||
|
||||
@ -4,9 +4,9 @@ import { catchError, map, tap } from 'rxjs/operators';
|
||||
import { FileDataModel } from '@models/file/file-data.model';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { File } from '@red/domain';
|
||||
import { FileManagementService } from '../shared/services/file-management.service';
|
||||
import { FileManagementService } from '@services/entity-services/file-management.service';
|
||||
import { RedactionLogService } from './redaction-log.service';
|
||||
import { ViewedPagesService } from '../shared/services/viewed-pages.service';
|
||||
import { ViewedPagesService } from '@services/entity-services/viewed-pages.service';
|
||||
|
||||
@Injectable()
|
||||
export class PdfViewerDataService {
|
||||
|
||||
@ -17,9 +17,9 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { LongPressEvent } from '@shared/directives/long-press.directive';
|
||||
import { FileActionService } from '../../services/file-action.service';
|
||||
import { FileAssignService } from '../../services/file-assign.service';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
import { FileManagementService } from '../../services/file-management.service';
|
||||
import { FileManagementService } from '@services/entity-services/file-management.service';
|
||||
import { FilesService } from '@services/entity-services/files.service';
|
||||
import { ReanalysisService } from '@services/reanalysis.service';
|
||||
|
||||
@ -69,7 +69,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
readonly appStateService: AppStateService,
|
||||
readonly dossiersService: DossiersService,
|
||||
private readonly _dialogService: DossiersDialogService,
|
||||
private readonly _fileActionService: FileActionService,
|
||||
private readonly _fileAssignService: FileAssignService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _fileManagementService: FileManagementService,
|
||||
private readonly _filesService: FilesService,
|
||||
@ -139,7 +139,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
async assignToMe($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
|
||||
await this._fileActionService.assignToMe([this.file], () => {
|
||||
await this._fileAssignService.assignToMe([this.file], () => {
|
||||
this.reloadFiles('reanalyse');
|
||||
});
|
||||
}
|
||||
@ -159,9 +159,9 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
$event.stopPropagation();
|
||||
const dossier = this.dossiersService.find(this.file.dossierId);
|
||||
if (dossier.approverIds.length > 1) {
|
||||
this._fileActionService.assignFile('approver', $event, this.file, () => this.reloadFiles('assign-reviewer'), true);
|
||||
this._fileAssignService.assignApprover($event, this.file, () => this.reloadFiles('assign-reviewer'), true);
|
||||
} else {
|
||||
await this._fileActionService.setFilesUnderApproval([this.file]).toPromise();
|
||||
await this._filesService.setUnderApprovalFor([this.file.id], dossier.id, dossier.approverIds[0]).toPromise();
|
||||
this.reloadFiles('set-under-approval');
|
||||
}
|
||||
}
|
||||
@ -193,7 +193,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
}
|
||||
|
||||
setFileUnderReview($event: MouseEvent, ignoreDialogChanges = false) {
|
||||
this._fileActionService.assignFile('reviewer', $event, this.file, () => this.reloadFiles('assign-reviewer'), ignoreDialogChanges);
|
||||
this._fileAssignService.assignReviewer($event, this.file, () => this.reloadFiles('assign-reviewer'), ignoreDialogChanges);
|
||||
}
|
||||
|
||||
reloadFiles(action: string) {
|
||||
|
||||
@ -8,7 +8,7 @@ import { ConfirmationDialogInput, Toaster } from '@iqser/common-ui';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
|
||||
@Injectable()
|
||||
export class FileActionService {
|
||||
export class FileAssignService {
|
||||
constructor(
|
||||
private readonly _dialogService: DossiersDialogService,
|
||||
private readonly _userService: UserService,
|
||||
@ -38,27 +38,21 @@ export class FileActionService {
|
||||
});
|
||||
}
|
||||
|
||||
setFilesUnderApproval(files: File[], approverId?: string) {
|
||||
const dossier = this._getDossier(files[0]);
|
||||
|
||||
if (!approverId) {
|
||||
approverId = dossier.approverIds[0];
|
||||
}
|
||||
|
||||
return this._filesService.setUnderApprovalFor(
|
||||
files.map(f => f.fileId),
|
||||
dossier.id,
|
||||
approverId,
|
||||
);
|
||||
assignReviewer($event: MouseEvent, file: File, callback?: Function, ignoreChanged = false): void {
|
||||
this._assignFile('reviewer', $event, file, callback, ignoreChanged);
|
||||
}
|
||||
|
||||
assignFile(mode: 'reviewer' | 'approver', $event: MouseEvent, file: File, callback?: Function, ignoreChanged = false) {
|
||||
assignApprover($event: MouseEvent, file: File, callback?: Function, ignoreChanged = false): void {
|
||||
this._assignFile('approver', $event, file, callback, ignoreChanged);
|
||||
}
|
||||
|
||||
private _assignFile(mode: 'reviewer' | 'approver', $event: MouseEvent, file: File, callback?: Function, ignoreChanged = false) {
|
||||
const dossier = this._dossiersService.find(file.dossierId);
|
||||
const userIds = this._getUserIds(mode, dossier);
|
||||
if (userIds.length === 1 || userIds.includes(this._userService.currentUser.id)) {
|
||||
$event?.stopPropagation(); // event$ is null when called from workflow view
|
||||
const userId = userIds.length === 1 ? userIds[0] : this._userService.currentUser.id;
|
||||
this._assignFile(userId, mode, [file]).then(async () => {
|
||||
this._makeAssignFileRequest(userId, mode, [file]).then(async () => {
|
||||
if (callback) {
|
||||
await callback();
|
||||
}
|
||||
@ -73,11 +67,7 @@ export class FileActionService {
|
||||
}
|
||||
}
|
||||
|
||||
private _getDossier(file: File): Dossier {
|
||||
return this._dossiersService.find(file.dossierId);
|
||||
}
|
||||
|
||||
private async _assignFile(userId: string, mode: 'reviewer' | 'approver', files: File[]) {
|
||||
private async _makeAssignFileRequest(userId: string, mode: 'reviewer' | 'approver', files: File[]) {
|
||||
try {
|
||||
if (mode === 'reviewer') {
|
||||
await this._filesService
|
||||
@ -1,6 +1,6 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FileActionService } from './services/file-action.service';
|
||||
import { FileAssignService } from './services/file-assign.service';
|
||||
import { FileActionsComponent } from './components/file-actions/file-actions.component';
|
||||
import { IqserIconsModule } from '@iqser/common-ui';
|
||||
import { SharedModule } from '@shared/shared.module';
|
||||
@ -10,7 +10,7 @@ const components = [FileActionsComponent];
|
||||
@NgModule({
|
||||
declarations: [...components],
|
||||
exports: [...components],
|
||||
providers: [FileActionService],
|
||||
providers: [FileAssignService],
|
||||
imports: [CommonModule, IqserIconsModule, SharedModule],
|
||||
})
|
||||
export class SharedDossiersModule {}
|
||||
|
||||
@ -43,9 +43,9 @@ export class FilesService extends EntitiesService<File, IFile> {
|
||||
}
|
||||
|
||||
@Validate()
|
||||
setUnderApprovalFor(@RequiredParam() body: List, @RequiredParam() dossierId: string, approverId: string) {
|
||||
setUnderApprovalFor(@RequiredParam() fileIds: List, @RequiredParam() dossierId: string, approverId: string) {
|
||||
const url = `${this._defaultModelPath}/under-approval/${dossierId}/bulk`;
|
||||
return this._post<unknown>(body, url, [{ key: 'approverId', value: approverId }]);
|
||||
return this._post<unknown>(fileIds, url, [{ key: 'approverId', value: approverId }]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -3,11 +3,11 @@ import { GenericService } from '@iqser/common-ui';
|
||||
import { IMatchedDocument, ISearchInput, ISearchRequest, ISearchResponse } from '@red/domain';
|
||||
import { Observable, of, zip } from 'rxjs';
|
||||
import { mapTo, switchMap } from 'rxjs/operators';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
import { FilesMapService } from '@services/entity-services/files-map.service';
|
||||
import { FilesService } from '@services/entity-services/files.service';
|
||||
import { DossiersService } from './dossiers.service';
|
||||
import { FilesMapService } from './files-map.service';
|
||||
import { FilesService } from './files.service';
|
||||
|
||||
@Injectable()
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PlatformSearchService extends GenericService<ISearchResponse> {
|
||||
constructor(
|
||||
protected readonly _injector: Injector,
|
||||
Loading…
x
Reference in New Issue
Block a user