Add confirm dialog to changes of file status

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/4/head
trivernis 3 years ago
parent db7c0adcaa
commit 0edd5e4edc

@ -2,6 +2,7 @@
{{title}} {{title}}
</h1> </h1>
<div mat-dialog-content> <div mat-dialog-content>
<app-content-aware-image *ngIf="this.image" [imageSrc]="this.image"></app-content-aware-image>
{{message}} {{message}}
</div> </div>
<div class="confirm-dialog-actions" mat-dialog-actions> <div class="confirm-dialog-actions" mat-dialog-actions>

@ -6,3 +6,7 @@
margin-left: 1em; margin-left: 1em;
} }
} }
app-content-aware-image {
margin-bottom: 1em;
}

@ -1,6 +1,17 @@
import {Component, Inject} from "@angular/core"; import {Component, Inject} from "@angular/core";
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {ThemePalette} from "@angular/material/core"; import {ThemePalette} from "@angular/material/core";
import {SafeResourceUrl} from "@angular/platform-browser";
export type ConfirmDialogData = {
title: string,
message: string,
image?: string | SafeResourceUrl,
confirmAction: string,
denyAction?: string,
confirmColor?: ThemePalette,
denyColor?: ThemePalette
};
@Component({ @Component({
selector: "app-confirm-dialog", selector: "app-confirm-dialog",
@ -9,17 +20,18 @@ import {ThemePalette} from "@angular/material/core";
}) })
export class ConfirmDialogComponent { export class ConfirmDialogComponent {
title = ""; public title = "";
message = ""; public message = "";
confirmAction = ""; public confirmAction = "";
confirmColor: ThemePalette = "primary"; public image?: string | SafeResourceUrl;
denyAction = "Cancel"; public confirmColor: ThemePalette = "primary";
denyColor: ThemePalette = "accent"; public denyAction = "Cancel";
public denyColor: ThemePalette = "accent";
constructor( constructor(
public dialogRef: MatDialogRef<ConfirmDialogComponent>, public dialogRef: MatDialogRef<ConfirmDialogComponent>,
@Inject( @Inject(
MAT_DIALOG_DATA) data: { title: string, message: string, confirmAction: string, denyAction?: string, confirmColor?: ThemePalette, denyColor?: ThemePalette} MAT_DIALOG_DATA) data: ConfirmDialogData
) { ) {
this.title = data.title; this.title = data.title;
this.message = data.message; this.message = data.message;
@ -27,6 +39,7 @@ export class ConfirmDialogComponent {
this.denyAction = data.denyAction ?? this.denyAction; this.denyAction = data.denyAction ?? this.denyAction;
this.confirmColor = data.confirmColor ?? this.confirmColor; this.confirmColor = data.confirmColor ?? this.confirmColor;
this.denyColor = data.denyColor ?? this.denyColor; this.denyColor = data.denyColor ?? this.denyColor;
this.image = data.image;
} }
public closeDialog(result: boolean) { public closeDialog(result: boolean) {

@ -1,4 +1,4 @@
import {Component, EventEmitter, Output, ViewChild} from "@angular/core"; import {Component, EventEmitter, OnChanges, Output, SimpleChanges, ViewChild} from "@angular/core";
import {File} from "../../../../../api/models/File"; import {File} from "../../../../../api/models/File";
import {ContextMenuComponent} from "../../app-common/context-menu/context-menu.component"; import {ContextMenuComponent} from "../../app-common/context-menu/context-menu.component";
import {clipboard} from "@tauri-apps/api"; import {clipboard} from "@tauri-apps/api";
@ -6,9 +6,11 @@ import {FileService} from "../../../../services/file/file.service";
import {ErrorBrokerService} from "../../../../services/error-broker/error-broker.service"; import {ErrorBrokerService} from "../../../../services/error-broker/error-broker.service";
import {FileHelper} from "../../../../services/file/file.helper"; import {FileHelper} from "../../../../services/file/file.helper";
import {FileStatus} from "../../../../../api/api-types/files"; import {FileStatus} from "../../../../../api/api-types/files";
import {MatDialog, MatDialogRef} from "@angular/material/dialog"; import {MatDialog, MatDialogConfig, MatDialogRef} from "@angular/material/dialog";
import {BusyDialogComponent} from "../../app-common/busy-dialog/busy-dialog.component"; import {BusyDialogComponent} from "../../app-common/busy-dialog/busy-dialog.component";
import {BehaviorSubject} from "rxjs"; import {BehaviorSubject} from "rxjs";
import {ConfirmDialogComponent, ConfirmDialogData} from "../../app-common/confirm-dialog/confirm-dialog.component";
import {SafeResourceUrl} from "@angular/platform-browser";
type ProgressDialogContext = { type ProgressDialogContext = {
dialog: MatDialogRef<BusyDialogComponent>, dialog: MatDialogRef<BusyDialogComponent>,
@ -21,7 +23,7 @@ type ProgressDialogContext = {
templateUrl: "./file-context-menu.component.html", templateUrl: "./file-context-menu.component.html",
styleUrls: ["./file-context-menu.component.scss"] styleUrls: ["./file-context-menu.component.scss"]
}) })
export class FileContextMenuComponent { export class FileContextMenuComponent implements OnChanges {
public files: File[] = []; public files: File[] = [];
@ -37,6 +39,12 @@ export class FileContextMenuComponent {
constructor(private fileService: FileService, private errorBroker: ErrorBrokerService, private dialog: MatDialog) { constructor(private fileService: FileService, private errorBroker: ErrorBrokerService, private dialog: MatDialog) {
} }
public ngOnChanges(changes: SimpleChanges): void {
if (changes["files"]) {
this.applyStatus();
}
}
public onContextMenu(event: MouseEvent, files: File[]) { public onContextMenu(event: MouseEvent, files: File[]) {
this.files = files; this.files = files;
this.applyStatus(); this.applyStatus();
@ -61,9 +69,34 @@ export class FileContextMenuComponent {
public async updateStatus(status: FileStatus) { public async updateStatus(status: FileStatus) {
if (this.files.length === 1) { if (this.files.length === 1) {
let changeConfirmed;
if (status === "Deleted") {
changeConfirmed = await this.openConfirmDialog(
"Confirm deletion",
"Do you really want to move this file to trash?",
"Delete",
"warn",
this.getImageThumbnail(this.files[0])
);
} else {
changeConfirmed = true;
}
if (changeConfirmed) {
const newFile = await this.fileService.updateFileStatus(this.files[0].id, status); const newFile = await this.fileService.updateFileStatus(this.files[0].id, status);
this.files[0].status = newFile.status; this.files[0].status = newFile.status;
this.fileUpdate.emit();
this.applyStatus();
}
} else { } else {
const statusChangeConfirmed = await this.openConfirmDialog(
"Confirm mass status change",
`Do you really want to change the status of ${this.files.length} files to '${status}'?`,
"Change status",
status === "Deleted" ? "warn" : "primary"
);
if (statusChangeConfirmed) {
await this.iterateWithProgress( await this.iterateWithProgress(
`Updating file status to '${status}'`, `Updating file status to '${status}'`,
this.files, this.files,
@ -72,34 +105,68 @@ export class FileContextMenuComponent {
file.status = newFile.status; file.status = newFile.status;
} }
); );
}
this.fileUpdate.emit(); this.fileUpdate.emit();
this.applyStatus();
}
}
} }
public async deletePermanently() { public async deletePermanently() {
if (this.files.length === 1) { if (this.files.length === 1) {
const deletionConfirmed = await this.openConfirmDialog(
"Confirm deletion",
"Do you really want to permanently delete this file?",
"Delete permanently",
"warn",
this.getImageThumbnail(this.files[0]),
);
if (deletionConfirmed) {
await this.fileService.deleteFile(this.files[0].id); await this.fileService.deleteFile(this.files[0].id);
this.fileUpdate.emit();
this.applyStatus();
}
} else { } else {
const deletionConfirmed = await this.openConfirmDialog(
"Confirm mass deletion",
`Do you really want to permanently delete ${this.files.length} files?`,
"Delete permanently",
"warn"
);
if (deletionConfirmed) {
await this.iterateWithProgress( await this.iterateWithProgress(
"Deleting files", "Deleting files",
this.files, this.files,
async (file) => this.fileService.deleteFile(file.id) async (file) => this.fileService.deleteFile(file.id)
); );
}
this.fileUpdate.emit(); this.fileUpdate.emit();
this.applyStatus();
}
}
} }
private applyStatus() { private applyStatus() {
this.actionDeletePermantently = true; this.actionDeletePermantently = true;
this.actionDelete = this.actionArchive = this.actionImported = this.actionRestore = false;
for (const file of this.files) { for (const file of this.files) {
this.actionDeletePermantently &&= file.status === "Deleted"; this.actionDeletePermantently &&= file.status === "Deleted";
this.actionDelete ||= file.status !== "Deleted"; this.actionDelete ||= file.status !== "Deleted";
this.actionArchive ||= file.status !== "Archived"; this.actionArchive ||= file.status !== "Archived" && file.status !== "Deleted";
this.actionImported ||= file.status !== "Imported"; this.actionImported ||= file.status !== "Imported" && file.status !== "Deleted";
this.actionRestore ||= file.status === "Deleted"; this.actionRestore ||= file.status === "Deleted";
} }
} }
private getImageThumbnail(file: File): SafeResourceUrl | undefined {
const mimeParts = FileHelper.parseMime(file.mimeType);
if (mimeParts && ["image", "video"].includes(mimeParts[0])) {
return this.fileService.buildThumbnailUrl(file, 250, 250);
} else {
return;
}
}
private async iterateWithProgress<T>(title: string, items: T[], action: (arg: T) => Promise<any>): Promise<void> { private async iterateWithProgress<T>(title: string, items: T[], action: (arg: T) => Promise<any>): Promise<void> {
const totalCount = items.length; const totalCount = items.length;
const dialogCtx = this.openProgressDialog(title, `0/${totalCount}`); const dialogCtx = this.openProgressDialog(title, `0/${totalCount}`);
@ -135,4 +202,24 @@ export class FileContextMenuComponent {
progress: dialogProgress, progress: dialogProgress,
}; };
} }
private openConfirmDialog(
title: string,
question: string,
confirmAction: string,
confirmColor?: "primary" | "warn",
image?: SafeResourceUrl | string
): Promise<boolean> {
const dialog = this.dialog.open(ConfirmDialogComponent, {
data: {
title,
message: question,
confirmAction,
denyAction: "Cancel",
confirmColor,
image
}
} as MatDialogConfig & { data: ConfirmDialogData });
return dialog.afterClosed().toPromise();
}
} }

@ -29,7 +29,7 @@ function tagQueryToString(tagQuery: TagQuery): string {
export function propertyQueryToString(propertyQuery: PropertyQuery): string { export function propertyQueryToString(propertyQuery: PropertyQuery): string {
if ("Status" in propertyQuery) { if ("Status" in propertyQuery) {
return buildExpression("Status", "is", propertyQuery.Status); return buildExpression("Status", "=", propertyQuery.Status);
} else if ("FileSize" in propertyQuery) { } else if ("FileSize" in propertyQuery) {
return buildExpression( return buildExpression(
"FileSize", "FileSize",
@ -61,9 +61,9 @@ export function propertyQueryToString(propertyQuery: PropertyQuery): string {
getValue(propertyQuery.TagCount).toString() getValue(propertyQuery.TagCount).toString()
); );
} else if ("Cd" in propertyQuery) { } else if ("Cd" in propertyQuery) {
return buildExpression("ContentDescriptor", "is", propertyQuery.Cd); return buildExpression("ContentDescriptor", "=", propertyQuery.Cd);
} else if ("Id" in propertyQuery) { } else if ("Id" in propertyQuery) {
return buildExpression("FileId", "is", propertyQuery.Id.toString()); return buildExpression("FileId", "=", propertyQuery.Id.toString());
} else { } else {
return "Invalid Expression"; return "Invalid Expression";
} }

Loading…
Cancel
Save