Selaa lähdekoodia

Merge branch 'main' into video_no_thum

Neeraj Gupta 1 vuosi sitten
vanhempi
commit
23ff7e4b69

+ 13 - 0
lib/db/embeddings_db.dart

@@ -48,6 +48,19 @@ class EmbeddingsDB {
     return await _isar.embeddings.filter().updationTimeEqualTo(null).findAll();
   }
 
+  Future<void> deleteEmbeddings(List<int> fileIDs) async {
+    await _isar.writeTxn(() async {
+      final embeddings = <Embedding>[];
+      for (final fileID in fileIDs) {
+        embeddings.addAll(
+          await _isar.embeddings.filter().fileIDEqualTo(fileID).findAll(),
+        );
+      }
+      await _isar.embeddings.deleteAll(embeddings.map((e) => e.id).toList());
+      Bus.instance.fire(EmbeddingUpdatedEvent());
+    });
+  }
+
   Future<void> deleteAllForModel(Model model) async {
     await _isar.writeTxn(() async {
       final embeddings =

+ 10 - 1
lib/services/machine_learning/semantic_search/embedding_store.dart

@@ -53,13 +53,22 @@ class EmbeddingStore {
     final fileMap = await FilesDB.instance
         .getFilesFromIDs(pendingItems.map((e) => e.fileID).toList());
     _logger.info("Pushing ${pendingItems.length} embeddings");
+    final deletedEntries = <int>[];
     for (final item in pendingItems) {
       try {
-        await _pushEmbedding(fileMap[item.fileID]!, item);
+        final file = fileMap[item.fileID];
+        if (file != null) {
+          await _pushEmbedding(file, item);
+        } else {
+          deletedEntries.add(item.fileID);
+        }
       } catch (e, s) {
         _logger.severe(e, s);
       }
     }
+    if (deletedEntries.isNotEmpty) {
+      await EmbeddingsDB.instance.deleteEmbeddings(deletedEntries);
+    }
   }
 
   Future<void> storeEmbedding(EnteFile file, Embedding embedding) async {

+ 9 - 1
lib/services/machine_learning/semantic_search/semantic_search_service.dart

@@ -243,15 +243,23 @@ class SemanticSearchService {
 
     final ignoredCollections =
         CollectionsService.instance.getHiddenCollectionIds();
+    final deletedEntries = <int>[];
     for (final result in queryResults) {
       final file = filesMap[result.id];
       if (file != null && !ignoredCollections.contains(file.collectionID)) {
-        results.add(filesMap[result.id]!);
+        results.add(file);
+      }
+      if (file == null) {
+        deletedEntries.add(result.id);
       }
     }
 
     _logger.info(results.length.toString() + " results");
 
+    if (deletedEntries.isNotEmpty) {
+      unawaited(EmbeddingsDB.instance.deleteEmbeddings(deletedEntries));
+    }
+
     return results;
   }
 

+ 73 - 82
lib/ui/account/password_reentry_page.dart

@@ -42,7 +42,7 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
       _passwordController.text = _volatilePassword!;
       Future.delayed(
         Duration.zero,
-            () => verifyPassword(_volatilePassword!),
+        () => verifyPassword(_volatilePassword!),
       );
     }
     _passwordFocusNode.addListener(() {
@@ -100,69 +100,68 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
   }
 
   Future<void> verifyPassword(String password) async {
-      FocusScope.of(context).unfocus();
-      final dialog =
-      createProgressDialog(context, S.of(context).pleaseWait);
-      await dialog.show();
-      try {
-        final kek = await Configuration.instance.decryptSecretsAndGetKeyEncKey(
-          password,
-          Configuration.instance.getKeyAttributes()!,
-        );
-        _registerSRPForExistingUsers(kek).ignore();
-      } on KeyDerivationError catch (e, s) {
-        _logger.severe("Password verification failed", e, s);
-        await dialog.hide();
-        final dialogChoice = await showChoiceDialog(
-          context,
-          title: S.of(context).recreatePasswordTitle,
-          body: S.of(context).recreatePasswordBody,
-          firstButtonLabel: S.of(context).useRecoveryKey,
-        );
-        if (dialogChoice!.action == ButtonAction.first) {
-          // ignore: unawaited_futures
-          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();
-        final dialogChoice = await showChoiceDialog(
-          context,
-          title: S.of(context).incorrectPasswordTitle,
-          body: S.of(context).pleaseTryAgain,
-          firstButtonLabel: S.of(context).contactSupport,
-          secondButtonLabel: S.of(context).ok,
-        );
-        if (dialogChoice!.action == ButtonAction.first) {
-          await sendLogs(
-            context,
-            S.of(context).contactSupport,
-            "support@ente.io",
-            postShare: () {},
-          );
-        }
-        return;
-      }
+    FocusScope.of(context).unfocus();
+    final dialog = createProgressDialog(context, S.of(context).pleaseWait);
+    await dialog.show();
+    try {
+      final kek = await Configuration.instance.decryptSecretsAndGetKeyEncKey(
+        password,
+        Configuration.instance.getKeyAttributes()!,
+      );
+      _registerSRPForExistingUsers(kek).ignore();
+    } on KeyDerivationError catch (e, s) {
+      _logger.severe("Password verification failed", e, s);
       await dialog.hide();
-      Configuration.instance.setVolatilePassword(null);
-      Bus.instance.fire(SubscriptionPurchasedEvent());
-      unawaited(
-        Navigator.of(context).pushAndRemoveUntil(
+      final dialogChoice = await showChoiceDialog(
+        context,
+        title: S.of(context).recreatePasswordTitle,
+        body: S.of(context).recreatePasswordBody,
+        firstButtonLabel: S.of(context).useRecoveryKey,
+      );
+      if (dialogChoice!.action == ButtonAction.first) {
+        // ignore: unawaited_futures
+        Navigator.of(context).push(
           MaterialPageRoute(
             builder: (BuildContext context) {
-              return const HomeWidget();
+              return const RecoveryPage();
             },
           ),
-              (route) => false,
-        ),
+        );
+      }
+      return;
+    } catch (e, s) {
+      _logger.severe("Password verification failed", e, s);
+      await dialog.hide();
+      final dialogChoice = await showChoiceDialog(
+        context,
+        title: S.of(context).incorrectPasswordTitle,
+        body: S.of(context).pleaseTryAgain,
+        firstButtonLabel: S.of(context).contactSupport,
+        secondButtonLabel: S.of(context).ok,
       );
+      if (dialogChoice!.action == ButtonAction.first) {
+        await sendLogs(
+          context,
+          S.of(context).contactSupport,
+          "support@ente.io",
+          postShare: () {},
+        );
+      }
+      return;
+    }
+    await dialog.hide();
+    Configuration.instance.setVolatilePassword(null);
+    Bus.instance.fire(SubscriptionPurchasedEvent());
+    unawaited(
+      Navigator.of(context).pushAndRemoveUntil(
+        MaterialPageRoute(
+          builder: (BuildContext context) {
+            return const HomeWidget();
+          },
+        ),
+        (route) => false,
+      ),
+    );
   }
 
   Future<void> _registerSRPForExistingUsers(Uint8List key) async {
@@ -266,8 +265,8 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
                 ),
                 Padding(
                   padding: const EdgeInsets.symmetric(horizontal: 20),
-                  child: Row(
-                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                  child: Wrap(
+                    alignment: WrapAlignment.spaceBetween,
                     children: [
                       GestureDetector(
                         behavior: HitTestBehavior.opaque,
@@ -280,17 +279,13 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
                             ),
                           );
                         },
-                        child: Center(
-                          child: Text(
-                            S.of(context).forgotPassword,
-                            style: Theme.of(context)
-                                .textTheme
-                                .titleMedium!
-                                .copyWith(
-                                  fontSize: 14,
-                                  decoration: TextDecoration.underline,
-                                ),
-                          ),
+                        child: Text(
+                          S.of(context).forgotPassword,
+                          style:
+                              Theme.of(context).textTheme.titleMedium!.copyWith(
+                                    fontSize: 14,
+                                    decoration: TextDecoration.underline,
+                                  ),
                         ),
                       ),
                       GestureDetector(
@@ -306,17 +301,13 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
                           Navigator.of(context)
                               .popUntil((route) => route.isFirst);
                         },
-                        child: Center(
-                          child: Text(
-                            S.of(context).changeEmail,
-                            style: Theme.of(context)
-                                .textTheme
-                                .titleMedium!
-                                .copyWith(
-                                  fontSize: 14,
-                                  decoration: TextDecoration.underline,
-                                ),
-                          ),
+                        child: Text(
+                          S.of(context).changeEmail,
+                          style:
+                              Theme.of(context).textTheme.titleMedium!.copyWith(
+                                    fontSize: 14,
+                                    decoration: TextDecoration.underline,
+                                  ),
                         ),
                       ),
                     ],

+ 0 - 1
lib/ui/common/dynamic_fab.dart

@@ -60,7 +60,6 @@ class DynamicFAB extends StatelessWidget {
     } else {
       return Container(
         width: double.infinity,
-        height: 56,
         padding: const EdgeInsets.symmetric(horizontal: 20),
         child: OutlinedButton(
           onPressed:

+ 1 - 11
lib/ui/payment/subscription.dart

@@ -1,5 +1,4 @@
 import 'package:flutter/cupertino.dart';
-import 'package:photos/core/configuration.dart';
 import 'package:photos/services/feature_flag_service.dart';
 import 'package:photos/services/update_service.dart';
 import "package:photos/ui/payment/store_subscription_page.dart";
@@ -9,18 +8,9 @@ StatefulWidget getSubscriptionPage({bool isOnBoarding = false}) {
   if (UpdateService.instance.isIndependentFlavor()) {
     return StripeSubscriptionPage(isOnboarding: isOnBoarding);
   }
-  if (FeatureFlagService.instance.enableStripe() &&
-      _isUserCreatedPostStripeSupport()) {
+  if (FeatureFlagService.instance.enableStripe()) {
     return StripeSubscriptionPage(isOnboarding: isOnBoarding);
   } else {
     return StoreSubscriptionPage(isOnboarding: isOnBoarding);
   }
 }
-
-// return true if the user was created after we added support for stripe payment
-// on frame. We do this check to avoid showing Stripe payment option for earlier
-// users who might have paid via playStore. This method should be removed once
-// we have better handling for active play/app store subscription & stripe plans.
-bool _isUserCreatedPostStripeSupport() {
-  return Configuration.instance.getUserID()! > 1580559962386460;
-}

+ 3 - 7
lib/ui/payment/subscription_common_widgets.dart

@@ -36,13 +36,9 @@ class _SubscriptionHeaderWidgetState extends State<SubscriptionHeaderWidget> {
         child: Column(
           crossAxisAlignment: CrossAxisAlignment.start,
           children: [
-            Row(
-              children: [
-                Text(
-                  S.of(context).selectYourPlan,
-                  style: Theme.of(context).textTheme.headlineMedium,
-                ),
-              ],
+            Text(
+              S.of(context).selectYourPlan,
+              style: Theme.of(context).textTheme.headlineMedium,
             ),
             const SizedBox(height: 10),
             Text(

+ 8 - 4
lib/ui/viewer/file/video_widget_new.dart

@@ -252,10 +252,14 @@ class _VideoWidgetNewState extends State<VideoWidgetNew>
 
   void _setVideoController(String url) {
     if (mounted) {
-      setState(() {
-        player.setPlaylistMode(PlaylistMode.single);
-        controller = VideoController(player);
-        player.open(Media(url), play: _isAppInFG);
+      setState(() async {
+        try {
+          await player.setPlaylistMode(PlaylistMode.single);
+          controller = VideoController(player);
+          await player.open(Media(url), play: _isAppInFG);
+        } catch (e, s) {
+          _logger.severe("failed to set video url", e, s);
+        }
       });
     }
   }

+ 0 - 1
lib/ui/viewer/search/search_widget.dart

@@ -129,7 +129,6 @@ class SearchWidgetState extends State<SearchWidget> {
               child: Container(
                 color: colorScheme.backgroundBase,
                 child: Container(
-                  height: 44,
                   color: colorScheme.fillFaint,
                   child: TextFormField(
                     controller: textController,

+ 30 - 5
lib/utils/file_uploader.dart

@@ -35,6 +35,7 @@ import "package:photos/services/user_service.dart";
 import 'package:photos/utils/crypto_util.dart';
 import 'package:photos/utils/file_download_util.dart';
 import 'package:photos/utils/file_uploader_util.dart';
+import "package:photos/utils/file_util.dart";
 import 'package:shared_preferences/shared_preferences.dart';
 import 'package:tuple/tuple.dart';
 import "package:uuid/uuid.dart";
@@ -309,12 +310,36 @@ class FileUploader {
         return file.path.contains(kUploadTempPrefix) &&
             file.path.contains(".encrypted");
       });
-      if (filesToDelete.isEmpty) {
-        return;
+      if (filesToDelete.isNotEmpty) {
+        _logger.info('cleaning up state files ${filesToDelete.length}');
+        for (final file in filesToDelete) {
+          await file.delete();
+        }
       }
-      _logger.info('cleaning up state files ${filesToDelete.length}');
-      for (final file in filesToDelete) {
-        await file.delete();
+
+      if (Platform.isAndroid) {
+        final sharedMediaDir =
+            Configuration.instance.getSharedMediaDirectory() + "/";
+        final sharedFiles = await Directory(sharedMediaDir).list().toList();
+        if (sharedFiles.isNotEmpty) {
+          _logger.info('Shared media directory cleanup ${sharedFiles.length}');
+          final int ownerID = Configuration.instance.getUserID()!;
+          final existingLocalFileIDs =
+              await FilesDB.instance.getExistingLocalFileIDs(ownerID);
+          final Set<String> trackedSharedFilePaths = {};
+          for (String localID in existingLocalFileIDs) {
+            if (localID.contains(sharedMediaIdentifier)) {
+              trackedSharedFilePaths
+                  .add(getSharedMediaPathFromLocalID(localID));
+            }
+          }
+          for (final file in sharedFiles) {
+            if (!trackedSharedFilePaths.contains(file.path)) {
+              _logger.info('Deleting stale shared media file ${file.path}');
+              await file.delete();
+            }
+          }
+        }
       }
     } catch (e, s) {
       _logger.severe("Failed to remove stale files", e, s);