浏览代码

fix(mobile): better app state handling (#4989)

* fix(mobile): better app state handling

* watch -> read

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
Fynn Petersen-Frey 1 年之前
父节点
当前提交
38983838fd

+ 1 - 1
mobile/lib/modules/backup/providers/manual_upload.provider.dart

@@ -274,7 +274,7 @@ class ManualUploadNotifier extends StateNotifier<ManualUploadState> {
     // The app is currently in background. Perform the necessary cleanups which
     // are on-hold for upload completion
     if (appState != AppStateEnum.active && appState != AppStateEnum.resumed) {
-      ref.read(appStateProvider.notifier).handleAppInactivity();
+      ref.read(backupProvider.notifier).cancelBackup();
     }
   }
 

+ 37 - 33
mobile/lib/shared/providers/app_state.provider.dart

@@ -28,13 +28,10 @@ enum AppStateEnum {
 }
 
 class AppStateNotiifer extends StateNotifier<AppStateEnum> {
-  final Ref ref;
+  final Ref _ref;
+  bool _wasPaused = false;
 
-  AppStateNotiifer(this.ref) : super(AppStateEnum.active);
-
-  void updateAppState(AppStateEnum appState) {
-    state = appState;
-  }
+  AppStateNotiifer(this._ref) : super(AppStateEnum.active);
 
   AppStateEnum getAppState() {
     return state;
@@ -43,65 +40,72 @@ class AppStateNotiifer extends StateNotifier<AppStateEnum> {
   void handleAppResume() {
     state = AppStateEnum.resumed;
 
-    var isAuthenticated = ref.watch(authenticationProvider).isAuthenticated;
-    final permission = ref.watch(galleryPermissionNotifier);
+    // no need to resume because app was never really paused
+    if (!_wasPaused) return;
+    _wasPaused = false;
+
+    final isAuthenticated = _ref.read(authenticationProvider).isAuthenticated;
+    final permission = _ref.read(galleryPermissionNotifier);
 
     // Needs to be logged in and have gallery permissions
     if (isAuthenticated && (permission.isGranted || permission.isLimited)) {
-      ref.read(backupProvider.notifier).resumeBackup();
-      ref.read(backgroundServiceProvider).resumeServiceIfEnabled();
-      ref.read(serverInfoProvider.notifier).getServerVersion();
-      switch (ref.read(tabProvider)) {
+      _ref.read(backupProvider.notifier).resumeBackup();
+      _ref.read(backgroundServiceProvider).resumeServiceIfEnabled();
+      _ref.read(serverInfoProvider.notifier).getServerVersion();
+      switch (_ref.read(tabProvider)) {
         case TabEnum.home:
-          ref.read(assetProvider.notifier).getAllAsset();
-          ref.read(assetProvider.notifier).getPartnerAssets();
+          _ref.read(assetProvider.notifier).getAllAsset();
+          _ref.read(assetProvider.notifier).getPartnerAssets();
         case TabEnum.search:
         // nothing to do
         case TabEnum.sharing:
-          ref.read(assetProvider.notifier).getPartnerAssets();
-          ref.read(sharedAlbumProvider.notifier).getAllSharedAlbums();
+          _ref.read(assetProvider.notifier).getPartnerAssets();
+          _ref.read(sharedAlbumProvider.notifier).getAllSharedAlbums();
         case TabEnum.library:
-          ref.read(albumProvider.notifier).getAllAlbums();
+          _ref.read(albumProvider.notifier).getAllAlbums();
       }
     }
 
-    ref.watch(websocketProvider.notifier).connect();
+    _ref.read(websocketProvider.notifier).connect();
 
-    ref.watch(releaseInfoProvider.notifier).checkGithubReleaseInfo();
+    _ref.read(releaseInfoProvider.notifier).checkGithubReleaseInfo();
 
-    ref
-        .watch(notificationPermissionProvider.notifier)
+    _ref
+        .read(notificationPermissionProvider.notifier)
         .getNotificationPermission();
-    ref.watch(galleryPermissionNotifier.notifier).getGalleryPermissionStatus();
+    _ref.read(galleryPermissionNotifier.notifier).getGalleryPermissionStatus();
 
-    ref.read(iOSBackgroundSettingsProvider.notifier).refresh();
+    _ref.read(iOSBackgroundSettingsProvider.notifier).refresh();
 
-    ref.invalidate(memoryFutureProvider);
+    _ref.invalidate(memoryFutureProvider);
   }
 
   void handleAppInactivity() {
     state = AppStateEnum.inactive;
-
-    // Do not handle inactivity if manual upload is in progress
-    if (ref.watch(backupProvider.notifier).backupProgress !=
-        BackUpProgressEnum.manualInProgress) {
-      ImmichLogger().flush();
-      ref.read(websocketProvider.notifier).disconnect();
-      ref.read(backupProvider.notifier).cancelBackup();
-    }
+    // do not stop/clean up anything on inactivity: issued on every orientation change
   }
 
   void handleAppPause() {
     state = AppStateEnum.paused;
+    _wasPaused = true;
+    // Do not cancel backup if manual upload is in progress
+    if (_ref.read(backupProvider.notifier).backupProgress !=
+        BackUpProgressEnum.manualInProgress) {
+      _ref.read(backupProvider.notifier).cancelBackup();
+    }
+    _ref.read(websocketProvider.notifier).disconnect();
+    ImmichLogger().flush();
   }
 
   void handleAppDetached() {
     state = AppStateEnum.detached;
-    ref.watch(manualUploadProvider.notifier).cancelBackup();
+    // no guarantee this is called at all
+    _ref.read(manualUploadProvider.notifier).cancelBackup();
   }
 
   void handleAppHidden() {
     state = AppStateEnum.hidden;
+    // do not stop/clean up anything on inactivity: issued on every orientation change
   }
 }
 

+ 25 - 27
mobile/lib/shared/providers/websocket.provider.dart

@@ -63,21 +63,19 @@ class WebsocketState {
 }
 
 class WebsocketNotifier extends StateNotifier<WebsocketState> {
-  WebsocketNotifier(this.ref)
+  WebsocketNotifier(this._ref)
       : super(
           WebsocketState(socket: null, isConnected: false, pendingChanges: []),
-        ) {
-    debounce = Debounce(
-      const Duration(milliseconds: 500),
-    );
-  }
+        );
 
-  final log = Logger('WebsocketNotifier');
-  final Ref ref;
-  late final Debounce debounce;
+  final _log = Logger('WebsocketNotifier');
+  final Ref _ref;
+  final Debounce _debounce = Debounce(const Duration(milliseconds: 500));
 
-  connect() {
-    var authenticationState = ref.read(authenticationProvider);
+  /// Connects websocket to server unless already connected
+  void connect() {
+    if (state.isConnected) return;
+    final authenticationState = _ref.read(authenticationProvider);
 
     if (authenticationState.isAuthenticated) {
       final accessToken = Store.get(StoreKey.accessToken);
@@ -118,7 +116,7 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
         });
 
         socket.on('error', (errorMessage) {
-          log.severe("Websocket Error - $errorMessage");
+          _log.severe("Websocket Error - $errorMessage");
           state = WebsocketState(
             isConnected: false,
             socket: null,
@@ -138,7 +136,7 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
     }
   }
 
-  disconnect() {
+  void disconnect() {
     debugPrint("Attempting to disconnect from websocket");
 
     var socket = state.socket?.disconnect();
@@ -152,30 +150,30 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
     }
   }
 
-  stopListenToEvent(String eventName) {
+  void stopListenToEvent(String eventName) {
     debugPrint("Stop listening to event $eventName");
     state.socket?.off(eventName);
   }
 
-  listenUploadEvent() {
+  void listenUploadEvent() {
     debugPrint("Start listening to event on_upload_success");
     state.socket?.on('on_upload_success', _handleOnUploadSuccess);
   }
 
-  addPendingChange(PendingAction action, dynamic value) {
+  void addPendingChange(PendingAction action, dynamic value) {
     state = state.copyWith(
       pendingChanges: [...state.pendingChanges, PendingChange(action, value)],
     );
   }
 
-  handlePendingChanges() {
+  void handlePendingChanges() {
     final deleteChanges = state.pendingChanges
         .where((c) => c.action == PendingAction.assetDelete)
         .toList();
     if (deleteChanges.isNotEmpty) {
       List<String> remoteIds =
           deleteChanges.map((a) => a.value.toString()).toList();
-      ref.read(syncServiceProvider).handleRemoteAssetRemoval(remoteIds);
+      _ref.read(syncServiceProvider).handleRemoteAssetRemoval(remoteIds);
       state = state.copyWith(
         pendingChanges: state.pendingChanges
             .where((c) => c.action != PendingAction.assetDelete)
@@ -184,27 +182,27 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
     }
   }
 
-  _handleOnUploadSuccess(dynamic data) {
+  void _handleOnUploadSuccess(dynamic data) {
     final dto = AssetResponseDto.fromJson(data);
     if (dto != null) {
       final newAsset = Asset.remote(dto);
-      ref.watch(assetProvider.notifier).onNewAssetUploaded(newAsset);
+      _ref.watch(assetProvider.notifier).onNewAssetUploaded(newAsset);
     }
   }
 
-  _handleOnConfigUpdate(dynamic _) {
-    ref.read(serverInfoProvider.notifier).getServerFeatures();
-    ref.read(serverInfoProvider.notifier).getServerConfig();
+  void _handleOnConfigUpdate(dynamic _) {
+    _ref.read(serverInfoProvider.notifier).getServerFeatures();
+    _ref.read(serverInfoProvider.notifier).getServerConfig();
   }
 
   // Refresh updated assets
-  _handleServerUpdates(dynamic _) {
-    ref.read(assetProvider.notifier).getAllAsset();
+  void _handleServerUpdates(dynamic _) {
+    _ref.read(assetProvider.notifier).getAllAsset();
   }
 
-  _handleOnAssetDelete(dynamic data) {
+  void _handleOnAssetDelete(dynamic data) {
     addPendingChange(PendingAction.assetDelete, data);
-    debounce(handlePendingChanges);
+    _debounce(handlePendingChanges);
   }
 }