merge ambiguously cased directories
This commit is contained in:
parent
9fb221d549
commit
220342f9bc
2 changed files with 73 additions and 3 deletions
|
@ -4,6 +4,7 @@ import 'dart:ui';
|
||||||
|
|
||||||
import 'package:aves/geo/countries.dart';
|
import 'package:aves/geo/countries.dart';
|
||||||
import 'package:aves/model/entry_cache.dart';
|
import 'package:aves/model/entry_cache.dart';
|
||||||
|
import 'package:aves/model/entry_dirs.dart';
|
||||||
import 'package:aves/model/favourites.dart';
|
import 'package:aves/model/favourites.dart';
|
||||||
import 'package:aves/model/geotiff.dart';
|
import 'package:aves/model/geotiff.dart';
|
||||||
import 'package:aves/model/metadata/address.dart';
|
import 'package:aves/model/metadata/address.dart';
|
||||||
|
@ -31,7 +32,8 @@ class AvesEntry {
|
||||||
// `sizeBytes`, `dateModifiedSecs` can be missing in viewer mode
|
// `sizeBytes`, `dateModifiedSecs` can be missing in viewer mode
|
||||||
int id;
|
int id;
|
||||||
String uri;
|
String uri;
|
||||||
String? _path, _directory, _filename, _extension, _sourceTitle;
|
String? _path, _filename, _extension, _sourceTitle;
|
||||||
|
EntryDir? _directory;
|
||||||
int? pageId, contentId;
|
int? pageId, contentId;
|
||||||
final String sourceMimeType;
|
final String sourceMimeType;
|
||||||
int width, height, sourceRotationDegrees;
|
int width, height, sourceRotationDegrees;
|
||||||
|
@ -175,8 +177,8 @@ class AvesEntry {
|
||||||
|
|
||||||
// directory path, without the trailing separator
|
// directory path, without the trailing separator
|
||||||
String? get directory {
|
String? get directory {
|
||||||
_directory ??= path != null ? pContext.dirname(path!) : null;
|
_directory ??= entryDirRepo.getOrCreate(path != null ? pContext.dirname(path!) : null);
|
||||||
return _directory;
|
return _directory!.resolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
String? get filenameWithoutExtension {
|
String? get filenameWithoutExtension {
|
||||||
|
|
68
lib/model/entry_dirs.dart
Normal file
68
lib/model/entry_dirs.dart
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:aves/services/common/services.dart';
|
||||||
|
import 'package:aves/utils/android_file_utils.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
|
final entryDirRepo = EntryDirRepo._private();
|
||||||
|
|
||||||
|
class EntryDirRepo {
|
||||||
|
EntryDirRepo._private();
|
||||||
|
|
||||||
|
// mapping between the raw entry directory path to a resolvable directory
|
||||||
|
final Map<String?, EntryDir> _dirs = {};
|
||||||
|
final StreamController<EntryDir> _ambiguousDirStreamController = StreamController.broadcast();
|
||||||
|
|
||||||
|
Stream<EntryDir> get ambiguousDirStream => _ambiguousDirStreamController.stream;
|
||||||
|
|
||||||
|
// get a resolvable directory for a raw entry directory path
|
||||||
|
EntryDir getOrCreate(String? asIs) {
|
||||||
|
var entryDir = _dirs[asIs];
|
||||||
|
if (entryDir != null) return entryDir;
|
||||||
|
|
||||||
|
final asIsLower = asIs?.toLowerCase();
|
||||||
|
entryDir = _dirs.values.firstWhereOrNull((dir) => dir.asIsLower == asIsLower);
|
||||||
|
if (entryDir != null && !entryDir.ambiguous) {
|
||||||
|
entryDir.ambiguous = true;
|
||||||
|
_ambiguousDirStreamController.add(entryDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _dirs.putIfAbsent(asIs, () => entryDir ?? EntryDir(asIs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some directories are ambiguous because they use different cases,
|
||||||
|
// but the OS merge and present them as one directory.
|
||||||
|
// This class resolves ambiguous directories to get the directory path
|
||||||
|
// with the right case, as presented by the OS.
|
||||||
|
class EntryDir {
|
||||||
|
final String? asIs, asIsLower;
|
||||||
|
bool ambiguous = false;
|
||||||
|
String? _resolved;
|
||||||
|
|
||||||
|
EntryDir(this.asIs) : asIsLower = asIs?.toLowerCase();
|
||||||
|
|
||||||
|
String? get resolved {
|
||||||
|
if (!ambiguous) return asIs;
|
||||||
|
if (asIs == null) return null;
|
||||||
|
|
||||||
|
_resolved ??= _resolve();
|
||||||
|
return _resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? _resolve() {
|
||||||
|
final vrl = VolumeRelativeDirectory.fromPath(asIs!);
|
||||||
|
if (vrl == null || vrl.relativeDir.isEmpty) return asIs;
|
||||||
|
|
||||||
|
var resolved = vrl.volumePath;
|
||||||
|
final parts = pContext.split(vrl.relativeDir);
|
||||||
|
for (final part in parts) {
|
||||||
|
final partLower = part.toLowerCase();
|
||||||
|
final childrenDirs = Directory(resolved).listSync().where((v) => v.absolute is Directory).toSet();
|
||||||
|
final found = childrenDirs.firstWhereOrNull((v) => pContext.basename(v.path).toLowerCase() == partLower);
|
||||||
|
resolved = found?.path ?? '$resolved${pContext.separator}$part';
|
||||||
|
}
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue