feat: add remote pull and push

This commit is contained in:
2026-09-23 00:19:58 +02:00
parent 9aaba69e3c
commit ccf5165417
29 changed files with 3042 additions and 24 deletions
+7
View File
@@ -6,6 +6,7 @@ import 'app_lifecycle_lock.dart';
import '../models/settings.dart';
import '../viewmodels/admin_pin_view_model.dart';
import '../viewmodels/pin_lock_view_model.dart';
import '../viewmodels/remote_sync_view_model.dart';
import '../viewmodels/settings_view_model.dart';
import '../theme.dart';
import '../l10n/app_localizations.dart';
@@ -31,6 +32,12 @@ class _KoolTabAppState extends State<KoolTabApp> {
context.read<PinLockViewModel>().lock();
context.read<AdminPinViewModel>().lock();
},
onBackground: () {
context.read<RemoteSyncViewModel>().pause();
},
onForeground: () {
context.read<RemoteSyncViewModel>().resume();
},
shouldLock: () =>
context.read<SettingsViewModel>().settings.autoLockEnabled,
);
+4 -1
View File
@@ -7,6 +7,7 @@ import '../services/default_product_seeder.dart';
import '../services/export_service.dart';
import '../viewmodels/bar_screen_view_model.dart';
import '../viewmodels/product_list_view_model.dart';
import '../viewmodels/remote_sync_view_model.dart';
class AppBootstrap extends StatefulWidget {
final Widget child;
@@ -33,6 +34,8 @@ class _AppBootstrapState extends State<AppBootstrap> {
DefaultExportService(database: database).uploadToServer();
context.read<RemoteSyncViewModel>().init();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
});
}
@@ -41,4 +44,4 @@ class _AppBootstrapState extends State<AppBootstrap> {
Widget build(BuildContext context) {
return widget.child;
}
}
}
+10 -2
View File
@@ -2,9 +2,16 @@ import 'package:flutter/material.dart';
class AppLifecycleLockObserver extends WidgetsBindingObserver {
final VoidCallback onLock;
final VoidCallback? onBackground;
final VoidCallback? onForeground;
final bool Function()? shouldLock;
AppLifecycleLockObserver({required this.onLock, this.shouldLock});
AppLifecycleLockObserver({
required this.onLock,
this.onBackground,
this.onForeground,
this.shouldLock,
});
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
@@ -14,8 +21,9 @@ class AppLifecycleLockObserver extends WidgetsBindingObserver {
case AppLifecycleState.paused:
case AppLifecycleState.detached:
if (shouldLock?.call() ?? true) onLock();
onBackground?.call();
case AppLifecycleState.resumed:
break;
onForeground?.call();
}
}
}
+28 -2
View File
@@ -14,6 +14,7 @@ import '../views/import_names_view.dart';
import '../views/pin_lock_view.dart';
import '../views/product_form_view.dart';
import '../views/product_list_view.dart';
import '../views/registration_view.dart';
import '../views/settings_view.dart';
import '../views/update_progress_view.dart';
import '../utils/app_update_util.dart';
@@ -23,6 +24,7 @@ final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
String? _redirectApp({
required PinLockViewModel pinLockViewModel,
required AdminPinViewModel adminPinViewModel,
required ValueNotifier<bool> isConfiguredNotifier,
required GoRouterState state,
}) {
if (!pinLockViewModel.hasLoaded || !adminPinViewModel.hasLoaded) {
@@ -30,6 +32,18 @@ String? _redirectApp({
}
final location = state.uri.path;
final isConfigured = isConfiguredNotifier.value;
if (!isConfigured && location != '/register') {
return '/register';
}
if (isConfigured && location == '/register') {
return '/bar';
}
if (!isConfigured) return null;
final goingToLock = location == '/lock';
final goingToAdminLock = location == '/admin/lock';
final mustBeOnLock = _mustBeOnLock(pinLockViewModel);
@@ -83,19 +97,31 @@ bool _isDebugShortcutArea(String location) {
GoRouter createAppRouter(PinLockViewModel pinLockViewModel, {
required AdminPinViewModel adminPinViewModel,
required ValueNotifier<bool> isConfiguredNotifier,
}) {
return GoRouter(
initialLocation: '/bar',
observers: [routeObserver, SentryNavigatorObserver()],
refreshListenable: Listenable.merge([pinLockViewModel, adminPinViewModel]),
refreshListenable: Listenable.merge([
pinLockViewModel,
adminPinViewModel,
isConfiguredNotifier,
]),
redirect: (_, state) =>
_redirectApp(
pinLockViewModel: pinLockViewModel,
adminPinViewModel: adminPinViewModel,
isConfiguredNotifier: isConfiguredNotifier,
state: state,
),
routes: [
GoRoute(path: '/', redirect: (context, state) => '/bar'),
GoRoute(
path: '/register',
builder: (context, state) => const RegistrationView(),
),
GoRoute(
path: '/lock',
builder: (context, state) =>
@@ -187,4 +213,4 @@ GoRouter createAppRouter(PinLockViewModel pinLockViewModel, {
),
],
);
}
}