mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-01-23 09:16:19 +01:00
184 lines
5.4 KiB
Dart
184 lines
5.4 KiB
Dart
import 'package:bloc/bloc.dart';
|
|
import 'package:logging/logging.dart';
|
|
import 'package:nc_photos/account.dart';
|
|
import 'package:nc_photos/di_container.dart';
|
|
import 'package:nc_photos/entity/file.dart';
|
|
import 'package:nc_photos/entity/file_util.dart' as file_util;
|
|
import 'package:nc_photos/event/event.dart';
|
|
import 'package:nc_photos/throttler.dart';
|
|
import 'package:nc_photos/use_case/list_location_file.dart';
|
|
|
|
abstract class ListLocationFileBlocEvent {
|
|
const ListLocationFileBlocEvent();
|
|
}
|
|
|
|
class ListLocationFileBlocQuery extends ListLocationFileBlocEvent {
|
|
const ListLocationFileBlocQuery(this.account, this.place, this.countryCode);
|
|
|
|
@override
|
|
toString() => "$runtimeType {"
|
|
"account: $account, "
|
|
"place: $place, "
|
|
"countryCode: $countryCode, "
|
|
"}";
|
|
|
|
final Account account;
|
|
final String? place;
|
|
final String countryCode;
|
|
}
|
|
|
|
/// An external event has happened and may affect the state of this bloc
|
|
class _ListLocationFileBlocExternalEvent extends ListLocationFileBlocEvent {
|
|
const _ListLocationFileBlocExternalEvent();
|
|
|
|
@override
|
|
toString() => "$runtimeType {"
|
|
"}";
|
|
}
|
|
|
|
abstract class ListLocationFileBlocState {
|
|
const ListLocationFileBlocState(this.account, this.items);
|
|
|
|
@override
|
|
toString() => "$runtimeType {"
|
|
"account: $account, "
|
|
"items: List {length: ${items.length}}, "
|
|
"}";
|
|
|
|
final Account? account;
|
|
final List<File> items;
|
|
}
|
|
|
|
class ListLocationFileBlocInit extends ListLocationFileBlocState {
|
|
ListLocationFileBlocInit() : super(null, const []);
|
|
}
|
|
|
|
class ListLocationFileBlocLoading extends ListLocationFileBlocState {
|
|
const ListLocationFileBlocLoading(Account? account, List<File> items)
|
|
: super(account, items);
|
|
}
|
|
|
|
class ListLocationFileBlocSuccess extends ListLocationFileBlocState {
|
|
const ListLocationFileBlocSuccess(Account? account, List<File> items)
|
|
: super(account, items);
|
|
}
|
|
|
|
class ListLocationFileBlocFailure extends ListLocationFileBlocState {
|
|
const ListLocationFileBlocFailure(
|
|
Account? account, List<File> items, this.exception)
|
|
: super(account, items);
|
|
|
|
@override
|
|
toString() => "$runtimeType {"
|
|
"super: ${super.toString()}, "
|
|
"exception: $exception, "
|
|
"}";
|
|
|
|
final Object exception;
|
|
}
|
|
|
|
/// The state of this bloc is inconsistent. This typically means that the data
|
|
/// may have been changed externally
|
|
class ListLocationFileBlocInconsistent extends ListLocationFileBlocState {
|
|
const ListLocationFileBlocInconsistent(Account? account, List<File> items)
|
|
: super(account, items);
|
|
}
|
|
|
|
/// List all files associated with a specific tag
|
|
class ListLocationFileBloc
|
|
extends Bloc<ListLocationFileBlocEvent, ListLocationFileBlocState> {
|
|
ListLocationFileBloc(this._c)
|
|
: assert(require(_c)),
|
|
assert(ListLocationFile.require(_c)),
|
|
super(ListLocationFileBlocInit()) {
|
|
_fileRemovedEventListener.begin();
|
|
|
|
on<ListLocationFileBlocEvent>(_onEvent);
|
|
}
|
|
|
|
static bool require(DiContainer c) =>
|
|
DiContainer.has(c, DiType.taggedFileRepo);
|
|
|
|
@override
|
|
close() {
|
|
_fileRemovedEventListener.end();
|
|
return super.close();
|
|
}
|
|
|
|
Future<void> _onEvent(ListLocationFileBlocEvent event,
|
|
Emitter<ListLocationFileBlocState> emit) async {
|
|
_log.info("[_onEvent] $event");
|
|
if (event is ListLocationFileBlocQuery) {
|
|
await _onEventQuery(event, emit);
|
|
} else if (event is _ListLocationFileBlocExternalEvent) {
|
|
await _onExternalEvent(event, emit);
|
|
}
|
|
}
|
|
|
|
Future<void> _onEventQuery(ListLocationFileBlocQuery ev,
|
|
Emitter<ListLocationFileBlocState> emit) async {
|
|
try {
|
|
emit(ListLocationFileBlocLoading(ev.account, state.items));
|
|
emit(ListLocationFileBlocSuccess(ev.account, await _query(ev)));
|
|
} catch (e, stackTrace) {
|
|
_log.severe("[_onEventQuery] Exception while request", e, stackTrace);
|
|
emit(ListLocationFileBlocFailure(ev.account, state.items, e));
|
|
}
|
|
}
|
|
|
|
Future<void> _onExternalEvent(_ListLocationFileBlocExternalEvent ev,
|
|
Emitter<ListLocationFileBlocState> emit) async {
|
|
emit(ListLocationFileBlocInconsistent(state.account, state.items));
|
|
}
|
|
|
|
void _onFileRemovedEvent(FileRemovedEvent ev) {
|
|
if (state is ListLocationFileBlocInit) {
|
|
// no data in this bloc, ignore
|
|
return;
|
|
}
|
|
if (_isFileOfInterest(ev.file)) {
|
|
_refreshThrottler.trigger(
|
|
maxResponceTime: const Duration(seconds: 3),
|
|
maxPendingCount: 10,
|
|
);
|
|
}
|
|
}
|
|
|
|
Future<List<File>> _query(ListLocationFileBlocQuery ev) async {
|
|
final files = <File>[];
|
|
for (final r in ev.account.roots) {
|
|
final dir = File(path: file_util.unstripPath(ev.account, r));
|
|
files.addAll(await ListLocationFile(_c)(
|
|
ev.account, dir, ev.place, ev.countryCode));
|
|
}
|
|
return files.where((f) => file_util.isSupportedFormat(f)).toList();
|
|
}
|
|
|
|
bool _isFileOfInterest(File file) {
|
|
if (!file_util.isSupportedFormat(file)) {
|
|
return false;
|
|
}
|
|
|
|
for (final r in state.account?.roots ?? []) {
|
|
final dir = File(path: file_util.unstripPath(state.account!, r));
|
|
if (file_util.isUnderDir(file, dir)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
final DiContainer _c;
|
|
|
|
late final _fileRemovedEventListener =
|
|
AppEventListener<FileRemovedEvent>(_onFileRemovedEvent);
|
|
|
|
late final _refreshThrottler = Throttler(
|
|
onTriggered: (_) {
|
|
add(const _ListLocationFileBlocExternalEvent());
|
|
},
|
|
logTag: "ListLocationFileBloc.refresh",
|
|
);
|
|
|
|
static final _log = Logger("bloc.list_location_file.ListLocationFileBloc");
|
|
}
|