mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-02-02 06:46:22 +01:00
Resync with server after modifying person provider
This commit is contained in:
parent
4e03c76d0d
commit
f66345cded
10 changed files with 128 additions and 68 deletions
|
@ -4,6 +4,7 @@ import 'package:nc_photos/controller/account_pref_controller.dart';
|
|||
import 'package:nc_photos/controller/collections_controller.dart';
|
||||
import 'package:nc_photos/controller/persons_controller.dart';
|
||||
import 'package:nc_photos/controller/server_controller.dart';
|
||||
import 'package:nc_photos/controller/sync_controller.dart';
|
||||
import 'package:nc_photos/di_container.dart';
|
||||
|
||||
class AccountController {
|
||||
|
@ -17,6 +18,8 @@ class AccountController {
|
|||
_accountPrefController = null;
|
||||
_personsController?.dispose();
|
||||
_personsController = null;
|
||||
_syncController?.dispose();
|
||||
_syncController = null;
|
||||
}
|
||||
|
||||
Account get account => _account!;
|
||||
|
@ -45,9 +48,14 @@ class AccountController {
|
|||
accountPrefController: accountPrefController,
|
||||
);
|
||||
|
||||
SyncController get syncController => _syncController ??= SyncController(
|
||||
account: _account!,
|
||||
);
|
||||
|
||||
Account? _account;
|
||||
CollectionsController? _collectionsController;
|
||||
ServerController? _serverController;
|
||||
AccountPrefController? _accountPrefController;
|
||||
PersonsController? _personsController;
|
||||
SyncController? _syncController;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ class AccountPrefController {
|
|||
void dispose() {
|
||||
_shareFolderController.close();
|
||||
_accountLabelController.close();
|
||||
_personProviderController.close();
|
||||
}
|
||||
|
||||
ValueStream<String> get shareFolder => _shareFolderController.stream;
|
||||
|
|
51
app/lib/controller/sync_controller.dart
Normal file
51
app/lib/controller/sync_controller.dart
Normal file
|
@ -0,0 +1,51 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:nc_photos/account.dart';
|
||||
import 'package:nc_photos/entity/person.dart';
|
||||
import 'package:nc_photos/use_case/startup_sync.dart';
|
||||
|
||||
class SyncController {
|
||||
SyncController({
|
||||
required this.account,
|
||||
this.onPeopleUpdated,
|
||||
});
|
||||
|
||||
void dispose() {
|
||||
_isDisposed = true;
|
||||
}
|
||||
|
||||
Future<void> requestSync(
|
||||
Account account, PersonProvider personProvider) async {
|
||||
if (_isDisposed) {
|
||||
return;
|
||||
}
|
||||
if (_syncCompleter == null) {
|
||||
_syncCompleter = Completer();
|
||||
final result = await StartupSync.runInIsolate(account, personProvider);
|
||||
if (!_isDisposed && result.isSyncPersonUpdated) {
|
||||
onPeopleUpdated?.call();
|
||||
}
|
||||
_syncCompleter!.complete();
|
||||
} else {
|
||||
return _syncCompleter!.future;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> requestResync(
|
||||
Account account, PersonProvider personProvider) async {
|
||||
if (_syncCompleter?.isCompleted == true) {
|
||||
_syncCompleter = null;
|
||||
return requestSync(account, personProvider);
|
||||
} else {
|
||||
// already syncing
|
||||
return requestSync(account, personProvider);
|
||||
}
|
||||
}
|
||||
|
||||
final Account account;
|
||||
final VoidCallback? onPeopleUpdated;
|
||||
|
||||
Completer<void>? _syncCompleter;
|
||||
var _isDisposed = false;
|
||||
}
|
|
@ -4,7 +4,6 @@ import 'package:logging/logging.dart';
|
|||
import 'package:nc_photos/account.dart';
|
||||
import 'package:nc_photos/di_container.dart';
|
||||
import 'package:nc_photos/entity/person.dart';
|
||||
import 'package:nc_photos/entity/pref.dart';
|
||||
import 'package:nc_photos/use_case/face_recognition_person/sync_face_recognition_person.dart';
|
||||
import 'package:nc_photos/use_case/recognize_face/sync_recognize_face.dart';
|
||||
import 'package:np_codegen/np_codegen.dart';
|
||||
|
@ -18,9 +17,7 @@ class SyncPerson {
|
|||
/// Sync people in cache db with remote server
|
||||
///
|
||||
/// Return if any people were updated
|
||||
Future<bool> call(Account account, AccountPref accountPref) async {
|
||||
final provider =
|
||||
PersonProvider.fromValue(accountPref.getPersonProviderOr());
|
||||
Future<bool> call(Account account, PersonProvider provider) async {
|
||||
_log.info("[call] Current provider: $provider");
|
||||
switch (provider) {
|
||||
case PersonProvider.none:
|
||||
|
|
|
@ -4,11 +4,11 @@ import 'package:event_bus/event_bus.dart';
|
|||
import 'package:flutter_isolate/flutter_isolate.dart';
|
||||
import 'package:kiwi/kiwi.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:mutex/mutex.dart';
|
||||
import 'package:nc_photos/account.dart';
|
||||
import 'package:nc_photos/app_init.dart' as app_init;
|
||||
import 'package:nc_photos/di_container.dart';
|
||||
import 'package:nc_photos/entity/pref.dart';
|
||||
import 'package:nc_photos/entity/pref/provider/memory.dart';
|
||||
import 'package:nc_photos/entity/person.dart';
|
||||
import 'package:nc_photos/event/event.dart';
|
||||
import 'package:nc_photos/platform/k.dart' as platform_k;
|
||||
import 'package:nc_photos/use_case/person/sync_person.dart';
|
||||
|
@ -29,25 +29,28 @@ class StartupSync {
|
|||
|
||||
/// Sync in a background isolate
|
||||
static Future<SyncResult> runInIsolate(
|
||||
Account account, AccountPref accountPref) async {
|
||||
if (platform_k.isWeb) {
|
||||
// not supported on web
|
||||
final c = KiwiContainer().resolve<DiContainer>();
|
||||
return await StartupSync(c)(account, accountPref);
|
||||
} else {
|
||||
// we can't use regular isolate here because self-signed cert support
|
||||
// requires native plugins
|
||||
final resultJson = await flutterCompute(
|
||||
_isolateMain, await _IsolateMessage(account, accountPref).toJson());
|
||||
final result = SyncResult.fromJson(resultJson);
|
||||
// events fired in background isolate won't be noticed by the main isolate,
|
||||
// so we fire them again here
|
||||
_broadcastResult(account, result);
|
||||
return result;
|
||||
}
|
||||
Account account, PersonProvider personProvider) async {
|
||||
return _mutex.protect(() async {
|
||||
if (platform_k.isWeb) {
|
||||
// not supported on web
|
||||
final c = KiwiContainer().resolve<DiContainer>();
|
||||
return await StartupSync(c)(account, personProvider);
|
||||
} else {
|
||||
// we can't use regular isolate here because self-signed cert support
|
||||
// requires native plugins
|
||||
final resultJson = await flutterCompute(
|
||||
_isolateMain, _IsolateMessage(account, personProvider).toJson());
|
||||
final result = SyncResult.fromJson(resultJson);
|
||||
// events fired in background isolate won't be noticed by the main isolate,
|
||||
// so we fire them again here
|
||||
_broadcastResult(account, result);
|
||||
return result;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<SyncResult> call(Account account, AccountPref accountPref) async {
|
||||
Future<SyncResult> call(
|
||||
Account account, PersonProvider personProvider) async {
|
||||
_log.info("[_run] Begin sync");
|
||||
final stopwatch = Stopwatch()..start();
|
||||
late final int syncFavoriteCount;
|
||||
|
@ -64,7 +67,7 @@ class StartupSync {
|
|||
_log.shout("[_run] Failed while SyncTag", e, stackTrace);
|
||||
}
|
||||
try {
|
||||
isSyncPersonUpdated = await SyncPerson(_c)(account, accountPref);
|
||||
isSyncPersonUpdated = await SyncPerson(_c)(account, personProvider);
|
||||
} catch (e, stackTrace) {
|
||||
_log.shout("[_run] Failed while SyncPerson", e, stackTrace);
|
||||
}
|
||||
|
@ -83,6 +86,8 @@ class StartupSync {
|
|||
}
|
||||
|
||||
final DiContainer _c;
|
||||
|
||||
static final _mutex = Mutex();
|
||||
}
|
||||
|
||||
class SyncResult {
|
||||
|
@ -106,23 +111,23 @@ class SyncResult {
|
|||
}
|
||||
|
||||
class _IsolateMessage {
|
||||
const _IsolateMessage(this.account, this.accountPref);
|
||||
const _IsolateMessage(this.account, this.personProvider);
|
||||
|
||||
factory _IsolateMessage.fromJson(JsonObj json) => _IsolateMessage(
|
||||
Account.fromJson(
|
||||
json["account"].cast<String, dynamic>(),
|
||||
upgraderV1: const AccountUpgraderV1(),
|
||||
)!,
|
||||
AccountPref.scoped(PrefMemoryProvider.fromJson(json["accountPref"])),
|
||||
PersonProvider.fromValue(json["personProvider"]),
|
||||
);
|
||||
|
||||
Future<JsonObj> toJson() async => {
|
||||
JsonObj toJson() => <String, dynamic>{
|
||||
"account": account.toJson(),
|
||||
"accountPref": await accountPref.toJson(),
|
||||
"personProvider": personProvider.index,
|
||||
};
|
||||
|
||||
final Account account;
|
||||
final AccountPref accountPref;
|
||||
final PersonProvider personProvider;
|
||||
}
|
||||
|
||||
@pragma("vm:entry-point")
|
||||
|
@ -131,6 +136,6 @@ Future<JsonObj> _isolateMain(JsonObj messageJson) async {
|
|||
await app_init.init(app_init.InitIsolateType.flutterIsolate);
|
||||
|
||||
final c = KiwiContainer().resolve<DiContainer>();
|
||||
final result = await StartupSync(c)(message.account, message.accountPref);
|
||||
final result = await StartupSync(c)(message.account, message.personProvider);
|
||||
return result.toJson();
|
||||
}
|
||||
|
|
|
@ -37,7 +37,6 @@ import 'package:nc_photos/share_handler.dart';
|
|||
import 'package:nc_photos/snack_bar_manager.dart';
|
||||
import 'package:nc_photos/theme.dart';
|
||||
import 'package:nc_photos/throttler.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/collection_browser.dart';
|
||||
import 'package:nc_photos/widget/handler/add_selection_to_collection_handler.dart';
|
||||
|
@ -557,20 +556,6 @@ class _HomePhotosState extends State<HomePhotos>
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _startupSync() async {
|
||||
if (!_hasResyncedFavorites.value) {
|
||||
_hasResyncedFavorites.value = true;
|
||||
final result = await StartupSync.runInIsolate(
|
||||
widget.account, _accountPrefController.raw);
|
||||
if (mounted) {
|
||||
if (result.isSyncPersonUpdated) {
|
||||
unawaited(
|
||||
context.read<AccountController>().personsController.reload());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Transform a File list to grid items
|
||||
void _transformItems(
|
||||
List<FileDescriptor> files, {
|
||||
|
@ -611,7 +596,8 @@ class _HomePhotosState extends State<HomePhotos>
|
|||
|
||||
if (isPostSuccess) {
|
||||
_isScrollbarVisible = true;
|
||||
_startupSync();
|
||||
context.read<AccountController>().syncController.requestSync(
|
||||
widget.account, _accountPrefController.personProvider.value);
|
||||
_tryStartMetadataTask();
|
||||
}
|
||||
});
|
||||
|
@ -720,21 +706,6 @@ class _HomePhotosState extends State<HomePhotos>
|
|||
}
|
||||
}
|
||||
|
||||
Primitive<bool> get _hasResyncedFavorites {
|
||||
final name = bloc_util.getInstNameForRootAwareAccount(
|
||||
"HomePhotosState._hasResyncedFavorites", widget.account);
|
||||
try {
|
||||
_log.fine("[_hasResyncedFavorites] Resolving for '$name'");
|
||||
return KiwiContainer().resolve<Primitive<bool>>(name);
|
||||
} catch (_) {
|
||||
_log.info(
|
||||
"[_hasResyncedFavorites] New instance for account: ${widget.account}");
|
||||
final obj = Primitive(false);
|
||||
KiwiContainer().registerInstance<Primitive<bool>>(obj, name: name);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
late final _bloc = ScanAccountDirBloc.of(widget.account);
|
||||
late final _queryProgressBloc = ProgressBloc();
|
||||
late final _accountPrefController =
|
||||
|
|
|
@ -139,7 +139,10 @@ class _Bloc extends Bloc<_Event, _State> {
|
|||
void _onOnUpdatePersonProvider(
|
||||
_OnUpdatePersonProvider ev, Emitter<_State> emit) {
|
||||
_log.info(ev);
|
||||
emit(state.copyWith(personProvider: ev.personProvider));
|
||||
emit(state.copyWith(
|
||||
personProvider: ev.personProvider,
|
||||
shouldResync: true,
|
||||
));
|
||||
}
|
||||
|
||||
void _onSetError(_SetError ev, Emitter<_State> emit) {
|
||||
|
|
|
@ -9,6 +9,7 @@ class _State {
|
|||
required this.label,
|
||||
required this.shareFolder,
|
||||
required this.personProvider,
|
||||
required this.shouldResync,
|
||||
this.error,
|
||||
});
|
||||
|
||||
|
@ -24,6 +25,7 @@ class _State {
|
|||
label: label,
|
||||
shareFolder: shareFolder,
|
||||
personProvider: personProvider,
|
||||
shouldResync: false,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -35,6 +37,7 @@ class _State {
|
|||
final String? label;
|
||||
final String shareFolder;
|
||||
final PersonProvider personProvider;
|
||||
final bool shouldResync;
|
||||
|
||||
final ExceptionEvent? error;
|
||||
}
|
||||
|
|
|
@ -87,12 +87,29 @@ class _WrappedAccountSettings extends StatefulWidget {
|
|||
const _WrappedAccountSettings();
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _WrappedDeveloperSettingsState();
|
||||
State<StatefulWidget> createState() => __WrappedAccountSettingsState();
|
||||
}
|
||||
|
||||
@npLog
|
||||
class _WrappedDeveloperSettingsState extends State<_WrappedAccountSettings>
|
||||
class __WrappedAccountSettingsState extends State<_WrappedAccountSettings>
|
||||
with RouteAware, PageVisibilityMixin {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_accountController = context.read<AccountController>();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (_bloc.state.shouldResync &&
|
||||
_bloc.state.personProvider != PersonProvider.none) {
|
||||
_log.fine("[dispose] Requesting to resync account");
|
||||
_accountController.syncController
|
||||
.requestResync(_bloc.state.account, _bloc.state.personProvider);
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
@ -245,6 +262,7 @@ class _WrappedDeveloperSettingsState extends State<_WrappedAccountSettings>
|
|||
}
|
||||
|
||||
late final _bloc = context.read<_Bloc>();
|
||||
late final AccountController _accountController;
|
||||
}
|
||||
|
||||
class _DoneButton extends StatelessWidget {
|
||||
|
|
|
@ -19,6 +19,7 @@ abstract class $_StateCopyWithWorker {
|
|||
String? label,
|
||||
String? shareFolder,
|
||||
PersonProvider? personProvider,
|
||||
bool? shouldResync,
|
||||
ExceptionEvent? error});
|
||||
}
|
||||
|
||||
|
@ -32,6 +33,7 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
|
|||
dynamic label = copyWithNull,
|
||||
dynamic shareFolder,
|
||||
dynamic personProvider,
|
||||
dynamic shouldResync,
|
||||
dynamic error = copyWithNull}) {
|
||||
return _State(
|
||||
shouldReload: shouldReload as bool? ?? that.shouldReload,
|
||||
|
@ -40,6 +42,7 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
|
|||
shareFolder: shareFolder as String? ?? that.shareFolder,
|
||||
personProvider:
|
||||
personProvider as PersonProvider? ?? that.personProvider,
|
||||
shouldResync: shouldResync as bool? ?? that.shouldResync,
|
||||
error: error == copyWithNull ? that.error : error as ExceptionEvent?);
|
||||
}
|
||||
|
||||
|
@ -55,13 +58,13 @@ extension $_StateCopyWith on _State {
|
|||
// NpLogGenerator
|
||||
// **************************************************************************
|
||||
|
||||
extension _$_WrappedDeveloperSettingsStateNpLog
|
||||
on _WrappedDeveloperSettingsState {
|
||||
extension _$__WrappedAccountSettingsStateNpLog
|
||||
on __WrappedAccountSettingsState {
|
||||
// ignore: unused_element
|
||||
Logger get _log => log;
|
||||
|
||||
static final log =
|
||||
Logger("widget.settings.account_settings._WrappedDeveloperSettingsState");
|
||||
Logger("widget.settings.account_settings.__WrappedAccountSettingsState");
|
||||
}
|
||||
|
||||
extension _$_PersonProviderDialogNpLog on _PersonProviderDialog {
|
||||
|
@ -86,7 +89,7 @@ extension _$_BlocNpLog on _Bloc {
|
|||
extension _$_StateToString on _State {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_State {shouldReload: $shouldReload, account: $account, label: $label, shareFolder: $shareFolder, personProvider: ${personProvider.name}, error: $error}";
|
||||
return "_State {shouldReload: $shouldReload, account: $account, label: $label, shareFolder: $shareFolder, personProvider: ${personProvider.name}, shouldResync: $shouldResync, error: $error}";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue