Abstract album content provider

This commit is contained in:
Ming Ming 2021-06-25 00:26:56 +08:00
parent d4785b1f74
commit 0e7f2462b6
13 changed files with 452 additions and 149 deletions

View file

@ -138,6 +138,7 @@ class AppDbAlbumEntry {
Album.fromJson(
json["album"].cast<String, dynamic>(),
upgraderV1: AlbumUpgraderV1(),
upgraderV2: AlbumUpgraderV2(),
),
);
}

View file

@ -1,4 +1,3 @@
import 'dart:collection';
import 'dart:convert';
import 'dart:math';
@ -8,6 +7,7 @@ import 'package:idb_sqflite/idb_sqflite.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/account.dart';
import 'package:nc_photos/app_db.dart';
import 'package:nc_photos/entity/album/provider.dart';
import 'package:nc_photos/entity/album/upgrader.dart';
import 'package:nc_photos/entity/file.dart';
import 'package:nc_photos/entity/file/data_source.dart';
@ -126,15 +126,15 @@ class Album with EquatableMixin {
Album({
DateTime lastUpdated,
@required String name,
@required List<AlbumItem> items,
@required this.provider,
this.albumFile,
}) : this.lastUpdated = (lastUpdated ?? DateTime.now()).toUtc(),
this.name = name ?? "",
this.items = UnmodifiableListView(items);
this.name = name ?? "";
factory Album.fromJson(
Map<String, dynamic> json, {
AlbumUpgraderV1 upgraderV1,
AlbumUpgraderV2 upgraderV2,
}) {
final jsonVersion = json["version"];
if (jsonVersion < 2) {
@ -144,14 +144,20 @@ class Album with EquatableMixin {
return null;
}
}
if (jsonVersion < 3) {
json = upgraderV2?.call(json);
if (json == null) {
_log.info("[fromJson] Version $jsonVersion not compatible");
return null;
}
}
return Album(
lastUpdated: json["lastUpdated"] == null
? null
: DateTime.parse(json["lastUpdated"]),
name: json["name"],
items: (json["items"] as List)
.map((e) => AlbumItem.fromJson(e.cast<String, dynamic>()))
.toList(),
provider:
AlbumProvider.fromJson(json["provider"].cast<String, dynamic>()),
albumFile: json["albumFile"] == null
? null
: File.fromJson(json["albumFile"].cast<String, dynamic>()),
@ -160,12 +166,10 @@ class Album with EquatableMixin {
@override
toString({bool isDeep = false}) {
final itemsStr =
isDeep ? items.toReadableString() : "List {length: ${items.length}}";
return "$runtimeType {"
"lastUpdated: $lastUpdated, "
"name: $name, "
"items: $itemsStr, "
"provider: ${provider.toString(isDeep: isDeep)}, "
"albumFile: $albumFile, "
"}";
}
@ -178,13 +182,13 @@ class Album with EquatableMixin {
Album copyWith({
DateTime lastUpdated,
String name,
List<AlbumItem> items,
AlbumProvider provider,
File albumFile,
}) {
return Album(
lastUpdated: lastUpdated,
name: name ?? this.name,
items: items ?? this.items,
provider: provider ?? this.provider,
albumFile: albumFile ?? this.albumFile,
);
}
@ -194,7 +198,7 @@ class Album with EquatableMixin {
"version": version,
"lastUpdated": lastUpdated.toIso8601String(),
"name": name,
"items": items.map((e) => e.toJson()).toList(),
"provider": provider.toJson(),
// ignore albumFile
};
}
@ -204,7 +208,7 @@ class Album with EquatableMixin {
"version": version,
"lastUpdated": lastUpdated.toIso8601String(),
"name": name,
"items": items.map((e) => e.toJson()).toList(),
"provider": provider.toJson(),
if (albumFile != null) "albumFile": albumFile.toJson(),
};
}
@ -213,15 +217,14 @@ class Album with EquatableMixin {
get props => [
lastUpdated,
name,
items,
provider,
albumFile,
];
final DateTime lastUpdated;
final String name;
/// Immutable list of items. Modifying the list will result in an error
final List<AlbumItem> items;
final AlbumProvider provider;
/// How is this album stored on server
///
@ -229,7 +232,7 @@ class Album with EquatableMixin {
final File albumFile;
/// versioning of this class, use to upgrade old persisted album
static const version = 2;
static const version = 3;
}
class AlbumRepo {
@ -281,6 +284,7 @@ class AlbumRemoteDataSource implements AlbumDataSource {
return Album.fromJson(
jsonDecode(utf8.decode(data)),
upgraderV1: AlbumUpgraderV1(),
upgraderV2: AlbumUpgraderV2(),
).copyWith(albumFile: albumFile);
} catch (e, stacktrace) {
dynamic d = data;
@ -343,11 +347,19 @@ class AlbumAppDbDataSource implements AlbumDataSource {
if (results?.isNotEmpty == true) {
final entries = results
.map((e) => AppDbAlbumEntry.fromJson(e.cast<String, dynamic>()));
final items = entries.map((e) {
_log.info("[get] ${e.path}[${e.index}]");
return e.album.items;
}).reduce((value, element) => value + element);
return entries.first.album.copyWith(items: items);
if (entries.length > 1) {
final items = entries.map((e) {
_log.info("[get] ${e.path}[${e.index}]");
return AlbumStaticProvider.of(e.album).items;
}).reduce((value, element) => value + element);
return entries.first.album.copyWith(
provider: AlbumStaticProvider(
items: items,
),
);
} else {
return entries.first.album;
}
} else {
throw CacheNotFoundException("No entry: $path");
}
@ -461,27 +473,37 @@ Future<void> _cacheAlbum(
final range = KeyRange.bound([path, 0], [path, int_util.int32Max]);
// count number of entries for this album
final count = await index.count(range);
int newCount = 0;
var albumItemLists =
partition(album.items, AppDbAlbumEntry.maxDataSize).toList();
if (albumItemLists.isEmpty) {
albumItemLists = [<AlbumItem>[]];
// cut large album into smaller pieces, needed to workaround Android DB
// limitation
final entries = <AppDbAlbumEntry>[];
if (album.provider is AlbumStaticProvider) {
var albumItemLists = partition(
AlbumStaticProvider.of(album).items, AppDbAlbumEntry.maxDataSize)
.toList();
if (albumItemLists.isEmpty) {
albumItemLists = [<AlbumItem>[]];
}
entries.addAll(albumItemLists.withIndex().map((pair) => AppDbAlbumEntry(
path,
pair.item1,
album.copyWith(
provider: AlbumStaticProvider(items: pair.item2),
))));
} else {
entries.add(AppDbAlbumEntry(path, 0, album));
}
for (final pair in albumItemLists.withIndex()) {
_log.info(
"[_cacheAlbum] Caching $path[${pair.item1}], length: ${pair.item2.length}");
await store.put(
AppDbAlbumEntry(path, pair.item1, album.copyWith(items: pair.item2))
.toJson(),
AppDbAlbumEntry.toPrimaryKey(account, album.albumFile, pair.item1),
);
++newCount;
for (final e in entries) {
_log.info("[_cacheAlbum] Caching ${e.path}[${e.index}]");
await store.put(e.toJson(),
AppDbAlbumEntry.toPrimaryKey(account, e.album.albumFile, e.index));
}
if (count > newCount) {
if (count > entries.length) {
// index is 0-based
final rmRange = KeyRange.bound([path, newCount], [path, int_util.int32Max]);
final rmRange =
KeyRange.bound([path, entries.length], [path, int_util.int32Max]);
final rmKeys = await index
.openKeyCursor(range: rmRange, autoAdvance: true)
.map((cursor) => cursor.primaryKey)

View file

@ -0,0 +1,85 @@
import 'dart:collection';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/entity/album.dart';
import 'package:nc_photos/iterable_extension.dart';
abstract class AlbumProvider with EquatableMixin {
const AlbumProvider();
factory AlbumProvider.fromJson(Map<String, dynamic> json) {
final type = json["type"];
final content = json["content"];
switch (type) {
case AlbumStaticProvider._type:
return AlbumStaticProvider.fromJson(content.cast<String, dynamic>());
default:
_log.shout("[fromJson] Unknown type: $type");
throw ArgumentError.value(type, "type");
}
}
Map<String, dynamic> toJson() {
String getType() {
if (this is AlbumStaticProvider) {
return AlbumStaticProvider._type;
} else {
throw StateError("Unknwon subtype");
}
}
return {
"type": getType(),
"content": _toContentJson(),
};
}
@override
toString({bool isDeep = false});
Map<String, dynamic> _toContentJson();
static final _log = Logger("entity.album.provider.AlbumProvider");
}
class AlbumStaticProvider extends AlbumProvider {
AlbumStaticProvider({
@required List<AlbumItem> items,
}) : this.items = UnmodifiableListView(items);
factory AlbumStaticProvider.fromJson(Map<String, dynamic> json) {
return AlbumStaticProvider(
items: (json["items"] as List)
.map((e) => AlbumItem.fromJson(e.cast<String, dynamic>()))
.toList(),
);
}
@override
toString({bool isDeep = false}) {
final itemsStr =
isDeep ? items.toReadableString() : "List {length: ${items.length}}";
return "$runtimeType {"
"items: $itemsStr, "
"}";
}
@override
get props => [
items,
];
@override
_toContentJson() {
return {
"items": items.map((e) => e.toJson()).toList(),
};
}
/// Immutable list of items. Modifying the list will result in an error
final List<AlbumItem> items;
static const _type = "static";
}

View file

@ -23,3 +23,29 @@ class AlbumUpgraderV1 implements AlbumUpgrader {
static final _log = Logger("entity.album.upgrader.AlbumUpgraderV1");
}
/// Upgrade v2 Album to v3
class AlbumUpgraderV2 implements AlbumUpgrader {
AlbumUpgraderV2({
this.logFilePath,
});
Map<String, dynamic> call(Map<String, dynamic> json) {
// move v2 items to v3 provider
_log.fine("[call] Upgrade v2 Album for file: $logFilePath");
final result = Map<String, dynamic>.from(json);
result["provider"] = <String, dynamic>{
"type": "static",
"content": <String, dynamic>{
"items": result["items"],
}
};
result.remove("items");
return result;
}
/// File path for logging only
final String logFilePath;
static final _log = Logger("entity.album.upgrader.AlbumUpgraderV2");
}

View file

@ -3,6 +3,7 @@ import 'package:kiwi/kiwi.dart';
import 'package:logging/logging.dart';
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/entity/file.dart';
import 'package:nc_photos/event/event.dart';
import 'package:nc_photos/use_case/list_album.dart';
@ -23,18 +24,27 @@ class Remove {
Future<void> _cleanUpAlbums(Account account, File file) async {
final albums = await ListAlbum(fileRepo, albumRepo)(account);
for (final a in albums) {
// clean up only make sense for static albums
for (final a
in albums.where((element) => element.provider is AlbumStaticProvider)) {
try {
if (a.items.any((element) =>
final provider = AlbumStaticProvider.of(a);
if (provider.items.any((element) =>
element is AlbumFileItem && element.file.path == file.path)) {
final newItems = a.items.where((element) {
final newItems = provider.items.where((element) {
if (element is AlbumFileItem) {
return element.file.path != file.path;
} else {
return true;
}
}).toList();
await UpdateAlbum(albumRepo)(account, a.copyWith(items: newItems));
await UpdateAlbum(albumRepo)(
account,
a.copyWith(
provider: AlbumStaticProvider(
items: newItems,
),
));
}
} catch (e, stacktrace) {
_log.shout(

View file

@ -4,18 +4,24 @@ import 'package:logging/logging.dart';
import 'package:nc_photos/account.dart';
import 'package:nc_photos/app_db.dart';
import 'package:nc_photos/entity/album.dart';
import 'package:nc_photos/entity/album/provider.dart';
import 'package:nc_photos/entity/file_util.dart' as file_util;
/// Resync files inside an album with the file db
class ResyncAlbum {
Future<Album> call(Account account, Album album) async {
if (album.provider is! AlbumStaticProvider) {
_log.warning(
"[call] Resync only make sense for static albums: ${album.name}");
return album;
}
return await AppDb.use((db) async {
final transaction =
db.transaction(AppDb.fileDbStoreName, idbModeReadWrite);
final store = transaction.objectStore(AppDb.fileDbStoreName);
final index = store.index(AppDbFileDbEntry.indexName);
final newItems = <AlbumItem>[];
for (final item in album.items) {
for (final item in AlbumStaticProvider.of(album).items) {
if (item is AlbumFileItem) {
try {
newItems.add(await _syncOne(account, item, store, index));
@ -30,7 +36,7 @@ class ResyncAlbum {
newItems.add(item);
}
}
return album.copyWith(items: newItems);
return album.copyWith(provider: AlbumStaticProvider(items: newItems));
});
}

View file

@ -7,6 +7,7 @@ import 'package:logging/logging.dart';
import 'package:nc_photos/account.dart';
import 'package:nc_photos/bloc/list_album.dart';
import 'package:nc_photos/entity/album.dart';
import 'package:nc_photos/entity/album/provider.dart';
import 'package:nc_photos/exception_util.dart' as exception_util;
import 'package:nc_photos/k.dart' as k;
import 'package:nc_photos/snack_bar_manager.dart';
@ -139,7 +140,8 @@ class _AlbumPickerDialogState extends State<AlbumPickerDialog> {
void _transformItems(List<Album> albums) {
_items.clear();
_items.addAll(albums);
_items.addAll(
albums.where((element) => element.provider is AlbumStaticProvider));
}
void _reqQuery() {

View file

@ -8,6 +8,7 @@ import 'package:nc_photos/account.dart';
import 'package:nc_photos/api/api.dart';
import 'package:nc_photos/api/api_util.dart' as api_util;
import 'package:nc_photos/entity/album.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/exception_util.dart' as exception_util;
@ -77,6 +78,7 @@ class _AlbumViewerState extends State<AlbumViewer>
get itemStreamListCellSize => _thumbSize;
void _initAlbum() {
assert(widget.album.provider is AlbumStaticProvider);
ResyncAlbum()(widget.account, widget.album).then((album) {
if (_shouldPropagateResyncedAlbum(album)) {
UpdateAlbum(AlbumRepo(AlbumCachedDataSource()))(widget.account, album)
@ -247,7 +249,7 @@ class _AlbumViewerState extends State<AlbumViewer>
final selectedIndexes =
selectedListItems.map((e) => itemStreamListItems.indexOf(e)).toList();
final selectedFiles = _backingFiles.takeIndex(selectedIndexes).toList();
final newItems = _album.items.where((element) {
final newItems = _getAlbumItemsOf(_album).where((element) {
if (element is AlbumFileItem) {
return !selectedFiles.any((select) => select.path == element.file.path);
} else {
@ -256,7 +258,9 @@ class _AlbumViewerState extends State<AlbumViewer>
}).toList();
final albumRepo = AlbumRepo(AlbumCachedDataSource());
final newAlbum = _album.copyWith(
items: newItems,
provider: AlbumStaticProvider(
items: newItems,
),
);
UpdateAlbum(albumRepo)(widget.account, newAlbum).then((_) {
SnackBarManager().showSnackBar(SnackBar(
@ -286,7 +290,7 @@ class _AlbumViewerState extends State<AlbumViewer>
}
void _transformItems() {
_backingFiles = _album.items
_backingFiles = _getAlbumItemsOf(_album)
.whereType<AlbumFileItem>()
.map((e) => e.file)
.where((element) => file_util.isSupportedFormat(element))
@ -320,12 +324,14 @@ class _AlbumViewerState extends State<AlbumViewer>
}
bool _shouldPropagateResyncedAlbum(Album album) {
if (widget.album.items.length != album.items.length) {
final origItems = _getAlbumItemsOf(widget.album);
final resyncItems = _getAlbumItemsOf(album);
if (origItems.length != resyncItems.length) {
_log.info(
"[_shouldPropagateResyncedAlbum] Item length differ: ${widget.album.items.length}, ${album.items.length}");
"[_shouldPropagateResyncedAlbum] Item length differ: ${origItems.length}, ${resyncItems.length}");
return true;
}
for (final z in zip([widget.album.items, album.items])) {
for (final z in zip([origItems, resyncItems])) {
final a = z[0], b = z[1];
bool isEqual;
if (a is AlbumFileItem && b is AlbumFileItem) {
@ -344,6 +350,9 @@ class _AlbumViewerState extends State<AlbumViewer>
return false;
}
static List<AlbumItem> _getAlbumItemsOf(Album a) =>
AlbumStaticProvider.of(a).items;
int get _thumbSize {
switch (_thumbZoomLevel) {
case 1:

View file

@ -12,6 +12,7 @@ import 'package:nc_photos/api/api.dart';
import 'package:nc_photos/api/api_util.dart' as api_util;
import 'package:nc_photos/bloc/list_album.dart';
import 'package:nc_photos/entity/album.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;
@ -175,10 +176,15 @@ class _HomeAlbumsState extends State<HomeAlbums> {
Widget _buildAlbumItem(BuildContext context, int index) {
final item = _items[index];
var subtitle = "";
if (item.album.provider is AlbumStaticProvider) {
subtitle = AppLocalizations.of(context)
.albumSize(AlbumStaticProvider.of(item.album).items.length);
}
return AlbumGridItem(
cover: _buildAlbumCover(context, item.album),
title: item.album.name,
subtitle: AppLocalizations.of(context).albumSize(item.album.items.length),
subtitle: subtitle,
isSelected: _selectedItems.contains(item),
onTap: () => _onItemTap(item),
onLongPress: _isSelectionMode ? null : () => _onItemLongPress(item),
@ -386,7 +392,8 @@ class _HomeAlbumsState extends State<HomeAlbums> {
final sortedAlbums = albums.map((e) {
// find the latest file in this album
try {
final lastItem = e.items
final lastItem = AlbumStaticProvider.of(e)
.items
.whereType<AlbumFileItem>()
.map((e) => e.file)
.where((element) => file_util.isSupportedFormat(element))

View file

@ -12,6 +12,7 @@ import 'package:nc_photos/account.dart';
import 'package:nc_photos/api/api_util.dart' as api_util;
import 'package:nc_photos/bloc/scan_dir.dart';
import 'package:nc_photos/entity/album.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;
@ -311,6 +312,7 @@ class _HomePhotosState extends State<HomePhotos>
}
Future<void> _addSelectedToAlbum(BuildContext context, Album album) async {
assert(album.provider is AlbumStaticProvider);
final selected = selectedListItems
.whereType<_FileListItem>()
.map((e) => AlbumFileItem(file: e.file))
@ -320,10 +322,12 @@ class _HomePhotosState extends State<HomePhotos>
await UpdateAlbum(albumRepo)(
widget.account,
album.copyWith(
items: makeDistinctAlbumItems([
...album.items,
...selected,
]),
provider: AlbumStaticProvider(
items: makeDistinctAlbumItems([
...AlbumStaticProvider.of(album).items,
...selected,
]),
),
));
} catch (e, stacktrace) {
_log.shout(

View file

@ -4,6 +4,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:logging/logging.dart';
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/use_case/create_album.dart';
/// Dialog to create a new album
@ -63,7 +64,9 @@ class _NewAlbumDialogState extends State<NewAlbumDialog> {
_formKey.currentState.save();
final album = Album(
name: _formValue.name,
items: const [],
provider: AlbumStaticProvider(
items: const [],
),
);
_log.info("[_onOkPressed] Creating album: $album");
final albumRepo = AlbumRepo(AlbumCachedDataSource());

View file

@ -9,6 +9,7 @@ import 'package:logging/logging.dart';
import 'package:nc_photos/account.dart';
import 'package:nc_photos/double_extension.dart';
import 'package:nc_photos/entity/album.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/exception_util.dart' as exception_util;
@ -348,10 +349,12 @@ class _ViewerDetailPaneState extends State<ViewerDetailPane> {
}
Future<void> _addToAlbum(BuildContext context, Album album) async {
assert(album.provider is AlbumStaticProvider);
try {
final albumRepo = AlbumRepo(AlbumCachedDataSource());
final newItem = AlbumFileItem(file: widget.file);
if (album.items
if (AlbumStaticProvider.of(album)
.items
.whereType<AlbumFileItem>()
.containsIf(newItem, (a, b) => a.file.path == b.file.path)) {
// already added, do nothing
@ -366,7 +369,12 @@ class _ViewerDetailPaneState extends State<ViewerDetailPane> {
await UpdateAlbum(albumRepo)(
widget.account,
album.copyWith(
items: [...album.items, AlbumFileItem(file: widget.file)],
provider: AlbumStaticProvider(
items: [
...AlbumStaticProvider.of(album).items,
AlbumFileItem(file: widget.file),
],
),
));
} catch (e, stacktrace) {
_log.shout("[_addToAlbum] Failed while updating album", e, stacktrace);

View file

@ -1,4 +1,5 @@
import 'package:nc_photos/entity/album.dart';
import 'package:nc_photos/entity/album/provider.dart';
import 'package:nc_photos/entity/album/upgrader.dart';
import 'package:nc_photos/entity/file.dart';
import 'package:test/test.dart';
@ -10,14 +11,21 @@ void main() {
final json = <String, dynamic>{
"version": Album.version,
"lastUpdated": "2020-01-02T03:04:05.678901Z",
"items": [],
"provider": <String, dynamic>{
"type": "static",
"content": <String, dynamic>{
"items": [],
},
},
};
expect(
Album.fromJson(json),
Album(
lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901),
name: "",
items: [],
provider: AlbumStaticProvider(
items: [],
),
));
});
@ -26,54 +34,68 @@ void main() {
"version": Album.version,
"lastUpdated": "2020-01-02T03:04:05.678901Z",
"name": "album",
"items": [],
"provider": <String, dynamic>{
"type": "static",
"content": <String, dynamic>{
"items": [],
},
},
};
expect(
Album.fromJson(json),
Album(
lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901),
name: "album",
items: [],
provider: AlbumStaticProvider(
items: [],
),
));
});
test("items", () {
test("AlbumStaticProvider", () {
final json = <String, dynamic>{
"version": Album.version,
"lastUpdated": "2020-01-02T03:04:05.678901Z",
"name": "",
"items": [
<String, dynamic>{
"type": "file",
"content": <String, dynamic>{
"file": <String, dynamic>{
"path": "remote.php/dav/files/admin/test1.jpg",
"provider": <String, dynamic>{
"type": "static",
"content": <String, dynamic>{
"items": [
<String, dynamic>{
"type": "file",
"content": <String, dynamic>{
"file": <String, dynamic>{
"path": "remote.php/dav/files/admin/test1.jpg",
},
},
},
},
},
<String, dynamic>{
"type": "file",
"content": <String, dynamic>{
"file": <String, dynamic>{
"path": "remote.php/dav/files/admin/test2.jpg",
<String, dynamic>{
"type": "file",
"content": <String, dynamic>{
"file": <String, dynamic>{
"path": "remote.php/dav/files/admin/test2.jpg",
},
},
},
},
],
},
]
},
};
expect(
Album.fromJson(json),
Album(
lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901),
name: "",
items: [
AlbumFileItem(
file: File(path: "remote.php/dav/files/admin/test1.jpg"),
),
AlbumFileItem(
file: File(path: "remote.php/dav/files/admin/test2.jpg"),
),
],
provider: AlbumStaticProvider(
items: [
AlbumFileItem(
file: File(path: "remote.php/dav/files/admin/test1.jpg"),
),
AlbumFileItem(
file: File(path: "remote.php/dav/files/admin/test2.jpg"),
),
],
),
));
});
@ -81,7 +103,12 @@ void main() {
final json = <String, dynamic>{
"version": Album.version,
"lastUpdated": "2020-01-02T03:04:05.678901Z",
"items": [],
"provider": <String, dynamic>{
"type": "static",
"content": <String, dynamic>{
"items": [],
},
},
"albumFile": <String, dynamic>{
"path": "remote.php/dav/files/admin/test1.jpg",
},
@ -91,7 +118,9 @@ void main() {
Album(
lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901),
name: "",
items: [],
provider: AlbumStaticProvider(
items: [],
),
albumFile: File(path: "remote.php/dav/files/admin/test1.jpg"),
));
});
@ -102,13 +131,20 @@ void main() {
final album = Album(
lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901),
name: "",
items: [],
provider: AlbumStaticProvider(
items: [],
),
);
expect(album.toRemoteJson(), <String, dynamic>{
"version": Album.version,
"lastUpdated": "2020-01-02T03:04:05.678901Z",
"name": "",
"items": [],
"provider": <String, dynamic>{
"type": "static",
"content": <String, dynamic>{
"items": [],
},
},
});
});
@ -116,51 +152,65 @@ void main() {
final album = Album(
lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901),
name: "album",
items: [],
provider: AlbumStaticProvider(
items: [],
),
);
expect(album.toRemoteJson(), <String, dynamic>{
"version": Album.version,
"lastUpdated": "2020-01-02T03:04:05.678901Z",
"name": "album",
"items": [],
"provider": <String, dynamic>{
"type": "static",
"content": <String, dynamic>{
"items": [],
},
},
});
});
test("items", () {
test("AlbumStaticProvider", () {
final album = Album(
lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901),
name: "",
items: [
AlbumFileItem(
file: File(path: "remote.php/dav/files/admin/test1.jpg"),
),
AlbumFileItem(
file: File(path: "remote.php/dav/files/admin/test2.jpg"),
),
],
provider: AlbumStaticProvider(
items: [
AlbumFileItem(
file: File(path: "remote.php/dav/files/admin/test1.jpg"),
),
AlbumFileItem(
file: File(path: "remote.php/dav/files/admin/test2.jpg"),
),
],
),
);
expect(album.toRemoteJson(), <String, dynamic>{
"version": Album.version,
"lastUpdated": "2020-01-02T03:04:05.678901Z",
"name": "",
"items": [
<String, dynamic>{
"type": "file",
"content": <String, dynamic>{
"file": <String, dynamic>{
"path": "remote.php/dav/files/admin/test1.jpg",
"provider": <String, dynamic>{
"type": "static",
"content": <String, dynamic>{
"items": [
<String, dynamic>{
"type": "file",
"content": <String, dynamic>{
"file": <String, dynamic>{
"path": "remote.php/dav/files/admin/test1.jpg",
},
},
},
},
},
<String, dynamic>{
"type": "file",
"content": <String, dynamic>{
"file": <String, dynamic>{
"path": "remote.php/dav/files/admin/test2.jpg",
<String, dynamic>{
"type": "file",
"content": <String, dynamic>{
"file": <String, dynamic>{
"path": "remote.php/dav/files/admin/test2.jpg",
},
},
},
},
],
},
]
},
});
});
});
@ -170,13 +220,20 @@ void main() {
final album = Album(
lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901),
name: "",
items: [],
provider: AlbumStaticProvider(
items: [],
),
);
expect(album.toAppDbJson(), <String, dynamic>{
"version": Album.version,
"lastUpdated": "2020-01-02T03:04:05.678901Z",
"name": "",
"items": [],
"provider": <String, dynamic>{
"type": "static",
"content": <String, dynamic>{
"items": [],
},
},
});
});
@ -184,51 +241,65 @@ void main() {
final album = Album(
lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901),
name: "album",
items: [],
provider: AlbumStaticProvider(
items: [],
),
);
expect(album.toAppDbJson(), <String, dynamic>{
"version": Album.version,
"lastUpdated": "2020-01-02T03:04:05.678901Z",
"name": "album",
"items": [],
"provider": <String, dynamic>{
"type": "static",
"content": <String, dynamic>{
"items": [],
},
},
});
});
test("items", () {
test("AlbumStaticProvider", () {
final album = Album(
lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901),
name: "",
items: [
AlbumFileItem(
file: File(path: "remote.php/dav/files/admin/test1.jpg"),
),
AlbumFileItem(
file: File(path: "remote.php/dav/files/admin/test2.jpg"),
),
],
provider: AlbumStaticProvider(
items: [
AlbumFileItem(
file: File(path: "remote.php/dav/files/admin/test1.jpg"),
),
AlbumFileItem(
file: File(path: "remote.php/dav/files/admin/test2.jpg"),
),
],
),
);
expect(album.toAppDbJson(), <String, dynamic>{
"version": Album.version,
"lastUpdated": "2020-01-02T03:04:05.678901Z",
"name": "",
"items": [
<String, dynamic>{
"type": "file",
"content": <String, dynamic>{
"file": <String, dynamic>{
"path": "remote.php/dav/files/admin/test1.jpg",
"provider": <String, dynamic>{
"type": "static",
"content": <String, dynamic>{
"items": [
<String, dynamic>{
"type": "file",
"content": <String, dynamic>{
"file": <String, dynamic>{
"path": "remote.php/dav/files/admin/test1.jpg",
},
},
},
},
},
<String, dynamic>{
"type": "file",
"content": <String, dynamic>{
"file": <String, dynamic>{
"path": "remote.php/dav/files/admin/test2.jpg",
<String, dynamic>{
"type": "file",
"content": <String, dynamic>{
"file": <String, dynamic>{
"path": "remote.php/dav/files/admin/test2.jpg",
},
},
},
},
],
},
]
},
});
});
@ -236,14 +307,21 @@ void main() {
final album = Album(
lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901),
name: "",
items: [],
provider: AlbumStaticProvider(
items: [],
),
albumFile: File(path: "remote.php/dav/files/admin/test1.jpg"),
);
expect(album.toAppDbJson(), <String, dynamic>{
"version": Album.version,
"lastUpdated": "2020-01-02T03:04:05.678901Z",
"name": "",
"items": [],
"provider": <String, dynamic>{
"type": "static",
"content": <String, dynamic>{
"items": [],
},
},
"albumFile": <String, dynamic>{
"path": "remote.php/dav/files/admin/test1.jpg",
},
@ -278,5 +356,47 @@ void main() {
},
});
});
test("AlbumUpgraderV2", () {
final json = <String, dynamic>{
"version": 2,
"lastUpdated": "2020-01-02T03:04:05.678901Z",
"items": [
<String, dynamic>{
"type": "file",
"content": <String, dynamic>{
"file": <String, dynamic>{
"path": "remote.php/dav/files/admin/test1.jpg",
},
},
},
],
"albumFile": <String, dynamic>{
"path": "remote.php/dav/files/admin/test1.json",
},
};
expect(AlbumUpgraderV2()(json), <String, dynamic>{
"version": 2,
"lastUpdated": "2020-01-02T03:04:05.678901Z",
"provider": <String, dynamic>{
"type": "static",
"content": <String, dynamic>{
"items": [
<String, dynamic>{
"type": "file",
"content": <String, dynamic>{
"file": <String, dynamic>{
"path": "remote.php/dav/files/admin/test1.jpg",
},
},
},
],
},
},
"albumFile": <String, dynamic>{
"path": "remote.php/dav/files/admin/test1.json",
},
});
});
});
}