Commit ea668a7e authored by Florent POITTEVIN's avatar Florent POITTEVIN
Browse files

refactor: migrate preview into solidify

parent a154649c
......@@ -40,6 +40,7 @@ import {
AbstractAppComponent,
AppStatusService,
BreakpointService,
ChemicalMoleculeVisualizationEnum,
isNotNullNorUndefined,
LoggingService,
MARK_AS_TRANSLATABLE,
......
......@@ -97,6 +97,7 @@ import {
StandardErrorsHandlerService,
STORE_DIALOG_SERVICE,
STORE_ROUTE_SERVICE,
VisualizationState,
} from "solidify-frontend";
import {AppComponent} from "./app.component";
import {MainToolbarDesktopHorizontalPresentational} from "./components/presentationals/main-toolbar/main-toolbar-desktop-horizontal/main-toolbar-desktop-horizontal.presentational";
......@@ -144,6 +145,7 @@ export const appModuleState = [
AppSystemPropertyState,
AppBannerState,
AppCarouselState,
VisualizationState,
];
const appInitializerFn = (appConfig: AppConfigService) => () => appConfig.mergeConfig(environment).toPromise();
......
......@@ -8,10 +8,7 @@ import {
OnInit,
ViewChild,
} from "@angular/core";
import {
MatDialog,
MatDialogRef,
} from "@angular/material/dialog";
import {MatDialog} from "@angular/material/dialog";
import {ActivatedRoute} from "@angular/router";
import {AppState} from "@app/stores/app.state";
import {
......@@ -42,9 +39,9 @@ import {
Select,
Store,
} from "@ngxs/store";
import {SharedFileAndAipInformationContainer} from "@shared/components/containers/shared-file-and-aip-information/shared-file-and-aip-information.container";
import {SharedPreviewDialogData} from "@shared/components/dialogs/shared-preview/shared-preview-dialog-data.model";
import {SharedPreviewDialog} from "@shared/components/dialogs/shared-preview/shared-preview.dialog";
import {ApiActionNameEnum} from "@shared/enums/api-action-name.enum";
import {ApiResourceNameEnum} from "@shared/enums/api-resource-name.enum";
import {ApiEnum} from "@shared/enums/api.enum";
import {DataTableComponentEnum} from "@shared/enums/data-table-component.enum";
import {DataTestEnum} from "@shared/enums/data-test.enum";
import {FileViewModeEnum} from "@shared/enums/file-view-mode.enum";
......@@ -55,11 +52,7 @@ import {
DepositRoutesEnum,
} from "@shared/enums/routes.enum";
import {StateEnum} from "@shared/enums/state.enum";
import {FileVisualizerHelper} from "@shared/filevisualizer/helpers/file-visualizer.helper";
import {
AbstractFileVisualizer,
FILE_VISUALIZERS,
} from "@shared/filevisualizer/services/abstract-file-visualizer.service";
import {DataFileHelper} from "@shared/helpers/data-file.helper";
import {DataTableComponentHelper} from "@shared/helpers/data-table-component.helper";
import {SecurityService} from "@shared/services/security.service";
import {Observable} from "rxjs";
......@@ -72,6 +65,7 @@ import {
} from "rxjs/operators";
import {
AbstractDetailEditRoutable,
AbstractFileVisualizer,
ConfirmDialog,
ConfirmDialogData,
DataTableActions,
......@@ -81,6 +75,10 @@ import {
DataTablePresentational,
DeleteDialog,
DeleteDialogData,
FILE_VISUALIZERS,
FilePreviewDialog,
FilePreviewDialogData,
FileVisualizerHelper,
isNonEmptyArray,
isNullOrUndefined,
isTrue,
......@@ -159,7 +157,7 @@ export class DepositFileContainer extends AbstractDetailEditRoutable<Deposit, De
callback: (depositDataFile: DepositDataFile) => this._showPreview(depositDataFile),
placeholder: current => LabelTranslateEnum.showPreview,
displayOnCondition: (depositDataFile: DepositDataFile) => FileVisualizerHelper.canHandle(this._fileVisualizers, {
dataFile: depositDataFile,
dataFile: DataFileHelper.dataFileAdapter(depositDataFile),
fileExtension: FileVisualizerHelper.getFileExtension(depositDataFile.fileName),
}),
},
......@@ -428,16 +426,16 @@ export class DepositFileContainer extends AbstractDetailEditRoutable<Deposit, De
}
private _showPreview(dataFile: DepositDataFile): void {
this._dialog.open(SharedPreviewDialog, {
this._dialog.open(FilePreviewDialog, {
width: "max-content",
maxWidth: "90vw",
height: "min-content",
data: {
fileInput: {
dataFile: dataFile,
dataFile: DataFileHelper.dataFileAdapter(dataFile),
},
} as SharedPreviewDialogData,
fileDownloadUrl: `${ApiEnum.preIngestDeposits}/${dataFile.infoPackage.resId}/${ApiResourceNameEnum.DATAFILE}/${dataFile.resId}/${ApiActionNameEnum.DL}`,
} as FilePreviewDialogData,
});
}
......
import {Deposit} from "@models";
import {DataFile} from "@shared/models/business/data-file.model";
export interface DepositDataFile extends DataFile {
export interface DepositDataFile extends DataFile<Deposit> {
}
......@@ -34,10 +34,6 @@ import {
HomePageRoutesEnum,
RoutesEnum,
} from "@shared/enums/routes.enum";
import {
AbstractFileVisualizer,
FILE_VISUALIZERS,
} from "@shared/filevisualizer/services/abstract-file-visualizer.service";
import {DataTableComponentHelper} from "@shared/helpers/data-table-component.helper";
import {LocalStateModel} from "@shared/models/local-state.model";
import {SecurityService} from "@shared/services/security.service";
......@@ -47,11 +43,13 @@ import {
tap,
} from "rxjs/operators";
import {
AbstractFileVisualizer,
DataTableActions,
DataTableBulkActions,
DataTableColumns,
DataTableFieldTypeEnum,
DataTablePresentational,
FILE_VISUALIZERS,
isUndefined,
MemoizedUtil,
OrderEnum,
......
import {Archive} from "@home/models/archive.model";
import {DataFile} from "@shared/models/business/data-file.model";
export interface ArchiveDataFile extends DataFile {
export interface ArchiveDataFile extends DataFile<Archive> {
}
import {Dip} from "@models";
import {DataFile} from "@shared/models/business/data-file.model";
export interface DipDataFile extends DataFile {
export interface DipDataFile extends DataFile<Dip> {
}
......@@ -54,14 +54,14 @@
<solidify-icon [iconName]="iconNameEnum.fullScreenEnter"></solidify-icon>
</button>
</div>
<dlcm-file-visualizer
(errorMessageChange)="visualizationErrorMessage = $event"
(fullScreenAvailableChange)="changeFullScreen($event)"
[class.has-error-message]="visualizationErrorMessage"
[fileToVisualize]="getFileInput()"
[zoomEnable]="zoomEnable"
class="visualization-mol"
></dlcm-file-visualizer>
<solidify-file-visualizer (errorMessageChange)="visualizationErrorMessage = $event"
(fullScreenAvailableChange)="changeFullScreen($event)"
[class.has-error-message]="visualizationErrorMessage"
[fileToVisualize]="getFileInput()"
[zoomEnable]="zoomEnable"
[fileDownloadUrl]="fileDownloadUrl"
class="visualization-mol"
></solidify-file-visualizer>
<div *ngIf="visualizationErrorMessage"
class="warning-area"
>
......
......@@ -18,16 +18,14 @@ import {
Store,
} from "@ngxs/store";
import {SharedAbstractContainer} from "@shared/components/containers/shared-abstract/shared-abstract.container";
import {ApiActionNameEnum} from "@shared/enums/api-action-name.enum";
import {ApiResourceNameEnum} from "@shared/enums/api-resource-name.enum";
import {ApiEnum} from "@shared/enums/api.enum";
import {DataTableComponentEnum} from "@shared/enums/data-table-component.enum";
import {IconNameEnum} from "@shared/enums/icon-name.enum";
import {LabelTranslateEnum} from "@shared/enums/label-translate.enum";
import {urlSeparator} from "@shared/enums/routes.enum";
import {FileVisualizerHelper} from "@shared/filevisualizer/helpers/file-visualizer.helper";
import {FileInput} from "@shared/filevisualizer/models/file-info.model";
import {
AbstractFileVisualizer,
FILE_VISUALIZERS,
} from "@shared/filevisualizer/services/abstract-file-visualizer.service";
import {DataFileHelper} from "@shared/helpers/data-file.helper";
import {DataTableComponentHelper} from "@shared/helpers/data-table-component.helper";
import {DataFile} from "@shared/models/business/data-file.model";
import {LocalStateModel} from "@shared/models/local-state.model";
......@@ -41,6 +39,7 @@ import {
Observable,
} from "rxjs";
import {
AbstractFileVisualizer,
BaseResource,
BreakpointService,
ClipboardUtil,
......@@ -48,7 +47,12 @@ import {
DataTableFieldTypeEnum,
DateUtil,
ExtraButtonToolbar,
FILE_VISUALIZERS,
FileInput,
FileVisualizerHelper,
isNullOrUndefined,
LABEL_TRANSLATE,
LabelTranslateInterface,
MARK_AS_TRANSLATABLE,
MemoizedUtil,
NotificationService,
......@@ -116,6 +120,10 @@ export class SharedFileAndAipInformationContainer<TResource extends BaseResource
return Enums.DataFile.StatusEnum;
}
get fileDownloadUrl(): string {
return `${ApiEnum.preIngestDeposits}/${this.data.dataFile.infoPackage.resId}/${ApiResourceNameEnum.DATAFILE}/${this.data.dataFile.resId}/${ApiActionNameEnum.DL}`;
}
private readonly _locationBS: BehaviorSubject<void> = new BehaviorSubject<void>(undefined);
@Output("locationChange")
readonly locationObs: Observable<void> = ObservableUtil.asObservable(this._locationBS);
......@@ -147,7 +155,9 @@ export class SharedFileAndAipInformationContainer<TResource extends BaseResource
protected readonly _breakpointService: BreakpointService,
protected readonly _notificationService: NotificationService,
protected readonly _changeDetector: ChangeDetectorRef,
@Inject(FILE_VISUALIZERS) protected readonly _fileVisualizers: AbstractFileVisualizer[]) {
@Inject(FILE_VISUALIZERS) protected readonly _fileVisualizers: AbstractFileVisualizer[],
@Inject(LABEL_TRANSLATE) protected readonly _labelTranslateInterface: LabelTranslateInterface,
) {
super();
}
......@@ -207,14 +217,14 @@ export class SharedFileAndAipInformationContainer<TResource extends BaseResource
return;
}
this.previewAvailable = this.data.mode === SharedFileAndAipDetailDialogModeEnum.file && FileVisualizerHelper.canHandle(this._fileVisualizers, {
dataFile: this.data.dataFile,
dataFile: DataFileHelper.dataFileAdapter(this.data.dataFile),
fileExtension: FileVisualizerHelper.getFileExtension(this.data.dataFile.fileName),
});
if (!this.previewAvailable && this.data.mode === SharedFileAndAipDetailDialogModeEnum.file) {
this.visualizationErrorMessage = FileVisualizerHelper.errorMessage(this._fileVisualizers, {
dataFile: this.data.dataFile,
dataFile: DataFileHelper.dataFileAdapter(this.data.dataFile),
fileExtension: FileVisualizerHelper.getFileExtension(this.data.dataFile.fileName),
});
}, this._labelTranslateInterface);
}
}
......@@ -236,7 +246,7 @@ export class SharedFileAndAipInformationContainer<TResource extends BaseResource
getFileInput(): FileInput {
return {
dataFile: this.data.dataFile,
dataFile: DataFileHelper.dataFileAdapter(this.data.dataFile),
};
}
......
import {FileInput} from "@shared/filevisualizer/models/file-info.model";
export interface SharedPreviewDialogData {
fileInput?: FileInput;
}
<solidify-base-info-dialog [titleToTranslate]="'app.visualizer.title' | translate"
>
<dlcm-file-visualizer
(closeVisualizerChange)="onClosePopup($event)"
[fileToVisualize]="data?.fileInput"
[zoomEnable]="true"
></dlcm-file-visualizer>
</solidify-base-info-dialog>
import {
ChangeDetectionStrategy,
Component,
Inject,
} from "@angular/core";
import {
MAT_DIALOG_DATA,
MatDialogRef,
} from "@angular/material/dialog";
import {SharedAbstractContainer} from "@shared/components/containers/shared-abstract/shared-abstract.container";
import {SharedPreviewDialogData} from "@shared/components/dialogs/shared-preview/shared-preview-dialog-data.model";
@Component({
selector: "dlcm-shared-preview-dialog",
templateUrl: "./shared-preview.dialog.html",
styleUrls: ["./shared-preview.dialog.scss"],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SharedPreviewDialog extends SharedAbstractContainer {
constructor(@Inject(MAT_DIALOG_DATA) public data: SharedPreviewDialogData,
public dialogRef: MatDialogRef<SharedPreviewDialog>) {
super();
}
onClosePopup(needToClosePopup: boolean): void {
this.dialogRef.close();
}
}
export enum ChemicalMoleculeVisualizationEnum {
disabled = 0,
threeDimensionalOnly,
threeAndTwoDimensional,
}
......@@ -70,7 +70,6 @@ enum StateExtendEnum {
shared_archive = "shared_archive",
shared_archiveAcl = "shared_archiveAcl",
shared_language = "shared_language",
shared_visualization = "shared_visualization",
shared_notification = "shared_notification",
shared_license = "shared_license",
shared_deposit = "shared_deposit",
......
import {Aip} from "@models";
import {DataFile} from "@shared/models/business/data-file.model";
export interface AipDataFile extends DataFile {
export interface AipDataFile extends DataFile<Aip> {
checksums?: any;
}
<div #visualizationContainer
class="visualization-container"
>
<ng-template dlcmExternalPlugin></ng-template>
<div *ngIf="isLoadingVisualizationObs | async"
[solidifySpinner]="isLoadingVisualizationObs | async"
class="spinner-background"
></div>
</div>
:host {
display: block;
min-height: 300px;
min-width: 300px;
.visualization-container {
height: 100%;
overflow-x: hidden;
overflow-y: auto;
}
.spinner-background {
background-color: rgba(255, 255, 255, 0.66);
height: 100%;
min-height: 300px;
min-width: 300px;
animation: from-transparent-background-color 0.5s ease-in-out;
}
.zoom-in {
cursor: zoom-in;
overflow-y: auto;
max-height: 100%;
}
.zoom-out {
cursor: zoom-out;
overflow-y: auto;
max-height: 100%;
}
}
import {
AfterContentInit,
Component,
ElementRef,
HostListener,
Inject,
Input,
OnDestroy,
Output,
ViewChild,
} from "@angular/core";
import {Store} from "@ngxs/store";
import {AddExternalComponentDirective} from "@shared/filevisualizer/directives/add-external-component.directive";
import {FileVisualizerHelper} from "@shared/filevisualizer/helpers/file-visualizer.helper";
import {BlobElementModel} from "@shared/filevisualizer/models/blob-element.model";
import {FileInput} from "@shared/filevisualizer/models/file-info.model";
import {
AbstractFileVisualizer,
FILE_VISUALIZERS,
} from "@shared/filevisualizer/services/abstract-file-visualizer.service";
import {DownloadService} from "@shared/services/download.service";
import {SharedVisualizationState} from "@shared/stores/visualization/shared-visualization.state";
import {SharedVisualizationAction} from "@shared/stores/visualization/shared-visualization.action";
import {
BehaviorSubject,
Observable,
} from "rxjs";
import {
filter,
take,
} from "rxjs/operators";
import {
CoreAbstractAngularElement,
isNotNullNorUndefined,
isTruthyObject,
MARK_AS_TRANSLATABLE,
MemoizedUtil,
NotificationService,
ObservableUtil,
} from "solidify-frontend";
@Component({
selector: "dlcm-file-visualizer",
templateUrl: "./file-visualizer.component.html",
styleUrls: ["./file-visualizer.component.scss"],
})
export class FileVisualizerPresentational extends CoreAbstractAngularElement implements AfterContentInit, OnDestroy {
isLoadingVisualizationObs: Observable<boolean> = MemoizedUtil.isLoading(this._store, SharedVisualizationState);
previewFile: Observable<BlobElementModel> = MemoizedUtil.select(this._store, SharedVisualizationState, state =>
({
blob: state.blob,
blobId: state.blobId,
}));
private static filePreviewId: number = -1;
@HostListener("click", ["$event"])
click(mouseEvent: MouseEvent): void {
mouseEvent.stopPropagation();
const elem = mouseEvent.target as Element;
if (this.visualizationContainer.nativeElement.contains(elem)) {
const element = this._fileVisualizers.find(value => value.isVisualizationOnGoing(this.fileToVisualize, this.visualizationContainer.nativeElement));
element?.doAction(this.fileToVisualize, this.visualizationContainer.nativeElement);
}
}
@ViewChild("visualizationContainer", {static: true})
visualizationContainer: ElementRef<Element>;
@ViewChild(AddExternalComponentDirective, {static: true}) addExternalPlugin: AddExternalComponentDirective;
private readonly _closeBS: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
@Output("closeVisualizerChange")
readonly closeVisualizerObs: Observable<boolean> = ObservableUtil.asObservable(this._closeBS);
private readonly _errorMessageBS: BehaviorSubject<string> = new BehaviorSubject<string>("");
@Output("errorMessageChange")
readonly errorMessageObs: Observable<string> = ObservableUtil.asObservable(this._errorMessageBS);
private readonly _fullScreenAvailableBS: BehaviorSubject<boolean | undefined> = new BehaviorSubject<boolean>(undefined);
@Output("fullScreenAvailableChange")
readonly fullScreenAvailableObs: Observable<boolean | undefined> = ObservableUtil.asObservable(this._fullScreenAvailableBS);
@Input()
visualizationContainerInput: ElementRef<Element>;
@Input()
fileToVisualize: FileInput;
_zoomEnable: boolean = false;
@Input()
set zoomEnable(zoomEnable: boolean) {
if (zoomEnable !== this._zoomEnable) {
this.updateZoom();
}
this._zoomEnable = zoomEnable;
}
constructor(@Inject(FILE_VISUALIZERS) private readonly _fileVisualizers: AbstractFileVisualizer[],
private readonly _store: Store,
private _notificationService: NotificationService,
private readonly _downloadService: DownloadService) {
super();
}
ngAfterContentInit(): void {
if (isTruthyObject(this.fileToVisualize)) {
this.visualize(this.fileToVisualize, this.visualizationContainer.nativeElement);
}
}
ngOnDestroy(): void {
super.ngOnDestroy();
this.closeVisualization();
}
canHandleBySize(fileInfo: FileInput): boolean {
return isTruthyObject(fileInfo) && this._fileVisualizers.some(value => value.canHandleWithSize(fileInfo));
}
visualize(fileInfoParam: FileInput, documentElement: Element): void {
const fileExtention = FileVisualizerHelper.getFileExtension(fileInfoParam.dataFile.fileName);
let fileInfo: FileInput = {
fileExtension: fileExtention,
dataFile: fileInfoParam.dataFile,
blob: fileInfoParam?.blob,
container: documentElement,
};
if (!FileVisualizerHelper.canHandle(this._fileVisualizers, fileInfo)) {
this.closeVisualization();
if (!this.canHandleBySize(fileInfo)) {
const sizeNotSupported = MARK_AS_TRANSLATABLE("notification.file.visualizer.file.tooBig");
this._notificationService.showInformation(sizeNotSupported);
this._errorMessageBS.next(sizeNotSupported);
} else {
const notSupportedMessage = MARK_AS_TRANSLATABLE("notification.file.visualizer.file.notSupported");
this._notificationService.showInformation(notSupportedMessage);
this._errorMessageBS.next(notSupportedMessage);
}
return;
}
const filePreviewId = FileVisualizerPresentational.filePreviewId++;
const handleByPronomId = this._fileVisualizers.find(value => value.canHandleByPuid(fileInfo));
const handleByContentType = this._fileVisualizers.find(value => value.canHandleByMimeTypeContentType(fileInfo));
const handleByExtension = this._fileVisualizers.find(value => value.canHandleByExtension(fileInfo));
this.subscribe(this.previewFile.pipe(
filter(value => isTruthyObject(value.blob) && filePreviewId === value.blobId),
take(1)),
value => {
fileInfo = {
fileExtension: fileExtention,
dataFile: fileInfo.dataFile,
container: documentElement,
blob: value.blob,
};
if (isTruthyObject(handleByPronomId)) {
handleByPronomId.openVisualizer(fileInfo, documentElement, this.addExternalPlugin);
this._fullScreenAvailableBS.next(handleByPronomId.isFullScreenSupported());
} else if (isTruthyObject(handleByContentType)) {
if (this.isPlainTextFile(fileInfo) && !isTruthyObject(handleByExtension)) {
this._fileVisualizers.find(visualizer => visualizer.type === "textPlugin").openVisualizer(fileInfo, documentElement);