Jelajahi Sumber

Merge branch 'master' into photos_manger_upgrade

Neeraj Gupta 3 tahun lalu
induk
melakukan
ae1ee2ca4a

+ 2 - 2
lib/models/user_details.dart

@@ -54,8 +54,8 @@ class UserDetails {
     return UserDetails(
       map['email'] as String,
       map['usage'] as int,
-      map['fileCount'] as int,
-      map['sharedCollectionsCount'] as int,
+      map['fileCount'] as int ?? 0,
+      map['sharedCollectionsCount'] as int ?? 0,
       Subscription.fromMap(map['subscription']),
       FamilyData.fromMap(map['familyData']),
     );

+ 1 - 2
lib/ui/common/dialogs.dart

@@ -1,4 +1,3 @@
-import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 
 enum DialogUserChoice { firstChoice, secondChoice }
@@ -8,7 +7,7 @@ enum ActionType {
   critical,
 }
 // if dialog is dismissed by tapping outside, this will return null
-Future<T> showChoiceDialog<T>(
+Future<DialogUserChoice> showChoiceDialog<T>(
   BuildContext context,
   String title,
   String content, {

+ 32 - 25
lib/ui/payment/stripe_subscription_page.dart

@@ -210,37 +210,37 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
           ),
         ),
       ]);
+    }
 
-      if (!widget.isOnboarding) {
-        widgets.addAll([
-          Align(
-            alignment: Alignment.topCenter,
-            child: GestureDetector(
-              onTap: () async {
-                await _launchFamilyPortal();
-              },
-              child: Container(
-                padding: EdgeInsets.fromLTRB(40, 0, 40, 80),
-                child: Column(
-                  children: [
-                    RichText(
-                      text: TextSpan(
-                        text: "manage family",
-                        style: TextStyle(
-                          color: Colors.blue,
-                          fontFamily: 'Ubuntu',
-                          fontSize: 15,
-                        ),
+    if (!widget.isOnboarding) {
+      widgets.addAll([
+        Align(
+          alignment: Alignment.topCenter,
+          child: GestureDetector(
+            onTap: () async {
+              await _launchFamilyPortal();
+            },
+            child: Container(
+              padding: EdgeInsets.fromLTRB(40, 0, 40, 80),
+              child: Column(
+                children: [
+                  RichText(
+                    text: TextSpan(
+                      text: "manage family",
+                      style: TextStyle(
+                        color: Colors.blue,
+                        fontFamily: 'Ubuntu',
+                        fontSize: 15,
                       ),
-                      textAlign: TextAlign.center,
                     ),
-                  ],
-                ),
+                    textAlign: TextAlign.center,
+                  ),
+                ],
               ),
             ),
           ),
-        ]);
-      }
+        ),
+      ]);
     }
 
     return SingleChildScrollView(
@@ -270,6 +270,13 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
   }
 
   Future<void> _launchFamilyPortal() async {
+    if (_userDetails.subscription.productID == kFreeProductID) {
+      await showErrorDialog(
+          context,
+          "Now you can share your storage plan with your family members!",
+          "Customers on paid plans can add up to 5 family members without paying extra. Each member gets their own private space.");
+      return;
+    }
     await _dialog.show();
     try {
       final String jwtToken = await _userService.getFamiliesToken();

+ 10 - 0
lib/ui/payment/subscription_page.dart

@@ -237,6 +237,8 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
           ),
         ),
       ]);
+    }
+    if (!widget.isOnboarding) {
       widgets.addAll([
         Align(
           alignment: Alignment.topCenter,
@@ -434,7 +436,15 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
     );
   }
 
+  // todo: refactor manage family in common widget
   Future<void> _launchFamilyPortal() async {
+    if (_userDetails.subscription.productID == kFreeProductID) {
+      await showErrorDialog(
+          context,
+          "Now you can share your storage plan with your family members!",
+          "Customers on paid plans can add up to 5 family members without paying extra. Each member gets their own private space.");
+      return;
+    }
     await _dialog.show();
     try {
       final String jwtToken = await _userService.getFamiliesToken();

+ 43 - 6
lib/utils/delete_file_util.dart

@@ -5,7 +5,6 @@ import 'dart:math';
 
 import 'package:device_info/device_info.dart';
 import 'package:flutter/material.dart';
-import 'package:flutter/widgets.dart';
 import 'package:logging/logging.dart';
 import 'package:photo_manager/photo_manager.dart';
 import 'package:photos/core/configuration.dart';
@@ -36,6 +35,7 @@ Future<void> deleteFilesFromEverywhere(
   final List<String> localAssetIDs = [];
   final List<String> localSharedMediaIDs = [];
   final List<String> alreadyDeletedIDs = []; // to ignore already deleted files
+  bool hasLocalOnlyFiles = false;
   for (final file in files) {
     if (file.localID != null) {
       if (!(await _localFileExist(file))) {
@@ -47,6 +47,16 @@ Future<void> deleteFilesFromEverywhere(
         localAssetIDs.add(file.localID);
       }
     }
+    if (file.uploadedFileID == null) {
+      hasLocalOnlyFiles = true;
+    }
+  }
+  if (hasLocalOnlyFiles && Platform.isAndroid) {
+    final shouldProceed = await shouldProceedWithDeletion(context);
+    if (!shouldProceed) {
+      await dialog.hide();
+      return;
+    }
   }
   Set<String> deletedIDs = <String>{};
   try {
@@ -108,7 +118,11 @@ Future<void> deleteFilesFromEverywhere(
   if (deletedFiles.isNotEmpty) {
     Bus.instance.fire(LocalPhotosUpdatedEvent(deletedFiles,
         type: EventType.deletedFromEverywhere));
-    showShortToast("moved to trash");
+    if (hasLocalOnlyFiles && Platform.isAndroid) {
+      showShortToast("files deleted");
+    } else {
+      showShortToast("moved to trash");
+    }
   }
   await dialog.hide();
   if (uploadedFilesToBeTrashed.isNotEmpty) {
@@ -151,8 +165,8 @@ Future<void> deleteFilesFromRemoteOnly(
       type: EventType.deletedFromRemote,
     ));
   }
-  Bus.instance.fire(
-      LocalPhotosUpdatedEvent(files, type: EventType.deletedFromRemote));
+  Bus.instance
+      .fire(LocalPhotosUpdatedEvent(files, type: EventType.deletedFromRemote));
   SyncService.instance.sync();
   await dialog.hide();
   RemoteSyncService.instance.sync(silently: true);
@@ -166,6 +180,7 @@ Future<void> deleteFilesOnDeviceOnly(
   final List<String> localAssetIDs = [];
   final List<String> localSharedMediaIDs = [];
   final List<String> alreadyDeletedIDs = []; // to ignore already deleted files
+  bool hasLocalOnlyFiles = false;
   for (final file in files) {
     if (file.localID != null) {
       if (!(await _localFileExist(file))) {
@@ -177,6 +192,16 @@ Future<void> deleteFilesOnDeviceOnly(
         localAssetIDs.add(file.localID);
       }
     }
+    if (file.uploadedFileID == null) {
+      hasLocalOnlyFiles = true;
+    }
+  }
+  if (hasLocalOnlyFiles && Platform.isAndroid) {
+    final shouldProceed = await shouldProceedWithDeletion(context);
+    if (!shouldProceed) {
+      await dialog.hide();
+      return;
+    }
   }
   Set<String> deletedIDs = <String>{};
   try {
@@ -216,8 +241,8 @@ Future<bool> deleteFromTrash(BuildContext context, List<File> files) async {
     await TrashSyncService.instance.deleteFromTrash(files);
     showToast("successfully deleted");
     await dialog.hide();
-    Bus.instance.fire(
-        FilesUpdatedEvent(files, type: EventType.deletedFromEverywhere));
+    Bus.instance
+        .fire(FilesUpdatedEvent(files, type: EventType.deletedFromEverywhere));
     return true;
   } catch (e, s) {
     _logger.info("failed to delete from trash", e, s);
@@ -390,3 +415,15 @@ Future<List<String>> _tryDeleteSharedMediaFiles(List<String> localIDs) {
     return Future.value(actuallyDeletedIDs);
   }
 }
+
+Future<bool> shouldProceedWithDeletion(BuildContext context) async {
+  final choice = await showChoiceDialog(
+    context,
+    "are you sure?",
+    "some of the files you are trying to delete are only available on your device and cannot be recovered if deleted",
+    firstAction: "cancel",
+    secondAction: "delete",
+    secondActionColor: Colors.red,
+  );
+  return choice == DialogUserChoice.secondChoice;
+}

+ 1 - 1
pubspec.yaml

@@ -11,7 +11,7 @@ description: ente photos application
 # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
 # Read more about iOS versioning at
 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
-version: 0.5.16+296
+version: 0.5.21+301
 
 environment:
   sdk: ">=2.10.0 <3.0.0"