scope batch rename conflicts by parent directory

This commit is contained in:
ngfchl
2026-07-18 19:28:54 +08:00
parent 189334b6fa
commit 5a23f438f1
4 changed files with 101 additions and 5 deletions
+10 -5
View File
@@ -123,7 +123,7 @@ List<BatchRenamePreview> buildRenamePreviews(
}
final existing = <String, Set<String>>{};
for (final file in files) {
final key = _destinationKey(file.cloudPath, file.name);
final key = _destinationKey(file, file.name);
(existing[key] ??= <String>{}).add(file.id);
}
final reserved = <String, Set<String>>{
@@ -131,7 +131,7 @@ List<BatchRenamePreview> buildRenamePreviews(
};
final proposedCounts = <String, int>{};
for (final item in values.where((item) => item.applicable)) {
final key = _destinationKey(item.file.cloudPath, item.newName);
final key = _destinationKey(item.file, item.newName);
proposedCounts[key] = (proposedCounts[key] ?? 0) + 1;
}
final result = <BatchRenamePreview>[];
@@ -141,7 +141,7 @@ List<BatchRenamePreview> buildRenamePreviews(
continue;
}
var targetName = item.newName;
var targetKey = _destinationKey(item.file.cloudPath, targetName);
var targetKey = _destinationKey(item.file, targetName);
if (conflictStrategy == BatchRenameConflictStrategy.reject &&
proposedCounts[targetKey]! > 1) {
result.add(
@@ -160,7 +160,7 @@ List<BatchRenamePreview> buildRenamePreviews(
var index = 2;
do {
targetName = _appendIndex(item.newName, index++);
targetKey = _destinationKey(item.file.cloudPath, targetName);
targetKey = _destinationKey(item.file, targetName);
} while (conflicts());
}
if (conflicts()) {
@@ -188,7 +188,12 @@ String _appendIndex(String name, int index) {
}
/// Collision scope is a single cloud directory, never the whole drive.
String _destinationKey(String path, String name) {
String _destinationKey(CloudFile file, String name) {
final parentID = file.parentID?.trim();
if (parentID != null && parentID.isNotEmpty) {
return 'id:$parentID/${name.toLowerCase()}';
}
final path = file.cloudPath;
final separator = path.lastIndexOf('/');
final parent = separator < 0 ? '' : path.substring(0, separator);
return '${parent.toLowerCase()}/${name.toLowerCase()}';
+19
View File
@@ -35,6 +35,11 @@ class CloudFile {
final int? subFileCount;
final String modifiedAt;
final String cloudPath;
/// Stable parent directory ID from the cloud API, used for directory-scoped
/// operations even when the API omits a full display path.
final String? parentID;
final String? fullParentIDs;
final int fileType;
const CloudFile({
@@ -47,6 +52,8 @@ class CloudFile {
this.subFileCount,
this.modifiedAt = '',
this.cloudPath = '',
this.parentID,
this.fullParentIDs,
this.fileType = 0,
});
@@ -104,6 +111,8 @@ class CloudFile {
int? subFileCount,
String? modifiedAt,
String? cloudPath,
String? parentID,
String? fullParentIDs,
int? fileType,
}) {
return CloudFile(
@@ -116,6 +125,8 @@ class CloudFile {
subFileCount: subFileCount ?? this.subFileCount,
modifiedAt: modifiedAt ?? this.modifiedAt,
cloudPath: cloudPath ?? this.cloudPath,
parentID: parentID ?? this.parentID,
fullParentIDs: fullParentIDs ?? this.fullParentIDs,
fileType: fileType ?? this.fileType,
);
}
@@ -191,6 +202,12 @@ class CloudFile {
cloudPath:
_extractString(json, ['location', 'path', 'fullPath']) ??
name.toString(),
parentID: _extractString(json, ['parentId', 'parent_id', 'parentID']),
fullParentIDs: _extractString(json, [
'fullParentIds',
'fullParentIDs',
'full_parent_ids',
]),
fileType: type,
);
}
@@ -205,6 +222,8 @@ class CloudFile {
'subFileCount': subFileCount,
'updateTime': modifiedAt,
'path': cloudPath,
'parentId': parentID,
'fullParentIds': fullParentIDs,
'fileType': fileType,
};