Procházet zdrojové kódy

Merge branch 'master' into collaboration_view

Neeraj Gupta před 2 roky
rodič
revize
8891b0ce58

+ 1 - 0
lib/core/constants.dart

@@ -44,6 +44,7 @@ const supportEmail = 'support@ente.io';
 class FFDefault {
   static const bool enableStripe = true;
   static const bool disableCFWorker = false;
+  static const bool enableCollect = false;
 }
 
 const kDefaultProductionEndpoint = 'https://api.ente.io';

+ 4 - 0
lib/models/collection.dart

@@ -288,6 +288,7 @@ class PublicURL {
   int deviceLimit;
   int validTill;
   bool enableDownload;
+  bool enableCollect;
   bool passwordEnabled;
 
   PublicURL({
@@ -296,6 +297,7 @@ class PublicURL {
     required this.validTill,
     this.enableDownload = true,
     this.passwordEnabled = false,
+    this.enableCollect = false,
   });
 
   Map<String, dynamic> toMap() {
@@ -305,6 +307,7 @@ class PublicURL {
       'validTill': validTill,
       'enableDownload': enableDownload,
       'passwordEnabled': passwordEnabled,
+      'enableCollect': enableCollect,
     };
   }
 
@@ -323,6 +326,7 @@ class PublicURL {
       validTill: map['validTill'] ?? 0,
       enableDownload: map['enableDownload'] ?? true,
       passwordEnabled: map['passwordEnabled'] ?? false,
+      enableCollect: map['enableCollect'] ?? false,
     );
   }
 }

+ 24 - 0
lib/models/file.dart

@@ -1,3 +1,5 @@
+import 'dart:io';
+
 import 'package:logging/logging.dart';
 import 'package:path/path.dart';
 import 'package:photo_manager/photo_manager.dart';
@@ -177,13 +179,35 @@ class File extends EnteFile {
         duration = asset.duration;
       }
     }
+    bool hasExifTime = false;
     if (fileType == FileType.image && mediaUploadData.sourceFile != null) {
       final exifTime =
           await getCreationTimeFromEXIF(mediaUploadData.sourceFile!);
       if (exifTime != null) {
+        hasExifTime = true;
         creationTime = exifTime.microsecondsSinceEpoch;
       }
     }
+    // Try to get the timestamp from fileName. In case of iOS, file names are
+    // generic IMG_XXXX, so only parse it on Android devices
+    if (!hasExifTime && Platform.isAndroid && title != null) {
+      final timeFromFileName = parseDateTimeFromFileNameV2(title!);
+      if (timeFromFileName != null) {
+        // only use timeFromFileName if the existing creationTime and
+        // timeFromFilename belongs to different date.
+        // This is done because many times the fileTimeStamp will only give us
+        // the date, not time value but the photo_manager's creation time will
+        // contain the time.
+        final bool useFileTimeStamp = creationTime == null ||
+            !areFromSameDay(
+              creationTime!,
+              timeFromFileName.microsecondsSinceEpoch,
+            );
+        if (useFileTimeStamp) {
+          creationTime = timeFromFileName.microsecondsSinceEpoch;
+        }
+      }
+    }
     hash = mediaUploadData.hashData?.fileHash;
     return metadata;
   }

+ 17 - 0
lib/services/feature_flag_service.dart

@@ -62,6 +62,18 @@ class FeatureFlagService {
     }
   }
 
+  bool enableCollect() {
+    if (isInternalUserOrDebugBuild()) {
+      return true;
+    }
+    try {
+      return _getFeatureFlags().enableCollect;
+    } catch (e) {
+      _logger.severe(e);
+      return FFDefault.enableCollect;
+    }
+  }
+
   bool isInternalUserOrDebugBuild() {
     final String? email = Configuration.instance.getEmail();
     return (email != null && email.endsWith("@ente.io")) || kDebugMode;
@@ -85,20 +97,24 @@ class FeatureFlags {
   static FeatureFlags defaultFlags = FeatureFlags(
     disableCFWorker: FFDefault.disableCFWorker,
     enableStripe: FFDefault.enableStripe,
+    enableCollect: FFDefault.enableCollect,
   );
 
   final bool disableCFWorker;
   final bool enableStripe;
+  final bool enableCollect;
 
   FeatureFlags({
     required this.disableCFWorker,
     required this.enableStripe,
+    required this.enableCollect,
   });
 
   Map<String, dynamic> toMap() {
     return {
       "disableCFWorker": disableCFWorker,
       "enableStripe": enableStripe,
+      "enableCollect": enableCollect,
     };
   }
 
@@ -111,6 +127,7 @@ class FeatureFlags {
     return FeatureFlags(
       disableCFWorker: json["disableCFWorker"] ?? FFDefault.disableCFWorker,
       enableStripe: json["enableStripe"] ?? FFDefault.enableStripe,
+      enableCollect: json["enableCollect"] ?? FFDefault.enableCollect,
     );
   }
 }

+ 1 - 1
lib/ui/components/captioned_text_widget.dart

@@ -49,7 +49,7 @@ class CaptionedTextWidget extends StatelessWidget {
         Text(
           subTitle!,
           style: enteTextTheme.small.copyWith(
-            color: enteColorScheme.textMuted,
+            color: subTitleColor ?? enteColorScheme.textMuted,
           ),
         ),
       );

+ 38 - 0
lib/ui/sharing/manage_links_widget.dart

@@ -10,6 +10,7 @@ import 'package:flutter_sodium/flutter_sodium.dart';
 import 'package:photos/ente_theme_data.dart';
 import 'package:photos/models/collection.dart';
 import 'package:photos/services/collections_service.dart';
+import 'package:photos/services/feature_flag_service.dart';
 import 'package:photos/theme/colors.dart';
 import 'package:photos/theme/ente_theme.dart';
 import 'package:photos/ui/actions/collection/collection_sharing_actions.dart';
@@ -61,6 +62,42 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
   Widget build(BuildContext context) {
     final enteColorScheme = getEnteColorScheme(context);
     final PublicURL url = widget.collection?.publicURLs?.firstOrNull;
+
+    final enableCollectFeature = FeatureFlagService.instance.enableCollect();
+    final Widget collect = enableCollectFeature
+        ? Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              MenuItemWidget(
+                captionedTextWidget: const CaptionedTextWidget(
+                  title: "Allow adding photos",
+                ),
+                alignCaptionedTextToLeft: true,
+                menuItemColor: getEnteColorScheme(context).fillFaint,
+                pressedColor: getEnteColorScheme(context).fillFaint,
+                trailingWidget: Switch.adaptive(
+                  value: widget
+                          .collection.publicURLs?.firstOrNull?.enableCollect ??
+                      false,
+                  onChanged: (value) async {
+                    await _updateUrlSettings(
+                      context,
+                      {'enableCollect': value},
+                    );
+
+                    setState(() {});
+                  },
+                ),
+              ),
+              const MenuSectionDescriptionWidget(
+                content:
+                    "Allow people with the link to also add photos to the shared "
+                    "album.",
+              ),
+              const SizedBox(height: 24)
+            ],
+          )
+        : const SizedBox.shrink();
     return Scaffold(
       backgroundColor: Theme.of(context).backgroundColor,
       appBar: AppBar(
@@ -77,6 +114,7 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
               child: Column(
                 crossAxisAlignment: CrossAxisAlignment.start,
                 children: [
+                  collect,
                   MenuItemWidget(
                     alignCaptionedTextToLeft: true,
                     captionedTextWidget: CaptionedTextWidget(

+ 1 - 9
lib/ui/viewer/gallery/gallery.dart

@@ -285,7 +285,7 @@ class _GalleryState extends State<Gallery> {
     final List<List<File>> collatedFiles = [];
     for (int index = 0; index < files.length; index++) {
       if (index > 0 &&
-          !_areFromSameDay(
+          !areFromSameDay(
             files[index - 1].creationTime,
             files[index].creationTime,
           )) {
@@ -303,14 +303,6 @@ class _GalleryState extends State<Gallery> {
         .sort((a, b) => b[0].creationTime.compareTo(a[0].creationTime));
     return collatedFiles;
   }
-
-  bool _areFromSameDay(int firstCreationTime, int secondCreationTime) {
-    final firstDate = DateTime.fromMicrosecondsSinceEpoch(firstCreationTime);
-    final secondDate = DateTime.fromMicrosecondsSinceEpoch(secondCreationTime);
-    return firstDate.year == secondDate.year &&
-        firstDate.month == secondDate.month &&
-        firstDate.day == secondDate.day;
-  }
 }
 
 class GalleryIndexUpdatedEvent {

+ 21 - 2
lib/utils/date_time_util.dart

@@ -54,6 +54,20 @@ String getMonthAndYear(DateTime dateTime) {
   return _months[dateTime.month]! + " " + dateTime.year.toString();
 }
 
+int daysBetween(DateTime from, DateTime to) {
+  from = DateTime(from.year, from.month, from.day);
+  to = DateTime(to.year, to.month, to.day);
+  return (to.difference(from).inHours / 24).round();
+}
+
+bool areFromSameDay(int firstCreationTime, int secondCreationTime) {
+  final firstDate = DateTime.fromMicrosecondsSinceEpoch(firstCreationTime);
+  final secondDate = DateTime.fromMicrosecondsSinceEpoch(secondCreationTime);
+  return firstDate.year == secondDate.year &&
+      firstDate.month == secondDate.month &&
+      firstDate.day == secondDate.day;
+}
+
 //Thu, 30 Jun
 String getDayAndMonth(DateTime dateTime) {
   return _days[dateTime.weekday]! +
@@ -202,9 +216,14 @@ Widget getDayWidget(
   final textTheme = getEnteTextTheme(context);
   final textStyle =
       photoGridSize < photoGridSizeMax ? textTheme.body : textTheme.small;
-  final double paddingValue = photoGridSize < photoGridSizeMax ? 12.0 : 8.0;
+  final double horizontalPadding =
+      photoGridSize < photoGridSizeMax ? 12.0 : 8.0;
+  final double verticalPadding = photoGridSize < photoGridSizeMax ? 12.0 : 14.0;
   return Padding(
-    padding: EdgeInsets.all(paddingValue),
+    padding: EdgeInsets.symmetric(
+      horizontal: horizontalPadding,
+      vertical: verticalPadding,
+    ),
     child: Container(
       alignment: Alignment.centerLeft,
       child: Text(

+ 5 - 1
test/utils/date_time_util_test.dart

@@ -32,7 +32,11 @@ void main() {
   });
 
   test("test invalid datetime parsing", () {
-    final List<String> badParsing = ["Snapchat-431959199.mp4."];
+    final List<String> badParsing = [
+      "Snapchat-431959199.mp4.",
+      "Snapchat-400000000.mp4",
+      "Snapchat-900000000.mp4"
+    ];
     for (String val in badParsing) {
       final parsedValue = parseDateTimeFromFileNameV2(val);
       expect(