فهرست منبع

Merge branch 'master' into settings-to-ham

ashilkn 2 سال پیش
والد
کامیت
c60bd49269

+ 0 - 5
.gitmodules

@@ -6,11 +6,6 @@
 	path = thirdparty/plugins
 	path = thirdparty/plugins
 	url = https://github.com/ente-io/plugins.git
 	url = https://github.com/ente-io/plugins.git
 
 
-[submodule "thirdparty/sentry-dart"]
-	path = thirdparty/sentry-dart
-	url = https://github.com/ente-io/sentry-dart.git
-	branch = sentry_flutter_ente
-
 [submodule "thirdparty/extended_image"]
 [submodule "thirdparty/extended_image"]
     path = thirdparty/extended_image
     path = thirdparty/extended_image
     url = https://github.com/ente-io/extended_image.git
     url = https://github.com/ente-io/extended_image.git

+ 7 - 0
.vscode/settings.json

@@ -0,0 +1,7 @@
+{
+    "workbench.colorCustomizations": {
+        "activityBar.background": "#123046",
+        "titleBar.activeBackground": "#194363",
+        "titleBar.activeForeground": "#F9FBFD"
+    }
+}

+ 12 - 12
ios/Podfile.lock

@@ -100,7 +100,7 @@ PODS:
     - GoogleUtilities/Logger
     - GoogleUtilities/Logger
   - image_editor (1.0.0):
   - image_editor (1.0.0):
     - Flutter
     - Flutter
-  - in_app_purchase (0.0.1):
+  - in_app_purchase_storekit (0.0.1):
     - Flutter
     - Flutter
   - libwebp (1.2.3):
   - libwebp (1.2.3):
     - libwebp/demux (= 1.2.3)
     - libwebp/demux (= 1.2.3)
@@ -145,13 +145,13 @@ PODS:
   - SDWebImageWebPCoder (0.9.1):
   - SDWebImageWebPCoder (0.9.1):
     - libwebp (~> 1.0)
     - libwebp (~> 1.0)
     - SDWebImage/Core (~> 5.13)
     - SDWebImage/Core (~> 5.13)
-  - Sentry (7.25.1):
-    - Sentry/Core (= 7.25.1)
-  - Sentry/Core (7.25.1)
+  - Sentry (7.27.1):
+    - Sentry/Core (= 7.27.1)
+  - Sentry/Core (7.27.1)
   - sentry_flutter (0.0.1):
   - sentry_flutter (0.0.1):
     - Flutter
     - Flutter
     - FlutterMacOS
     - FlutterMacOS
-    - Sentry (~> 7.25.1)
+    - Sentry (~> 7.27.1)
   - share_plus (0.0.1):
   - share_plus (0.0.1):
     - Flutter
     - Flutter
   - shared_preferences_ios (0.0.1):
   - shared_preferences_ios (0.0.1):
@@ -190,7 +190,7 @@ DEPENDENCIES:
   - flutter_sodium (from `.symlinks/plugins/flutter_sodium/ios`)
   - flutter_sodium (from `.symlinks/plugins/flutter_sodium/ios`)
   - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
   - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
   - image_editor (from `.symlinks/plugins/image_editor/ios`)
   - image_editor (from `.symlinks/plugins/image_editor/ios`)
-  - in_app_purchase (from `.symlinks/plugins/in_app_purchase/ios`)
+  - in_app_purchase_storekit (from `.symlinks/plugins/in_app_purchase_storekit/ios`)
   - local_auth (from `.symlinks/plugins/local_auth/ios`)
   - local_auth (from `.symlinks/plugins/local_auth/ios`)
   - motionphoto (from `.symlinks/plugins/motionphoto/ios`)
   - motionphoto (from `.symlinks/plugins/motionphoto/ios`)
   - move_to_background (from `.symlinks/plugins/move_to_background/ios`)
   - move_to_background (from `.symlinks/plugins/move_to_background/ios`)
@@ -266,8 +266,8 @@ EXTERNAL SOURCES:
     :path: ".symlinks/plugins/fluttertoast/ios"
     :path: ".symlinks/plugins/fluttertoast/ios"
   image_editor:
   image_editor:
     :path: ".symlinks/plugins/image_editor/ios"
     :path: ".symlinks/plugins/image_editor/ios"
-  in_app_purchase:
-    :path: ".symlinks/plugins/in_app_purchase/ios"
+  in_app_purchase_storekit:
+    :path: ".symlinks/plugins/in_app_purchase_storekit/ios"
   local_auth:
   local_auth:
     :path: ".symlinks/plugins/local_auth/ios"
     :path: ".symlinks/plugins/local_auth/ios"
   motionphoto:
   motionphoto:
@@ -316,7 +316,7 @@ SPEC CHECKSUMS:
   FirebaseInstallations: 0a115432c4e223c5ab20b0dbbe4cbefa793a0e8e
   FirebaseInstallations: 0a115432c4e223c5ab20b0dbbe4cbefa793a0e8e
   FirebaseMessaging: 732623518591384f61c287e3d8f65294beb7ffb3
   FirebaseMessaging: 732623518591384f61c287e3d8f65294beb7ffb3
   fk_user_agent: 1f47ec39291e8372b1d692b50084b0d54103c545
   fk_user_agent: 1f47ec39291e8372b1d692b50084b0d54103c545
-  Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
+  Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
   flutter_email_sender: 02d7443217d8c41483223627972bfdc09f74276b
   flutter_email_sender: 02d7443217d8c41483223627972bfdc09f74276b
   flutter_image_compress: 5a5e9aee05b6553048b8df1c3bc456d0afaac433
   flutter_image_compress: 5a5e9aee05b6553048b8df1c3bc456d0afaac433
   flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721
   flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721
@@ -330,7 +330,7 @@ SPEC CHECKSUMS:
   GoogleDataTransport: 1c8145da7117bd68bbbed00cf304edb6a24de00f
   GoogleDataTransport: 1c8145da7117bd68bbbed00cf304edb6a24de00f
   GoogleUtilities: 1d20a6ad97ef46f67bbdec158ce00563a671ebb7
   GoogleUtilities: 1d20a6ad97ef46f67bbdec158ce00563a671ebb7
   image_editor: eab82a302a6623a866da5145b7c4c0ee8a4ffbb4
   image_editor: eab82a302a6623a866da5145b7c4c0ee8a4ffbb4
-  in_app_purchase: 3e2155afa9d03d4fa32d9e62d567885080ce97d6
+  in_app_purchase_storekit: d7fcf4646136ec258e237872755da8ea6c1b6096
   libwebp: 60305b2e989864154bd9be3d772730f08fc6a59c
   libwebp: 60305b2e989864154bd9be3d772730f08fc6a59c
   local_auth: 1740f55d7af0a2e2a8684ce225fe79d8931e808c
   local_auth: 1740f55d7af0a2e2a8684ce225fe79d8931e808c
   Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
   Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
@@ -347,8 +347,8 @@ SPEC CHECKSUMS:
   receive_sharing_intent: c0d87310754e74c0f9542947e7cbdf3a0335a3b1
   receive_sharing_intent: c0d87310754e74c0f9542947e7cbdf3a0335a3b1
   SDWebImage: e5cc87bf736e60f49592f307bdf9e157189298a3
   SDWebImage: e5cc87bf736e60f49592f307bdf9e157189298a3
   SDWebImageWebPCoder: 18503de6621dd2c420d680e33d46bf8e1d5169b0
   SDWebImageWebPCoder: 18503de6621dd2c420d680e33d46bf8e1d5169b0
-  Sentry: dd29c18c32b0af9269949f079cf631d581ca76ca
-  sentry_flutter: 544b23de27343d0cd12d8d16b0fac71dc884f0e6
+  Sentry: bc644307e2eb6a4c9c55cf117a80b895bb2a25a7
+  sentry_flutter: 649559f0512e00d3f6fc92cf51f74bc2fe68d1d3
   share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
   share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
   shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad
   shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad
   sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
   sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904

+ 2 - 2
ios/Runner.xcodeproj/project.pbxproj

@@ -286,7 +286,7 @@
 				"${BUILT_PRODUCTS_DIR}/flutter_sodium/flutter_sodium.framework",
 				"${BUILT_PRODUCTS_DIR}/flutter_sodium/flutter_sodium.framework",
 				"${BUILT_PRODUCTS_DIR}/fluttertoast/fluttertoast.framework",
 				"${BUILT_PRODUCTS_DIR}/fluttertoast/fluttertoast.framework",
 				"${BUILT_PRODUCTS_DIR}/image_editor/image_editor.framework",
 				"${BUILT_PRODUCTS_DIR}/image_editor/image_editor.framework",
-				"${BUILT_PRODUCTS_DIR}/in_app_purchase/in_app_purchase.framework",
+				"${BUILT_PRODUCTS_DIR}/in_app_purchase_storekit/in_app_purchase_storekit.framework",
 				"${BUILT_PRODUCTS_DIR}/libwebp/libwebp.framework",
 				"${BUILT_PRODUCTS_DIR}/libwebp/libwebp.framework",
 				"${BUILT_PRODUCTS_DIR}/local_auth/local_auth.framework",
 				"${BUILT_PRODUCTS_DIR}/local_auth/local_auth.framework",
 				"${BUILT_PRODUCTS_DIR}/motionphoto/motionphoto.framework",
 				"${BUILT_PRODUCTS_DIR}/motionphoto/motionphoto.framework",
@@ -339,7 +339,7 @@
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_sodium.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_sodium.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/fluttertoast.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/fluttertoast.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_editor.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_editor.framework",
-				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/in_app_purchase.framework",
+				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/in_app_purchase_storekit.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/local_auth.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/local_auth.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/motionphoto.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/motionphoto.framework",

+ 0 - 2
lib/main.dart

@@ -7,7 +7,6 @@ import 'package:background_fetch/background_fetch.dart';
 import 'package:firebase_messaging/firebase_messaging.dart';
 import 'package:firebase_messaging/firebase_messaging.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
-import 'package:in_app_purchase/in_app_purchase.dart';
 import 'package:logging/logging.dart';
 import 'package:logging/logging.dart';
 import 'package:path_provider/path_provider.dart';
 import 'package:path_provider/path_provider.dart';
 import 'package:photos/app.dart';
 import 'package:photos/app.dart';
@@ -128,7 +127,6 @@ Future<void> _init(bool isBackground, {String via = ''}) async {
   } else {
   } else {
     AppLifecycleService.instance.onAppInForeground('init via: $via');
     AppLifecycleService.instance.onAppInForeground('init via: $via');
   }
   }
-  InAppPurchaseConnection.enablePendingPurchases();
   CryptoUtil.init();
   CryptoUtil.init();
   await NotificationService.instance.init();
   await NotificationService.instance.init();
   await Network.instance.init();
   await Network.instance.init();

+ 3 - 4
lib/services/billing_service.dart

@@ -38,12 +38,11 @@ class BillingService {
   Future<BillingPlans> _future;
   Future<BillingPlans> _future;
 
 
   Future<void> init() async {
   Future<void> init() async {
-    InAppPurchaseConnection.enablePendingPurchases();
     // if (Platform.isIOS && kDebugMode) {
     // if (Platform.isIOS && kDebugMode) {
     //   await FlutterInappPurchase.instance.initConnection;
     //   await FlutterInappPurchase.instance.initConnection;
     //   FlutterInappPurchase.instance.clearTransactionIOS();
     //   FlutterInappPurchase.instance.clearTransactionIOS();
     // }
     // }
-    InAppPurchaseConnection.instance.purchaseUpdatedStream.listen((purchases) {
+    InAppPurchase.instance.purchaseStream.listen((purchases) {
       if (_isOnSubscriptionPage) {
       if (_isOnSubscriptionPage) {
         return;
         return;
       }
       }
@@ -54,11 +53,11 @@ class BillingService {
             purchase.verificationData.serverVerificationData,
             purchase.verificationData.serverVerificationData,
           ).then((response) {
           ).then((response) {
             if (response != null) {
             if (response != null) {
-              InAppPurchaseConnection.instance.completePurchase(purchase);
+              InAppPurchase.instance.completePurchase(purchase);
             }
             }
           });
           });
         } else if (Platform.isIOS && purchase.pendingCompletePurchase) {
         } else if (Platform.isIOS && purchase.pendingCompletePurchase) {
-          InAppPurchaseConnection.instance.completePurchase(purchase);
+          InAppPurchase.instance.completePurchase(purchase);
         }
         }
       }
       }
     });
     });

+ 18 - 1
lib/services/collections_service.dart

@@ -292,7 +292,7 @@ class CollectionsService {
   Uint8List _getDecryptedKey(Collection collection) {
   Uint8List _getDecryptedKey(Collection collection) {
     final encryptedKey = Sodium.base642bin(collection.encryptedKey);
     final encryptedKey = Sodium.base642bin(collection.encryptedKey);
     if (collection.owner.id == _config.getUserID()) {
     if (collection.owner.id == _config.getUserID()) {
-      if(_config.getKey() == null) {
+      if (_config.getKey() == null) {
         throw Exception("key can not be null");
         throw Exception("key can not be null");
       }
       }
       return CryptoUtil.decryptSync(
       return CryptoUtil.decryptSync(
@@ -334,6 +334,23 @@ class CollectionsService {
     }
     }
   }
   }
 
 
+  Future<void> leaveAlbum(Collection collection) async {
+    try {
+      await _dio.post(
+        Configuration.instance.getHttpEndpoint() +
+            "/collections/leave/${collection.id}",
+        options: Options(
+          headers: {"X-Auth-Token": Configuration.instance.getToken()},
+        ),
+      );
+      // trigger sync to fetch the latest name from server
+      sync();
+    } catch (e, s) {
+      _logger.severe("failed to leave collection", e, s);
+      rethrow;
+    }
+  }
+
   Future<void> updateMagicMetadata(
   Future<void> updateMagicMetadata(
     Collection collection,
     Collection collection,
     Map<String, dynamic> newMetadataUpdate,
     Map<String, dynamic> newMetadataUpdate,

+ 3 - 1
lib/ui/payment/stripe_subscription_page.dart

@@ -289,7 +289,9 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
                   RichText(
                   RichText(
                     text: TextSpan(
                     text: TextSpan(
                       text: "Manage family",
                       text: "Manage family",
-                      style: Theme.of(context).textTheme.overline,
+                      style: Theme.of(context).textTheme.bodyMedium.copyWith(
+                            decoration: TextDecoration.underline,
+                          ),
                     ),
                     ),
                     textAlign: TextAlign.center,
                     textAlign: TextAlign.center,
                   ),
                   ),

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

@@ -132,7 +132,9 @@ class SubFaqWidget extends StatelessWidget {
           child: RichText(
           child: RichText(
             text: TextSpan(
             text: TextSpan(
               text: "Questions?",
               text: "Questions?",
-              style: Theme.of(context).textTheme.overline,
+              style: Theme.of(context).textTheme.bodyMedium.copyWith(
+                    decoration: TextDecoration.underline,
+                  ),
             ),
             ),
           ),
           ),
         ),
         ),

+ 16 - 35
lib/ui/payment/subscription_page.dart

@@ -60,9 +60,8 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
   }
   }
 
 
   void _setupPurchaseUpdateStreamListener() {
   void _setupPurchaseUpdateStreamListener() {
-    _purchaseUpdateSubscription = InAppPurchaseConnection
-        .instance.purchaseUpdatedStream
-        .listen((purchases) async {
+    _purchaseUpdateSubscription =
+        InAppPurchase.instance.purchaseStream.listen((purchases) async {
       if (!_dialog.isShowing()) {
       if (!_dialog.isShowing()) {
         await _dialog.show();
         await _dialog.show();
       }
       }
@@ -74,7 +73,7 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
               purchase.productID,
               purchase.productID,
               purchase.verificationData.serverVerificationData,
               purchase.verificationData.serverVerificationData,
             );
             );
-            await InAppPurchaseConnection.instance.completePurchase(purchase);
+            await InAppPurchase.instance.completePurchase(purchase);
             String text = "Thank you for subscribing!";
             String text = "Thank you for subscribing!";
             if (!widget.isOnboarding) {
             if (!widget.isOnboarding) {
               final isUpgrade = _hasActiveSubscription &&
               final isUpgrade = _hasActiveSubscription &&
@@ -121,7 +120,7 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
             return;
             return;
           }
           }
         } else if (Platform.isIOS && purchase.pendingCompletePurchase) {
         } else if (Platform.isIOS && purchase.pendingCompletePurchase) {
-          await InAppPurchaseConnection.instance.completePurchase(purchase);
+          await InAppPurchase.instance.completePurchase(purchase);
           await _dialog.hide();
           await _dialog.hide();
         } else if (purchase.status == PurchaseStatus.error) {
         } else if (purchase.status == PurchaseStatus.error) {
           await _dialog.hide();
           await _dialog.hide();
@@ -296,7 +295,9 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
                   RichText(
                   RichText(
                     text: TextSpan(
                     text: TextSpan(
                       text: "Manage family",
                       text: "Manage family",
-                      style: Theme.of(context).textTheme.overline,
+                      style: Theme.of(context).textTheme.bodyMedium.copyWith(
+                            decoration: TextDecoration.underline,
+                          ),
                     ),
                     ),
                     textAlign: TextAlign.center,
                     textAlign: TextAlign.center,
                   ),
                   ),
@@ -391,14 +392,13 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
                 showErrorDialog(
                 showErrorDialog(
                   context,
                   context,
                   "Sorry",
                   "Sorry",
-                  "you cannot downgrade to this plan",
+                  "You cannot downgrade to this plan",
                 );
                 );
                 return;
                 return;
               }
               }
               await _dialog.show();
               await _dialog.show();
               final ProductDetailsResponse response =
               final ProductDetailsResponse response =
-                  await InAppPurchaseConnection.instance
-                      .queryProductDetails({productID});
+                  await InAppPurchase.instance.queryProductDetails({productID});
               if (response.notFoundIDs.isNotEmpty) {
               if (response.notFoundIDs.isNotEmpty) {
                 _logger.severe(
                 _logger.severe(
                   "Could not find products: " + response.notFoundIDs.toString(),
                   "Could not find products: " + response.notFoundIDs.toString(),
@@ -412,34 +412,15 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
                   _currentSubscription.productID != freeProductID &&
                   _currentSubscription.productID != freeProductID &&
                   _currentSubscription.productID != plan.androidID;
                   _currentSubscription.productID != plan.androidID;
               if (isCrossGradingOnAndroid) {
               if (isCrossGradingOnAndroid) {
-                final existingProductDetailsResponse =
-                    await InAppPurchaseConnection.instance
-                        .queryProductDetails({_currentSubscription.productID});
-                if (existingProductDetailsResponse.notFoundIDs.isNotEmpty) {
-                  _logger.severe(
-                    "Could not find existing products: " +
-                        response.notFoundIDs.toString(),
-                  );
-                  await _dialog.hide();
-                  showGenericErrorDialog(context);
-                  return;
-                }
-                final subscriptionChangeParam = ChangeSubscriptionParam(
-                  oldPurchaseDetails: PurchaseDetails(
-                    purchaseID: null,
-                    productID: _currentSubscription.productID,
-                    verificationData: null,
-                    transactionDate: null,
-                  ),
-                );
-                await InAppPurchaseConnection.instance.buyNonConsumable(
-                  purchaseParam: PurchaseParam(
-                    productDetails: response.productDetails[0],
-                    changeSubscriptionParam: subscriptionChangeParam,
-                  ),
+                await _dialog.hide();
+                showErrorDialog(
+                  context,
+                  "Could not update subscription",
+                  "Please contact support@ente.io and we will be happy to help!",
                 );
                 );
+                return;
               } else {
               } else {
-                await InAppPurchaseConnection.instance.buyNonConsumable(
+                await InAppPurchase.instance.buyNonConsumable(
                   purchaseParam: PurchaseParam(
                   purchaseParam: PurchaseParam(
                     productDetails: response.productDetails[0],
                     productDetails: response.productDetails[0],
                   ),
                   ),

+ 42 - 0
lib/ui/viewer/file/fading_app_bar.dart

@@ -7,6 +7,7 @@ import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:like_button/like_button.dart';
 import 'package:like_button/like_button.dart';
 import 'package:logging/logging.dart';
 import 'package:logging/logging.dart';
+import 'package:media_extension/media_extension.dart';
 import 'package:path/path.dart' as file_path;
 import 'package:path/path.dart' as file_path;
 import 'package:photo_manager/photo_manager.dart';
 import 'package:photo_manager/photo_manager.dart';
 import 'package:photos/core/event_bus.dart';
 import 'package:photos/core/event_bus.dart';
@@ -153,6 +154,27 @@ class FadingAppBarState extends State<FadingAppBar> {
               ),
               ),
             );
             );
           }
           }
+          if ((widget.file.fileType == FileType.image ||
+                  widget.file.fileType == FileType.livePhoto) &&
+              Platform.isAndroid) {
+            items.add(
+              PopupMenuItem(
+                value: 3,
+                child: Row(
+                  children: [
+                    Icon(
+                      Icons.wallpaper_outlined,
+                      color: Theme.of(context).iconTheme.color,
+                    ),
+                    const Padding(
+                      padding: EdgeInsets.all(8),
+                    ),
+                    const Text("Use as"),
+                  ],
+                ),
+              ),
+            );
+          }
           return items;
           return items;
         },
         },
         onSelected: (value) {
         onSelected: (value) {
@@ -160,6 +182,8 @@ class FadingAppBarState extends State<FadingAppBar> {
             _download(widget.file);
             _download(widget.file);
           } else if (value == 2) {
           } else if (value == 2) {
             _showDeleteSheet(widget.file);
             _showDeleteSheet(widget.file);
+          } else if (value == 3) {
+            _setAs(widget.file);
           }
           }
         },
         },
       ),
       ),
@@ -344,4 +368,22 @@ class FadingAppBarState extends State<FadingAppBar> {
       showToast(context, "File saved to gallery");
       showToast(context, "File saved to gallery");
     }
     }
   }
   }
+
+  Future<void> _setAs(File file) async {
+    final dialog = createProgressDialog(context, "Please wait...");
+    await dialog.show();
+    try {
+      final io.File fileToSave = await getFile(file);
+      var m = MediaExtension();
+      final bool result = await m.setAs("file://${fileToSave.path}", "image/*");
+      if (result == false) {
+        showShortToast(context, "Something went wrong");
+      }
+      dialog.hide();
+    } catch (e) {
+      dialog.hide();
+      _logger.severe("Failed to use as", e);
+      showGenericErrorDialog(context);
+    }
+  }
 }
 }

+ 116 - 65
lib/ui/viewer/gallery/gallery_app_bar_widget.dart

@@ -49,6 +49,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
   Function() _selectedFilesListener;
   Function() _selectedFilesListener;
   String _appBarTitle;
   String _appBarTitle;
   final GlobalKey shareButtonKey = GlobalKey();
   final GlobalKey shareButtonKey = GlobalKey();
+
   @override
   @override
   void initState() {
   void initState() {
     _selectedFilesListener = () {
     _selectedFilesListener = () {
@@ -127,6 +128,33 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
     }
     }
   }
   }
 
 
+  Future<dynamic> _leaveAlbum(BuildContext context) async {
+    final DialogUserChoice result = await showChoiceDialog(
+      context,
+      "Leave shared album?",
+      "You will leave the album, and it will stop being visible to you.",
+      firstAction: "Cancel",
+      secondAction: "Yes, Leave",
+      secondActionColor:
+          Theme.of(context).colorScheme.enteTheme.colorScheme.warning700,
+    );
+    if (result != DialogUserChoice.secondChoice) {
+      return;
+    }
+    final dialog = createProgressDialog(context, "Leaving album...");
+    await dialog.show();
+    try {
+      await CollectionsService.instance.leaveAlbum(widget.collection);
+      await dialog.hide();
+      if (mounted) {
+        Navigator.of(context).pop();
+      }
+    } catch (e) {
+      await dialog.hide();
+      showGenericErrorDialog(context);
+    }
+  }
+
   List<Widget> _getDefaultActions(BuildContext context) {
   List<Widget> _getDefaultActions(BuildContext context) {
     final List<Widget> actions = <Widget>[];
     final List<Widget> actions = <Widget>[];
     if (Configuration.instance.hasConfiguredAccount() &&
     if (Configuration.instance.hasConfiguredAccount() &&
@@ -160,78 +188,101 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
         ),
         ),
       );
       );
     }
     }
+    final List<PopupMenuItem> items = [];
     if (widget.type == GalleryType.ownedCollection) {
     if (widget.type == GalleryType.ownedCollection) {
-      actions.add(
-        PopupMenuButton(
-          itemBuilder: (context) {
-            final List<PopupMenuItem> items = [];
-            if (widget.collection.type != CollectionType.favorites) {
-              items.add(
-                PopupMenuItem(
-                  value: 1,
-                  child: Row(
-                    children: const [
-                      Icon(Icons.edit),
-                      Padding(
-                        padding: EdgeInsets.all(8),
-                      ),
-                      Text("Rename album"),
-                    ],
-                  ),
-                ),
-              );
-            }
-            final bool isArchived = widget.collection.isArchived();
-            items.add(
-              PopupMenuItem(
-                value: 2,
-                child: Row(
-                  children: [
-                    Icon(isArchived ? Icons.visibility : Icons.visibility_off),
-                    const Padding(
-                      padding: EdgeInsets.all(8),
-                    ),
-                    Text(isArchived ? "Unhide album" : "Hide album"),
-                  ],
+      if (widget.collection.type != CollectionType.favorites) {
+        items.add(
+          PopupMenuItem(
+            value: 1,
+            child: Row(
+              children: const [
+                Icon(Icons.edit),
+                Padding(
+                  padding: EdgeInsets.all(8),
                 ),
                 ),
+                Text("Rename album"),
+              ],
+            ),
+          ),
+        );
+      }
+      final bool isArchived = widget.collection.isArchived();
+      items.add(
+        PopupMenuItem(
+          value: 2,
+          child: Row(
+            children: [
+              Icon(isArchived ? Icons.visibility : Icons.visibility_off),
+              const Padding(
+                padding: EdgeInsets.all(8),
               ),
               ),
-            );
-            if (widget.collection.type != CollectionType.favorites) {
-              items.add(
-                PopupMenuItem(
-                  value: 3,
-                  child: Row(
-                    children: const [
-                      Icon(Icons.delete_outline),
-                      Padding(
-                        padding: EdgeInsets.all(8),
-                      ),
-                      Text("Delete album"),
-                    ],
-                  ),
+              Text(isArchived ? "Unhide album" : "Hide album"),
+            ],
+          ),
+        ),
+      );
+      if (widget.collection.type != CollectionType.favorites) {
+        items.add(
+          PopupMenuItem(
+            value: 3,
+            child: Row(
+              children: const [
+                Icon(Icons.delete_outline),
+                Padding(
+                  padding: EdgeInsets.all(8),
                 ),
                 ),
-              );
-            }
-            return items;
-          },
-          onSelected: (value) async {
-            if (value == 1) {
-              await _renameAlbum(context);
-            } else if (value == 2) {
-              await changeCollectionVisibility(
-                context,
-                widget.collection,
-                widget.collection.isArchived()
-                    ? visibilityVisible
-                    : visibilityArchive,
-              );
-            } else if (value == 3) {
-              await _trashCollection();
-            }
-          },
+                Text("Delete album"),
+              ],
+            ),
+          ),
+        );
+      }
+    } // ownedCollection open ends
+
+    if (widget.type == GalleryType.sharedCollection) {
+      items.add(
+        PopupMenuItem(
+          value: 4,
+          child: Row(
+            children: const [
+              Icon(Icons.logout),
+              Padding(
+                padding: EdgeInsets.all(8),
+              ),
+              Text("Leave album"),
+            ],
+          ),
         ),
         ),
       );
       );
     }
     }
+
+    actions.add(
+      PopupMenuButton(
+        itemBuilder: (context) {
+          return items;
+        },
+        onSelected: (value) async {
+          if (value == 1) {
+            await _renameAlbum(context);
+          } else if (value == 2) {
+            await changeCollectionVisibility(
+              context,
+              widget.collection,
+              widget.collection.isArchived()
+                  ? visibilityVisible
+                  : visibilityArchive,
+            );
+          } else if (value == 3) {
+            await _trashCollection();
+          } else if (value == 4) {
+            await _leaveAlbum(context);
+          } else {
+            showToast(context, "Something went wrong");
+          }
+        },
+      ),
+    );
+
     return actions;
     return actions;
   }
   }
 
 

+ 39 - 9
pubspec.lock

@@ -608,7 +608,28 @@ packages:
       name: in_app_purchase
       name: in_app_purchase
       url: "https://pub.dartlang.org"
       url: "https://pub.dartlang.org"
     source: hosted
     source: hosted
-    version: "0.5.2"
+    version: "3.0.7"
+  in_app_purchase_android:
+    dependency: transitive
+    description:
+      name: in_app_purchase_android
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.2.3+5"
+  in_app_purchase_platform_interface:
+    dependency: transitive
+    description:
+      name: in_app_purchase_platform_interface
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.3.2"
+  in_app_purchase_storekit:
+    dependency: transitive
+    description:
+      name: in_app_purchase_storekit
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.3.2+2"
   intl:
   intl:
     dependency: "direct main"
     dependency: "direct main"
     description:
     description:
@@ -686,6 +707,15 @@ packages:
       url: "https://pub.dartlang.org"
       url: "https://pub.dartlang.org"
     source: hosted
     source: hosted
     version: "0.1.5"
     version: "0.1.5"
+  media_extension:
+    dependency: "direct main"
+    description:
+      path: "."
+      ref: HEAD
+      resolved-ref: "8f70825a6384ccca750302430ba590a6059a2b3a"
+      url: "https://github.com/ente-io/media_extension.git"
+    source: git
+    version: "0.0.1"
   meta:
   meta:
     dependency: transitive
     dependency: transitive
     description:
     description:
@@ -964,17 +994,17 @@ packages:
   sentry:
   sentry:
     dependency: "direct main"
     dependency: "direct main"
     description:
     description:
-      path: "thirdparty/sentry-dart/dart"
-      relative: true
-    source: path
-    version: "6.11.0"
+      name: sentry
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "6.12.1"
   sentry_flutter:
   sentry_flutter:
     dependency: "direct main"
     dependency: "direct main"
     description:
     description:
-      path: "thirdparty/sentry-dart/flutter"
-      relative: true
-    source: path
-    version: "6.11.0"
+      name: sentry_flutter
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "6.12.1"
   share_plus:
   share_plus:
     dependency: "direct main"
     dependency: "direct main"
     description:
     description:

+ 7 - 6
pubspec.yaml

@@ -11,7 +11,8 @@ description: ente photos application
 # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
 # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
 # Read more about iOS versioning at
 # Read more about iOS versioning at
 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
-version: 0.6.41+371
+
+version: 0.6.42+372
 
 
 environment:
 environment:
   sdk: '>=2.17.0 <3.0.0'
   sdk: '>=2.17.0 <3.0.0'
@@ -69,13 +70,15 @@ dependencies:
   image: ^3.0.2
   image: ^3.0.2
   image_editor: ^1.0.0
   image_editor: ^1.0.0
   implicitly_animated_reorderable_list: ^0.4.0
   implicitly_animated_reorderable_list: ^0.4.0
-  in_app_purchase: ^0.5.2
+  in_app_purchase: ^3.0.7
   intl: ^0.17.0
   intl: ^0.17.0
   like_button: ^2.0.2
   like_button: ^2.0.2
   loading_animations: ^2.1.0
   loading_animations: ^2.1.0
   local_auth: ^1.1.5
   local_auth: ^1.1.5
   logging: ^1.0.1
   logging: ^1.0.1
   lottie: ^1.2.2
   lottie: ^1.2.2
+  media_extension:
+    git: "https://github.com/ente-io/media_extension.git"
   motionphoto:
   motionphoto:
     git: "https://github.com/ente-io/motionphoto.git"
     git: "https://github.com/ente-io/motionphoto.git"
   move_to_background: ^1.0.2
   move_to_background: ^1.0.2
@@ -95,10 +98,8 @@ dependencies:
   quiver: ^3.0.1
   quiver: ^3.0.1
   receive_sharing_intent: ^1.4.5
   receive_sharing_intent: ^1.4.5
   scrollable_positioned_list: ^0.2.2
   scrollable_positioned_list: ^0.2.2
-  sentry:
-    path: thirdparty/sentry-dart/dart
-  sentry_flutter:
-    path: thirdparty/sentry-dart/flutter
+  sentry: ^6.12.1
+  sentry_flutter: ^6.12.1
   share_plus: ^4.0.10
   share_plus: ^4.0.10
   shared_preferences: ^2.0.5
   shared_preferences: ^2.0.5
   sqflite: ^2.0.0+3
   sqflite: ^2.0.0+3