Fix no albums shown if any one of them erred

This commit is contained in:
Ming Ming 2021-07-10 00:33:07 +08:00
parent 60339968de
commit a124c99f33
4 changed files with 72 additions and 65 deletions

View file

@ -6,6 +6,7 @@ import 'package:nc_photos/entity/file.dart';
import 'package:nc_photos/entity/file/data_source.dart'; import 'package:nc_photos/entity/file/data_source.dart';
import 'package:nc_photos/event/event.dart'; import 'package:nc_photos/event/event.dart';
import 'package:nc_photos/use_case/list_album.dart'; import 'package:nc_photos/use_case/list_album.dart';
import 'package:tuple/tuple.dart';
abstract class ListAlbumBlocEvent { abstract class ListAlbumBlocEvent {
const ListAlbumBlocEvent(); const ListAlbumBlocEvent();
@ -124,30 +125,19 @@ class ListAlbumBloc extends Bloc<ListAlbumBlocEvent, ListAlbumBlocState> {
if (!hasContent) { if (!hasContent) {
// show something instantly on first load // show something instantly on first load
ListAlbumBlocState cacheState = ListAlbumBlocInit(); final cacheState = await _queryOffline(ev);
await for (final s in _queryOffline(ev, () => cacheState)) {
cacheState = s;
}
yield ListAlbumBlocLoading(ev.account, cacheState.albums); yield ListAlbumBlocLoading(ev.account, cacheState.albums);
hasContent = cacheState.albums.isNotEmpty; hasContent = cacheState.albums.isNotEmpty;
} }
ListAlbumBlocState newState = ListAlbumBlocInit(); final newState = await _queryOnline(ev);
if (!hasContent) { if (newState is ListAlbumBlocFailure) {
await for (final s in _queryOnline(ev, () => newState)) { yield ListAlbumBlocFailure(
newState = s; ev.account,
yield s; newState.albums?.isNotEmpty == true ? newState.albums : state.albums,
} newState.exception);
} else { } else {
await for (final s in _queryOnline(ev, () => newState)) { yield newState;
newState = s;
}
if (newState is ListAlbumBlocSuccess) {
yield newState;
} else if (newState is ListAlbumBlocFailure) {
yield ListAlbumBlocFailure(
ev.account, state.albums, newState.exception);
}
} }
} }
@ -182,29 +172,37 @@ class ListAlbumBloc extends Bloc<ListAlbumBlocEvent, ListAlbumBlocState> {
add(_ListAlbumBlocExternalEvent()); add(_ListAlbumBlocExternalEvent());
} }
Stream<ListAlbumBlocState> _queryOffline( Future<ListAlbumBlocState> _queryOffline(ListAlbumBlocQuery ev) =>
ListAlbumBlocQuery ev, ListAlbumBlocState Function() getState) =>
_queryWithAlbumDataSource( _queryWithAlbumDataSource(
ev, getState, FileAppDbDataSource(), AlbumAppDbDataSource()); ev, FileAppDbDataSource(), AlbumAppDbDataSource());
Stream<ListAlbumBlocState> _queryOnline( Future<ListAlbumBlocState> _queryOnline(ListAlbumBlocQuery ev) =>
ListAlbumBlocQuery ev, ListAlbumBlocState Function() getState) =>
_queryWithAlbumDataSource( _queryWithAlbumDataSource(
ev, getState, FileCachedDataSource(), AlbumCachedDataSource()); ev, FileCachedDataSource(), AlbumCachedDataSource());
Stream<ListAlbumBlocState> _queryWithAlbumDataSource( Future<ListAlbumBlocState> _queryWithAlbumDataSource(ListAlbumBlocQuery ev,
ListAlbumBlocQuery ev, FileDataSource fileDataSource, AlbumDataSource albumDataSrc) async {
ListAlbumBlocState Function() getState,
FileDataSource fileDataSource,
AlbumDataSource albumDataSrc) async* {
try { try {
final results = await ListAlbum( final albums = <Album>[];
FileRepo(fileDataSource), AlbumRepo(albumDataSrc))(ev.account); final errors = <dynamic>[];
yield ListAlbumBlocSuccess(ev.account, results); await for (final result in ListAlbum(
FileRepo(fileDataSource), AlbumRepo(albumDataSrc))(ev.account)) {
if (result is Tuple2) {
_log.severe("[_queryWithAlbumDataSource] Exception while ListAlbum",
result.item1, result.item2);
errors.add(result.item1);
} else if (result is Album) {
albums.add(result);
}
}
if (errors.isEmpty) {
return ListAlbumBlocSuccess(ev.account, albums);
} else {
return ListAlbumBlocFailure(ev.account, albums, errors.first);
}
} catch (e, stacktrace) { } catch (e, stacktrace) {
_log.severe( _log.severe("[_queryWithAlbumDataSource] Exception", e, stacktrace);
"[_queryWithAlbumDataSource] Exception while request", e, stacktrace); return ListAlbumBlocFailure(ev.account, [], e);
yield ListAlbumBlocFailure(ev.account, getState().albums, e);
} }
} }

View file

@ -102,7 +102,9 @@ class ListImportableAlbumBloc
try { try {
final fileRepo = FileRepo(FileCachedDataSource()); final fileRepo = FileRepo(FileCachedDataSource());
final albumRepo = AlbumRepo(AlbumCachedDataSource()); final albumRepo = AlbumRepo(AlbumCachedDataSource());
final albums = await ListAlbum(fileRepo, albumRepo)(ev.account); final albums = (await ListAlbum(fileRepo, albumRepo)(ev.account)
.where((event) => event is Album)
.toList()).cast<Album>();
final importedDirs = albums.map((a) { final importedDirs = albums.map((a) {
if (a.provider is! AlbumDirProvider) { if (a.provider is! AlbumDirProvider) {
return <File>[]; return <File>[];

View file

@ -6,54 +6,59 @@ import 'package:nc_photos/exception.dart';
import 'package:nc_photos/remote_storage_util.dart' as remote_storage_util; import 'package:nc_photos/remote_storage_util.dart' as remote_storage_util;
import 'package:nc_photos/use_case/compat/v15.dart'; import 'package:nc_photos/use_case/compat/v15.dart';
import 'package:nc_photos/use_case/ls.dart'; import 'package:nc_photos/use_case/ls.dart';
import 'package:tuple/tuple.dart';
class ListAlbum { class ListAlbum {
ListAlbum(this.fileRepo, this.albumRepo); ListAlbum(this.fileRepo, this.albumRepo);
/// List all albums associated with [account] /// List all albums associated with [account]
Future<List<Album>> call(Account account) async { ///
final results = await _call(account); /// The returned stream would emit either Album data or a tuple of exception
if (results.isEmpty) { /// and stacktrace
Stream<dynamic> call(Account account) async* {
bool hasAlbum = false;
await for (final result in _call(account)) {
hasAlbum = true;
yield result;
}
if (!hasAlbum) {
if (await CompatV15.migrateAlbumFiles(account, fileRepo)) { if (await CompatV15.migrateAlbumFiles(account, fileRepo)) {
// migrated // migrated, try again
return await _call(account); yield* _call(account);
} else {
// no need to migrate
return [];
} }
} else {
return results;
} }
} }
Future<List<Album>> _call(Account account) async { Stream<dynamic> _call(Account account) async* {
List<File> ls;
try { try {
final ls = await Ls(fileRepo)( ls = await Ls(fileRepo)(
account, account,
File( File(
path: remote_storage_util.getRemoteAlbumsDir(account), path: remote_storage_util.getRemoteAlbumsDir(account),
)); ));
final albumFiles =
ls.where((element) => element.isCollection != true).toList();
final albums = <Album>[];
for (final f in albumFiles) {
final album = await albumRepo.get(account, f);
albums.add(album);
}
try {
albumRepo.cleanUp(account, albumFiles);
} catch (e, stacktrace) {
// not important, log and ignore
_log.shout("[_call] Failed while cleanUp", e, stacktrace);
}
return albums;
} catch (e) { } catch (e) {
if (e is ApiException && e.response.statusCode == 404) { if (e is ApiException && e.response.statusCode == 404) {
// no albums // no albums
return []; return;
} }
rethrow; rethrow;
} }
final albumFiles =
ls.where((element) => element.isCollection != true).toList();
for (final f in albumFiles) {
try {
yield await albumRepo.get(account, f);
} catch (e, stacktrace) {
yield Tuple2(e, stacktrace);
}
}
try {
albumRepo.cleanUp(account, albumFiles);
} catch (e, stacktrace) {
// not important, log and ignore
_log.shout("[_call] Failed while cleanUp", e, stacktrace);
}
} }
final FileRepo fileRepo; final FileRepo fileRepo;

View file

@ -24,7 +24,9 @@ class Remove {
} }
Future<void> _cleanUpAlbums(Account account, File file) async { Future<void> _cleanUpAlbums(Account account, File file) async {
final albums = await ListAlbum(fileRepo, albumRepo)(account); final albums = (await ListAlbum(fileRepo, albumRepo)(account)
.where((event) => event is Album)
.toList()).cast<Album>();
// clean up only make sense for static albums // clean up only make sense for static albums
for (final a for (final a
in albums.where((element) => element.provider is AlbumStaticProvider)) { in albums.where((element) => element.provider is AlbumStaticProvider)) {