nx: deduplicate confirmations for deletion

Signed-off-by: Varun Patil <radialapps@gmail.com>
dexie
Varun Patil 2023-09-30 13:38:46 -07:00
parent ccf037e44a
commit 0b745fef6f
5 changed files with 32 additions and 13 deletions

View File

@ -774,15 +774,12 @@ export default defineComponent({
* Delete the currently selected photos * Delete the currently selected photos
*/ */
async deleteSelection(selection: Selection) { async deleteSelection(selection: Selection) {
if (!(await utils.dialogs.moveToTrash(selection.size))) return;
try { try {
for await (const delIds of dav.deletePhotos(selection.photosNoDupFileId())) { for await (const delIds of dav.deletePhotos(selection.photosNoDupFileId())) {
this.deleteSelectedPhotosById(delIds, selection); this.deleteSelectedPhotosById(delIds, selection);
} }
} catch (e) { } catch (e) {
console.error(e); console.error(e);
showError(this.t('memories', 'Failed to delete files'));
} }
}, },

View File

@ -935,7 +935,6 @@ export default defineComponent({
/** Delete this photo and refresh */ /** Delete this photo and refresh */
async deleteCurrent() { async deleteCurrent() {
if (this.routeIsPublic) return; if (this.routeIsPublic) return;
if (!(await utils.dialogs.moveToTrash(1))) return;
let idx = this.photoswipe!.currIndex - this.globalAnchor; let idx = this.photoswipe!.currIndex - this.globalAnchor;
const photo = this.list[idx]; const photo = this.list[idx];
@ -948,7 +947,6 @@ export default defineComponent({
if (!p[0]) return; if (!p[0]) return;
} }
} catch { } catch {
showError(this.t('memories', 'Failed to delete photo'));
return; return;
} finally { } finally {
this.updateLoading(-1); this.updateLoading(-1);

View File

@ -1,6 +1,7 @@
import axios from '@nextcloud/axios'; import axios from '@nextcloud/axios';
import { generateUrl } from '@nextcloud/router'; import { generateUrl } from '@nextcloud/router';
import type { IDay, IPhoto, IImageInfo } from './types'; import type { IDay, IPhoto } from './types';
import { API as SAPI } from './services/API';
const euc = encodeURIComponent; const euc = encodeURIComponent;
/** Access NativeX over localhost */ /** Access NativeX over localhost */
@ -34,6 +35,7 @@ export const API = {
* Delete files using local fileids. * Delete files using local fileids.
* @regex ^/api/image/delete/\d+(,\d+)*$ * @regex ^/api/image/delete/\d+(,\d+)*$
* @param fileIds List of AUIDs to delete * @param fileIds List of AUIDs to delete
* @param dry (Query) Only check for confirmation and count of local files
* @returns {void} * @returns {void}
* @throws Return an error code if the user denies the deletion. * @throws Return an error code if the user denies the deletion.
*/ */
@ -326,13 +328,15 @@ export async function extendDayWithLocal(dayId: number, photos: IPhoto[]) {
/** /**
* Request deletion of local photos wherever available. * Request deletion of local photos wherever available.
* @param photos List of photos to delete * @param photos List of photos to delete
* @returns The number of photos for which confirmation was received
* @throws If the request fails * @throws If the request fails
*/ */
export async function deleteLocalPhotos(photos: IPhoto[]): Promise<void> { export async function deleteLocalPhotos(photos: IPhoto[], dry: boolean = false): Promise<number> {
if (!has()) return; if (!has()) return 0;
const auids = photos.map((p) => p.auid).filter((a) => !!a) as number[]; const auids = photos.map((p) => p.auid).filter((a) => !!a) as number[];
await axios.get(API.IMAGE_DELETE(auids)); const res = await axios.get(SAPI.Q(API.IMAGE_DELETE(auids), { dry }));
return res.data.confirms ? res.data.count : 0;
} }
/** /**

View File

@ -37,7 +37,7 @@ export enum DaysFilterType {
} }
export class API { export class API {
static Q(url: string, query: Record<string, string | number | undefined | null>): string { static Q(url: string, query: Record<string, string | number | undefined | boolean | null>): string {
if (!query) return url; if (!query) return url;
// Get everything as strings // Get everything as strings
@ -49,6 +49,12 @@ export class API {
continue; continue;
} }
if (typeof query[key] === 'boolean') {
if (!query[key]) continue;
records[key] = '1';
continue;
}
records[key] = String(query[key]); records[key] = String(query[key]);
} }

View File

@ -187,9 +187,10 @@ async function extendWithLivePhotos(photos: IPhoto[]) {
* Delete all files in a given list of Ids * Delete all files in a given list of Ids
* *
* @param photos list of photos to delete * @param photos list of photos to delete
* @param confirm whether to show a confirmation dialog (default true)
* @returns list of file ids that were deleted * @returns list of file ids that were deleted
*/ */
export async function* deletePhotos(photos: IPhoto[]) { export async function* deletePhotos(photos: IPhoto[], confirm: boolean = true) {
if (photos.length === 0) return; if (photos.length === 0) return;
// Extend with Live Photos unless this is an album // Extend with Live Photos unless this is an album
@ -209,8 +210,21 @@ export async function* deletePhotos(photos: IPhoto[]) {
// Check for locally available files and delete them. // Check for locally available files and delete them.
// For albums, we are not actually deleting. // For albums, we are not actually deleting.
if (nativex.has() && !routeIsAlbums) { const hasNative = nativex.has() && !routeIsAlbums;
// Delete local files. This will throw if user cancels.
// Check if native confirmation is available
if (hasNative) {
confirm &&= (await nativex.deleteLocalPhotos(photos, true)) !== photos.length;
}
// Show confirmation dialog if required
if (confirm && !(await utils.dialogs.moveToTrash(photos.length))) {
throw new Error('User cancelled deletion');
}
// Delete local files.
if (hasNative) {
// Delete local files.
await nativex.deleteLocalPhotos(photos); await nativex.deleteLocalPhotos(photos);
// Remove purely local files // Remove purely local files