Add fileName property to models and update related components for file handling
This commit is contained in:
@@ -1,12 +1,16 @@
|
||||
import {Component, CUSTOM_ELEMENTS_SCHEMA, inject} from '@angular/core';
|
||||
import {Component, ElementRef, inject, ViewChild} from '@angular/core';
|
||||
import {MatCard, MatCardContent, MatCardHeader} from '@angular/material/card';
|
||||
import {MatMiniFabButton} from '@angular/material/button';
|
||||
import {MatToolbar, MatToolbarRow} from '@angular/material/toolbar';
|
||||
import {MatTooltip} from '@angular/material/tooltip';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
import {FileInfo} from '../../../models/file-info';
|
||||
import {UserApiService} from '../user-api.service';
|
||||
import {Base64ImagePayload, UploadImageResponse, UserApiService} from '../user-api.service';
|
||||
import {CropService, ImageCroppedEvent, ImageCropperComponent, ImageTransform, LoadedImage} from 'ngx-image-cropper';
|
||||
import {DomSanitizer, SafeUrl} from '@angular/platform-browser';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {DialogCroppImageComponent} from '../dialog-cropp-image.component/dialog-cropp-image.component';
|
||||
import {MatChipListbox, MatChipOption, MatChipSelectionChange} from '@angular/material/chips';
|
||||
|
||||
@Component({
|
||||
selector: 'app-images.component',
|
||||
@@ -18,17 +22,49 @@ import {MatDialog} from '@angular/material/dialog';
|
||||
MatMiniFabButton,
|
||||
MatToolbar,
|
||||
MatToolbarRow,
|
||||
MatTooltip
|
||||
MatTooltip,
|
||||
ImageCropperComponent,
|
||||
MatChipListbox,
|
||||
MatChipOption
|
||||
],
|
||||
templateUrl: './images.component.html',
|
||||
styleUrl: './images.component.scss',
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
styleUrl: './images.component.scss'
|
||||
})
|
||||
export class ImagesComponent {
|
||||
|
||||
fileInfos?: FileInfo[] = [];
|
||||
|
||||
readonly dialog = inject(MatDialog);
|
||||
|
||||
constructor(private userApiService: UserApiService) {
|
||||
imageChangedEvent: Event | null = null;
|
||||
croppedImage: string | null | undefined = '';
|
||||
|
||||
croppedImageBase64: string | null | undefined;
|
||||
|
||||
@ViewChild('imagePreview') imagePreview!: ElementRef;
|
||||
|
||||
canvasRotation = 0;
|
||||
transform: ImageTransform = {
|
||||
translateUnit: 'px',
|
||||
scale: 1,
|
||||
rotate: 0,
|
||||
flipH: false,
|
||||
flipV: false,
|
||||
translateH: 0,
|
||||
translateV: 0
|
||||
};
|
||||
cropperStaticWidth = 0;
|
||||
cropperStaticHeight = 0;
|
||||
|
||||
loading = false;
|
||||
|
||||
currentCroppedFile: FileInfo | undefined;
|
||||
|
||||
constructor(
|
||||
|
||||
private userApiService: UserApiService,
|
||||
private sanitizer: DomSanitizer,
|
||||
) {
|
||||
this.userApiService.getImages().subscribe(data =>{
|
||||
this.fileInfos = data;
|
||||
console.log(data);
|
||||
@@ -36,6 +72,214 @@ export class ImagesComponent {
|
||||
}
|
||||
|
||||
downloadImage(url: string | undefined) {
|
||||
const link = document.createElement('a');
|
||||
// @ts-ignore
|
||||
link.download = url;
|
||||
// @ts-ignore
|
||||
link.href = url;
|
||||
link.click();
|
||||
}
|
||||
|
||||
openCropImageDialog(imageUrl: string | undefined, enterAnimationDuration: string, exitAnimationDuration: string) {
|
||||
let dialogCropImageRef = this.dialog.open(DialogCroppImageComponent, {
|
||||
height: '500px',
|
||||
width: '800px',
|
||||
enterAnimationDuration,
|
||||
exitAnimationDuration,
|
||||
|
||||
}
|
||||
|
||||
);
|
||||
dialogCropImageRef.componentInstance.cropImage(imageUrl);
|
||||
|
||||
// dialogCropImageRef.componentInstance.uploadClicked.subscribe(result => {
|
||||
// dialogCropImageRef.close();
|
||||
// this.openUploadImageDialog(enterAnimationDuration, exitAnimationDuration);
|
||||
// })
|
||||
|
||||
// const dialogSelectSubscription = dialogCropImageRef.componentInstance.selectClicked
|
||||
// .subscribe(result => {
|
||||
// console.log('Got the data!', result);
|
||||
//
|
||||
// if (result == null) {
|
||||
// return;
|
||||
// }
|
||||
// this.tutorial.titleimage = result.url;
|
||||
//
|
||||
// });
|
||||
}
|
||||
|
||||
cropImage(fileInfo: FileInfo) {
|
||||
|
||||
// close the crop view for a previous cropped file
|
||||
if(this.currentCroppedFile !== undefined){
|
||||
this.currentCroppedFile.mode = "View";
|
||||
}
|
||||
|
||||
fileInfo.mode = "CropView";
|
||||
this.setBase64String(fileInfo);
|
||||
this.currentCroppedFile = fileInfo;
|
||||
|
||||
}
|
||||
|
||||
setBase64String(fileInfo:FileInfo) {
|
||||
if(fileInfo.url !== undefined){
|
||||
this.userApiService.loadImageAsBase64(fileInfo.url).subscribe(data => {
|
||||
fileInfo.base64string = data;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
imageCropped(event: ImageCroppedEvent) {
|
||||
if (event.objectUrl != null) {
|
||||
|
||||
// this.croppedImage = this.sanitizer.bypassSecurityTrustUrl(event.objectUrl);
|
||||
|
||||
}
|
||||
else {
|
||||
this.croppedImageBase64 = event.base64;
|
||||
this.croppedImage = this.croppedImageBase64;
|
||||
}
|
||||
this.imagePreview.nativeElement.height = event.height;
|
||||
this.imagePreview.nativeElement.width = event.width;
|
||||
}
|
||||
|
||||
imageLoaded(image: LoadedImage) {
|
||||
// show cropper
|
||||
}
|
||||
cropperReady() {
|
||||
// cropper ready
|
||||
}
|
||||
loadImageFailed() {
|
||||
// show message
|
||||
}
|
||||
|
||||
zoomIn() {
|
||||
this.transform = {
|
||||
...this.transform,
|
||||
scale: this.transform.scale! + .1
|
||||
};
|
||||
}
|
||||
|
||||
zoomOut() {
|
||||
this.transform = {
|
||||
...this.transform,
|
||||
scale: this.transform.scale! - .1
|
||||
};
|
||||
}
|
||||
|
||||
moveLeft() {
|
||||
this.transform = {
|
||||
...this.transform,
|
||||
translateH: this.transform.translateH! - 1
|
||||
};
|
||||
}
|
||||
moveRight() {
|
||||
this.transform = {
|
||||
...this.transform,
|
||||
translateH: this.transform.translateH! + 1
|
||||
};
|
||||
}
|
||||
|
||||
moveDown() {
|
||||
this.transform = {
|
||||
...this.transform,
|
||||
translateV: this.transform.translateV! + 1
|
||||
};
|
||||
}
|
||||
|
||||
moveUp() {
|
||||
this.transform = {
|
||||
...this.transform,
|
||||
translateV: this.transform.translateV! - 1
|
||||
};
|
||||
}
|
||||
|
||||
flipHorizontal() {
|
||||
this.transform = {
|
||||
...this.transform,
|
||||
flipH: !this.transform.flipH
|
||||
};
|
||||
}
|
||||
|
||||
flipVertical() {
|
||||
this.transform = {
|
||||
...this.transform,
|
||||
flipV: !this.transform.flipV
|
||||
};
|
||||
}
|
||||
|
||||
rotateLeft() {
|
||||
this.loading = true;
|
||||
setTimeout(() => { // Use timeout because rotating image is a heavy operation and will block the ui thread
|
||||
this.canvasRotation--;
|
||||
this.flipAfterRotate();
|
||||
});
|
||||
}
|
||||
|
||||
rotateRight() {
|
||||
this.loading = true;
|
||||
setTimeout(() => {
|
||||
this.canvasRotation++;
|
||||
this.flipAfterRotate();
|
||||
});
|
||||
}
|
||||
|
||||
private flipAfterRotate() {
|
||||
const flippedH = this.transform.flipH;
|
||||
const flippedV = this.transform.flipV;
|
||||
this.transform = {
|
||||
...this.transform,
|
||||
flipH: flippedV,
|
||||
flipV: flippedH,
|
||||
translateH: 0,
|
||||
translateV: 0
|
||||
};
|
||||
}
|
||||
|
||||
cropFixedSize($event: MatChipSelectionChange) {
|
||||
if ($event.source.selected) {
|
||||
this.cropperStaticWidth = 716;
|
||||
this.cropperStaticHeight = 400;
|
||||
}
|
||||
else {
|
||||
this.cropperStaticWidth = 0;
|
||||
this.cropperStaticHeight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
save() {
|
||||
const payload = this.getBase64ImagePayload();
|
||||
this.userApiService.uploadUserBase64Image(payload).subscribe({
|
||||
next: (res:UploadImageResponse) =>{
|
||||
const fileInfo: FileInfo = {
|
||||
name: res.fileName,
|
||||
url: res.url,
|
||||
mode: "View",
|
||||
base64string: undefined
|
||||
}
|
||||
this.fileInfos?.push(fileInfo);
|
||||
console.log("Uploaded:", res);
|
||||
} ,
|
||||
error: (err) => console.error("Upload failed", err),
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private getBase64ImagePayload(): Base64ImagePayload {
|
||||
|
||||
const match = this.croppedImageBase64?.match(/^data:(.+);base64,(.*)$/);
|
||||
if(match)
|
||||
{
|
||||
const [, mime, base64] = match;
|
||||
return {
|
||||
mime: mime,
|
||||
data: base64
|
||||
};
|
||||
|
||||
}
|
||||
else {
|
||||
throw new Error("Invalid base64 string");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user