feat: safely select duplicate file cleanup

This commit is contained in:
ngfchl
2026-07-18 12:49:41 +08:00
parent e5369ffacf
commit b0c8e79399
+111 -15
View File
@@ -521,14 +521,58 @@ class _CleanupList extends ConsumerWidget {
}
}
class _DuplicateGroup extends ConsumerWidget {
class _DuplicateGroup extends ConsumerStatefulWidget {
final List<CloudFile> files;
const _DuplicateGroup({required this.files});
@override
Widget build(BuildContext context, WidgetRef ref) {
ConsumerState<_DuplicateGroup> createState() => _DuplicateGroupState();
}
class _DuplicateGroupState extends ConsumerState<_DuplicateGroup> {
late Set<String> _selectedIDs;
@override
void initState() {
super.initState();
_selectedIDs = widget.files.skip(1).map((file) => file.id).toSet();
}
Future<void> _confirmDelete() async {
final targets = widget.files
.where((file) => _selectedIDs.contains(file.id))
.toList();
if (targets.isEmpty) return;
final confirmed = await showShadDialog<bool>(
context: context,
builder: (dialogContext) => ShadDialog(
title: Text('删除 ${targets.length} 个重复文件?'),
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 || !mounted) return;
await ref.read(fileProvider.notifier).deleteFiles(targets);
if (mounted) setState(_selectedIDs.clear);
}
@override
Widget build(BuildContext context) {
final cs = ShadTheme.of(context).colorScheme;
final selectedCount = _selectedIDs.length;
return Container(
margin: const EdgeInsets.only(top: 10),
padding: const EdgeInsets.all(10),
@@ -538,21 +582,73 @@ class _DuplicateGroup extends ConsumerWidget {
),
child: Column(
children: [
for (final file in files.skip(1))
ListTile(
dense: true,
contentPadding: EdgeInsets.zero,
title: Text(file.name),
subtitle: Text(
file.cloudPath,
maxLines: 1,
overflow: TextOverflow.ellipsis,
Row(
children: [
Text(
'重复组 · ${widget.files.length}',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w700,
color: cs.primary,
),
),
trailing: ShadButton.destructive(
const Spacer(),
ShadButton.destructive(
size: ShadButtonSize.sm,
onPressed: () =>
ref.read(fileProvider.notifier).deleteFiles([file]),
child: const Text('删除副本'),
onPressed: selectedCount == 0 ? null : _confirmDelete,
child: Text('删除已选 $selectedCount'),
),
],
),
for (final file in widget.files)
Padding(
padding: const EdgeInsets.only(top: 7),
child: Row(
children: [
ShadCheckbox(
value: _selectedIDs.contains(file.id),
onChanged: (value) => setState(() {
if (value == true) {
if (_selectedIDs.length < widget.files.length - 1) {
_selectedIDs.add(file.id);
}
} else {
_selectedIDs.remove(file.id);
}
}),
),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
file.name,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Text(
file.cloudPath,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 11,
color: cs.mutedForeground,
),
),
],
),
),
Text(
_selectedIDs.contains(file.id) ? '将删除' : '保留',
style: TextStyle(
fontSize: 12,
color: _selectedIDs.contains(file.id)
? cs.destructive
: cs.primary,
),
),
],
),
),
],