mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-02-02 06:46:22 +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:
|
case AlbumManualCoverProvider._type:
|
||||||
return AlbumManualCoverProvider.fromJson(
|
return AlbumManualCoverProvider.fromJson(
|
||||||
content.cast<String, dynamic>());
|
content.cast<String, dynamic>());
|
||||||
case AlbumMemoryCoverProvider._type:
|
|
||||||
return AlbumMemoryCoverProvider.fromJson(
|
|
||||||
content.cast<String, dynamic>());
|
|
||||||
default:
|
default:
|
||||||
_log.shout("[fromJson] Unknown type: $type");
|
_log.shout("[fromJson] Unknown type: $type");
|
||||||
throw ArgumentError.value(type, "type");
|
throw ArgumentError.value(type, "type");
|
||||||
|
@ -154,38 +151,3 @@ class AlbumManualCoverProvider extends AlbumCoverProvider {
|
||||||
|
|
||||||
static const _type = "manual";
|
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}}";
|
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();
|
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()}}";
|
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: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/sorter.dart';
|
||||||
import 'package:nc_photos/entity/collection_item/util.dart';
|
import 'package:nc_photos/entity/collection_item/util.dart';
|
||||||
import 'package:to_string/to_string.dart';
|
import 'package:to_string/to_string.dart';
|
||||||
|
@ -8,7 +9,7 @@ part 'collection.g.dart';
|
||||||
/// Describe a group of items
|
/// Describe a group of items
|
||||||
@genCopyWith
|
@genCopyWith
|
||||||
@toString
|
@toString
|
||||||
class Collection {
|
class Collection with EquatableMixin {
|
||||||
const Collection({
|
const Collection({
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.contentProvider,
|
required this.contentProvider,
|
||||||
|
@ -40,11 +41,25 @@ class Collection {
|
||||||
CollectionItemSort get itemSort => contentProvider.itemSort;
|
CollectionItemSort get itemSort => contentProvider.itemSort;
|
||||||
|
|
||||||
/// See [CollectionContentProvider.getCoverUrl]
|
/// See [CollectionContentProvider.getCoverUrl]
|
||||||
String? getCoverUrl(int width, int height) =>
|
String? getCoverUrl(
|
||||||
contentProvider.getCoverUrl(width, height);
|
int width,
|
||||||
|
int height, {
|
||||||
|
bool? isKeepAspectRatio,
|
||||||
|
}) =>
|
||||||
|
contentProvider.getCoverUrl(
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
isKeepAspectRatio: isKeepAspectRatio,
|
||||||
|
);
|
||||||
|
|
||||||
CollectionSorter getSorter() => CollectionSorter.fromSortType(itemSort);
|
CollectionSorter getSorter() => CollectionSorter.fromSortType(itemSort);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [
|
||||||
|
name,
|
||||||
|
contentProvider,
|
||||||
|
];
|
||||||
|
|
||||||
final String name;
|
final String name;
|
||||||
final CollectionContentProvider contentProvider;
|
final CollectionContentProvider contentProvider;
|
||||||
}
|
}
|
||||||
|
@ -65,7 +80,7 @@ enum CollectionCapability {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provide the actual content of a collection
|
/// Provide the actual content of a collection
|
||||||
abstract class CollectionContentProvider {
|
abstract class CollectionContentProvider with EquatableMixin {
|
||||||
const CollectionContentProvider();
|
const CollectionContentProvider();
|
||||||
|
|
||||||
/// Unique FourCC of this provider type
|
/// 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
|
/// The [width] and [height] are provided as a hint only, implementations are
|
||||||
/// free to ignore them if it's not supported
|
/// 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.dart';
|
||||||
import 'package:nc_photos/entity/collection/adapter/album.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/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/nc_album.dart';
|
||||||
import 'package:nc_photos/entity/collection/adapter/person.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/adapter/tag.dart';
|
||||||
import 'package:nc_photos/entity/collection/content_provider/album.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/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/nc_album.dart';
|
||||||
import 'package:nc_photos/entity/collection/content_provider/person.dart';
|
import 'package:nc_photos/entity/collection/content_provider/person.dart';
|
||||||
import 'package:nc_photos/entity/collection/content_provider/tag.dart';
|
import 'package:nc_photos/entity/collection/content_provider/tag.dart';
|
||||||
|
@ -29,6 +31,8 @@ abstract class CollectionAdapter {
|
||||||
return CollectionAlbumAdapter(c, account, collection);
|
return CollectionAlbumAdapter(c, account, collection);
|
||||||
case CollectionLocationGroupProvider:
|
case CollectionLocationGroupProvider:
|
||||||
return CollectionLocationGroupAdapter(c, account, collection);
|
return CollectionLocationGroupAdapter(c, account, collection);
|
||||||
|
case CollectionMemoryProvider:
|
||||||
|
return CollectionMemoryAdapter(c, account, collection);
|
||||||
case CollectionNcAlbumProvider:
|
case CollectionNcAlbumProvider:
|
||||||
return CollectionNcAlbumAdapter(c, account, collection);
|
return CollectionNcAlbumAdapter(c, account, collection);
|
||||||
case CollectionPersonProvider:
|
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:copy_with/copy_with.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:nc_photos/account.dart';
|
import 'package:nc_photos/account.dart';
|
||||||
import 'package:nc_photos/api/api_util.dart' as api_util;
|
import 'package:nc_photos/api/api_util.dart' as api_util;
|
||||||
import 'package:nc_photos/entity/album.dart';
|
import 'package:nc_photos/entity/album.dart';
|
||||||
|
@ -12,7 +13,9 @@ part 'album.g.dart';
|
||||||
/// Album provided by our app
|
/// Album provided by our app
|
||||||
@genCopyWith
|
@genCopyWith
|
||||||
@toString
|
@toString
|
||||||
class CollectionAlbumProvider implements CollectionContentProvider {
|
class CollectionAlbumProvider
|
||||||
|
with EquatableMixin
|
||||||
|
implements CollectionContentProvider {
|
||||||
const CollectionAlbumProvider({
|
const CollectionAlbumProvider({
|
||||||
required this.account,
|
required this.account,
|
||||||
required this.album,
|
required this.album,
|
||||||
|
@ -64,7 +67,11 @@ class CollectionAlbumProvider implements CollectionContentProvider {
|
||||||
CollectionItemSort get itemSort => album.sortProvider.toCollectionItemSort();
|
CollectionItemSort get itemSort => album.sortProvider.toCollectionItemSort();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String? getCoverUrl(int width, int height) {
|
String? getCoverUrl(
|
||||||
|
int width,
|
||||||
|
int height, {
|
||||||
|
bool? isKeepAspectRatio,
|
||||||
|
}) {
|
||||||
final fd = album.coverProvider.getCover(album);
|
final fd = album.coverProvider.getCover(album);
|
||||||
if (fd == null) {
|
if (fd == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -74,11 +81,14 @@ class CollectionAlbumProvider implements CollectionContentProvider {
|
||||||
fd.fdId,
|
fd.fdId,
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
isKeepAspectRatio: false,
|
isKeepAspectRatio: isKeepAspectRatio ?? false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [account, album];
|
||||||
|
|
||||||
final Account account;
|
final Account account;
|
||||||
final Album album;
|
final Album album;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:nc_photos/account.dart';
|
import 'package:nc_photos/account.dart';
|
||||||
import 'package:nc_photos/api/api_util.dart' as api_util;
|
import 'package:nc_photos/api/api_util.dart' as api_util;
|
||||||
import 'package:nc_photos/entity/collection.dart';
|
import 'package:nc_photos/entity/collection.dart';
|
||||||
import 'package:nc_photos/entity/collection_item/util.dart';
|
import 'package:nc_photos/entity/collection_item/util.dart';
|
||||||
import 'package:nc_photos/use_case/list_location_group.dart';
|
import 'package:nc_photos/use_case/list_location_group.dart';
|
||||||
|
|
||||||
class CollectionLocationGroupProvider implements CollectionContentProvider {
|
class CollectionLocationGroupProvider
|
||||||
|
with EquatableMixin
|
||||||
|
implements CollectionContentProvider {
|
||||||
const CollectionLocationGroupProvider({
|
const CollectionLocationGroupProvider({
|
||||||
required this.account,
|
required this.account,
|
||||||
required this.location,
|
required this.location,
|
||||||
|
@ -29,16 +32,23 @@ class CollectionLocationGroupProvider implements CollectionContentProvider {
|
||||||
CollectionItemSort get itemSort => CollectionItemSort.dateDescending;
|
CollectionItemSort get itemSort => CollectionItemSort.dateDescending;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String? getCoverUrl(int width, int height) {
|
String? getCoverUrl(
|
||||||
|
int width,
|
||||||
|
int height, {
|
||||||
|
bool? isKeepAspectRatio,
|
||||||
|
}) {
|
||||||
return api_util.getFilePreviewUrlByFileId(
|
return api_util.getFilePreviewUrlByFileId(
|
||||||
account,
|
account,
|
||||||
location.latestFileId,
|
location.latestFileId,
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
isKeepAspectRatio: false,
|
isKeepAspectRatio: isKeepAspectRatio ?? false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [account, location];
|
||||||
|
|
||||||
final Account account;
|
final Account account;
|
||||||
final LocationGroup location;
|
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:clock/clock.dart';
|
||||||
import 'package:copy_with/copy_with.dart';
|
import 'package:copy_with/copy_with.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:nc_photos/account.dart';
|
import 'package:nc_photos/account.dart';
|
||||||
import 'package:nc_photos/api/api_util.dart' as api_util;
|
import 'package:nc_photos/api/api_util.dart' as api_util;
|
||||||
import 'package:nc_photos/entity/collection.dart';
|
import 'package:nc_photos/entity/collection.dart';
|
||||||
|
@ -12,7 +13,9 @@ part 'nc_album.g.dart';
|
||||||
/// Album provided by our app
|
/// Album provided by our app
|
||||||
@genCopyWith
|
@genCopyWith
|
||||||
@toString
|
@toString
|
||||||
class CollectionNcAlbumProvider implements CollectionContentProvider {
|
class CollectionNcAlbumProvider
|
||||||
|
with EquatableMixin
|
||||||
|
implements CollectionContentProvider {
|
||||||
const CollectionNcAlbumProvider({
|
const CollectionNcAlbumProvider({
|
||||||
required this.account,
|
required this.account,
|
||||||
required this.album,
|
required this.album,
|
||||||
|
@ -43,7 +46,11 @@ class CollectionNcAlbumProvider implements CollectionContentProvider {
|
||||||
CollectionItemSort get itemSort => CollectionItemSort.dateDescending;
|
CollectionItemSort get itemSort => CollectionItemSort.dateDescending;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String? getCoverUrl(int width, int height) {
|
String? getCoverUrl(
|
||||||
|
int width,
|
||||||
|
int height, {
|
||||||
|
bool? isKeepAspectRatio,
|
||||||
|
}) {
|
||||||
if (album.lastPhoto == null) {
|
if (album.lastPhoto == null) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
|
@ -52,11 +59,14 @@ class CollectionNcAlbumProvider implements CollectionContentProvider {
|
||||||
album.lastPhoto!,
|
album.lastPhoto!,
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
isKeepAspectRatio: false,
|
isKeepAspectRatio: isKeepAspectRatio ?? false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [account, album];
|
||||||
|
|
||||||
final Account account;
|
final Account account;
|
||||||
final NcAlbum album;
|
final NcAlbum album;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:clock/clock.dart';
|
import 'package:clock/clock.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:nc_photos/account.dart';
|
import 'package:nc_photos/account.dart';
|
||||||
import 'package:nc_photos/api/api_util.dart' as api_util;
|
import 'package:nc_photos/api/api_util.dart' as api_util;
|
||||||
import 'package:nc_photos/entity/collection.dart';
|
import 'package:nc_photos/entity/collection.dart';
|
||||||
import 'package:nc_photos/entity/collection_item/util.dart';
|
import 'package:nc_photos/entity/collection_item/util.dart';
|
||||||
import 'package:nc_photos/entity/person.dart';
|
import 'package:nc_photos/entity/person.dart';
|
||||||
|
|
||||||
class CollectionPersonProvider implements CollectionContentProvider {
|
class CollectionPersonProvider
|
||||||
|
with EquatableMixin
|
||||||
|
implements CollectionContentProvider {
|
||||||
const CollectionPersonProvider({
|
const CollectionPersonProvider({
|
||||||
required this.account,
|
required this.account,
|
||||||
required this.person,
|
required this.person,
|
||||||
|
@ -32,11 +35,18 @@ class CollectionPersonProvider implements CollectionContentProvider {
|
||||||
CollectionItemSort get itemSort => CollectionItemSort.dateDescending;
|
CollectionItemSort get itemSort => CollectionItemSort.dateDescending;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String? getCoverUrl(int width, int height) {
|
String? getCoverUrl(
|
||||||
|
int width,
|
||||||
|
int height, {
|
||||||
|
bool? isKeepAspectRatio,
|
||||||
|
}) {
|
||||||
return api_util.getFacePreviewUrl(account, person.thumbFaceId,
|
return api_util.getFacePreviewUrl(account, person.thumbFaceId,
|
||||||
size: math.max(width, height));
|
size: math.max(width, height));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [account, person];
|
||||||
|
|
||||||
final Account account;
|
final Account account;
|
||||||
final Person person;
|
final Person person;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import 'package:clock/clock.dart';
|
import 'package:clock/clock.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:nc_photos/account.dart';
|
import 'package:nc_photos/account.dart';
|
||||||
import 'package:nc_photos/entity/collection.dart';
|
import 'package:nc_photos/entity/collection.dart';
|
||||||
import 'package:nc_photos/entity/collection_item/util.dart';
|
import 'package:nc_photos/entity/collection_item/util.dart';
|
||||||
import 'package:nc_photos/entity/tag.dart';
|
import 'package:nc_photos/entity/tag.dart';
|
||||||
|
|
||||||
class CollectionTagProvider implements CollectionContentProvider {
|
class CollectionTagProvider
|
||||||
|
with EquatableMixin
|
||||||
|
implements CollectionContentProvider {
|
||||||
CollectionTagProvider({
|
CollectionTagProvider({
|
||||||
required this.account,
|
required this.account,
|
||||||
required this.tags,
|
required this.tags,
|
||||||
|
@ -29,7 +32,15 @@ class CollectionTagProvider implements CollectionContentProvider {
|
||||||
CollectionItemSort get itemSort => CollectionItemSort.dateDescending;
|
CollectionItemSort get itemSort => CollectionItemSort.dateDescending;
|
||||||
|
|
||||||
@override
|
@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 Account account;
|
||||||
final List<Tag> tags;
|
final List<Tag> tags;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:copy_with/copy_with.dart';
|
import 'package:copy_with/copy_with.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:nc_photos/account.dart';
|
import 'package:nc_photos/account.dart';
|
||||||
import 'package:np_common/string_extension.dart';
|
import 'package:np_common/string_extension.dart';
|
||||||
import 'package:to_string/to_string.dart';
|
import 'package:to_string/to_string.dart';
|
||||||
|
@ -8,7 +9,7 @@ part 'nc_album.g.dart';
|
||||||
/// Server-side album since Nextcloud 25
|
/// Server-side album since Nextcloud 25
|
||||||
@toString
|
@toString
|
||||||
@genCopyWith
|
@genCopyWith
|
||||||
class NcAlbum {
|
class NcAlbum with EquatableMixin {
|
||||||
NcAlbum({
|
NcAlbum({
|
||||||
required String path,
|
required String path,
|
||||||
required this.lastPhoto,
|
required this.lastPhoto,
|
||||||
|
@ -37,6 +38,17 @@ class NcAlbum {
|
||||||
@override
|
@override
|
||||||
String toString() => _$toString();
|
String toString() => _$toString();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [
|
||||||
|
path,
|
||||||
|
lastPhoto,
|
||||||
|
nbItems,
|
||||||
|
location,
|
||||||
|
dateStart,
|
||||||
|
dateEnd,
|
||||||
|
collaborators,
|
||||||
|
];
|
||||||
|
|
||||||
final String path;
|
final String path;
|
||||||
|
|
||||||
/// File ID of the last photo
|
/// 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/item.dart';
|
||||||
import 'package:nc_photos/entity/album/provider.dart';
|
import 'package:nc_photos/entity/album/provider.dart';
|
||||||
import 'package:nc_photos/entity/file.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/exception_event.dart';
|
||||||
import 'package:nc_photos/use_case/list_tagged_file.dart';
|
import 'package:nc_photos/use_case/list_tagged_file.dart';
|
||||||
import 'package:nc_photos/use_case/scan_dir.dart';
|
import 'package:nc_photos/use_case/scan_dir.dart';
|
||||||
|
@ -32,8 +30,6 @@ class PopulateAlbum {
|
||||||
return _populateDirAlbum(account, album);
|
return _populateDirAlbum(account, album);
|
||||||
} else if (album.provider is AlbumTagProvider) {
|
} else if (album.provider is AlbumTagProvider) {
|
||||||
return _populateTagAlbum(account, album);
|
return _populateTagAlbum(account, album);
|
||||||
} else if (album.provider is AlbumMemoryProvider) {
|
|
||||||
return _populateMemoryAlbum(account, album);
|
|
||||||
} else {
|
} else {
|
||||||
throw ArgumentError(
|
throw ArgumentError(
|
||||||
"Unknown album provider: ${album.provider.runtimeType}");
|
"Unknown album provider: ${album.provider.runtimeType}");
|
||||||
|
@ -80,25 +76,5 @@ class PopulateAlbum {
|
||||||
return products;
|
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;
|
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.dart';
|
||||||
import 'package:nc_photos/entity/album/provider.dart';
|
import 'package:nc_photos/entity/album/provider.dart';
|
||||||
import 'package:nc_photos/widget/album_browser.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
|
/// Push the corresponding browser route for this album
|
||||||
void push(BuildContext context, Account account, Album album) {
|
void push(BuildContext context, Account account, Album album) {
|
||||||
if (album.provider is AlbumStaticProvider) {
|
if (album.provider is AlbumStaticProvider) {
|
||||||
Navigator.of(context).pushNamed(AlbumBrowser.routeName,
|
Navigator.of(context).pushNamed(AlbumBrowser.routeName,
|
||||||
arguments: AlbumBrowserArguments(account, album));
|
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/account.dart';
|
||||||
import 'package:nc_photos/app_init.dart' as app_init;
|
import 'package:nc_photos/app_init.dart' as app_init;
|
||||||
import 'package:nc_photos/app_localizations.dart';
|
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_descriptor.dart';
|
||||||
import 'package:nc_photos/entity/file_util.dart' as file_util;
|
import 'package:nc_photos/entity/file_util.dart' as file_util;
|
||||||
import 'package:nc_photos/object_extension.dart';
|
import 'package:nc_photos/object_extension.dart';
|
||||||
|
@ -49,12 +49,12 @@ class PhotoListItemBuilderResult {
|
||||||
const PhotoListItemBuilderResult(
|
const PhotoListItemBuilderResult(
|
||||||
this.backingFiles,
|
this.backingFiles,
|
||||||
this.listItems, {
|
this.listItems, {
|
||||||
this.smartAlbums = const [],
|
this.smartCollections = const [],
|
||||||
});
|
});
|
||||||
|
|
||||||
final List<FileDescriptor> backingFiles;
|
final List<FileDescriptor> backingFiles;
|
||||||
final List<SelectableItem> listItems;
|
final List<SelectableItem> listItems;
|
||||||
final List<Album> smartAlbums;
|
final List<Collection> smartCollections;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef PhotoListItemSorter = int Function(FileDescriptor, FileDescriptor);
|
typedef PhotoListItemSorter = int Function(FileDescriptor, FileDescriptor);
|
||||||
|
@ -137,7 +137,7 @@ class _PhotoListItemBuilder {
|
||||||
Account account, List<FileDescriptor> files) {
|
Account account, List<FileDescriptor> files) {
|
||||||
final today = clock.now();
|
final today = clock.now();
|
||||||
final memoryAlbumHelper = smartAlbumConfig != null
|
final memoryAlbumHelper = smartAlbumConfig != null
|
||||||
? MemoryAlbumHelper(
|
? MemoryCollectionHelper(account,
|
||||||
today: today, dayRange: smartAlbumConfig!.memoriesDayRange)
|
today: today, dayRange: smartAlbumConfig!.memoriesDayRange)
|
||||||
: null;
|
: null;
|
||||||
final listItems = <SelectableItem>[];
|
final listItems = <SelectableItem>[];
|
||||||
|
@ -155,7 +155,7 @@ class _PhotoListItemBuilder {
|
||||||
return PhotoListItemBuilderResult(
|
return PhotoListItemBuilderResult(
|
||||||
files,
|
files,
|
||||||
listItems,
|
listItems,
|
||||||
smartAlbums: smartAlbums ?? [],
|
smartCollections: smartAlbums ?? [],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
|
||||||
required this.collectionsController,
|
required this.collectionsController,
|
||||||
required Collection collection,
|
required Collection collection,
|
||||||
}) : _c = container,
|
}) : _c = container,
|
||||||
|
_isAdHocCollection = !collectionsController.stream.value.data
|
||||||
|
.any((e) => e.collection.compareIdentity(collection)),
|
||||||
super(_State.init(
|
super(_State.init(
|
||||||
collection: collection,
|
collection: collection,
|
||||||
coverUrl: _getCoverUrl(collection),
|
coverUrl: _getCoverUrl(collection),
|
||||||
|
@ -45,14 +47,16 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
|
||||||
on<_SetError>(_onSetError);
|
on<_SetError>(_onSetError);
|
||||||
on<_SetMessage>(_onSetMessage);
|
on<_SetMessage>(_onSetMessage);
|
||||||
|
|
||||||
_collectionControllerSubscription =
|
if (!_isAdHocCollection) {
|
||||||
collectionsController.stream.listen((event) {
|
_collectionControllerSubscription =
|
||||||
final c = event.data
|
collectionsController.stream.listen((event) {
|
||||||
.firstWhere((d) => state.collection.compareIdentity(d.collection));
|
final c = event.data
|
||||||
if (!identical(c, state.collection)) {
|
.firstWhere((d) => state.collection.compareIdentity(d.collection));
|
||||||
add(_UpdateCollection(c.collection));
|
if (!identical(c, state.collection)) {
|
||||||
}
|
add(_UpdateCollection(c.collection));
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
_itemsControllerSubscription = itemsController.stream.listen(
|
_itemsControllerSubscription = itemsController.stream.listen(
|
||||||
(_) {},
|
(_) {},
|
||||||
onError: (e, stackTrace) {
|
onError: (e, stackTrace) {
|
||||||
|
@ -448,6 +452,10 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
|
||||||
final CollectionsController collectionsController;
|
final CollectionsController collectionsController;
|
||||||
late final CollectionItemsController itemsController;
|
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? _collectionControllerSubscription;
|
||||||
StreamSubscription? _itemsControllerSubscription;
|
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/compute_queue.dart';
|
||||||
import 'package:nc_photos/di_container.dart';
|
import 'package:nc_photos/di_container.dart';
|
||||||
import 'package:nc_photos/download_handler.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/file_descriptor.dart';
|
||||||
import 'package:nc_photos/entity/sqlite/database.dart' as sql;
|
import 'package:nc_photos/entity/sqlite/database.dart' as sql;
|
||||||
import 'package:nc_photos/event/event.dart';
|
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/throttler.dart';
|
||||||
import 'package:nc_photos/use_case/startup_sync.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/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/add_selection_to_collection_handler.dart';
|
||||||
import 'package:nc_photos/widget/handler/archive_selection_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/double_tap_exit_handler.dart';
|
||||||
import 'package:nc_photos/widget/handler/remove_selection_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/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/page_visibility_mixin.dart';
|
||||||
import 'package:nc_photos/widget/photo_list_item.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/photo_list_util.dart' as photo_list_util;
|
||||||
import 'package:nc_photos/widget/selectable_item_stream_list_mixin.dart';
|
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/selection_app_bar.dart';
|
||||||
import 'package:nc_photos/widget/settings.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/viewer.dart';
|
||||||
import 'package:nc_photos/widget/zoom_menu_button.dart';
|
import 'package:nc_photos/widget/zoom_menu_button.dart';
|
||||||
import 'package:np_codegen/np_codegen.dart';
|
import 'package:np_codegen/np_codegen.dart';
|
||||||
|
@ -192,7 +191,7 @@ class _HomePhotosState extends State<HomePhotos>
|
||||||
_web?.buildContent(context),
|
_web?.buildContent(context),
|
||||||
if (AccountPref.of(widget.account)
|
if (AccountPref.of(widget.account)
|
||||||
.isEnableMemoryAlbumOr(true) &&
|
.isEnableMemoryAlbumOr(true) &&
|
||||||
_smartAlbums.isNotEmpty)
|
_smartCollections.isNotEmpty)
|
||||||
_buildSmartAlbumList(context),
|
_buildSmartAlbumList(context),
|
||||||
BlocBuilder<ScanAccountDirBloc, ScanAccountDirBlocState>(
|
BlocBuilder<ScanAccountDirBloc, ScanAccountDirBlocState>(
|
||||||
bloc: _bloc,
|
bloc: _bloc,
|
||||||
|
@ -352,9 +351,9 @@ class _HomePhotosState extends State<HomePhotos>
|
||||||
|
|
||||||
Widget _buildSmartAlbumList(BuildContext context) {
|
Widget _buildSmartAlbumList(BuildContext context) {
|
||||||
return SliverToBoxAdapter(
|
return SliverToBoxAdapter(
|
||||||
child: _SmartAlbumList(
|
child: _SmartCollectionList(
|
||||||
account: widget.account,
|
account: widget.account,
|
||||||
albums: _smartAlbums,
|
collections: _smartCollections,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -613,7 +612,7 @@ class _HomePhotosState extends State<HomePhotos>
|
||||||
setState(() {
|
setState(() {
|
||||||
_backingFiles = result.backingFiles;
|
_backingFiles = result.backingFiles;
|
||||||
itemStreamListItems = result.listItems;
|
itemStreamListItems = result.listItems;
|
||||||
_smartAlbums = result.smartAlbums;
|
_smartCollections = result.smartCollections;
|
||||||
|
|
||||||
if (isPostSuccess) {
|
if (isPostSuccess) {
|
||||||
_isScrollbarVisible = true;
|
_isScrollbarVisible = true;
|
||||||
|
@ -680,7 +679,7 @@ class _HomePhotosState extends State<HomePhotos>
|
||||||
final metadataTaskHeaderExtent = _web?.getHeaderHeight() ?? 0;
|
final metadataTaskHeaderExtent = _web?.getHeaderHeight() ?? 0;
|
||||||
final smartAlbumListHeight =
|
final smartAlbumListHeight =
|
||||||
AccountPref.of(widget.account).isEnableMemoryAlbumOr(true) &&
|
AccountPref.of(widget.account).isEnableMemoryAlbumOr(true) &&
|
||||||
_smartAlbums.isNotEmpty
|
_smartCollections.isNotEmpty
|
||||||
? _SmartAlbumItem.height
|
? _SmartAlbumItem.height
|
||||||
: 0;
|
: 0;
|
||||||
// scroll extent = list height - widget viewport height
|
// scroll extent = list height - widget viewport height
|
||||||
|
@ -745,7 +744,7 @@ class _HomePhotosState extends State<HomePhotos>
|
||||||
late final _queryProgressBloc = ProgressBloc();
|
late final _queryProgressBloc = ProgressBloc();
|
||||||
|
|
||||||
var _backingFiles = <FileDescriptor>[];
|
var _backingFiles = <FileDescriptor>[];
|
||||||
var _smartAlbums = <Album>[];
|
var _smartCollections = <Collection>[];
|
||||||
|
|
||||||
final _buildItemQueue =
|
final _buildItemQueue =
|
||||||
ComputeQueue<PhotoListItemBuilderArguments, PhotoListItemBuilderResult>();
|
ComputeQueue<PhotoListItemBuilderArguments, PhotoListItemBuilderResult>();
|
||||||
|
@ -1006,10 +1005,10 @@ class _MetadataTaskLoadingIcon extends AnimatedWidget {
|
||||||
Animation<double> get _progress => listenable as Animation<double>;
|
Animation<double> get _progress => listenable as Animation<double>;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SmartAlbumList extends StatelessWidget {
|
class _SmartCollectionList extends StatelessWidget {
|
||||||
const _SmartAlbumList({
|
const _SmartCollectionList({
|
||||||
required this.account,
|
required this.account,
|
||||||
required this.albums,
|
required this.collections,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -1019,19 +1018,20 @@ class _SmartAlbumList extends StatelessWidget {
|
||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
itemCount: albums.length,
|
itemCount: collections.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final a = albums[index];
|
final c = collections[index];
|
||||||
final coverFile = a.coverProvider.getCover(a);
|
|
||||||
return _SmartAlbumItem(
|
return _SmartAlbumItem(
|
||||||
account: account,
|
account: account,
|
||||||
previewUrl: coverFile == null
|
previewUrl: c.getCoverUrl(
|
||||||
? null
|
k.photoThumbSize,
|
||||||
: NetworkRectThumbnail.imageUrlForFile(account, coverFile),
|
k.photoThumbSize,
|
||||||
label: a.name,
|
isKeepAspectRatio: true,
|
||||||
|
),
|
||||||
|
label: c.name,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pushNamed(SmartAlbumBrowser.routeName,
|
Navigator.of(context).pushNamed(CollectionBrowser.routeName,
|
||||||
arguments: SmartAlbumBrowserArguments(account, a));
|
arguments: CollectionBrowserArguments(c));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -1041,7 +1041,7 @@ class _SmartAlbumList extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
final Account account;
|
final Account account;
|
||||||
final List<Album> albums;
|
final List<Collection> collections;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SmartAlbumItem extends StatelessWidget {
|
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/sharing_browser.dart';
|
||||||
import 'package:nc_photos/widget/sign_in.dart';
|
import 'package:nc_photos/widget/sign_in.dart';
|
||||||
import 'package:nc_photos/widget/slideshow_viewer.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/splash.dart';
|
||||||
import 'package:nc_photos/widget/trashbin_browser.dart';
|
import 'package:nc_photos/widget/trashbin_browser.dart';
|
||||||
import 'package:nc_photos/widget/trashbin_viewer.dart';
|
import 'package:nc_photos/widget/trashbin_viewer.dart';
|
||||||
|
@ -195,7 +194,6 @@ class _WrappedAppState extends State<_WrappedApp>
|
||||||
route ??= _handleAlbumShareOutlierBrowserRoute(settings);
|
route ??= _handleAlbumShareOutlierBrowserRoute(settings);
|
||||||
route ??= _handleAccountSettingsRoute(settings);
|
route ??= _handleAccountSettingsRoute(settings);
|
||||||
route ??= _handleShareFolderPickerRoute(settings);
|
route ??= _handleShareFolderPickerRoute(settings);
|
||||||
route ??= _handleSmartAlbumBrowserRoute(settings);
|
|
||||||
route ??= _handleEnhancedPhotoBrowserRoute(settings);
|
route ??= _handleEnhancedPhotoBrowserRoute(settings);
|
||||||
route ??= _handleLocalFileViewerRoute(settings);
|
route ??= _handleLocalFileViewerRoute(settings);
|
||||||
route ??= _handleEnhancementSettingsRoute(settings);
|
route ??= _handleEnhancementSettingsRoute(settings);
|
||||||
|
@ -455,20 +453,6 @@ class _WrappedAppState extends State<_WrappedApp>
|
||||||
return null;
|
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) {
|
Route<dynamic>? _handleEnhancedPhotoBrowserRoute(RouteSettings settings) {
|
||||||
try {
|
try {
|
||||||
if (settings.name == EnhancedPhotoBrowser.routeName &&
|
if (settings.name == EnhancedPhotoBrowser.routeName &&
|
||||||
|
|
|
@ -3,11 +3,10 @@ import 'dart:math' as math;
|
||||||
import 'package:clock/clock.dart';
|
import 'package:clock/clock.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:nc_photos/account.dart';
|
||||||
import 'package:nc_photos/date_time_extension.dart';
|
import 'package:nc_photos/date_time_extension.dart';
|
||||||
import 'package:nc_photos/entity/album.dart';
|
import 'package:nc_photos/entity/collection.dart';
|
||||||
import 'package:nc_photos/entity/album/cover_provider.dart';
|
import 'package:nc_photos/entity/collection/content_provider/memory.dart';
|
||||||
import 'package:nc_photos/entity/album/provider.dart';
|
|
||||||
import 'package:nc_photos/entity/album/sort_provider.dart';
|
|
||||||
import 'package:nc_photos/entity/file_descriptor.dart';
|
import 'package:nc_photos/entity/file_descriptor.dart';
|
||||||
import 'package:np_codegen/np_codegen.dart';
|
import 'package:np_codegen/np_codegen.dart';
|
||||||
|
|
||||||
|
@ -34,12 +33,13 @@ class DateGroupHelper {
|
||||||
DateTime? _currentDate;
|
DateTime? _currentDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build memory album from files
|
/// Build memory collection from files
|
||||||
///
|
///
|
||||||
/// Feb 29 is treated as Mar 1 on non leap years
|
/// Feb 29 is treated as Mar 1 on non leap years
|
||||||
@npLog
|
@npLog
|
||||||
class MemoryAlbumHelper {
|
class MemoryCollectionHelper {
|
||||||
MemoryAlbumHelper({
|
MemoryCollectionHelper(
|
||||||
|
this.account, {
|
||||||
DateTime? today,
|
DateTime? today,
|
||||||
required int dayRange,
|
required int dayRange,
|
||||||
}) : today = (today?.toLocal() ?? clock.now()).toMidnight(),
|
}) : 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
|
/// [nameBuilder] is a function that return the name of the album for a
|
||||||
/// particular year
|
/// particular year
|
||||||
List<Album> build(String Function(int year) nameBuilder) {
|
List<Collection> build(String Function(int year) nameBuilder) {
|
||||||
return _data.entries
|
return _data.entries
|
||||||
.sorted((a, b) => b.key.compareTo(a.key))
|
.sorted((a, b) => b.key.compareTo(a.key))
|
||||||
.map((e) => Album(
|
.map((e) => Collection(
|
||||||
name: nameBuilder(e.key),
|
name: nameBuilder(e.key),
|
||||||
provider: AlbumMemoryProvider(
|
contentProvider: CollectionMemoryProvider(
|
||||||
year: e.key, month: today.month, day: today.day),
|
account: account,
|
||||||
coverProvider:
|
year: e.key,
|
||||||
AlbumMemoryCoverProvider(coverFile: e.value.coverFile),
|
month: today.month,
|
||||||
sortProvider: const AlbumTimeSortProvider(isAscending: false),
|
day: today.day,
|
||||||
|
cover: e.value.coverFile,
|
||||||
|
),
|
||||||
))
|
))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
@ -83,9 +85,9 @@ class MemoryAlbumHelper {
|
||||||
final item = _data[year];
|
final item = _data[year];
|
||||||
final date = today.copyWith(year: year);
|
final date = today.copyWith(year: year);
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
_data[year] = _MemoryAlbumHelperItem(date, f);
|
_data[year] = _MemoryCollectionHelperItem(date, f);
|
||||||
} else {
|
} else {
|
||||||
final coverDiff = _MemoryAlbumHelperItem.getCoverDiff(date, f);
|
final coverDiff = _MemoryCollectionHelperItem.getCoverDiff(date, f);
|
||||||
if (coverDiff < item.coverDiff) {
|
if (coverDiff < item.coverDiff) {
|
||||||
item.coverFile = f;
|
item.coverFile = f;
|
||||||
item.coverDiff = coverDiff;
|
item.coverDiff = coverDiff;
|
||||||
|
@ -93,9 +95,10 @@ class MemoryAlbumHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Account account;
|
||||||
final DateTime today;
|
final DateTime today;
|
||||||
final int dayRange;
|
final int dayRange;
|
||||||
final _data = <int, _MemoryAlbumHelperItem>{};
|
final _data = <int, _MemoryCollectionHelperItem>{};
|
||||||
}
|
}
|
||||||
|
|
||||||
int getThumbSize(int zoomLevel) {
|
int getThumbSize(int zoomLevel) {
|
||||||
|
@ -115,8 +118,8 @@ int getThumbSize(int zoomLevel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MemoryAlbumHelperItem {
|
class _MemoryCollectionHelperItem {
|
||||||
_MemoryAlbumHelperItem(this.date, this.coverFile)
|
_MemoryCollectionHelperItem(this.date, this.coverFile)
|
||||||
: coverDiff = getCoverDiff(date, coverFile);
|
: coverDiff = getCoverDiff(date, coverFile);
|
||||||
|
|
||||||
static Duration getCoverDiff(DateTime date, FileDescriptor f) =>
|
static Duration getCoverDiff(DateTime date, FileDescriptor f) =>
|
||||||
|
|
|
@ -6,9 +6,9 @@ part of 'photo_list_util.dart';
|
||||||
// NpLogGenerator
|
// NpLogGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
extension _$MemoryAlbumHelperNpLog on MemoryAlbumHelper {
|
extension _$MemoryCollectionHelperNpLog on MemoryCollectionHelper {
|
||||||
// ignore: unused_element
|
// ignore: unused_element
|
||||||
Logger get _log => log;
|
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/collection.dart';
|
||||||
import 'package:nc_photos/entity/album/cover_provider.dart';
|
import 'package:nc_photos/entity/collection/content_provider/memory.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/widget/photo_list_util.dart';
|
import 'package:nc_photos/widget/photo_list_util.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
import '../test_util.dart' as util;
|
import '../test_util.dart' as util;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group("MemoryAlbumHelper", () {
|
group("MemoryCollectionHelper", () {
|
||||||
test("same year", _sameYear);
|
test("same year", _sameYear);
|
||||||
test("next year", _nextYear);
|
test("next year", _nextYear);
|
||||||
group("prev year", () {
|
group("prev year", () {
|
||||||
|
@ -54,8 +51,9 @@ void main() {
|
||||||
/// File: 2021-02-01
|
/// File: 2021-02-01
|
||||||
/// Expect: empty
|
/// Expect: empty
|
||||||
void _sameYear() {
|
void _sameYear() {
|
||||||
|
final account = util.buildAccount();
|
||||||
final today = DateTime(2021, 2, 3);
|
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(
|
final file = util.buildJpegFile(
|
||||||
path: "", fileId: 0, lastModified: DateTime.utc(2021, 2, 3));
|
path: "", fileId: 0, lastModified: DateTime.utc(2021, 2, 3));
|
||||||
obj.addFile(file);
|
obj.addFile(file);
|
||||||
|
@ -69,8 +67,9 @@ void _sameYear() {
|
||||||
/// File: 2022-02-03
|
/// File: 2022-02-03
|
||||||
/// Expect: empty
|
/// Expect: empty
|
||||||
void _nextYear() {
|
void _nextYear() {
|
||||||
|
final account = util.buildAccount();
|
||||||
final today = DateTime(2021, 2, 3);
|
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(
|
final file = util.buildJpegFile(
|
||||||
path: "", fileId: 0, lastModified: DateTime.utc(2022, 2, 3));
|
path: "", fileId: 0, lastModified: DateTime.utc(2022, 2, 3));
|
||||||
obj.addFile(file);
|
obj.addFile(file);
|
||||||
|
@ -83,24 +82,19 @@ void _nextYear() {
|
||||||
/// File: 2020-02-03
|
/// File: 2020-02-03
|
||||||
/// Expect: [2020]
|
/// Expect: [2020]
|
||||||
void _prevYear() {
|
void _prevYear() {
|
||||||
|
final account = util.buildAccount();
|
||||||
final today = DateTime(2021, 2, 3);
|
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(
|
final file = util.buildJpegFile(
|
||||||
path: "", fileId: 0, lastModified: DateTime.utc(2020, 2, 3));
|
path: "", fileId: 0, lastModified: DateTime.utc(2020, 2, 3));
|
||||||
obj.addFile(file);
|
obj.addFile(file);
|
||||||
expect(
|
expect(
|
||||||
obj
|
obj.build(_nameBuilder).toList(),
|
||||||
.build(_nameBuilder)
|
|
||||||
.map((a) => a.copyWith(lastUpdated: OrNull(DateTime(2021))))
|
|
||||||
.toList(),
|
|
||||||
[
|
[
|
||||||
Album(
|
Collection(
|
||||||
name: "2020",
|
name: "2020",
|
||||||
provider:
|
contentProvider: CollectionMemoryProvider(
|
||||||
AlbumMemoryProvider(year: 2020, month: today.month, day: today.day),
|
account: account, year: 2020, month: 2, day: 3, cover: file),
|
||||||
coverProvider: AlbumMemoryCoverProvider(coverFile: file),
|
|
||||||
sortProvider: const AlbumTimeSortProvider(isAscending: false),
|
|
||||||
lastUpdated: DateTime(2021),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -112,8 +106,9 @@ void _prevYear() {
|
||||||
/// File: 2020-01-31
|
/// File: 2020-01-31
|
||||||
/// Expect: empty
|
/// Expect: empty
|
||||||
void _prevYear3DaysBefore() {
|
void _prevYear3DaysBefore() {
|
||||||
|
final account = util.buildAccount();
|
||||||
final today = DateTime(2021, 2, 3);
|
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(
|
final file = util.buildJpegFile(
|
||||||
path: "", fileId: 0, lastModified: DateTime.utc(2020, 1, 31));
|
path: "", fileId: 0, lastModified: DateTime.utc(2020, 1, 31));
|
||||||
obj.addFile(file);
|
obj.addFile(file);
|
||||||
|
@ -126,24 +121,19 @@ void _prevYear3DaysBefore() {
|
||||||
/// File: 2020-02-01
|
/// File: 2020-02-01
|
||||||
/// Expect: [2020]
|
/// Expect: [2020]
|
||||||
void _prevYear2DaysBefore() {
|
void _prevYear2DaysBefore() {
|
||||||
|
final account = util.buildAccount();
|
||||||
final today = DateTime(2021, 2, 3);
|
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(
|
final file = util.buildJpegFile(
|
||||||
path: "", fileId: 0, lastModified: DateTime.utc(2020, 2, 1));
|
path: "", fileId: 0, lastModified: DateTime.utc(2020, 2, 1));
|
||||||
obj.addFile(file);
|
obj.addFile(file);
|
||||||
expect(
|
expect(
|
||||||
obj
|
obj.build(_nameBuilder).toList(),
|
||||||
.build(_nameBuilder)
|
|
||||||
.map((a) => a.copyWith(lastUpdated: OrNull(DateTime(2021))))
|
|
||||||
.toList(),
|
|
||||||
[
|
[
|
||||||
Album(
|
Collection(
|
||||||
name: "2020",
|
name: "2020",
|
||||||
provider:
|
contentProvider: CollectionMemoryProvider(
|
||||||
AlbumMemoryProvider(year: 2020, month: today.month, day: today.day),
|
account: account, year: 2020, month: 2, day: 3, cover: file),
|
||||||
coverProvider: AlbumMemoryCoverProvider(coverFile: file),
|
|
||||||
sortProvider: const AlbumTimeSortProvider(isAscending: false),
|
|
||||||
lastUpdated: DateTime(2021),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -155,8 +145,9 @@ void _prevYear2DaysBefore() {
|
||||||
/// File: 2020-02-06
|
/// File: 2020-02-06
|
||||||
/// Expect: empty
|
/// Expect: empty
|
||||||
void _prevYear3DaysAfter() {
|
void _prevYear3DaysAfter() {
|
||||||
|
final account = util.buildAccount();
|
||||||
final today = DateTime(2021, 2, 3);
|
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(
|
final file = util.buildJpegFile(
|
||||||
path: "", fileId: 0, lastModified: DateTime.utc(2020, 2, 6));
|
path: "", fileId: 0, lastModified: DateTime.utc(2020, 2, 6));
|
||||||
obj.addFile(file);
|
obj.addFile(file);
|
||||||
|
@ -169,24 +160,19 @@ void _prevYear3DaysAfter() {
|
||||||
/// File: 2020-02-05
|
/// File: 2020-02-05
|
||||||
/// Expect: [2020]
|
/// Expect: [2020]
|
||||||
void _prevYear2DaysAfter() {
|
void _prevYear2DaysAfter() {
|
||||||
|
final account = util.buildAccount();
|
||||||
final today = DateTime(2021, 2, 3);
|
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(
|
final file = util.buildJpegFile(
|
||||||
path: "", fileId: 0, lastModified: DateTime.utc(2020, 2, 5));
|
path: "", fileId: 0, lastModified: DateTime.utc(2020, 2, 5));
|
||||||
obj.addFile(file);
|
obj.addFile(file);
|
||||||
expect(
|
expect(
|
||||||
obj
|
obj.build(_nameBuilder).toList(),
|
||||||
.build(_nameBuilder)
|
|
||||||
.map((a) => a.copyWith(lastUpdated: OrNull(DateTime(2021))))
|
|
||||||
.toList(),
|
|
||||||
[
|
[
|
||||||
Album(
|
Collection(
|
||||||
name: "2020",
|
name: "2020",
|
||||||
provider:
|
contentProvider: CollectionMemoryProvider(
|
||||||
AlbumMemoryProvider(year: 2020, month: today.month, day: today.day),
|
account: account, year: 2020, month: 2, day: 3, cover: file),
|
||||||
coverProvider: AlbumMemoryCoverProvider(coverFile: file),
|
|
||||||
sortProvider: const AlbumTimeSortProvider(isAscending: false),
|
|
||||||
lastUpdated: DateTime(2021),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -198,8 +184,9 @@ void _prevYear2DaysAfter() {
|
||||||
/// File: 2019-02-26
|
/// File: 2019-02-26
|
||||||
/// Expect: empty
|
/// Expect: empty
|
||||||
void _onFeb29AddFeb26() {
|
void _onFeb29AddFeb26() {
|
||||||
|
final account = util.buildAccount();
|
||||||
final today = DateTime(2020, 2, 29);
|
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(
|
final file = util.buildJpegFile(
|
||||||
path: "", fileId: 0, lastModified: DateTime.utc(2019, 2, 26));
|
path: "", fileId: 0, lastModified: DateTime.utc(2019, 2, 26));
|
||||||
obj.addFile(file);
|
obj.addFile(file);
|
||||||
|
@ -212,24 +199,19 @@ void _onFeb29AddFeb26() {
|
||||||
/// File: 2019-02-27
|
/// File: 2019-02-27
|
||||||
/// Expect: [2019]
|
/// Expect: [2019]
|
||||||
void _onFeb29AddFeb27() {
|
void _onFeb29AddFeb27() {
|
||||||
|
final account = util.buildAccount();
|
||||||
final today = DateTime(2020, 2, 29);
|
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(
|
final file = util.buildJpegFile(
|
||||||
path: "", fileId: 0, lastModified: DateTime.utc(2019, 2, 27));
|
path: "", fileId: 0, lastModified: DateTime.utc(2019, 2, 27));
|
||||||
obj.addFile(file);
|
obj.addFile(file);
|
||||||
expect(
|
expect(
|
||||||
obj
|
obj.build(_nameBuilder).toList(),
|
||||||
.build(_nameBuilder)
|
|
||||||
.map((a) => a.copyWith(lastUpdated: OrNull(DateTime(2021))))
|
|
||||||
.toList(),
|
|
||||||
[
|
[
|
||||||
Album(
|
Collection(
|
||||||
name: "2019",
|
name: "2019",
|
||||||
provider:
|
contentProvider: CollectionMemoryProvider(
|
||||||
AlbumMemoryProvider(year: 2019, month: today.month, day: today.day),
|
account: account, year: 2019, month: 2, day: 29, cover: file),
|
||||||
coverProvider: AlbumMemoryCoverProvider(coverFile: file),
|
|
||||||
sortProvider: const AlbumTimeSortProvider(isAscending: false),
|
|
||||||
lastUpdated: DateTime(2021),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -241,8 +223,9 @@ void _onFeb29AddFeb27() {
|
||||||
/// File: 2019-03-04
|
/// File: 2019-03-04
|
||||||
/// Expect: empty
|
/// Expect: empty
|
||||||
void _onFeb29AddMar4() {
|
void _onFeb29AddMar4() {
|
||||||
|
final account = util.buildAccount();
|
||||||
final today = DateTime(2020, 2, 29);
|
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(
|
final file = util.buildJpegFile(
|
||||||
path: "", fileId: 0, lastModified: DateTime.utc(2019, 3, 4));
|
path: "", fileId: 0, lastModified: DateTime.utc(2019, 3, 4));
|
||||||
obj.addFile(file);
|
obj.addFile(file);
|
||||||
|
@ -255,24 +238,19 @@ void _onFeb29AddMar4() {
|
||||||
/// File: 2019-03-03
|
/// File: 2019-03-03
|
||||||
/// Expect: [2019]
|
/// Expect: [2019]
|
||||||
void _onFeb29AddMar3() {
|
void _onFeb29AddMar3() {
|
||||||
|
final account = util.buildAccount();
|
||||||
final today = DateTime(2020, 2, 29);
|
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(
|
final file = util.buildJpegFile(
|
||||||
path: "", fileId: 0, lastModified: DateTime.utc(2019, 3, 3));
|
path: "", fileId: 0, lastModified: DateTime.utc(2019, 3, 3));
|
||||||
obj.addFile(file);
|
obj.addFile(file);
|
||||||
expect(
|
expect(
|
||||||
obj
|
obj.build(_nameBuilder).toList(),
|
||||||
.build(_nameBuilder)
|
|
||||||
.map((a) => a.copyWith(lastUpdated: OrNull(DateTime(2021))))
|
|
||||||
.toList(),
|
|
||||||
[
|
[
|
||||||
Album(
|
Collection(
|
||||||
name: "2019",
|
name: "2019",
|
||||||
provider:
|
contentProvider: CollectionMemoryProvider(
|
||||||
AlbumMemoryProvider(year: 2019, month: today.month, day: today.day),
|
account: account, year: 2019, month: 2, day: 29, cover: file),
|
||||||
coverProvider: AlbumMemoryCoverProvider(coverFile: file),
|
|
||||||
sortProvider: const AlbumTimeSortProvider(isAscending: false),
|
|
||||||
lastUpdated: DateTime(2021),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -284,8 +262,9 @@ void _onFeb29AddMar3() {
|
||||||
/// File: 2016-03-03
|
/// File: 2016-03-03
|
||||||
/// Expect: empty
|
/// Expect: empty
|
||||||
void _onFeb29AddMar3LeapYear() {
|
void _onFeb29AddMar3LeapYear() {
|
||||||
|
final account = util.buildAccount();
|
||||||
final today = DateTime(2020, 2, 29);
|
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(
|
final file = util.buildJpegFile(
|
||||||
path: "", fileId: 0, lastModified: DateTime.utc(2016, 3, 3));
|
path: "", fileId: 0, lastModified: DateTime.utc(2016, 3, 3));
|
||||||
obj.addFile(file);
|
obj.addFile(file);
|
||||||
|
@ -298,24 +277,19 @@ void _onFeb29AddMar3LeapYear() {
|
||||||
/// File: 2016-03-02
|
/// File: 2016-03-02
|
||||||
/// Expect: [2016]
|
/// Expect: [2016]
|
||||||
void _onFeb29AddMar2LeapYear() {
|
void _onFeb29AddMar2LeapYear() {
|
||||||
|
final account = util.buildAccount();
|
||||||
final today = DateTime(2020, 2, 29);
|
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(
|
final file = util.buildJpegFile(
|
||||||
path: "", fileId: 0, lastModified: DateTime.utc(2016, 3, 2));
|
path: "", fileId: 0, lastModified: DateTime.utc(2016, 3, 2));
|
||||||
obj.addFile(file);
|
obj.addFile(file);
|
||||||
expect(
|
expect(
|
||||||
obj
|
obj.build(_nameBuilder).toList(),
|
||||||
.build(_nameBuilder)
|
|
||||||
.map((a) => a.copyWith(lastUpdated: OrNull(DateTime(2021))))
|
|
||||||
.toList(),
|
|
||||||
[
|
[
|
||||||
Album(
|
Collection(
|
||||||
name: "2016",
|
name: "2016",
|
||||||
provider:
|
contentProvider: CollectionMemoryProvider(
|
||||||
AlbumMemoryProvider(year: 2016, month: today.month, day: today.day),
|
account: account, year: 2016, month: 2, day: 29, cover: file),
|
||||||
coverProvider: AlbumMemoryCoverProvider(coverFile: file),
|
|
||||||
sortProvider: const AlbumTimeSortProvider(isAscending: false),
|
|
||||||
lastUpdated: DateTime(2021),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -327,8 +301,9 @@ void _onFeb29AddMar2LeapYear() {
|
||||||
/// File: 2019-12-31
|
/// File: 2019-12-31
|
||||||
/// Expect: empty
|
/// Expect: empty
|
||||||
void _onJan1AddDec31() {
|
void _onJan1AddDec31() {
|
||||||
|
final account = util.buildAccount();
|
||||||
final today = DateTime(2020, 1, 1);
|
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(
|
final file = util.buildJpegFile(
|
||||||
path: "", fileId: 0, lastModified: DateTime.utc(2019, 12, 31));
|
path: "", fileId: 0, lastModified: DateTime.utc(2019, 12, 31));
|
||||||
obj.addFile(file);
|
obj.addFile(file);
|
||||||
|
@ -341,24 +316,19 @@ void _onJan1AddDec31() {
|
||||||
/// File: 2018-12-31
|
/// File: 2018-12-31
|
||||||
/// Expect: [2019]
|
/// Expect: [2019]
|
||||||
void _onJan1AddDec31PrevYear() {
|
void _onJan1AddDec31PrevYear() {
|
||||||
|
final account = util.buildAccount();
|
||||||
final today = DateTime(2020, 1, 1);
|
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(
|
final file = util.buildJpegFile(
|
||||||
path: "", fileId: 0, lastModified: DateTime.utc(2018, 12, 31));
|
path: "", fileId: 0, lastModified: DateTime.utc(2018, 12, 31));
|
||||||
obj.addFile(file);
|
obj.addFile(file);
|
||||||
expect(
|
expect(
|
||||||
obj
|
obj.build(_nameBuilder).toList(),
|
||||||
.build(_nameBuilder)
|
|
||||||
.map((a) => a.copyWith(lastUpdated: OrNull(DateTime(2021))))
|
|
||||||
.toList(),
|
|
||||||
[
|
[
|
||||||
Album(
|
Collection(
|
||||||
name: "2019",
|
name: "2019",
|
||||||
provider:
|
contentProvider: CollectionMemoryProvider(
|
||||||
AlbumMemoryProvider(year: 2019, month: today.month, day: today.day),
|
account: account, year: 2019, month: 1, day: 1, cover: file),
|
||||||
coverProvider: AlbumMemoryCoverProvider(coverFile: file),
|
|
||||||
sortProvider: const AlbumTimeSortProvider(isAscending: false),
|
|
||||||
lastUpdated: DateTime(2021),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -370,24 +340,19 @@ void _onJan1AddDec31PrevYear() {
|
||||||
/// File: 2020-01-01
|
/// File: 2020-01-01
|
||||||
/// Expect: [2019]
|
/// Expect: [2019]
|
||||||
void _onDec31AddJan1() {
|
void _onDec31AddJan1() {
|
||||||
|
final account = util.buildAccount();
|
||||||
final today = DateTime(2020, 12, 31);
|
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(
|
final file = util.buildJpegFile(
|
||||||
path: "", fileId: 0, lastModified: DateTime.utc(2020, 1, 1));
|
path: "", fileId: 0, lastModified: DateTime.utc(2020, 1, 1));
|
||||||
obj.addFile(file);
|
obj.addFile(file);
|
||||||
expect(
|
expect(
|
||||||
obj
|
obj.build(_nameBuilder).toList(),
|
||||||
.build(_nameBuilder)
|
|
||||||
.map((a) => a.copyWith(lastUpdated: OrNull(DateTime(2021))))
|
|
||||||
.toList(),
|
|
||||||
[
|
[
|
||||||
Album(
|
Collection(
|
||||||
name: "2019",
|
name: "2019",
|
||||||
provider:
|
contentProvider: CollectionMemoryProvider(
|
||||||
AlbumMemoryProvider(year: 2019, month: today.month, day: today.day),
|
account: account, year: 2019, month: 12, day: 31, cover: file),
|
||||||
coverProvider: AlbumMemoryCoverProvider(coverFile: file),
|
|
||||||
sortProvider: const AlbumTimeSortProvider(isAscending: false),
|
|
||||||
lastUpdated: DateTime(2021),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -399,24 +364,19 @@ void _onDec31AddJan1() {
|
||||||
/// File: 2021-05-15
|
/// File: 2021-05-15
|
||||||
/// Expect: [2022]
|
/// Expect: [2022]
|
||||||
void _onMay15AddMay15Range0() {
|
void _onMay15AddMay15Range0() {
|
||||||
|
final account = util.buildAccount();
|
||||||
final today = DateTime(2022, 5, 15);
|
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(
|
final file = util.buildJpegFile(
|
||||||
path: "", fileId: 0, lastModified: DateTime.utc(2021, 5, 15));
|
path: "", fileId: 0, lastModified: DateTime.utc(2021, 5, 15));
|
||||||
obj.addFile(file);
|
obj.addFile(file);
|
||||||
expect(
|
expect(
|
||||||
obj
|
obj.build(_nameBuilder).toList(),
|
||||||
.build(_nameBuilder)
|
|
||||||
.map((a) => a.copyWith(lastUpdated: OrNull(DateTime(2021))))
|
|
||||||
.toList(),
|
|
||||||
[
|
[
|
||||||
Album(
|
Collection(
|
||||||
name: "2021",
|
name: "2021",
|
||||||
provider:
|
contentProvider: CollectionMemoryProvider(
|
||||||
AlbumMemoryProvider(year: 2021, month: today.month, day: today.day),
|
account: account, year: 2021, month: 5, day: 15, cover: file),
|
||||||
coverProvider: AlbumMemoryCoverProvider(coverFile: file),
|
|
||||||
sortProvider: const AlbumTimeSortProvider(isAscending: false),
|
|
||||||
lastUpdated: DateTime(2021),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -428,8 +388,9 @@ void _onMay15AddMay15Range0() {
|
||||||
/// File: 2021-05-16
|
/// File: 2021-05-16
|
||||||
/// Expect: []
|
/// Expect: []
|
||||||
void _onMay15AddMay16Range0() {
|
void _onMay15AddMay16Range0() {
|
||||||
|
final account = util.buildAccount();
|
||||||
final today = DateTime(2022, 5, 15);
|
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(
|
final file = util.buildJpegFile(
|
||||||
path: "", fileId: 0, lastModified: DateTime.utc(2021, 5, 16));
|
path: "", fileId: 0, lastModified: DateTime.utc(2021, 5, 16));
|
||||||
obj.addFile(file);
|
obj.addFile(file);
|
||||||
|
@ -442,8 +403,9 @@ void _onMay15AddMay16Range0() {
|
||||||
/// File: 2021-05-16
|
/// File: 2021-05-16
|
||||||
/// Expect: []
|
/// Expect: []
|
||||||
void _onMay15AddMay16RangeNegative() {
|
void _onMay15AddMay16RangeNegative() {
|
||||||
|
final account = util.buildAccount();
|
||||||
final today = DateTime(2022, 5, 15);
|
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(
|
final file = util.buildJpegFile(
|
||||||
path: "", fileId: 0, lastModified: DateTime.utc(2021, 5, 16));
|
path: "", fileId: 0, lastModified: DateTime.utc(2021, 5, 16));
|
||||||
obj.addFile(file);
|
obj.addFile(file);
|
||||||
|
|
Loading…
Reference in a new issue