mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-01-22 16:56:19 +01:00
Migrate Memory to use collection browser
This commit is contained in:
parent
71d2b56607
commit
53b51b77b1
26 changed files with 392 additions and 740 deletions
|
@ -26,9 +26,6 @@ abstract class AlbumCoverProvider with EquatableMixin {
|
|||
case AlbumManualCoverProvider._type:
|
||||
return AlbumManualCoverProvider.fromJson(
|
||||
content.cast<String, dynamic>());
|
||||
case AlbumMemoryCoverProvider._type:
|
||||
return AlbumMemoryCoverProvider.fromJson(
|
||||
content.cast<String, dynamic>());
|
||||
default:
|
||||
_log.shout("[fromJson] Unknown type: $type");
|
||||
throw ArgumentError.value(type, "type");
|
||||
|
@ -154,38 +151,3 @@ class AlbumManualCoverProvider extends AlbumCoverProvider {
|
|||
|
||||
static const _type = "manual";
|
||||
}
|
||||
|
||||
/// Cover selected when building a Memory album
|
||||
@toString
|
||||
class AlbumMemoryCoverProvider extends AlbumCoverProvider {
|
||||
AlbumMemoryCoverProvider({
|
||||
required this.coverFile,
|
||||
});
|
||||
|
||||
factory AlbumMemoryCoverProvider.fromJson(JsonObj json) {
|
||||
return AlbumMemoryCoverProvider(
|
||||
coverFile:
|
||||
FileDescriptor.fromJson(json["coverFile"].cast<String, dynamic>()),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
@override
|
||||
getCover(Album album) => coverFile;
|
||||
|
||||
@override
|
||||
get props => [
|
||||
coverFile,
|
||||
];
|
||||
|
||||
@override
|
||||
_toContentJson() => {
|
||||
"coverFile": coverFile.toFdJson(),
|
||||
};
|
||||
|
||||
final FileDescriptor coverFile;
|
||||
|
||||
static const _type = "memory";
|
||||
}
|
||||
|
|
|
@ -30,10 +30,3 @@ extension _$AlbumManualCoverProviderToString on AlbumManualCoverProvider {
|
|||
return "AlbumManualCoverProvider {coverFile: ${coverFile.fdPath}}";
|
||||
}
|
||||
}
|
||||
|
||||
extension _$AlbumMemoryCoverProviderToString on AlbumMemoryCoverProvider {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "AlbumMemoryCoverProvider {coverFile: ${coverFile.fdPath}}";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -284,28 +284,3 @@ abstract class AlbumSmartProvider extends AlbumProviderBase {
|
|||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
||||
/// Memory album is created based on dates
|
||||
@ToString(extraParams: r"{bool isDeep = false}")
|
||||
class AlbumMemoryProvider extends AlbumSmartProvider {
|
||||
AlbumMemoryProvider({
|
||||
required this.year,
|
||||
required this.month,
|
||||
required this.day,
|
||||
}) : super(latestItemTime: DateTime(year, month, day));
|
||||
|
||||
@override
|
||||
String toString({bool isDeep = false}) => _$toString(isDeep: isDeep);
|
||||
|
||||
@override
|
||||
get props => [
|
||||
...super.props,
|
||||
year,
|
||||
month,
|
||||
day,
|
||||
];
|
||||
|
||||
final int year;
|
||||
final int month;
|
||||
final int day;
|
||||
}
|
||||
|
|
|
@ -37,10 +37,3 @@ extension _$AlbumTagProviderToString on AlbumTagProvider {
|
|||
return "AlbumTagProvider {latestItemTime: $latestItemTime, tags: ${tags.map((t) => t.displayName).toReadableString()}}";
|
||||
}
|
||||
}
|
||||
|
||||
extension _$AlbumMemoryProviderToString on AlbumMemoryProvider {
|
||||
String _$toString({bool isDeep = false}) {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "AlbumMemoryProvider {latestItemTime: $latestItemTime, year: $year, month: $month, day: $day}";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:copy_with/copy_with.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:nc_photos/entity/collection_item/sorter.dart';
|
||||
import 'package:nc_photos/entity/collection_item/util.dart';
|
||||
import 'package:to_string/to_string.dart';
|
||||
|
@ -8,7 +9,7 @@ part 'collection.g.dart';
|
|||
/// Describe a group of items
|
||||
@genCopyWith
|
||||
@toString
|
||||
class Collection {
|
||||
class Collection with EquatableMixin {
|
||||
const Collection({
|
||||
required this.name,
|
||||
required this.contentProvider,
|
||||
|
@ -40,11 +41,25 @@ class Collection {
|
|||
CollectionItemSort get itemSort => contentProvider.itemSort;
|
||||
|
||||
/// See [CollectionContentProvider.getCoverUrl]
|
||||
String? getCoverUrl(int width, int height) =>
|
||||
contentProvider.getCoverUrl(width, height);
|
||||
String? getCoverUrl(
|
||||
int width,
|
||||
int height, {
|
||||
bool? isKeepAspectRatio,
|
||||
}) =>
|
||||
contentProvider.getCoverUrl(
|
||||
width,
|
||||
height,
|
||||
isKeepAspectRatio: isKeepAspectRatio,
|
||||
);
|
||||
|
||||
CollectionSorter getSorter() => CollectionSorter.fromSortType(itemSort);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
name,
|
||||
contentProvider,
|
||||
];
|
||||
|
||||
final String name;
|
||||
final CollectionContentProvider contentProvider;
|
||||
}
|
||||
|
@ -65,7 +80,7 @@ enum CollectionCapability {
|
|||
}
|
||||
|
||||
/// Provide the actual content of a collection
|
||||
abstract class CollectionContentProvider {
|
||||
abstract class CollectionContentProvider with EquatableMixin {
|
||||
const CollectionContentProvider();
|
||||
|
||||
/// Unique FourCC of this provider type
|
||||
|
@ -95,5 +110,11 @@ abstract class CollectionContentProvider {
|
|||
///
|
||||
/// The [width] and [height] are provided as a hint only, implementations are
|
||||
/// free to ignore them if it's not supported
|
||||
String? getCoverUrl(int width, int height);
|
||||
///
|
||||
/// [isKeepAspectRatio] is only a hint and implementations may ignore it
|
||||
String? getCoverUrl(
|
||||
int width,
|
||||
int height, {
|
||||
bool? isKeepAspectRatio,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,11 +4,13 @@ import 'package:nc_photos/di_container.dart';
|
|||
import 'package:nc_photos/entity/collection.dart';
|
||||
import 'package:nc_photos/entity/collection/adapter/album.dart';
|
||||
import 'package:nc_photos/entity/collection/adapter/location_group.dart';
|
||||
import 'package:nc_photos/entity/collection/adapter/memory.dart';
|
||||
import 'package:nc_photos/entity/collection/adapter/nc_album.dart';
|
||||
import 'package:nc_photos/entity/collection/adapter/person.dart';
|
||||
import 'package:nc_photos/entity/collection/adapter/tag.dart';
|
||||
import 'package:nc_photos/entity/collection/content_provider/album.dart';
|
||||
import 'package:nc_photos/entity/collection/content_provider/location_group.dart';
|
||||
import 'package:nc_photos/entity/collection/content_provider/memory.dart';
|
||||
import 'package:nc_photos/entity/collection/content_provider/nc_album.dart';
|
||||
import 'package:nc_photos/entity/collection/content_provider/person.dart';
|
||||
import 'package:nc_photos/entity/collection/content_provider/tag.dart';
|
||||
|
@ -29,6 +31,8 @@ abstract class CollectionAdapter {
|
|||
return CollectionAlbumAdapter(c, account, collection);
|
||||
case CollectionLocationGroupProvider:
|
||||
return CollectionLocationGroupAdapter(c, account, collection);
|
||||
case CollectionMemoryProvider:
|
||||
return CollectionMemoryAdapter(c, account, collection);
|
||||
case CollectionNcAlbumProvider:
|
||||
return CollectionNcAlbumAdapter(c, account, collection);
|
||||
case CollectionPersonProvider:
|
||||
|
|
59
app/lib/entity/collection/adapter/memory.dart
Normal file
59
app/lib/entity/collection/adapter/memory.dart
Normal file
|
@ -0,0 +1,59 @@
|
|||
import 'package:nc_photos/account.dart';
|
||||
import 'package:nc_photos/di_container.dart';
|
||||
import 'package:nc_photos/entity/collection.dart';
|
||||
import 'package:nc_photos/entity/collection/adapter.dart';
|
||||
import 'package:nc_photos/entity/collection/adapter/read_only_adapter.dart';
|
||||
import 'package:nc_photos/entity/collection/content_provider/memory.dart';
|
||||
import 'package:nc_photos/entity/collection_item.dart';
|
||||
import 'package:nc_photos/entity/collection_item/basic_item.dart';
|
||||
import 'package:nc_photos/entity/file/data_source.dart';
|
||||
import 'package:nc_photos/entity/file_util.dart' as file_util;
|
||||
import 'package:nc_photos/use_case/list_location_file.dart';
|
||||
|
||||
class CollectionMemoryAdapter
|
||||
with CollectionReadOnlyAdapter
|
||||
implements CollectionAdapter {
|
||||
CollectionMemoryAdapter(this._c, this.account, this.collection)
|
||||
: assert(require(_c)),
|
||||
_provider = collection.contentProvider as CollectionMemoryProvider;
|
||||
|
||||
static bool require(DiContainer c) => ListLocationFile.require(c);
|
||||
|
||||
@override
|
||||
Stream<List<CollectionItem>> listItem() async* {
|
||||
final date = DateTime(_provider.year, _provider.month, _provider.day);
|
||||
final dayRange = _c.pref.getMemoriesRangeOr();
|
||||
final from = date.subtract(Duration(days: dayRange));
|
||||
final to = date.add(Duration(days: dayRange + 1));
|
||||
final files = await FileSqliteDbDataSource(_c).listByDate(
|
||||
account, from.millisecondsSinceEpoch, to.millisecondsSinceEpoch);
|
||||
yield files
|
||||
.where((f) => file_util.isSupportedFormat(f))
|
||||
.map((f) => BasicCollectionFileItem(f))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<CollectionItem> adaptToNewItem(CollectionItem original) async {
|
||||
if (original is CollectionFileItem) {
|
||||
return BasicCollectionFileItem(original.file);
|
||||
} else {
|
||||
throw UnsupportedError("Unsupported type: ${original.runtimeType}");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> remove() {
|
||||
throw UnsupportedError("Operation not supported");
|
||||
}
|
||||
|
||||
@override
|
||||
bool isPermitted(CollectionCapability capability) =>
|
||||
_provider.capabilities.contains(capability);
|
||||
|
||||
final DiContainer _c;
|
||||
final Account account;
|
||||
final Collection collection;
|
||||
|
||||
final CollectionMemoryProvider _provider;
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:copy_with/copy_with.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:nc_photos/account.dart';
|
||||
import 'package:nc_photos/api/api_util.dart' as api_util;
|
||||
import 'package:nc_photos/entity/album.dart';
|
||||
|
@ -12,7 +13,9 @@ part 'album.g.dart';
|
|||
/// Album provided by our app
|
||||
@genCopyWith
|
||||
@toString
|
||||
class CollectionAlbumProvider implements CollectionContentProvider {
|
||||
class CollectionAlbumProvider
|
||||
with EquatableMixin
|
||||
implements CollectionContentProvider {
|
||||
const CollectionAlbumProvider({
|
||||
required this.account,
|
||||
required this.album,
|
||||
|
@ -64,7 +67,11 @@ class CollectionAlbumProvider implements CollectionContentProvider {
|
|||
CollectionItemSort get itemSort => album.sortProvider.toCollectionItemSort();
|
||||
|
||||
@override
|
||||
String? getCoverUrl(int width, int height) {
|
||||
String? getCoverUrl(
|
||||
int width,
|
||||
int height, {
|
||||
bool? isKeepAspectRatio,
|
||||
}) {
|
||||
final fd = album.coverProvider.getCover(album);
|
||||
if (fd == null) {
|
||||
return null;
|
||||
|
@ -74,11 +81,14 @@ class CollectionAlbumProvider implements CollectionContentProvider {
|
|||
fd.fdId,
|
||||
width: width,
|
||||
height: height,
|
||||
isKeepAspectRatio: false,
|
||||
isKeepAspectRatio: isKeepAspectRatio ?? false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [account, album];
|
||||
|
||||
final Account account;
|
||||
final Album album;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import 'package:equatable/equatable.dart';
|
||||
import 'package:nc_photos/account.dart';
|
||||
import 'package:nc_photos/api/api_util.dart' as api_util;
|
||||
import 'package:nc_photos/entity/collection.dart';
|
||||
import 'package:nc_photos/entity/collection_item/util.dart';
|
||||
import 'package:nc_photos/use_case/list_location_group.dart';
|
||||
|
||||
class CollectionLocationGroupProvider implements CollectionContentProvider {
|
||||
class CollectionLocationGroupProvider
|
||||
with EquatableMixin
|
||||
implements CollectionContentProvider {
|
||||
const CollectionLocationGroupProvider({
|
||||
required this.account,
|
||||
required this.location,
|
||||
|
@ -29,16 +32,23 @@ class CollectionLocationGroupProvider implements CollectionContentProvider {
|
|||
CollectionItemSort get itemSort => CollectionItemSort.dateDescending;
|
||||
|
||||
@override
|
||||
String? getCoverUrl(int width, int height) {
|
||||
String? getCoverUrl(
|
||||
int width,
|
||||
int height, {
|
||||
bool? isKeepAspectRatio,
|
||||
}) {
|
||||
return api_util.getFilePreviewUrlByFileId(
|
||||
account,
|
||||
location.latestFileId,
|
||||
width: width,
|
||||
height: height,
|
||||
isKeepAspectRatio: false,
|
||||
isKeepAspectRatio: isKeepAspectRatio ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [account, location];
|
||||
|
||||
final Account account;
|
||||
final LocationGroup location;
|
||||
}
|
||||
|
|
68
app/lib/entity/collection/content_provider/memory.dart
Normal file
68
app/lib/entity/collection/content_provider/memory.dart
Normal file
|
@ -0,0 +1,68 @@
|
|||
import 'package:equatable/equatable.dart';
|
||||
import 'package:nc_photos/account.dart';
|
||||
import 'package:nc_photos/api/api_util.dart' as api_util;
|
||||
import 'package:nc_photos/entity/collection.dart';
|
||||
import 'package:nc_photos/entity/collection_item/util.dart';
|
||||
import 'package:nc_photos/entity/file_descriptor.dart';
|
||||
import 'package:nc_photos/object_extension.dart';
|
||||
import 'package:to_string/to_string.dart';
|
||||
|
||||
part 'memory.g.dart';
|
||||
|
||||
@toString
|
||||
class CollectionMemoryProvider
|
||||
with EquatableMixin
|
||||
implements CollectionContentProvider {
|
||||
const CollectionMemoryProvider({
|
||||
required this.account,
|
||||
required this.year,
|
||||
required this.month,
|
||||
required this.day,
|
||||
this.cover,
|
||||
});
|
||||
|
||||
@override
|
||||
String get fourCc => "MEMY";
|
||||
|
||||
@override
|
||||
String get id => "$year-$month-$day";
|
||||
|
||||
@override
|
||||
int? get count => null;
|
||||
|
||||
@override
|
||||
DateTime get lastModified => DateTime(year, month, day);
|
||||
|
||||
@override
|
||||
List<CollectionCapability> get capabilities => [];
|
||||
|
||||
@override
|
||||
CollectionItemSort get itemSort => CollectionItemSort.dateDescending;
|
||||
|
||||
@override
|
||||
String? getCoverUrl(
|
||||
int width,
|
||||
int height, {
|
||||
bool? isKeepAspectRatio,
|
||||
}) {
|
||||
return cover?.run((cover) => api_util.getFilePreviewUrl(
|
||||
account,
|
||||
cover,
|
||||
width: width,
|
||||
height: height,
|
||||
isKeepAspectRatio: isKeepAspectRatio ?? false,
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [account, year, month, day, cover];
|
||||
|
||||
final Account account;
|
||||
final int year;
|
||||
final int month;
|
||||
final int day;
|
||||
final FileDescriptor? cover;
|
||||
}
|
14
app/lib/entity/collection/content_provider/memory.g.dart
Normal file
14
app/lib/entity/collection/content_provider/memory.g.dart
Normal file
|
@ -0,0 +1,14 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'memory.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// ToStringGenerator
|
||||
// **************************************************************************
|
||||
|
||||
extension _$CollectionMemoryProviderToString on CollectionMemoryProvider {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "CollectionMemoryProvider {account: $account, year: $year, month: $month, day: $day, cover: ${cover == null ? null : "${cover!.fdPath}"}}";
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:clock/clock.dart';
|
||||
import 'package:copy_with/copy_with.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:nc_photos/account.dart';
|
||||
import 'package:nc_photos/api/api_util.dart' as api_util;
|
||||
import 'package:nc_photos/entity/collection.dart';
|
||||
|
@ -12,7 +13,9 @@ part 'nc_album.g.dart';
|
|||
/// Album provided by our app
|
||||
@genCopyWith
|
||||
@toString
|
||||
class CollectionNcAlbumProvider implements CollectionContentProvider {
|
||||
class CollectionNcAlbumProvider
|
||||
with EquatableMixin
|
||||
implements CollectionContentProvider {
|
||||
const CollectionNcAlbumProvider({
|
||||
required this.account,
|
||||
required this.album,
|
||||
|
@ -43,7 +46,11 @@ class CollectionNcAlbumProvider implements CollectionContentProvider {
|
|||
CollectionItemSort get itemSort => CollectionItemSort.dateDescending;
|
||||
|
||||
@override
|
||||
String? getCoverUrl(int width, int height) {
|
||||
String? getCoverUrl(
|
||||
int width,
|
||||
int height, {
|
||||
bool? isKeepAspectRatio,
|
||||
}) {
|
||||
if (album.lastPhoto == null) {
|
||||
return null;
|
||||
} else {
|
||||
|
@ -52,11 +59,14 @@ class CollectionNcAlbumProvider implements CollectionContentProvider {
|
|||
album.lastPhoto!,
|
||||
width: width,
|
||||
height: height,
|
||||
isKeepAspectRatio: false,
|
||||
isKeepAspectRatio: isKeepAspectRatio ?? false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [account, album];
|
||||
|
||||
final Account account;
|
||||
final NcAlbum album;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
import 'dart:math' as math;
|
||||
|
||||
import 'package:clock/clock.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:nc_photos/account.dart';
|
||||
import 'package:nc_photos/api/api_util.dart' as api_util;
|
||||
import 'package:nc_photos/entity/collection.dart';
|
||||
import 'package:nc_photos/entity/collection_item/util.dart';
|
||||
import 'package:nc_photos/entity/person.dart';
|
||||
|
||||
class CollectionPersonProvider implements CollectionContentProvider {
|
||||
class CollectionPersonProvider
|
||||
with EquatableMixin
|
||||
implements CollectionContentProvider {
|
||||
const CollectionPersonProvider({
|
||||
required this.account,
|
||||
required this.person,
|
||||
|
@ -32,11 +35,18 @@ class CollectionPersonProvider implements CollectionContentProvider {
|
|||
CollectionItemSort get itemSort => CollectionItemSort.dateDescending;
|
||||
|
||||
@override
|
||||
String? getCoverUrl(int width, int height) {
|
||||
String? getCoverUrl(
|
||||
int width,
|
||||
int height, {
|
||||
bool? isKeepAspectRatio,
|
||||
}) {
|
||||
return api_util.getFacePreviewUrl(account, person.thumbFaceId,
|
||||
size: math.max(width, height));
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [account, person];
|
||||
|
||||
final Account account;
|
||||
final Person person;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import 'package:clock/clock.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:nc_photos/account.dart';
|
||||
import 'package:nc_photos/entity/collection.dart';
|
||||
import 'package:nc_photos/entity/collection_item/util.dart';
|
||||
import 'package:nc_photos/entity/tag.dart';
|
||||
|
||||
class CollectionTagProvider implements CollectionContentProvider {
|
||||
class CollectionTagProvider
|
||||
with EquatableMixin
|
||||
implements CollectionContentProvider {
|
||||
CollectionTagProvider({
|
||||
required this.account,
|
||||
required this.tags,
|
||||
|
@ -29,7 +32,15 @@ class CollectionTagProvider implements CollectionContentProvider {
|
|||
CollectionItemSort get itemSort => CollectionItemSort.dateDescending;
|
||||
|
||||
@override
|
||||
String? getCoverUrl(int width, int height) => null;
|
||||
String? getCoverUrl(
|
||||
int width,
|
||||
int height, {
|
||||
bool? isKeepAspectRatio,
|
||||
}) =>
|
||||
null;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [account, tags];
|
||||
|
||||
final Account account;
|
||||
final List<Tag> tags;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:copy_with/copy_with.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:nc_photos/account.dart';
|
||||
import 'package:np_common/string_extension.dart';
|
||||
import 'package:to_string/to_string.dart';
|
||||
|
@ -8,7 +9,7 @@ part 'nc_album.g.dart';
|
|||
/// Server-side album since Nextcloud 25
|
||||
@toString
|
||||
@genCopyWith
|
||||
class NcAlbum {
|
||||
class NcAlbum with EquatableMixin {
|
||||
NcAlbum({
|
||||
required String path,
|
||||
required this.lastPhoto,
|
||||
|
@ -37,6 +38,17 @@ class NcAlbum {
|
|||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
path,
|
||||
lastPhoto,
|
||||
nbItems,
|
||||
location,
|
||||
dateStart,
|
||||
dateEnd,
|
||||
collaborators,
|
||||
];
|
||||
|
||||
final String path;
|
||||
|
||||
/// File ID of the last photo
|
||||
|
|
|
@ -8,8 +8,6 @@ import 'package:nc_photos/entity/album.dart';
|
|||
import 'package:nc_photos/entity/album/item.dart';
|
||||
import 'package:nc_photos/entity/album/provider.dart';
|
||||
import 'package:nc_photos/entity/file.dart';
|
||||
import 'package:nc_photos/entity/file/data_source.dart';
|
||||
import 'package:nc_photos/entity/file_util.dart' as file_util;
|
||||
import 'package:nc_photos/exception_event.dart';
|
||||
import 'package:nc_photos/use_case/list_tagged_file.dart';
|
||||
import 'package:nc_photos/use_case/scan_dir.dart';
|
||||
|
@ -32,8 +30,6 @@ class PopulateAlbum {
|
|||
return _populateDirAlbum(account, album);
|
||||
} else if (album.provider is AlbumTagProvider) {
|
||||
return _populateTagAlbum(account, album);
|
||||
} else if (album.provider is AlbumMemoryProvider) {
|
||||
return _populateMemoryAlbum(account, album);
|
||||
} else {
|
||||
throw ArgumentError(
|
||||
"Unknown album provider: ${album.provider.runtimeType}");
|
||||
|
@ -80,25 +76,5 @@ class PopulateAlbum {
|
|||
return products;
|
||||
}
|
||||
|
||||
Future<List<AlbumItem>> _populateMemoryAlbum(
|
||||
Account account, Album album) async {
|
||||
assert(album.provider is AlbumMemoryProvider);
|
||||
final provider = album.provider as AlbumMemoryProvider;
|
||||
final date = DateTime(provider.year, provider.month, provider.day);
|
||||
final dayRange = _c.pref.getMemoriesRangeOr();
|
||||
final from = date.subtract(Duration(days: dayRange));
|
||||
final to = date.add(Duration(days: dayRange + 1));
|
||||
final files = await FileSqliteDbDataSource(_c).listByDate(
|
||||
account, from.millisecondsSinceEpoch, to.millisecondsSinceEpoch);
|
||||
return files
|
||||
.where((f) => file_util.isSupportedFormat(f))
|
||||
.map((f) => AlbumFileItem(
|
||||
addedBy: account.userId,
|
||||
addedAt: clock.now(),
|
||||
file: f,
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
final DiContainer _c;
|
||||
}
|
||||
|
|
|
@ -3,16 +3,12 @@ import 'package:nc_photos/account.dart';
|
|||
import 'package:nc_photos/entity/album.dart';
|
||||
import 'package:nc_photos/entity/album/provider.dart';
|
||||
import 'package:nc_photos/widget/album_browser.dart';
|
||||
import 'package:nc_photos/widget/smart_album_browser.dart';
|
||||
|
||||
/// Push the corresponding browser route for this album
|
||||
void push(BuildContext context, Account account, Album album) {
|
||||
if (album.provider is AlbumStaticProvider) {
|
||||
Navigator.of(context).pushNamed(AlbumBrowser.routeName,
|
||||
arguments: AlbumBrowserArguments(account, album));
|
||||
} else if (album.provider is AlbumSmartProvider) {
|
||||
Navigator.of(context).pushNamed(SmartAlbumBrowser.routeName,
|
||||
arguments: SmartAlbumBrowserArguments(account, album));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import 'package:logging/logging.dart';
|
|||
import 'package:nc_photos/account.dart';
|
||||
import 'package:nc_photos/app_init.dart' as app_init;
|
||||
import 'package:nc_photos/app_localizations.dart';
|
||||
import 'package:nc_photos/entity/album.dart';
|
||||
import 'package:nc_photos/entity/collection.dart';
|
||||
import 'package:nc_photos/entity/file_descriptor.dart';
|
||||
import 'package:nc_photos/entity/file_util.dart' as file_util;
|
||||
import 'package:nc_photos/object_extension.dart';
|
||||
|
@ -49,12 +49,12 @@ class PhotoListItemBuilderResult {
|
|||
const PhotoListItemBuilderResult(
|
||||
this.backingFiles,
|
||||
this.listItems, {
|
||||
this.smartAlbums = const [],
|
||||
this.smartCollections = const [],
|
||||
});
|
||||
|
||||
final List<FileDescriptor> backingFiles;
|
||||
final List<SelectableItem> listItems;
|
||||
final List<Album> smartAlbums;
|
||||
final List<Collection> smartCollections;
|
||||
}
|
||||
|
||||
typedef PhotoListItemSorter = int Function(FileDescriptor, FileDescriptor);
|
||||
|
@ -137,7 +137,7 @@ class _PhotoListItemBuilder {
|
|||
Account account, List<FileDescriptor> files) {
|
||||
final today = clock.now();
|
||||
final memoryAlbumHelper = smartAlbumConfig != null
|
||||
? MemoryAlbumHelper(
|
||||
? MemoryCollectionHelper(account,
|
||||
today: today, dayRange: smartAlbumConfig!.memoriesDayRange)
|
||||
: null;
|
||||
final listItems = <SelectableItem>[];
|
||||
|
@ -155,7 +155,7 @@ class _PhotoListItemBuilder {
|
|||
return PhotoListItemBuilderResult(
|
||||
files,
|
||||
listItems,
|
||||
smartAlbums: smartAlbums ?? [],
|
||||
smartCollections: smartAlbums ?? [],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
|
|||
required this.collectionsController,
|
||||
required Collection collection,
|
||||
}) : _c = container,
|
||||
_isAdHocCollection = !collectionsController.stream.value.data
|
||||
.any((e) => e.collection.compareIdentity(collection)),
|
||||
super(_State.init(
|
||||
collection: collection,
|
||||
coverUrl: _getCoverUrl(collection),
|
||||
|
@ -45,14 +47,16 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
|
|||
on<_SetError>(_onSetError);
|
||||
on<_SetMessage>(_onSetMessage);
|
||||
|
||||
_collectionControllerSubscription =
|
||||
collectionsController.stream.listen((event) {
|
||||
final c = event.data
|
||||
.firstWhere((d) => state.collection.compareIdentity(d.collection));
|
||||
if (!identical(c, state.collection)) {
|
||||
add(_UpdateCollection(c.collection));
|
||||
}
|
||||
});
|
||||
if (!_isAdHocCollection) {
|
||||
_collectionControllerSubscription =
|
||||
collectionsController.stream.listen((event) {
|
||||
final c = event.data
|
||||
.firstWhere((d) => state.collection.compareIdentity(d.collection));
|
||||
if (!identical(c, state.collection)) {
|
||||
add(_UpdateCollection(c.collection));
|
||||
}
|
||||
});
|
||||
}
|
||||
_itemsControllerSubscription = itemsController.stream.listen(
|
||||
(_) {},
|
||||
onError: (e, stackTrace) {
|
||||
|
@ -448,6 +452,10 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
|
|||
final CollectionsController collectionsController;
|
||||
late final CollectionItemsController itemsController;
|
||||
|
||||
/// Specify if the supplied [collection] is an "inline" one, which means it's
|
||||
/// not returned from the collection controller but rather created temporarily
|
||||
final bool _isAdHocCollection;
|
||||
|
||||
StreamSubscription? _collectionControllerSubscription;
|
||||
StreamSubscription? _itemsControllerSubscription;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ import 'package:nc_photos/bloc/scan_account_dir.dart';
|
|||
import 'package:nc_photos/compute_queue.dart';
|
||||
import 'package:nc_photos/di_container.dart';
|
||||
import 'package:nc_photos/download_handler.dart';
|
||||
import 'package:nc_photos/entity/album.dart';
|
||||
import 'package:nc_photos/entity/collection.dart';
|
||||
import 'package:nc_photos/entity/file_descriptor.dart';
|
||||
import 'package:nc_photos/entity/sqlite/database.dart' as sql;
|
||||
import 'package:nc_photos/event/event.dart';
|
||||
|
@ -38,19 +38,18 @@ import 'package:nc_photos/theme.dart';
|
|||
import 'package:nc_photos/throttler.dart';
|
||||
import 'package:nc_photos/use_case/startup_sync.dart';
|
||||
import 'package:nc_photos/widget/builder/photo_list_item_builder.dart';
|
||||
import 'package:nc_photos/widget/collection_browser.dart';
|
||||
import 'package:nc_photos/widget/handler/add_selection_to_collection_handler.dart';
|
||||
import 'package:nc_photos/widget/handler/archive_selection_handler.dart';
|
||||
import 'package:nc_photos/widget/handler/double_tap_exit_handler.dart';
|
||||
import 'package:nc_photos/widget/handler/remove_selection_handler.dart';
|
||||
import 'package:nc_photos/widget/home_app_bar.dart';
|
||||
import 'package:nc_photos/widget/network_thumbnail.dart';
|
||||
import 'package:nc_photos/widget/page_visibility_mixin.dart';
|
||||
import 'package:nc_photos/widget/photo_list_item.dart';
|
||||
import 'package:nc_photos/widget/photo_list_util.dart' as photo_list_util;
|
||||
import 'package:nc_photos/widget/selectable_item_stream_list_mixin.dart';
|
||||
import 'package:nc_photos/widget/selection_app_bar.dart';
|
||||
import 'package:nc_photos/widget/settings.dart';
|
||||
import 'package:nc_photos/widget/smart_album_browser.dart';
|
||||
import 'package:nc_photos/widget/viewer.dart';
|
||||
import 'package:nc_photos/widget/zoom_menu_button.dart';
|
||||
import 'package:np_codegen/np_codegen.dart';
|
||||
|
@ -192,7 +191,7 @@ class _HomePhotosState extends State<HomePhotos>
|
|||
_web?.buildContent(context),
|
||||
if (AccountPref.of(widget.account)
|
||||
.isEnableMemoryAlbumOr(true) &&
|
||||
_smartAlbums.isNotEmpty)
|
||||
_smartCollections.isNotEmpty)
|
||||
_buildSmartAlbumList(context),
|
||||
BlocBuilder<ScanAccountDirBloc, ScanAccountDirBlocState>(
|
||||
bloc: _bloc,
|
||||
|
@ -352,9 +351,9 @@ class _HomePhotosState extends State<HomePhotos>
|
|||
|
||||
Widget _buildSmartAlbumList(BuildContext context) {
|
||||
return SliverToBoxAdapter(
|
||||
child: _SmartAlbumList(
|
||||
child: _SmartCollectionList(
|
||||
account: widget.account,
|
||||
albums: _smartAlbums,
|
||||
collections: _smartCollections,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -613,7 +612,7 @@ class _HomePhotosState extends State<HomePhotos>
|
|||
setState(() {
|
||||
_backingFiles = result.backingFiles;
|
||||
itemStreamListItems = result.listItems;
|
||||
_smartAlbums = result.smartAlbums;
|
||||
_smartCollections = result.smartCollections;
|
||||
|
||||
if (isPostSuccess) {
|
||||
_isScrollbarVisible = true;
|
||||
|
@ -680,7 +679,7 @@ class _HomePhotosState extends State<HomePhotos>
|
|||
final metadataTaskHeaderExtent = _web?.getHeaderHeight() ?? 0;
|
||||
final smartAlbumListHeight =
|
||||
AccountPref.of(widget.account).isEnableMemoryAlbumOr(true) &&
|
||||
_smartAlbums.isNotEmpty
|
||||
_smartCollections.isNotEmpty
|
||||
? _SmartAlbumItem.height
|
||||
: 0;
|
||||
// scroll extent = list height - widget viewport height
|
||||
|
@ -745,7 +744,7 @@ class _HomePhotosState extends State<HomePhotos>
|
|||
late final _queryProgressBloc = ProgressBloc();
|
||||
|
||||
var _backingFiles = <FileDescriptor>[];
|
||||
var _smartAlbums = <Album>[];
|
||||
var _smartCollections = <Collection>[];
|
||||
|
||||
final _buildItemQueue =
|
||||
ComputeQueue<PhotoListItemBuilderArguments, PhotoListItemBuilderResult>();
|
||||
|
@ -1006,10 +1005,10 @@ class _MetadataTaskLoadingIcon extends AnimatedWidget {
|
|||
Animation<double> get _progress => listenable as Animation<double>;
|
||||
}
|
||||
|
||||
class _SmartAlbumList extends StatelessWidget {
|
||||
const _SmartAlbumList({
|
||||
class _SmartCollectionList extends StatelessWidget {
|
||||
const _SmartCollectionList({
|
||||
required this.account,
|
||||
required this.albums,
|
||||
required this.collections,
|
||||
});
|
||||
|
||||
@override
|
||||
|
@ -1019,19 +1018,20 @@ class _SmartAlbumList extends StatelessWidget {
|
|||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
itemCount: albums.length,
|
||||
itemCount: collections.length,
|
||||
itemBuilder: (context, index) {
|
||||
final a = albums[index];
|
||||
final coverFile = a.coverProvider.getCover(a);
|
||||
final c = collections[index];
|
||||
return _SmartAlbumItem(
|
||||
account: account,
|
||||
previewUrl: coverFile == null
|
||||
? null
|
||||
: NetworkRectThumbnail.imageUrlForFile(account, coverFile),
|
||||
label: a.name,
|
||||
previewUrl: c.getCoverUrl(
|
||||
k.photoThumbSize,
|
||||
k.photoThumbSize,
|
||||
isKeepAspectRatio: true,
|
||||
),
|
||||
label: c.name,
|
||||
onTap: () {
|
||||
Navigator.of(context).pushNamed(SmartAlbumBrowser.routeName,
|
||||
arguments: SmartAlbumBrowserArguments(account, a));
|
||||
Navigator.of(context).pushNamed(CollectionBrowser.routeName,
|
||||
arguments: CollectionBrowserArguments(c));
|
||||
},
|
||||
);
|
||||
},
|
||||
|
@ -1041,7 +1041,7 @@ class _SmartAlbumList extends StatelessWidget {
|
|||
}
|
||||
|
||||
final Account account;
|
||||
final List<Album> albums;
|
||||
final List<Collection> collections;
|
||||
}
|
||||
|
||||
class _SmartAlbumItem extends StatelessWidget {
|
||||
|
|
|
@ -39,7 +39,6 @@ import 'package:nc_photos/widget/shared_file_viewer.dart';
|
|||
import 'package:nc_photos/widget/sharing_browser.dart';
|
||||
import 'package:nc_photos/widget/sign_in.dart';
|
||||
import 'package:nc_photos/widget/slideshow_viewer.dart';
|
||||
import 'package:nc_photos/widget/smart_album_browser.dart';
|
||||
import 'package:nc_photos/widget/splash.dart';
|
||||
import 'package:nc_photos/widget/trashbin_browser.dart';
|
||||
import 'package:nc_photos/widget/trashbin_viewer.dart';
|
||||
|
@ -195,7 +194,6 @@ class _WrappedAppState extends State<_WrappedApp>
|
|||
route ??= _handleAlbumShareOutlierBrowserRoute(settings);
|
||||
route ??= _handleAccountSettingsRoute(settings);
|
||||
route ??= _handleShareFolderPickerRoute(settings);
|
||||
route ??= _handleSmartAlbumBrowserRoute(settings);
|
||||
route ??= _handleEnhancedPhotoBrowserRoute(settings);
|
||||
route ??= _handleLocalFileViewerRoute(settings);
|
||||
route ??= _handleEnhancementSettingsRoute(settings);
|
||||
|
@ -455,20 +453,6 @@ class _WrappedAppState extends State<_WrappedApp>
|
|||
return null;
|
||||
}
|
||||
|
||||
Route<dynamic>? _handleSmartAlbumBrowserRoute(RouteSettings settings) {
|
||||
try {
|
||||
if (settings.name == SmartAlbumBrowser.routeName &&
|
||||
settings.arguments != null) {
|
||||
final args = settings.arguments as SmartAlbumBrowserArguments;
|
||||
return SmartAlbumBrowser.buildRoute(args);
|
||||
}
|
||||
} catch (e) {
|
||||
_log.severe(
|
||||
"[_handleSmartAlbumBrowserRoute] Failed while handling route", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Route<dynamic>? _handleEnhancedPhotoBrowserRoute(RouteSettings settings) {
|
||||
try {
|
||||
if (settings.name == EnhancedPhotoBrowser.routeName &&
|
||||
|
|
|
@ -3,11 +3,10 @@ import 'dart:math' as math;
|
|||
import 'package:clock/clock.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:nc_photos/account.dart';
|
||||
import 'package:nc_photos/date_time_extension.dart';
|
||||
import 'package:nc_photos/entity/album.dart';
|
||||
import 'package:nc_photos/entity/album/cover_provider.dart';
|
||||
import 'package:nc_photos/entity/album/provider.dart';
|
||||
import 'package:nc_photos/entity/album/sort_provider.dart';
|
||||
import 'package:nc_photos/entity/collection.dart';
|
||||
import 'package:nc_photos/entity/collection/content_provider/memory.dart';
|
||||
import 'package:nc_photos/entity/file_descriptor.dart';
|
||||
import 'package:np_codegen/np_codegen.dart';
|
||||
|
||||
|
@ -34,12 +33,13 @@ class DateGroupHelper {
|
|||
DateTime? _currentDate;
|
||||
}
|
||||
|
||||
/// Build memory album from files
|
||||
/// Build memory collection from files
|
||||
///
|
||||
/// Feb 29 is treated as Mar 1 on non leap years
|
||||
@npLog
|
||||
class MemoryAlbumHelper {
|
||||
MemoryAlbumHelper({
|
||||
class MemoryCollectionHelper {
|
||||
MemoryCollectionHelper(
|
||||
this.account, {
|
||||
DateTime? today,
|
||||
required int dayRange,
|
||||
}) : today = (today?.toLocal() ?? clock.now()).toMidnight(),
|
||||
|
@ -65,16 +65,18 @@ class MemoryAlbumHelper {
|
|||
///
|
||||
/// [nameBuilder] is a function that return the name of the album for a
|
||||
/// particular year
|
||||
List<Album> build(String Function(int year) nameBuilder) {
|
||||
List<Collection> build(String Function(int year) nameBuilder) {
|
||||
return _data.entries
|
||||
.sorted((a, b) => b.key.compareTo(a.key))
|
||||
.map((e) => Album(
|
||||
.map((e) => Collection(
|
||||
name: nameBuilder(e.key),
|
||||
provider: AlbumMemoryProvider(
|
||||
year: e.key, month: today.month, day: today.day),
|
||||
coverProvider:
|
||||
AlbumMemoryCoverProvider(coverFile: e.value.coverFile),
|
||||
sortProvider: const AlbumTimeSortProvider(isAscending: false),
|
||||
contentProvider: CollectionMemoryProvider(
|
||||
account: account,
|
||||
year: e.key,
|
||||
month: today.month,
|
||||
day: today.day,
|
||||
cover: e.value.coverFile,
|
||||
),
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
@ -83,9 +85,9 @@ class MemoryAlbumHelper {
|
|||
final item = _data[year];
|
||||
final date = today.copyWith(year: year);
|
||||
if (item == null) {
|
||||
_data[year] = _MemoryAlbumHelperItem(date, f);
|
||||
_data[year] = _MemoryCollectionHelperItem(date, f);
|
||||
} else {
|
||||
final coverDiff = _MemoryAlbumHelperItem.getCoverDiff(date, f);
|
||||
final coverDiff = _MemoryCollectionHelperItem.getCoverDiff(date, f);
|
||||
if (coverDiff < item.coverDiff) {
|
||||
item.coverFile = f;
|
||||
item.coverDiff = coverDiff;
|
||||
|
@ -93,9 +95,10 @@ class MemoryAlbumHelper {
|
|||
}
|
||||
}
|
||||
|
||||
final Account account;
|
||||
final DateTime today;
|
||||
final int dayRange;
|
||||
final _data = <int, _MemoryAlbumHelperItem>{};
|
||||
final _data = <int, _MemoryCollectionHelperItem>{};
|
||||
}
|
||||
|
||||
int getThumbSize(int zoomLevel) {
|
||||
|
@ -115,8 +118,8 @@ int getThumbSize(int zoomLevel) {
|
|||
}
|
||||
}
|
||||
|
||||
class _MemoryAlbumHelperItem {
|
||||
_MemoryAlbumHelperItem(this.date, this.coverFile)
|
||||
class _MemoryCollectionHelperItem {
|
||||
_MemoryCollectionHelperItem(this.date, this.coverFile)
|
||||
: coverDiff = getCoverDiff(date, coverFile);
|
||||
|
||||
static Duration getCoverDiff(DateTime date, FileDescriptor f) =>
|
||||
|
|
|
@ -6,9 +6,9 @@ part of 'photo_list_util.dart';
|
|||
// NpLogGenerator
|
||||
// **************************************************************************
|
||||
|
||||
extension _$MemoryAlbumHelperNpLog on MemoryAlbumHelper {
|
||||
extension _$MemoryCollectionHelperNpLog on MemoryCollectionHelper {
|
||||
// ignore: unused_element
|
||||
Logger get _log => log;
|
||||
|
||||
static final log = Logger("widget.photo_list_util.MemoryAlbumHelper");
|
||||
static final log = Logger("widget.photo_list_util.MemoryCollectionHelper");
|
||||
}
|
||||
|
|
|
@ -1,403 +0,0 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||
import 'package:kiwi/kiwi.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:nc_photos/account.dart';
|
||||
import 'package:nc_photos/app_localizations.dart';
|
||||
import 'package:nc_photos/di_container.dart';
|
||||
import 'package:nc_photos/download_handler.dart';
|
||||
import 'package:nc_photos/entity/album.dart';
|
||||
import 'package:nc_photos/entity/album/item.dart';
|
||||
import 'package:nc_photos/entity/album/provider.dart';
|
||||
import 'package:nc_photos/entity/file.dart';
|
||||
import 'package:nc_photos/entity/file_util.dart' as file_util;
|
||||
import 'package:nc_photos/flutter_util.dart' as flutter_util;
|
||||
import 'package:nc_photos/object_extension.dart';
|
||||
import 'package:nc_photos/share_handler.dart';
|
||||
import 'package:nc_photos/use_case/preprocess_album.dart';
|
||||
import 'package:nc_photos/widget/album_browser_mixin.dart';
|
||||
import 'package:nc_photos/widget/handler/add_selection_to_collection_handler.dart';
|
||||
import 'package:nc_photos/widget/network_thumbnail.dart';
|
||||
import 'package:nc_photos/widget/photo_list_item.dart';
|
||||
import 'package:nc_photos/widget/selectable_item_stream_list_mixin.dart';
|
||||
import 'package:nc_photos/widget/viewer.dart';
|
||||
import 'package:np_codegen/np_codegen.dart';
|
||||
import 'package:to_string/to_string.dart';
|
||||
|
||||
part 'smart_album_browser.g.dart';
|
||||
|
||||
class SmartAlbumBrowserArguments {
|
||||
const SmartAlbumBrowserArguments(this.account, this.album);
|
||||
|
||||
final Account account;
|
||||
final Album album;
|
||||
}
|
||||
|
||||
class SmartAlbumBrowser extends StatefulWidget {
|
||||
static const routeName = "/smart-album-browser";
|
||||
|
||||
static Route buildRoute(SmartAlbumBrowserArguments args) => MaterialPageRoute(
|
||||
builder: (context) => SmartAlbumBrowser.fromArgs(args),
|
||||
);
|
||||
|
||||
const SmartAlbumBrowser({
|
||||
Key? key,
|
||||
required this.account,
|
||||
required this.album,
|
||||
}) : super(key: key);
|
||||
|
||||
SmartAlbumBrowser.fromArgs(SmartAlbumBrowserArguments args, {Key? key})
|
||||
: this(
|
||||
key: key,
|
||||
account: args.account,
|
||||
album: args.album,
|
||||
);
|
||||
|
||||
@override
|
||||
createState() => _SmartAlbumBrowserState();
|
||||
|
||||
final Account account;
|
||||
final Album album;
|
||||
}
|
||||
|
||||
@npLog
|
||||
class _SmartAlbumBrowserState extends State<SmartAlbumBrowser>
|
||||
with
|
||||
SelectableItemStreamListMixin<SmartAlbumBrowser>,
|
||||
AlbumBrowserMixin<SmartAlbumBrowser> {
|
||||
_SmartAlbumBrowserState() {
|
||||
final c = KiwiContainer().resolve<DiContainer>();
|
||||
assert(PreProcessAlbum.require(c));
|
||||
_c = c;
|
||||
}
|
||||
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
_initAlbum();
|
||||
}
|
||||
|
||||
@override
|
||||
build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Builder(
|
||||
builder: (context) => _buildContent(context),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
onItemTap(SelectableItem item, int index) {
|
||||
item.as<_ListItem>()?.onTap?.call();
|
||||
}
|
||||
|
||||
@override
|
||||
@protected
|
||||
get canEdit => false;
|
||||
|
||||
Future<void> _initAlbum() async {
|
||||
assert(widget.album.provider is AlbumSmartProvider);
|
||||
_log.info("[_initAlbum] ${widget.album}");
|
||||
final items = await PreProcessAlbum(_c)(widget.account, widget.album);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_album = widget.album;
|
||||
_transformItems(items);
|
||||
initCover(widget.account, widget.album);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildContent(BuildContext context) {
|
||||
if (_album == null) {
|
||||
return CustomScrollView(
|
||||
slivers: [
|
||||
buildNormalAppBar(context, widget.account, widget.album),
|
||||
const SliverToBoxAdapter(
|
||||
child: LinearProgressIndicator(),
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return buildItemStreamListOuter(
|
||||
context,
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
_buildAppBar(context),
|
||||
buildItemStreamList(
|
||||
maxCrossAxisExtent: thumbSize.toDouble(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildAppBar(BuildContext context) {
|
||||
if (isSelectionMode) {
|
||||
return _buildSelectionAppBar(context);
|
||||
} else {
|
||||
return _buildNormalAppBar(context);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildNormalAppBar(BuildContext context) {
|
||||
final menuItems = <PopupMenuEntry<int>>[
|
||||
PopupMenuItem(
|
||||
value: _menuValueDownload,
|
||||
child: Text(L10n.global().downloadTooltip),
|
||||
),
|
||||
];
|
||||
|
||||
return buildNormalAppBar(
|
||||
context,
|
||||
widget.account,
|
||||
_album!,
|
||||
menuItemBuilder: (_) => menuItems,
|
||||
onSelectedMenuItem: (option) {
|
||||
switch (option) {
|
||||
case _menuValueDownload:
|
||||
_onDownloadPressed();
|
||||
break;
|
||||
default:
|
||||
_log.shout("[_buildNormalAppBar] Unknown value: $option");
|
||||
break;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSelectionAppBar(BuildContext context) {
|
||||
return buildSelectionAppBar(context, [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.share),
|
||||
tooltip: L10n.global().shareTooltip,
|
||||
onPressed: () {
|
||||
_onSelectionSharePressed(context);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.add),
|
||||
tooltip: L10n.global().addToAlbumTooltip,
|
||||
onPressed: () => _onSelectionAddPressed(context),
|
||||
),
|
||||
PopupMenuButton<_SelectionMenuOption>(
|
||||
tooltip: MaterialLocalizations.of(context).moreButtonTooltip,
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
value: _SelectionMenuOption.download,
|
||||
child: Text(L10n.global().downloadTooltip),
|
||||
),
|
||||
],
|
||||
onSelected: (option) => _onSelectionMenuSelected(context, option),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
void _onItemTap(int index) {
|
||||
// convert item index to file index
|
||||
var fileIndex = index;
|
||||
for (int i = 0; i < index; ++i) {
|
||||
if (_sortedItems[i] is! AlbumFileItem ||
|
||||
!file_util
|
||||
.isSupportedFormat((_sortedItems[i] as AlbumFileItem).file)) {
|
||||
--fileIndex;
|
||||
}
|
||||
}
|
||||
Navigator.pushNamed(context, Viewer.routeName,
|
||||
arguments: ViewerArguments(widget.account, _backingFiles, fileIndex));
|
||||
}
|
||||
|
||||
void _onDownloadPressed() {
|
||||
final c = KiwiContainer().resolve<DiContainer>();
|
||||
DownloadHandler(c).downloadFiles(
|
||||
widget.account,
|
||||
_sortedItems.whereType<AlbumFileItem>().map((e) => e.file).toList(),
|
||||
parentDir: _album!.name,
|
||||
);
|
||||
}
|
||||
|
||||
void _onSelectionMenuSelected(
|
||||
BuildContext context, _SelectionMenuOption option) {
|
||||
switch (option) {
|
||||
case _SelectionMenuOption.download:
|
||||
_onSelectionDownloadPressed();
|
||||
break;
|
||||
default:
|
||||
_log.shout("[_onSelectionMenuSelected] Unknown option: $option");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _onSelectionSharePressed(BuildContext context) {
|
||||
final c = KiwiContainer().resolve<DiContainer>();
|
||||
final selected = selectedListItems
|
||||
.whereType<_FileListItem>()
|
||||
.map((e) => e.file)
|
||||
.toList();
|
||||
ShareHandler(
|
||||
c,
|
||||
context: context,
|
||||
clearSelection: () {
|
||||
setState(() {
|
||||
clearSelectedItems();
|
||||
});
|
||||
},
|
||||
).shareFiles(widget.account, selected);
|
||||
}
|
||||
|
||||
Future<void> _onSelectionAddPressed(BuildContext context) async {
|
||||
return const AddSelectionToCollectionHandler()(
|
||||
context: context,
|
||||
selection: selectedListItems
|
||||
.whereType<_FileListItem>()
|
||||
.map((e) => e.file)
|
||||
.toList(),
|
||||
clearSelection: () {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
clearSelectedItems();
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _onSelectionDownloadPressed() {
|
||||
final c = KiwiContainer().resolve<DiContainer>();
|
||||
final selected = selectedListItems
|
||||
.whereType<_FileListItem>()
|
||||
.map((e) => e.file)
|
||||
.toList();
|
||||
DownloadHandler(c).downloadFiles(widget.account, selected);
|
||||
setState(() {
|
||||
clearSelectedItems();
|
||||
});
|
||||
}
|
||||
|
||||
void _transformItems(List<AlbumItem> items) {
|
||||
// items come sorted for smart album
|
||||
_sortedItems = _album!.sortProvider.sort(items);
|
||||
_backingFiles = _sortedItems
|
||||
.whereType<AlbumFileItem>()
|
||||
.map((i) => i.file)
|
||||
.where((f) => file_util.isSupportedFormat(f))
|
||||
.toList();
|
||||
|
||||
itemStreamListItems = () sync* {
|
||||
for (int i = 0; i < _sortedItems.length; ++i) {
|
||||
final item = _sortedItems[i];
|
||||
if (item is AlbumFileItem) {
|
||||
final previewUrl =
|
||||
NetworkRectThumbnail.imageUrlForFile(widget.account, item.file);
|
||||
if (file_util.isSupportedImageFormat(item.file)) {
|
||||
yield _ImageListItem(
|
||||
index: i,
|
||||
file: item.file,
|
||||
account: widget.account,
|
||||
previewUrl: previewUrl,
|
||||
onTap: () => _onItemTap(i),
|
||||
);
|
||||
} else if (file_util.isSupportedVideoFormat(item.file)) {
|
||||
yield _VideoListItem(
|
||||
index: i,
|
||||
file: item.file,
|
||||
account: widget.account,
|
||||
previewUrl: previewUrl,
|
||||
onTap: () => _onItemTap(i),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
.toList();
|
||||
}
|
||||
|
||||
late final DiContainer _c;
|
||||
|
||||
Album? _album;
|
||||
var _sortedItems = <AlbumItem>[];
|
||||
var _backingFiles = <File>[];
|
||||
|
||||
static const _menuValueDownload = 1;
|
||||
}
|
||||
|
||||
enum _SelectionMenuOption {
|
||||
download,
|
||||
}
|
||||
|
||||
@toString
|
||||
abstract class _ListItem implements SelectableItem {
|
||||
const _ListItem({
|
||||
required this.index,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
get isTappable => onTap != null;
|
||||
|
||||
@override
|
||||
get isSelectable => true;
|
||||
|
||||
@override
|
||||
get staggeredTile => const StaggeredTile.count(1, 1);
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
final int index;
|
||||
|
||||
@ignore
|
||||
final VoidCallback? onTap;
|
||||
}
|
||||
|
||||
abstract class _FileListItem extends _ListItem {
|
||||
_FileListItem({
|
||||
required super.index,
|
||||
required this.file,
|
||||
super.onTap,
|
||||
});
|
||||
|
||||
final File file;
|
||||
}
|
||||
|
||||
class _ImageListItem extends _FileListItem {
|
||||
_ImageListItem({
|
||||
required super.index,
|
||||
required super.file,
|
||||
required this.account,
|
||||
required this.previewUrl,
|
||||
super.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
buildWidget(BuildContext context) => PhotoListImage(
|
||||
account: account,
|
||||
previewUrl: previewUrl,
|
||||
isGif: file.contentType == "image/gif",
|
||||
heroKey: flutter_util.getImageHeroTag(file),
|
||||
);
|
||||
|
||||
final Account account;
|
||||
final String previewUrl;
|
||||
}
|
||||
|
||||
class _VideoListItem extends _FileListItem {
|
||||
_VideoListItem({
|
||||
required super.index,
|
||||
required super.file,
|
||||
required this.account,
|
||||
required this.previewUrl,
|
||||
super.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
buildWidget(BuildContext context) => PhotoListVideo(
|
||||
account: account,
|
||||
previewUrl: previewUrl,
|
||||
);
|
||||
|
||||
final Account account;
|
||||
final String previewUrl;
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'smart_album_browser.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// NpLogGenerator
|
||||
// **************************************************************************
|
||||
|
||||
extension _$_SmartAlbumBrowserStateNpLog on _SmartAlbumBrowserState {
|
||||
// ignore: unused_element
|
||||
Logger get _log => log;
|
||||
|
||||
static final log =
|
||||
Logger("widget.smart_album_browser._SmartAlbumBrowserState");
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// ToStringGenerator
|
||||
// **************************************************************************
|
||||
|
||||
extension _$_ListItemToString on _ListItem {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "${objectRuntimeType(this, "_ListItem")} {index: $index}";
|
||||
}
|
||||
}
|
|
@ -1,15 +1,12 @@
|
|||
import 'package:nc_photos/entity/album.dart';
|
||||
import 'package:nc_photos/entity/album/cover_provider.dart';
|
||||
import 'package:nc_photos/entity/album/provider.dart';
|
||||
import 'package:nc_photos/entity/album/sort_provider.dart';
|
||||
import 'package:nc_photos/or_null.dart';
|
||||
import 'package:nc_photos/entity/collection.dart';
|
||||
import 'package:nc_photos/entity/collection/content_provider/memory.dart';
|
||||
import 'package:nc_photos/widget/photo_list_util.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../test_util.dart' as util;
|
||||
|
||||
void main() {
|
||||
group("MemoryAlbumHelper", () {
|
||||
group("MemoryCollectionHelper", () {
|
||||
test("same year", _sameYear);
|
||||
test("next year", _nextYear);
|
||||
group("prev year", () {
|
||||
|
@ -54,8 +51,9 @@ void main() {
|
|||
/// File: 2021-02-01
|
||||
/// Expect: empty
|
||||
void _sameYear() {
|
||||
final account = util.buildAccount();
|
||||
final today = DateTime(2021, 2, 3);
|
||||
final obj = MemoryAlbumHelper(today: today, dayRange: 2);
|
||||
final obj = MemoryCollectionHelper(account, today: today, dayRange: 2);
|
||||
final file = util.buildJpegFile(
|
||||
path: "", fileId: 0, lastModified: DateTime.utc(2021, 2, 3));
|
||||
obj.addFile(file);
|
||||
|
@ -69,8 +67,9 @@ void _sameYear() {
|
|||
/// File: 2022-02-03
|
||||
/// Expect: empty
|
||||
void _nextYear() {
|
||||
final account = util.buildAccount();
|
||||
final today = DateTime(2021, 2, 3);
|
||||
final obj = MemoryAlbumHelper(today: today, dayRange: 2);
|
||||
final obj = MemoryCollectionHelper(account, today: today, dayRange: 2);
|
||||
final file = util.buildJpegFile(
|
||||
path: "", fileId: 0, lastModified: DateTime.utc(2022, 2, 3));
|
||||
obj.addFile(file);
|
||||
|
@ -83,24 +82,19 @@ void _nextYear() {
|
|||
/// File: 2020-02-03
|
||||
/// Expect: [2020]
|
||||
void _prevYear() {
|
||||
final account = util.buildAccount();
|
||||
final today = DateTime(2021, 2, 3);
|
||||
final obj = MemoryAlbumHelper(today: today, dayRange: 2);
|
||||
final obj = MemoryCollectionHelper(account, today: today, dayRange: 2);
|
||||
final file = util.buildJpegFile(
|
||||
path: "", fileId: 0, lastModified: DateTime.utc(2020, 2, 3));
|
||||
obj.addFile(file);
|
||||
expect(
|
||||
obj
|
||||
.build(_nameBuilder)
|
||||
.map((a) => a.copyWith(lastUpdated: OrNull(DateTime(2021))))
|
||||
.toList(),
|
||||
obj.build(_nameBuilder).toList(),
|
||||
[
|
||||
Album(
|
||||
Collection(
|
||||
name: "2020",
|
||||
provider:
|
||||
AlbumMemoryProvider(year: 2020, month: today.month, day: today.day),
|
||||
coverProvider: AlbumMemoryCoverProvider(coverFile: file),
|
||||
sortProvider: const AlbumTimeSortProvider(isAscending: false),
|
||||
lastUpdated: DateTime(2021),
|
||||
contentProvider: CollectionMemoryProvider(
|
||||
account: account, year: 2020, month: 2, day: 3, cover: file),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -112,8 +106,9 @@ void _prevYear() {
|
|||
/// File: 2020-01-31
|
||||
/// Expect: empty
|
||||
void _prevYear3DaysBefore() {
|
||||
final account = util.buildAccount();
|
||||
final today = DateTime(2021, 2, 3);
|
||||
final obj = MemoryAlbumHelper(today: today, dayRange: 2);
|
||||
final obj = MemoryCollectionHelper(account, today: today, dayRange: 2);
|
||||
final file = util.buildJpegFile(
|
||||
path: "", fileId: 0, lastModified: DateTime.utc(2020, 1, 31));
|
||||
obj.addFile(file);
|
||||
|
@ -126,24 +121,19 @@ void _prevYear3DaysBefore() {
|
|||
/// File: 2020-02-01
|
||||
/// Expect: [2020]
|
||||
void _prevYear2DaysBefore() {
|
||||
final account = util.buildAccount();
|
||||
final today = DateTime(2021, 2, 3);
|
||||
final obj = MemoryAlbumHelper(today: today, dayRange: 2);
|
||||
final obj = MemoryCollectionHelper(account, today: today, dayRange: 2);
|
||||
final file = util.buildJpegFile(
|
||||
path: "", fileId: 0, lastModified: DateTime.utc(2020, 2, 1));
|
||||
obj.addFile(file);
|
||||
expect(
|
||||
obj
|
||||
.build(_nameBuilder)
|
||||
.map((a) => a.copyWith(lastUpdated: OrNull(DateTime(2021))))
|
||||
.toList(),
|
||||
obj.build(_nameBuilder).toList(),
|
||||
[
|
||||
Album(
|
||||
Collection(
|
||||
name: "2020",
|
||||
provider:
|
||||
AlbumMemoryProvider(year: 2020, month: today.month, day: today.day),
|
||||
coverProvider: AlbumMemoryCoverProvider(coverFile: file),
|
||||
sortProvider: const AlbumTimeSortProvider(isAscending: false),
|
||||
lastUpdated: DateTime(2021),
|
||||
contentProvider: CollectionMemoryProvider(
|
||||
account: account, year: 2020, month: 2, day: 3, cover: file),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -155,8 +145,9 @@ void _prevYear2DaysBefore() {
|
|||
/// File: 2020-02-06
|
||||
/// Expect: empty
|
||||
void _prevYear3DaysAfter() {
|
||||
final account = util.buildAccount();
|
||||
final today = DateTime(2021, 2, 3);
|
||||
final obj = MemoryAlbumHelper(today: today, dayRange: 2);
|
||||
final obj = MemoryCollectionHelper(account, today: today, dayRange: 2);
|
||||
final file = util.buildJpegFile(
|
||||
path: "", fileId: 0, lastModified: DateTime.utc(2020, 2, 6));
|
||||
obj.addFile(file);
|
||||
|
@ -169,24 +160,19 @@ void _prevYear3DaysAfter() {
|
|||
/// File: 2020-02-05
|
||||
/// Expect: [2020]
|
||||
void _prevYear2DaysAfter() {
|
||||
final account = util.buildAccount();
|
||||
final today = DateTime(2021, 2, 3);
|
||||
final obj = MemoryAlbumHelper(today: today, dayRange: 2);
|
||||
final obj = MemoryCollectionHelper(account, today: today, dayRange: 2);
|
||||
final file = util.buildJpegFile(
|
||||
path: "", fileId: 0, lastModified: DateTime.utc(2020, 2, 5));
|
||||
obj.addFile(file);
|
||||
expect(
|
||||
obj
|
||||
.build(_nameBuilder)
|
||||
.map((a) => a.copyWith(lastUpdated: OrNull(DateTime(2021))))
|
||||
.toList(),
|
||||
obj.build(_nameBuilder).toList(),
|
||||
[
|
||||
Album(
|
||||
Collection(
|
||||
name: "2020",
|
||||
provider:
|
||||
AlbumMemoryProvider(year: 2020, month: today.month, day: today.day),
|
||||
coverProvider: AlbumMemoryCoverProvider(coverFile: file),
|
||||
sortProvider: const AlbumTimeSortProvider(isAscending: false),
|
||||
lastUpdated: DateTime(2021),
|
||||
contentProvider: CollectionMemoryProvider(
|
||||
account: account, year: 2020, month: 2, day: 3, cover: file),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -198,8 +184,9 @@ void _prevYear2DaysAfter() {
|
|||
/// File: 2019-02-26
|
||||
/// Expect: empty
|
||||
void _onFeb29AddFeb26() {
|
||||
final account = util.buildAccount();
|
||||
final today = DateTime(2020, 2, 29);
|
||||
final obj = MemoryAlbumHelper(today: today, dayRange: 2);
|
||||
final obj = MemoryCollectionHelper(account, today: today, dayRange: 2);
|
||||
final file = util.buildJpegFile(
|
||||
path: "", fileId: 0, lastModified: DateTime.utc(2019, 2, 26));
|
||||
obj.addFile(file);
|
||||
|
@ -212,24 +199,19 @@ void _onFeb29AddFeb26() {
|
|||
/// File: 2019-02-27
|
||||
/// Expect: [2019]
|
||||
void _onFeb29AddFeb27() {
|
||||
final account = util.buildAccount();
|
||||
final today = DateTime(2020, 2, 29);
|
||||
final obj = MemoryAlbumHelper(today: today, dayRange: 2);
|
||||
final obj = MemoryCollectionHelper(account, today: today, dayRange: 2);
|
||||
final file = util.buildJpegFile(
|
||||
path: "", fileId: 0, lastModified: DateTime.utc(2019, 2, 27));
|
||||
obj.addFile(file);
|
||||
expect(
|
||||
obj
|
||||
.build(_nameBuilder)
|
||||
.map((a) => a.copyWith(lastUpdated: OrNull(DateTime(2021))))
|
||||
.toList(),
|
||||
obj.build(_nameBuilder).toList(),
|
||||
[
|
||||
Album(
|
||||
Collection(
|
||||
name: "2019",
|
||||
provider:
|
||||
AlbumMemoryProvider(year: 2019, month: today.month, day: today.day),
|
||||
coverProvider: AlbumMemoryCoverProvider(coverFile: file),
|
||||
sortProvider: const AlbumTimeSortProvider(isAscending: false),
|
||||
lastUpdated: DateTime(2021),
|
||||
contentProvider: CollectionMemoryProvider(
|
||||
account: account, year: 2019, month: 2, day: 29, cover: file),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -241,8 +223,9 @@ void _onFeb29AddFeb27() {
|
|||
/// File: 2019-03-04
|
||||
/// Expect: empty
|
||||
void _onFeb29AddMar4() {
|
||||
final account = util.buildAccount();
|
||||
final today = DateTime(2020, 2, 29);
|
||||
final obj = MemoryAlbumHelper(today: today, dayRange: 2);
|
||||
final obj = MemoryCollectionHelper(account, today: today, dayRange: 2);
|
||||
final file = util.buildJpegFile(
|
||||
path: "", fileId: 0, lastModified: DateTime.utc(2019, 3, 4));
|
||||
obj.addFile(file);
|
||||
|
@ -255,24 +238,19 @@ void _onFeb29AddMar4() {
|
|||
/// File: 2019-03-03
|
||||
/// Expect: [2019]
|
||||
void _onFeb29AddMar3() {
|
||||
final account = util.buildAccount();
|
||||
final today = DateTime(2020, 2, 29);
|
||||
final obj = MemoryAlbumHelper(today: today, dayRange: 2);
|
||||
final obj = MemoryCollectionHelper(account, today: today, dayRange: 2);
|
||||
final file = util.buildJpegFile(
|
||||
path: "", fileId: 0, lastModified: DateTime.utc(2019, 3, 3));
|
||||
obj.addFile(file);
|
||||
expect(
|
||||
obj
|
||||
.build(_nameBuilder)
|
||||
.map((a) => a.copyWith(lastUpdated: OrNull(DateTime(2021))))
|
||||
.toList(),
|
||||
obj.build(_nameBuilder).toList(),
|
||||
[
|
||||
Album(
|
||||
Collection(
|
||||
name: "2019",
|
||||
provider:
|
||||
AlbumMemoryProvider(year: 2019, month: today.month, day: today.day),
|
||||
coverProvider: AlbumMemoryCoverProvider(coverFile: file),
|
||||
sortProvider: const AlbumTimeSortProvider(isAscending: false),
|
||||
lastUpdated: DateTime(2021),
|
||||
contentProvider: CollectionMemoryProvider(
|
||||
account: account, year: 2019, month: 2, day: 29, cover: file),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -284,8 +262,9 @@ void _onFeb29AddMar3() {
|
|||
/// File: 2016-03-03
|
||||
/// Expect: empty
|
||||
void _onFeb29AddMar3LeapYear() {
|
||||
final account = util.buildAccount();
|
||||
final today = DateTime(2020, 2, 29);
|
||||
final obj = MemoryAlbumHelper(today: today, dayRange: 2);
|
||||
final obj = MemoryCollectionHelper(account, today: today, dayRange: 2);
|
||||
final file = util.buildJpegFile(
|
||||
path: "", fileId: 0, lastModified: DateTime.utc(2016, 3, 3));
|
||||
obj.addFile(file);
|
||||
|
@ -298,24 +277,19 @@ void _onFeb29AddMar3LeapYear() {
|
|||
/// File: 2016-03-02
|
||||
/// Expect: [2016]
|
||||
void _onFeb29AddMar2LeapYear() {
|
||||
final account = util.buildAccount();
|
||||
final today = DateTime(2020, 2, 29);
|
||||
final obj = MemoryAlbumHelper(today: today, dayRange: 2);
|
||||
final obj = MemoryCollectionHelper(account, today: today, dayRange: 2);
|
||||
final file = util.buildJpegFile(
|
||||
path: "", fileId: 0, lastModified: DateTime.utc(2016, 3, 2));
|
||||
obj.addFile(file);
|
||||
expect(
|
||||
obj
|
||||
.build(_nameBuilder)
|
||||
.map((a) => a.copyWith(lastUpdated: OrNull(DateTime(2021))))
|
||||
.toList(),
|
||||
obj.build(_nameBuilder).toList(),
|
||||
[
|
||||
Album(
|
||||
Collection(
|
||||
name: "2016",
|
||||
provider:
|
||||
AlbumMemoryProvider(year: 2016, month: today.month, day: today.day),
|
||||
coverProvider: AlbumMemoryCoverProvider(coverFile: file),
|
||||
sortProvider: const AlbumTimeSortProvider(isAscending: false),
|
||||
lastUpdated: DateTime(2021),
|
||||
contentProvider: CollectionMemoryProvider(
|
||||
account: account, year: 2016, month: 2, day: 29, cover: file),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -327,8 +301,9 @@ void _onFeb29AddMar2LeapYear() {
|
|||
/// File: 2019-12-31
|
||||
/// Expect: empty
|
||||
void _onJan1AddDec31() {
|
||||
final account = util.buildAccount();
|
||||
final today = DateTime(2020, 1, 1);
|
||||
final obj = MemoryAlbumHelper(today: today, dayRange: 2);
|
||||
final obj = MemoryCollectionHelper(account, today: today, dayRange: 2);
|
||||
final file = util.buildJpegFile(
|
||||
path: "", fileId: 0, lastModified: DateTime.utc(2019, 12, 31));
|
||||
obj.addFile(file);
|
||||
|
@ -341,24 +316,19 @@ void _onJan1AddDec31() {
|
|||
/// File: 2018-12-31
|
||||
/// Expect: [2019]
|
||||
void _onJan1AddDec31PrevYear() {
|
||||
final account = util.buildAccount();
|
||||
final today = DateTime(2020, 1, 1);
|
||||
final obj = MemoryAlbumHelper(today: today, dayRange: 2);
|
||||
final obj = MemoryCollectionHelper(account, today: today, dayRange: 2);
|
||||
final file = util.buildJpegFile(
|
||||
path: "", fileId: 0, lastModified: DateTime.utc(2018, 12, 31));
|
||||
obj.addFile(file);
|
||||
expect(
|
||||
obj
|
||||
.build(_nameBuilder)
|
||||
.map((a) => a.copyWith(lastUpdated: OrNull(DateTime(2021))))
|
||||
.toList(),
|
||||
obj.build(_nameBuilder).toList(),
|
||||
[
|
||||
Album(
|
||||
Collection(
|
||||
name: "2019",
|
||||
provider:
|
||||
AlbumMemoryProvider(year: 2019, month: today.month, day: today.day),
|
||||
coverProvider: AlbumMemoryCoverProvider(coverFile: file),
|
||||
sortProvider: const AlbumTimeSortProvider(isAscending: false),
|
||||
lastUpdated: DateTime(2021),
|
||||
contentProvider: CollectionMemoryProvider(
|
||||
account: account, year: 2019, month: 1, day: 1, cover: file),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -370,24 +340,19 @@ void _onJan1AddDec31PrevYear() {
|
|||
/// File: 2020-01-01
|
||||
/// Expect: [2019]
|
||||
void _onDec31AddJan1() {
|
||||
final account = util.buildAccount();
|
||||
final today = DateTime(2020, 12, 31);
|
||||
final obj = MemoryAlbumHelper(today: today, dayRange: 2);
|
||||
final obj = MemoryCollectionHelper(account, today: today, dayRange: 2);
|
||||
final file = util.buildJpegFile(
|
||||
path: "", fileId: 0, lastModified: DateTime.utc(2020, 1, 1));
|
||||
obj.addFile(file);
|
||||
expect(
|
||||
obj
|
||||
.build(_nameBuilder)
|
||||
.map((a) => a.copyWith(lastUpdated: OrNull(DateTime(2021))))
|
||||
.toList(),
|
||||
obj.build(_nameBuilder).toList(),
|
||||
[
|
||||
Album(
|
||||
Collection(
|
||||
name: "2019",
|
||||
provider:
|
||||
AlbumMemoryProvider(year: 2019, month: today.month, day: today.day),
|
||||
coverProvider: AlbumMemoryCoverProvider(coverFile: file),
|
||||
sortProvider: const AlbumTimeSortProvider(isAscending: false),
|
||||
lastUpdated: DateTime(2021),
|
||||
contentProvider: CollectionMemoryProvider(
|
||||
account: account, year: 2019, month: 12, day: 31, cover: file),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -399,24 +364,19 @@ void _onDec31AddJan1() {
|
|||
/// File: 2021-05-15
|
||||
/// Expect: [2022]
|
||||
void _onMay15AddMay15Range0() {
|
||||
final account = util.buildAccount();
|
||||
final today = DateTime(2022, 5, 15);
|
||||
final obj = MemoryAlbumHelper(today: today, dayRange: 0);
|
||||
final obj = MemoryCollectionHelper(account, today: today, dayRange: 0);
|
||||
final file = util.buildJpegFile(
|
||||
path: "", fileId: 0, lastModified: DateTime.utc(2021, 5, 15));
|
||||
obj.addFile(file);
|
||||
expect(
|
||||
obj
|
||||
.build(_nameBuilder)
|
||||
.map((a) => a.copyWith(lastUpdated: OrNull(DateTime(2021))))
|
||||
.toList(),
|
||||
obj.build(_nameBuilder).toList(),
|
||||
[
|
||||
Album(
|
||||
Collection(
|
||||
name: "2021",
|
||||
provider:
|
||||
AlbumMemoryProvider(year: 2021, month: today.month, day: today.day),
|
||||
coverProvider: AlbumMemoryCoverProvider(coverFile: file),
|
||||
sortProvider: const AlbumTimeSortProvider(isAscending: false),
|
||||
lastUpdated: DateTime(2021),
|
||||
contentProvider: CollectionMemoryProvider(
|
||||
account: account, year: 2021, month: 5, day: 15, cover: file),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -428,8 +388,9 @@ void _onMay15AddMay15Range0() {
|
|||
/// File: 2021-05-16
|
||||
/// Expect: []
|
||||
void _onMay15AddMay16Range0() {
|
||||
final account = util.buildAccount();
|
||||
final today = DateTime(2022, 5, 15);
|
||||
final obj = MemoryAlbumHelper(today: today, dayRange: 0);
|
||||
final obj = MemoryCollectionHelper(account, today: today, dayRange: 0);
|
||||
final file = util.buildJpegFile(
|
||||
path: "", fileId: 0, lastModified: DateTime.utc(2021, 5, 16));
|
||||
obj.addFile(file);
|
||||
|
@ -442,8 +403,9 @@ void _onMay15AddMay16Range0() {
|
|||
/// File: 2021-05-16
|
||||
/// Expect: []
|
||||
void _onMay15AddMay16RangeNegative() {
|
||||
final account = util.buildAccount();
|
||||
final today = DateTime(2022, 5, 15);
|
||||
final obj = MemoryAlbumHelper(today: today, dayRange: -1);
|
||||
final obj = MemoryCollectionHelper(account, today: today, dayRange: -1);
|
||||
final file = util.buildJpegFile(
|
||||
path: "", fileId: 0, lastModified: DateTime.utc(2021, 5, 16));
|
||||
obj.addFile(file);
|
||||
|
|
Loading…
Reference in a new issue