nc-photos/lib/entity/album/sort_provider.dart
2021-08-08 15:13:27 +08:00

159 lines
3.7 KiB
Dart

import 'package:equatable/equatable.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/entity/album/item.dart';
import 'package:nc_photos/entity/file.dart';
import 'package:nc_photos/iterable_extension.dart';
import 'package:nc_photos/type.dart';
import 'package:tuple/tuple.dart';
abstract class AlbumSortProvider with EquatableMixin {
const AlbumSortProvider();
factory AlbumSortProvider.fromJson(JsonObj json) {
final type = json["type"];
final content = json["content"];
switch (type) {
case AlbumNullSortProvider._type:
return AlbumNullSortProvider.fromJson(content.cast<String, dynamic>());
case AlbumTimeSortProvider._type:
return AlbumTimeSortProvider.fromJson(content.cast<String, dynamic>());
default:
_log.shout("[fromJson] Unknown type: $type");
throw ArgumentError.value(type, "type");
}
}
JsonObj toJson() {
String getType() {
if (this is AlbumNullSortProvider) {
return AlbumNullSortProvider._type;
} else if (this is AlbumTimeSortProvider) {
return AlbumTimeSortProvider._type;
} else {
throw StateError("Unknwon subtype");
}
}
return {
"type": getType(),
"content": _toContentJson(),
};
}
/// Return a sorted copy of [items]
List<AlbumItem> sort(List<AlbumItem> items);
JsonObj _toContentJson();
static final _log = Logger("entity.album.sort_provider.AlbumSortProvider");
}
/// Sort provider that does nothing
class AlbumNullSortProvider extends AlbumSortProvider {
const AlbumNullSortProvider();
factory AlbumNullSortProvider.fromJson(JsonObj json) {
return AlbumNullSortProvider();
}
@override
toString() {
return "$runtimeType {"
"}";
}
@override
sort(List<AlbumItem> items) {
return List.from(items);
}
@override
get props => [];
@override
_toContentJson() {
return {};
}
static const _type = "null";
}
abstract class AlbumReversibleSortProvider extends AlbumSortProvider {
const AlbumReversibleSortProvider({
required this.isAscending,
});
@override
toString() {
return "$runtimeType {"
"isAscending: $isAscending, "
"}";
}
@override
get props => [
isAscending,
];
@override
_toContentJson() {
return {
"isAscending": isAscending,
};
}
final bool isAscending;
}
/// Sort based on the time of the files
class AlbumTimeSortProvider extends AlbumReversibleSortProvider {
const AlbumTimeSortProvider({
required bool isAscending,
}) : super(isAscending: isAscending);
factory AlbumTimeSortProvider.fromJson(JsonObj json) {
return AlbumTimeSortProvider(
isAscending: json["isAscending"] ?? true,
);
}
@override
toString() {
return "$runtimeType {"
"super: ${super.toString()}, "
"}";
}
@override
sort(List<AlbumItem> items) {
DateTime? prevFileTime;
return items
.map((e) {
if (e is AlbumFileItem) {
// take the file time
prevFileTime = e.file.bestDateTime;
}
// for non file items, use the sibling file's time
return Tuple2(prevFileTime, e);
})
.stableSorted((x, y) {
if (x.item1 == null && y.item1 == null) {
return 0;
} else if (x.item1 == null) {
return -1;
} else if (y.item1 == null) {
return 1;
} else {
if (isAscending) {
return x.item1!.compareTo(y.item1!);
} else {
return y.item1!.compareTo(x.item1!);
}
}
})
.map((e) => e.item2)
.toList();
}
static const _type = "time";
}