From e5369ffacf9194667e4964457c90e90527732791 Mon Sep 17 00:00:00 2001 From: ngfchl Date: Sat, 18 Jul 2026 12:47:59 +0800 Subject: [PATCH] feat: verify empty folders before cleanup --- lib/pages/workspace_tools_page.dart | 101 +++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 2 deletions(-) diff --git a/lib/pages/workspace_tools_page.dart b/lib/pages/workspace_tools_page.dart index 210334a..44e1cf0 100644 --- a/lib/pages/workspace_tools_page.dart +++ b/lib/pages/workspace_tools_page.dart @@ -395,6 +395,104 @@ class _CleanupList extends ConsumerWidget { const _CleanupList({required this.files, required this.emptyText}); + Future _confirmDeleteEmpty( + BuildContext context, + WidgetRef ref, + CloudFile folder, + ) async { + final confirmed = await showShadDialog( + context: context, + builder: (dialogContext) => ShadDialog( + title: Text('删除空文件夹「${folder.name}」?'), + actions: [ + ShadButton.outline( + onPressed: () => Navigator.of(dialogContext).pop(false), + child: const Text('取消'), + ), + ShadButton.destructive( + onPressed: () => Navigator.of(dialogContext).pop(true), + child: const Text('重新校验并删除'), + ), + ], + child: const Padding( + padding: EdgeInsets.only(top: 10), + child: Text('删除前会重新检查目录是否为空;已发生变化的目录将被保留。'), + ), + ), + ); + if (confirmed != true || !context.mounted) return; + + try { + final response = await ref + .read(authProvider.notifier) + .api + .fsFiles(parentID: folder.id, pageSize: 1); + if (_hasCloudFile(response)) { + if (!context.mounted) return; + await showShadDialog( + context: context, + builder: (dialogContext) => ShadDialog( + title: const Text('已跳过删除'), + actions: [ + ShadButton( + onPressed: () => Navigator.of(dialogContext).pop(), + child: const Text('知道了'), + ), + ], + child: const Padding( + padding: EdgeInsets.only(top: 10), + child: Text('该文件夹在扫描后已有内容,未执行删除。'), + ), + ), + ); + return; + } + await ref.read(fileProvider.notifier).deleteFiles([folder]); + } catch (_) { + if (!context.mounted) return; + await showShadDialog( + context: context, + builder: (dialogContext) => ShadDialog( + title: const Text('无法校验文件夹'), + actions: [ + ShadButton( + onPressed: () => Navigator.of(dialogContext).pop(), + child: const Text('关闭'), + ), + ], + child: const Padding( + padding: EdgeInsets.only(top: 10), + child: Text('未能重新读取目录内容,因此没有执行删除。'), + ), + ), + ); + } + } + + bool _hasCloudFile(Map value) { + var found = false; + void visit(dynamic node) { + if (found) return; + if (node is Map) { + try { + CloudFile.fromJson(Map.from(node)); + found = true; + return; + } catch (_) {} + for (final child in node.values) { + visit(child); + } + } else if (node is List) { + for (final child in node) { + visit(child); + } + } + } + + visit(value); + return found; + } + @override Widget build(BuildContext context, WidgetRef ref) { final cs = ShadTheme.of(context).colorScheme; @@ -414,8 +512,7 @@ class _CleanupList extends ConsumerWidget { title: Text(file.name), trailing: ShadButton.destructive( size: ShadButtonSize.sm, - onPressed: () => - ref.read(fileProvider.notifier).deleteFiles([file]), + onPressed: () => _confirmDeleteEmpty(context, ref, file), child: const Text('删除'), ), ),