Selaa lähdekoodia

Merge branch 'master' into rewrite_device_sync

Neeraj Gupta 2 vuotta sitten
vanhempi
commit
a7766d3b30

+ 5 - 1
lib/core/configuration.dart

@@ -11,6 +11,7 @@ import 'package:logging/logging.dart';
 import 'package:path_provider/path_provider.dart';
 import 'package:photos/core/constants.dart';
 import 'package:photos/core/error-reporting/super_logging.dart';
+import 'package:photos/core/errors.dart';
 import 'package:photos/core/event_bus.dart';
 import 'package:photos/db/collections_db.dart';
 import 'package:photos/db/files_db.dart';
@@ -257,7 +258,10 @@ class Configuration {
       Sodium.base642bin(attributes.kekSalt),
       attributes.memLimit,
       attributes.opsLimit,
-    );
+    ).onError((e, s) {
+      _logger.severe('deriveKey failed', e, s);
+      throw KeyDerivationError();
+    });
 
     _logger.info('user-key done');
     Uint8List key;

+ 2 - 0
lib/core/errors.dart

@@ -40,3 +40,5 @@ class UnauthorizedEditError extends AssertionError {}
 class InvalidStateError extends AssertionError {
   InvalidStateError(String message) : super(message);
 }
+
+class KeyDerivationError extends Error {}

+ 3 - 0
lib/events/trash_updated_event.dart

@@ -0,0 +1,3 @@
+import 'package:photos/events/event.dart';
+
+class TrashUpdatedEvent extends Event {}

+ 17 - 4
lib/services/trash_sync_service.dart

@@ -10,6 +10,7 @@ import 'package:photos/core/network.dart';
 import 'package:photos/db/trash_db.dart';
 import 'package:photos/events/collection_updated_event.dart';
 import 'package:photos/events/force_reload_trash_page_event.dart';
+import 'package:photos/events/trash_updated_event.dart';
 import 'package:photos/models/file.dart';
 import 'package:photos/models/ignored_file.dart';
 import 'package:photos/models/trash_file.dart';
@@ -38,20 +39,24 @@ class TrashSyncService {
 
   Future<void> syncTrash() async {
     final lastSyncTime = _getSyncTime();
+    bool isLocalTrashUpdated = false;
     _logger.fine('sync trash sinceTime : $lastSyncTime');
     final diff = await _diffFetcher.getTrashFilesDiff(lastSyncTime);
     if (diff.trashedFiles.isNotEmpty) {
+      isLocalTrashUpdated = true;
       _logger.fine("inserting ${diff.trashedFiles.length} items in trash");
       await _trashDB.insertMultiple(diff.trashedFiles);
     }
     if (diff.deletedUploadIDs.isNotEmpty) {
       _logger.fine("discard ${diff.deletedUploadIDs.length} deleted items");
-      await _trashDB.delete(diff.deletedUploadIDs);
+      final itemsDeleted = await _trashDB.delete(diff.deletedUploadIDs);
+      isLocalTrashUpdated = isLocalTrashUpdated || itemsDeleted > 0;
     }
     if (diff.restoredFiles.isNotEmpty) {
       _logger.fine("discard ${diff.restoredFiles.length} restored items");
-      await _trashDB
+      final itemsDeleted = await _trashDB
           .delete(diff.restoredFiles.map((e) => e.uploadedFileID).toList());
+      isLocalTrashUpdated = isLocalTrashUpdated || itemsDeleted > 0;
     }
 
     await _updateIgnoredFiles(diff);
@@ -59,6 +64,11 @@ class TrashSyncService {
     if (diff.lastSyncedTimeStamp != 0) {
       await _setSyncTime(diff.lastSyncedTimeStamp);
     }
+    if (isLocalTrashUpdated) {
+      _logger
+          .fine('local trash updated, fire ${(TrashUpdatedEvent).toString()}');
+      Bus.instance.fire(TrashUpdatedEvent());
+    }
     if (diff.hasMore) {
       return await syncTrash();
     } else if (diff.trashedFiles.isNotEmpty ||
@@ -147,8 +157,10 @@ class TrashSyncService {
         ),
         data: params,
       );
-      _trashDB.delete(uniqueFileIds);
-      syncTrash();
+      await _trashDB.delete(uniqueFileIds);
+      Bus.instance.fire(TrashUpdatedEvent());
+      // no need to await on syncing trash from remote
+      unawaited(syncTrash());
     } catch (e, s) {
       _logger.severe("failed to delete from trash", e, s);
       rethrow;
@@ -170,6 +182,7 @@ class TrashSyncService {
       );
       await _trashDB.clearTable();
       unawaited(syncTrash());
+      Bus.instance.fire(TrashUpdatedEvent());
       Bus.instance.fire(ForceReloadTrashPageEvent());
     } catch (e, s) {
       _logger.severe("failed to empty trash", e, s);

+ 26 - 0
lib/ui/account/password_reentry_page.dart

@@ -3,6 +3,7 @@
 import 'package:flutter/material.dart';
 import 'package:logging/logging.dart';
 import 'package:photos/core/configuration.dart';
+import 'package:photos/core/errors.dart';
 import 'package:photos/core/event_bus.dart';
 import 'package:photos/events/subscription_purchased_event.dart';
 import 'package:photos/models/key_attributes.dart';
@@ -77,6 +78,31 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
               _passwordController.text,
               Configuration.instance.getKeyAttributes(),
             );
+          } on KeyDerivationError catch (e, s) {
+            _logger.severe("Password verification failed", e, s);
+            await dialog.hide();
+            final dialogUserChoice = await showChoiceDialog(
+              context,
+              "Recreate password",
+              "The current device is not powerful enough to verify your "
+                  "password, so we need to regenerate it once in a way that "
+                  "works with all devices. \n\nPlease login using your "
+                  "recovery key and regenerate your password (you can use the same one again if you wish).",
+              firstAction: "Cancel",
+              firstActionColor: Theme.of(context).colorScheme.primary,
+              secondAction: "Use recovery key",
+              secondActionColor: Theme.of(context).colorScheme.primary,
+            );
+            if (dialogUserChoice == DialogUserChoice.secondChoice) {
+              Navigator.of(context).push(
+                MaterialPageRoute(
+                  builder: (BuildContext context) {
+                    return const RecoveryPage();
+                  },
+                ),
+              );
+            }
+            return;
           } catch (e, s) {
             _logger.severe("Password verification failed", e, s);
             await dialog.hide();

+ 31 - 3
lib/ui/collections/trash_button_widget.dart

@@ -1,11 +1,15 @@
 // @dart=2.9
 
+import 'dart:async';
+
 import 'package:flutter/material.dart';
+import 'package:photos/core/event_bus.dart';
 import 'package:photos/db/trash_db.dart';
+import 'package:photos/events/trash_updated_event.dart';
 import 'package:photos/ui/viewer/gallery/trash_page.dart';
 import 'package:photos/utils/navigation_util.dart';
 
-class TrashButtonWidget extends StatelessWidget {
+class TrashButtonWidget extends StatefulWidget {
   const TrashButtonWidget(
     this.textStyle, {
     Key key,
@@ -13,6 +17,30 @@ class TrashButtonWidget extends StatelessWidget {
 
   final TextStyle textStyle;
 
+  @override
+  State<TrashButtonWidget> createState() => _TrashButtonWidgetState();
+}
+
+class _TrashButtonWidgetState extends State<TrashButtonWidget> {
+  StreamSubscription<TrashUpdatedEvent> _trashUpdatedEventSubscription;
+
+  @override
+  void initState() {
+    super.initState();
+    _trashUpdatedEventSubscription =
+        Bus.instance.on<TrashUpdatedEvent>().listen((event) {
+      if (mounted) {
+        setState(() {});
+      }
+    });
+  }
+
+  @override
+  void dispose() {
+    _trashUpdatedEventSubscription?.cancel();
+    super.dispose();
+  }
+
   @override
   Widget build(BuildContext context) {
     return OutlinedButton(
@@ -48,7 +76,7 @@ class TrashButtonWidget extends StatelessWidget {
                       if (snapshot.hasData && snapshot.data > 0) {
                         return RichText(
                           text: TextSpan(
-                            style: textStyle,
+                            style: widget.textStyle,
                             children: [
                               TextSpan(
                                 text: "Trash",
@@ -65,7 +93,7 @@ class TrashButtonWidget extends StatelessWidget {
                       } else {
                         return RichText(
                           text: TextSpan(
-                            style: textStyle,
+                            style: widget.textStyle,
                             children: [
                               TextSpan(
                                 text: "Trash",

+ 2 - 0
lib/utils/toast_util.dart

@@ -11,6 +11,7 @@ Future<void> showToast(
   BuildContext context,
   String message, {
   toastLength = Toast.LENGTH_LONG,
+  iOSDismissOnTap = true,
 }) async {
   if (Platform.isAndroid) {
     await Fluttertoast.cancel();
@@ -34,6 +35,7 @@ Future<void> showToast(
       message,
       duration: Duration(seconds: (toastLength == Toast.LENGTH_LONG ? 5 : 2)),
       toastPosition: EasyLoadingToastPosition.bottom,
+      dismissOnTap: iOSDismissOnTap,
     );
   }
 }