feat: safely select duplicate file cleanup
This commit is contained in:
@@ -521,14 +521,58 @@ class _CleanupList extends ConsumerWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DuplicateGroup extends ConsumerWidget {
|
class _DuplicateGroup extends ConsumerStatefulWidget {
|
||||||
final List<CloudFile> files;
|
final List<CloudFile> files;
|
||||||
|
|
||||||
const _DuplicateGroup({required this.files});
|
const _DuplicateGroup({required this.files});
|
||||||
|
|
||||||
@override
|
@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 cs = ShadTheme.of(context).colorScheme;
|
||||||
|
final selectedCount = _selectedIDs.length;
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(top: 10),
|
margin: const EdgeInsets.only(top: 10),
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
@@ -538,21 +582,73 @@ class _DuplicateGroup extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
for (final file in files.skip(1))
|
Row(
|
||||||
ListTile(
|
children: [
|
||||||
dense: true,
|
Text(
|
||||||
contentPadding: EdgeInsets.zero,
|
'重复组 · ${widget.files.length} 项',
|
||||||
title: Text(file.name),
|
style: TextStyle(
|
||||||
subtitle: Text(
|
fontSize: 12,
|
||||||
file.cloudPath,
|
fontWeight: FontWeight.w700,
|
||||||
maxLines: 1,
|
color: cs.primary,
|
||||||
overflow: TextOverflow.ellipsis,
|
),
|
||||||
),
|
),
|
||||||
trailing: ShadButton.destructive(
|
const Spacer(),
|
||||||
|
ShadButton.destructive(
|
||||||
size: ShadButtonSize.sm,
|
size: ShadButtonSize.sm,
|
||||||
onPressed: () =>
|
onPressed: selectedCount == 0 ? null : _confirmDelete,
|
||||||
ref.read(fileProvider.notifier).deleteFiles([file]),
|
child: Text('删除已选 $selectedCount 项'),
|
||||||
child: const Text('删除副本'),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user