mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-02-24 18:38:48 +01:00
Make PersonBrowser looks more like the photos tab
This commit is contained in:
parent
abe215aee0
commit
8b1ed216b0
2 changed files with 130 additions and 3 deletions
|
@ -5,6 +5,8 @@ import 'package:nc_photos/di_container.dart';
|
||||||
import 'package:nc_photos/entity/file.dart';
|
import 'package:nc_photos/entity/file.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/entity/person.dart';
|
import 'package:nc_photos/entity/person.dart';
|
||||||
|
import 'package:nc_photos/event/event.dart';
|
||||||
|
import 'package:nc_photos/throttler.dart';
|
||||||
import 'package:nc_photos/use_case/populate_person.dart';
|
import 'package:nc_photos/use_case/populate_person.dart';
|
||||||
|
|
||||||
abstract class ListFaceFileBlocEvent {
|
abstract class ListFaceFileBlocEvent {
|
||||||
|
@ -24,6 +26,15 @@ class ListFaceFileBlocQuery extends ListFaceFileBlocEvent {
|
||||||
final Person person;
|
final Person person;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An external event has happened and may affect the state of this bloc
|
||||||
|
class _ListFaceFileBlocExternalEvent extends ListFaceFileBlocEvent {
|
||||||
|
const _ListFaceFileBlocExternalEvent();
|
||||||
|
|
||||||
|
@override
|
||||||
|
toString() => "$runtimeType {"
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
|
||||||
abstract class ListFaceFileBlocState {
|
abstract class ListFaceFileBlocState {
|
||||||
const ListFaceFileBlocState(this.account, this.items);
|
const ListFaceFileBlocState(this.account, this.items);
|
||||||
|
|
||||||
|
@ -65,6 +76,13 @@ class ListFaceFileBlocFailure extends ListFaceFileBlocState {
|
||||||
final Object exception;
|
final Object exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The state of this bloc is inconsistent. This typically means that the data
|
||||||
|
/// may have been changed externally
|
||||||
|
class ListFaceFileBlocInconsistent extends ListFaceFileBlocState {
|
||||||
|
const ListFaceFileBlocInconsistent(Account? account, List<File> items)
|
||||||
|
: super(account, items);
|
||||||
|
}
|
||||||
|
|
||||||
/// List all people recognized in an account
|
/// List all people recognized in an account
|
||||||
class ListFaceFileBloc
|
class ListFaceFileBloc
|
||||||
extends Bloc<ListFaceFileBlocEvent, ListFaceFileBlocState> {
|
extends Bloc<ListFaceFileBlocEvent, ListFaceFileBlocState> {
|
||||||
|
@ -72,16 +90,28 @@ class ListFaceFileBloc
|
||||||
: assert(require(_c)),
|
: assert(require(_c)),
|
||||||
assert(PopulatePerson.require(_c)),
|
assert(PopulatePerson.require(_c)),
|
||||||
super(ListFaceFileBlocInit()) {
|
super(ListFaceFileBlocInit()) {
|
||||||
|
_fileRemovedEventListener.begin();
|
||||||
|
_filePropertyUpdatedEventListener.begin();
|
||||||
|
|
||||||
on<ListFaceFileBlocEvent>(_onEvent);
|
on<ListFaceFileBlocEvent>(_onEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool require(DiContainer c) => DiContainer.has(c, DiType.faceRepo);
|
static bool require(DiContainer c) => DiContainer.has(c, DiType.faceRepo);
|
||||||
|
|
||||||
|
@override
|
||||||
|
close() {
|
||||||
|
_fileRemovedEventListener.end();
|
||||||
|
_filePropertyUpdatedEventListener.end();
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _onEvent(
|
Future<void> _onEvent(
|
||||||
ListFaceFileBlocEvent event, Emitter<ListFaceFileBlocState> emit) async {
|
ListFaceFileBlocEvent event, Emitter<ListFaceFileBlocState> emit) async {
|
||||||
_log.info("[_onEvent] $event");
|
_log.info("[_onEvent] $event");
|
||||||
if (event is ListFaceFileBlocQuery) {
|
if (event is ListFaceFileBlocQuery) {
|
||||||
await _onEventQuery(event, emit);
|
await _onEventQuery(event, emit);
|
||||||
|
} else if (event is _ListFaceFileBlocExternalEvent) {
|
||||||
|
await _onExternalEvent(event, emit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,6 +126,59 @@ class ListFaceFileBloc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _onExternalEvent(_ListFaceFileBlocExternalEvent ev,
|
||||||
|
Emitter<ListFaceFileBlocState> emit) async {
|
||||||
|
emit(ListFaceFileBlocInconsistent(state.account, state.items));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onFileRemovedEvent(FileRemovedEvent ev) {
|
||||||
|
if (state is ListFaceFileBlocInit) {
|
||||||
|
// no data in this bloc, ignore
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_isFileOfInterest(ev.file)) {
|
||||||
|
_refreshThrottler.trigger(
|
||||||
|
maxResponceTime: const Duration(seconds: 3),
|
||||||
|
maxPendingCount: 10,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onFilePropertyUpdatedEvent(FilePropertyUpdatedEvent ev) {
|
||||||
|
if (!ev.hasAnyProperties([
|
||||||
|
FilePropertyUpdatedEvent.propMetadata,
|
||||||
|
FilePropertyUpdatedEvent.propIsArchived,
|
||||||
|
FilePropertyUpdatedEvent.propOverrideDateTime,
|
||||||
|
FilePropertyUpdatedEvent.propFavorite,
|
||||||
|
])) {
|
||||||
|
// not interested
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state is ListFaceFileBlocInit) {
|
||||||
|
// no data in this bloc, ignore
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!_isFileOfInterest(ev.file)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev.hasAnyProperties([
|
||||||
|
FilePropertyUpdatedEvent.propIsArchived,
|
||||||
|
FilePropertyUpdatedEvent.propOverrideDateTime,
|
||||||
|
FilePropertyUpdatedEvent.propFavorite,
|
||||||
|
])) {
|
||||||
|
_refreshThrottler.trigger(
|
||||||
|
maxResponceTime: const Duration(seconds: 3),
|
||||||
|
maxPendingCount: 10,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
_refreshThrottler.trigger(
|
||||||
|
maxResponceTime: const Duration(seconds: 10),
|
||||||
|
maxPendingCount: 10,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<List<File>> _query(ListFaceFileBlocQuery ev) async {
|
Future<List<File>> _query(ListFaceFileBlocQuery ev) async {
|
||||||
final faces = await _c.faceRepo.list(ev.account, ev.person);
|
final faces = await _c.faceRepo.list(ev.account, ev.person);
|
||||||
final files = await PopulatePerson(_c)(ev.account, faces);
|
final files = await PopulatePerson(_c)(ev.account, faces);
|
||||||
|
@ -109,7 +192,33 @@ class ListFaceFileBloc
|
||||||
.toList();
|
.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;
|
final DiContainer _c;
|
||||||
|
|
||||||
|
late final _fileRemovedEventListener =
|
||||||
|
AppEventListener<FileRemovedEvent>(_onFileRemovedEvent);
|
||||||
|
late final _filePropertyUpdatedEventListener =
|
||||||
|
AppEventListener<FilePropertyUpdatedEvent>(_onFilePropertyUpdatedEvent);
|
||||||
|
|
||||||
|
late final _refreshThrottler = Throttler(
|
||||||
|
onTriggered: (_) {
|
||||||
|
add(const _ListFaceFileBlocExternalEvent());
|
||||||
|
},
|
||||||
|
logTag: "ListFaceFileBloc.refresh",
|
||||||
|
);
|
||||||
|
|
||||||
static final _log = Logger("bloc.list_face_file.ListFaceFileBloc");
|
static final _log = Logger("bloc.list_face_file.ListFaceFileBloc");
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,8 +119,12 @@ class _PersonBrowserState extends State<PersonBrowser>
|
||||||
@override
|
@override
|
||||||
onItemTap(SelectableItem item, int index) {
|
onItemTap(SelectableItem item, int index) {
|
||||||
item.as<PhotoListFileItem>()?.run((fileItem) {
|
item.as<PhotoListFileItem>()?.run((fileItem) {
|
||||||
Navigator.pushNamed(context, Viewer.routeName,
|
Navigator.pushNamed(
|
||||||
arguments: ViewerArguments(widget.account, _backingFiles, index));
|
context,
|
||||||
|
Viewer.routeName,
|
||||||
|
arguments:
|
||||||
|
ViewerArguments(widget.account, _backingFiles, fileItem.fileIndex),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,6 +328,8 @@ class _PersonBrowserState extends State<PersonBrowser>
|
||||||
content: Text(exception_util.toUserString(state.exception)),
|
content: Text(exception_util.toUserString(state.exception)),
|
||||||
duration: k.snackBarDurationNormal,
|
duration: k.snackBarDurationNormal,
|
||||||
));
|
));
|
||||||
|
} else if (state is ListFaceFileBlocInconsistent) {
|
||||||
|
_reqQuery();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,11 +436,23 @@ class _PersonBrowserState extends State<PersonBrowser>
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _transformItems(List<File> files) async {
|
Future<void> _transformItems(List<File> files) async {
|
||||||
|
final PhotoListItemSorter? sorter;
|
||||||
|
final PhotoListItemGrouper? grouper;
|
||||||
|
if (Pref().isPhotosTabSortByNameOr()) {
|
||||||
|
sorter = photoListFilenameSorter;
|
||||||
|
grouper = null;
|
||||||
|
} else {
|
||||||
|
sorter = photoListFileDateTimeSorter;
|
||||||
|
grouper = PhotoListFileDateGrouper(isMonthOnly: _thumbZoomLevel < 0);
|
||||||
|
}
|
||||||
|
|
||||||
_buildItemQueue.addJob(
|
_buildItemQueue.addJob(
|
||||||
PhotoListItemBuilderArguments(
|
PhotoListItemBuilderArguments(
|
||||||
widget.account,
|
widget.account,
|
||||||
files,
|
files,
|
||||||
sorter: photoListFileDateTimeSorter,
|
sorter: sorter,
|
||||||
|
grouper: grouper,
|
||||||
|
shouldShowFavoriteBadge: true,
|
||||||
locale: language_util.getSelectedLocale() ??
|
locale: language_util.getSelectedLocale() ??
|
||||||
PlatformDispatcher.instance.locale,
|
PlatformDispatcher.instance.locale,
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in a new issue