فهرست منبع

Merge branch 'main' into handle_limited_settings

Neeraj Gupta 1 سال پیش
والد
کامیت
dd627408fc

+ 0 - 3
lib/core/constants.dart

@@ -25,9 +25,6 @@ const subGalleryMultiplier = 10;
 const String oldSharedMediaIdentifier = 'ente-shared://';
 const String sharedMediaIdentifier = 'ente-shared-media://';
 
-const int maxLivePhotoToastCount = 2;
-const String livePhotoToastCounterKey = "show_live_photo_toast";
-
 const thumbnailDiskLoadDeferDuration = Duration(milliseconds: 40);
 const thumbnailServerLoadDeferDuration = Duration(milliseconds: 80);
 

+ 2 - 0
lib/generated/intl/messages_en.dart

@@ -948,6 +948,8 @@ class MessageLookup extends MessageLookupByLibrary {
         "preserveMore": MessageLookupByLibrary.simpleMessage("Preserve more"),
         "pressAndHoldToPlayVideo": MessageLookupByLibrary.simpleMessage(
             "Press and hold to play video"),
+        "pressAndHoldToPlayVideoDetailed": MessageLookupByLibrary.simpleMessage(
+            "Press and hold on the image to  play video"),
         "privacy": MessageLookupByLibrary.simpleMessage("Privacy"),
         "privacyPolicyTitle":
             MessageLookupByLibrary.simpleMessage("Privacy Policy"),

+ 10 - 0
lib/generated/l10n.dart

@@ -5855,6 +5855,16 @@ class S {
     );
   }
 
+  /// `Press and hold on the image to  play video`
+  String get pressAndHoldToPlayVideoDetailed {
+    return Intl.message(
+      'Press and hold on the image to  play video',
+      name: 'pressAndHoldToPlayVideoDetailed',
+      desc: '',
+      args: [],
+    );
+  }
+
   /// `Download failed`
   String get downloadFailed {
     return Intl.message(

+ 1 - 0
lib/l10n/intl_en.arb

@@ -817,6 +817,7 @@
   "fileFailedToSaveToGallery": "Failed to save file to gallery",
   "download": "Download",
   "pressAndHoldToPlayVideo": "Press and hold to play video",
+  "pressAndHoldToPlayVideoDetailed": "Press and hold on the image to  play video",
   "downloadFailed": "Download failed",
   "deduplicateFiles": "Deduplicate Files",
   "deselectAll": "Deselect all",

+ 12 - 2
lib/models/file/extensions/file_props.dart

@@ -1,5 +1,7 @@
+import "package:photos/core/configuration.dart";
 import "package:photos/models/file/file.dart";
 import "package:photos/models/file/file_type.dart";
+import "package:photos/models/file/trash_file.dart";
 
 extension FilePropsExtn on EnteFile {
   bool get isLivePhoto => fileType == FileType.livePhoto;
@@ -8,7 +10,15 @@ extension FilePropsExtn on EnteFile {
 
   bool get isLiveOrMotionPhoto => isLivePhoto || isMotionPhoto;
 
-  bool isOwner(int userID) => (ownerID == null) || (ownerID! == userID);
+  bool get isOwner =>
+      (ownerID == null) || (ownerID! == Configuration.instance.getUserID()!);
 
-  bool canEditMetaInfo(int userID) => isUploaded && isOwner(userID);
+  bool get canEditMetaInfo => isUploaded && isOwner;
+
+  bool get isTrash => this is TrashFile;
+
+  // Return true if the file was uploaded via collect photos workflow
+  bool get isCollect => uploaderName != null;
+
+  String? get uploaderName => pubMagicMetadata?.uploaderName;
 }

+ 2 - 2
lib/services/remote_sync_service.dart

@@ -18,6 +18,7 @@ import 'package:photos/events/force_reload_home_gallery_event.dart';
 import 'package:photos/events/local_photos_updated_event.dart';
 import 'package:photos/events/sync_status_update_event.dart';
 import 'package:photos/models/device_collection.dart';
+import "package:photos/models/file/extensions/file_props.dart";
 import 'package:photos/models/file/file.dart';
 import 'package:photos/models/file/file_type.dart';
 import 'package:photos/models/upload_strategy.dart';
@@ -893,8 +894,7 @@ class RemoteSyncService {
       for (final file in files) {
         if (file.isUploaded && file.ownerID != userID) {
           sharedFilesIDs.add(file.uploadedFileID!);
-        } else if (file.isUploaded &&
-            file.pubMagicMetadata!.uploaderName != null) {
+        } else if (file.isUploaded && file.isCollect) {
           collectedFilesIDs.add(file.uploadedFileID!);
         }
       }

+ 1 - 1
lib/ui/collections/device/device_folder_item.dart

@@ -36,7 +36,7 @@ class DeviceFolderItem extends StatelessWidget {
                 child: Stack(
                   children: [
                     ThumbnailWidget(
-                      deviceCollection.thumbnail,
+                      deviceCollection.thumbnail!,
                       shouldShowSyncStatus: false,
                       key: Key(
                         "device_folder:" +

+ 6 - 1
lib/ui/viewer/file/detail_page.dart

@@ -102,6 +102,12 @@ class _DetailPageState extends State<DetailPage> {
 
   @override
   Widget build(BuildContext context) {
+    try {
+      _files![_selectedIndexNotifier.value];
+    } catch (e) {
+      _logger.severe(e);
+      Navigator.pop(context);
+    }
     _logger.info(
       "Opening " +
           _files![_selectedIndexNotifier.value].toString() +
@@ -119,7 +125,6 @@ class _DetailPageState extends State<DetailPage> {
             return FileAppBar(
               _files![selectedIndex],
               _onFileRemoved,
-              Configuration.instance.getUserID(),
               100,
               widget.config.mode == DetailPageMode.full,
               enableFullScreenNotifier: _enableFullScreenNotifier,

+ 12 - 4
lib/ui/viewer/file/file_app_bar.dart

@@ -10,6 +10,7 @@ import 'package:photos/core/event_bus.dart';
 import 'package:photos/db/files_db.dart';
 import 'package:photos/events/local_photos_updated_event.dart';
 import "package:photos/generated/l10n.dart";
+import "package:photos/models/file/extensions/file_props.dart";
 import 'package:photos/models/file/file.dart';
 import 'package:photos/models/file/file_type.dart';
 import 'package:photos/models/file/trash_file.dart';
@@ -34,13 +35,11 @@ class FileAppBar extends StatefulWidget {
   final Function(EnteFile) onFileRemoved;
   final double height;
   final bool shouldShowActions;
-  final int? userID;
   final ValueNotifier<bool> enableFullScreenNotifier;
 
   const FileAppBar(
     this.file,
     this.onFileRemoved,
-    this.userID,
     this.height,
     this.shouldShowActions, {
     required this.enableFullScreenNotifier,
@@ -94,8 +93,7 @@ class FileAppBarState extends State<FileAppBar> {
     final List<Widget> actions = [];
     final isTrashedFile = widget.file is TrashFile;
     final shouldShowActions = widget.shouldShowActions && !isTrashedFile;
-    final bool isOwnedByUser =
-        widget.file.ownerID == null || widget.file.ownerID == widget.userID;
+    final bool isOwnedByUser = widget.file.isOwner;
     final bool isFileUploaded = widget.file.isUploaded;
     bool isFileHidden = false;
     if (isOwnedByUser && isFileUploaded) {
@@ -104,6 +102,16 @@ class FileAppBarState extends State<FileAppBar> {
               ?.isHidden() ??
           false;
     }
+    if (widget.file.isLiveOrMotionPhoto) {
+      actions.add(
+        IconButton(
+          icon: const Icon(Icons.album_outlined),
+          onPressed: () {
+            showShortToast(context, S.of(context).pressAndHoldToPlayVideoDetailed);
+          },
+        ),
+      );
+    }
     // only show fav option for files owned by the user
     if (isOwnedByUser && !isFileHidden && isFileUploaded) {
       actions.add(FavoriteWidget(widget.file));

+ 1 - 6
lib/ui/viewer/file/file_details_widget.dart

@@ -217,12 +217,7 @@ class _FileDetailsWidgetState extends State<FileDetailsWidget> {
                   onTap: () => Navigator.pop(context),
                 ),
               ),
-              SliverToBoxAdapter(
-                child: AddedByWidget(
-                  widget.file,
-                  _currentUserID,
-                ),
-              ),
+              SliverToBoxAdapter(child: AddedByWidget(widget.file)),
               SliverList(
                 delegate: SliverChildBuilderDelegate(
                   (context, index) {

+ 32 - 40
lib/ui/viewer/file/thumbnail_widget.dart

@@ -1,7 +1,6 @@
 import 'package:flutter/material.dart';
 import 'package:logging/logging.dart';
 import 'package:photos/core/cache/thumbnail_in_memory_cache.dart';
-import 'package:photos/core/configuration.dart';
 import 'package:photos/core/constants.dart';
 import 'package:photos/core/errors.dart';
 import 'package:photos/core/event_bus.dart';
@@ -10,6 +9,7 @@ import 'package:photos/db/trash_db.dart';
 import 'package:photos/events/files_updated_event.dart';
 import "package:photos/events/local_photos_updated_event.dart";
 import "package:photos/models/api/collection/user.dart";
+import "package:photos/models/file/extensions/file_props.dart";
 import 'package:photos/models/file/file.dart';
 import 'package:photos/models/file/file_type.dart';
 import 'package:photos/models/file/trash_file.dart';
@@ -20,7 +20,7 @@ import 'package:photos/utils/file_util.dart';
 import 'package:photos/utils/thumbnail_util.dart';
 
 class ThumbnailWidget extends StatefulWidget {
-  final EnteFile? file;
+  final EnteFile file;
   final BoxFit fit;
   final bool shouldShowSyncStatus;
   final bool shouldShowArchiveStatus;
@@ -45,7 +45,7 @@ class ThumbnailWidget extends StatefulWidget {
     this.diskLoadDeferDuration,
     this.serverLoadDeferDuration,
     this.thumbnailSize = thumbnailSmallSize,
-  }) : super(key: key ?? Key(file!.tag));
+  }) : super(key: key ?? Key(file.tag));
 
   @override
   State<ThumbnailWidget> createState() => _ThumbnailWidgetState();
@@ -73,8 +73,8 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
     super.dispose();
     Future.delayed(const Duration(milliseconds: 10), () {
       // Cancel request only if the widget has been unmounted
-      if (!mounted && widget.file!.isRemoteFile && !_hasLoadedThumbnail) {
-        removePendingGetThumbnailRequestIfAny(widget.file!);
+      if (!mounted && widget.file.isRemoteFile && !_hasLoadedThumbnail) {
+        removePendingGetThumbnailRequestIfAny(widget.file);
       }
     });
   }
@@ -82,7 +82,7 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
   @override
   void didUpdateWidget(ThumbnailWidget oldWidget) {
     super.didUpdateWidget(oldWidget);
-    if (widget.file!.generatedID != oldWidget.file!.generatedID) {
+    if (widget.file.generatedID != oldWidget.file.generatedID) {
       _reset();
     }
   }
@@ -90,10 +90,10 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
   ///Assigned dimension will be the size of a grid item. The size will be
   ///assigned to the side which is smaller in dimension.
   void assignOptimizedImageDimensions() {
-    if (widget.file!.width == 0 || widget.file!.height == 0) {
+    if (widget.file.width == 0 || widget.file.height == 0) {
       return;
     }
-    if (widget.file!.width < widget.file!.height) {
+    if (widget.file.width < widget.file.height) {
       optimizedImageWidth = widget.thumbnailSize;
     } else {
       optimizedImageHeight = widget.thumbnailSize;
@@ -102,7 +102,7 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
 
   @override
   Widget build(BuildContext context) {
-    if (widget.file!.isRemoteFile) {
+    if (widget.file.isRemoteFile) {
       _loadNetworkImage();
     } else {
       _loadLocalImage(context);
@@ -127,37 +127,29 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
     if (image != null) {
       final List<Widget> contentChildren = [image];
       if (FavoritesService.instance.isFavoriteCache(
-        widget.file!,
+        widget.file,
         checkOnlyAlbum: widget.showFavForAlbumOnly,
       )) {
         contentChildren.add(const FavoriteOverlayIcon());
       }
-      if (widget.file!.fileType == FileType.video) {
+      if (widget.file.fileType == FileType.video) {
         contentChildren.add(const VideoOverlayIcon());
       } else if (widget.shouldShowLivePhotoOverlay &&
-          (widget.file!.fileType == FileType.livePhoto ||
-              ((widget.file!.pubMagicMetadata?.mvi ?? 0) > 0))) {
+          widget.file.isLiveOrMotionPhoto) {
         contentChildren.add(const LivePhotoOverlayIcon());
       }
       if (widget.shouldShowOwnerAvatar) {
-        if (widget.file!.ownerID != null &&
-            widget.file!.ownerID != Configuration.instance.getUserID()) {
+        if (!widget.file.isOwner) {
           final owner = CollectionsService.instance
-              .getFileOwner(widget.file!.ownerID!, widget.file!.collectionID);
-          // hide this icon if the current thumbnail is being showed as album
-          // cover
+              .getFileOwner(widget.file.ownerID!, widget.file.collectionID);
           contentChildren.add(
             OwnerAvatarOverlayIcon(owner),
           );
-        } else if (widget.file!.pubMagicMetadata!.uploaderName != null) {
+        } else if (widget.file.isCollect) {
           contentChildren.add(
             // Use -1 as userID for enforcing black avatar color
             OwnerAvatarOverlayIcon(
-              User(
-                id: -1,
-                email: '',
-                name: widget.file!.pubMagicMetadata!.uploaderName,
-              ),
+              User(id: -1, email: '', name: widget.file.uploaderName),
             ),
           );
         }
@@ -173,11 +165,11 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
       const ThumbnailPlaceHolder(),
       content ?? const SizedBox(),
     ];
-    if (widget.shouldShowSyncStatus && widget.file!.uploadedFileID == null) {
+    if (widget.shouldShowSyncStatus && !widget.file.isUploaded) {
       viewChildren.add(const UnSyncedIcon());
     }
 
-    if (widget.file is TrashFile) {
+    if (widget.file.isTrash) {
       viewChildren.add(TrashedFileOverlayText(widget.file as TrashFile));
     }
     // todo: Move this icon overlay to the collection widget.
@@ -200,7 +192,7 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
         !_isLoadingLocalThumbnail) {
       _isLoadingLocalThumbnail = true;
       final cachedSmallThumbnail =
-          ThumbnailInMemoryLruCache.get(widget.file!, thumbnailSmallSize);
+          ThumbnailInMemoryLruCache.get(widget.file, thumbnailSmallSize);
       if (cachedSmallThumbnail != null) {
         _imageProvider = Image.memory(cachedSmallThumbnail).image;
         _hasLoadedThumbnail = true;
@@ -220,26 +212,26 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
 
   Future _getThumbnailFromDisk() async {
     getThumbnailFromLocal(
-      widget.file!,
+      widget.file,
       size: widget.thumbnailSize,
     ).then((thumbData) async {
       if (thumbData == null) {
-        if (widget.file!.uploadedFileID != null) {
-          _logger.fine("Removing localID reference for " + widget.file!.tag);
-          widget.file!.localID = null;
-          if (widget.file is TrashFile) {
+        if (widget.file.isUploaded) {
+          _logger.fine("Removing localID reference for " + widget.file.tag);
+          widget.file.localID = null;
+          if (widget.file.isTrash) {
             TrashDB.instance.update(widget.file as TrashFile);
           } else {
-            FilesDB.instance.update(widget.file!);
+            FilesDB.instance.update(widget.file);
           }
           _loadNetworkImage();
         } else {
-          if (await doesLocalFileExist(widget.file!) == false) {
-            _logger.info("Deleting file " + widget.file!.tag);
-            FilesDB.instance.deleteLocalFile(widget.file!);
+          if (await doesLocalFileExist(widget.file) == false) {
+            _logger.info("Deleting file " + widget.file.tag);
+            FilesDB.instance.deleteLocalFile(widget.file);
             Bus.instance.fire(
               LocalPhotosUpdatedEvent(
-                [widget.file!],
+                [widget.file],
                 type: EventType.deletedFromDevice,
                 source: "thumbFileDeleted",
               ),
@@ -254,7 +246,7 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
         _cacheAndRender(imageProvider);
       }
       ThumbnailInMemoryLruCache.put(
-        widget.file!,
+        widget.file,
         thumbData,
         thumbnailSmallSize,
       );
@@ -269,7 +261,7 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
         !_errorLoadingRemoteThumbnail &&
         !_isLoadingRemoteThumbnail) {
       _isLoadingRemoteThumbnail = true;
-      final cachedThumbnail = ThumbnailInMemoryLruCache.get(widget.file!);
+      final cachedThumbnail = ThumbnailInMemoryLruCache.get(widget.file);
       if (cachedThumbnail != null) {
         _imageProvider = Image.memory(cachedThumbnail).image;
         _hasLoadedThumbnail = true;
@@ -289,7 +281,7 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
 
   void _getThumbnailFromServer() async {
     try {
-      final thumbnail = await getThumbnailFromServer(widget.file!);
+      final thumbnail = await getThumbnailFromServer(widget.file);
       if (mounted) {
         final imageProvider = Image.memory(thumbnail).image;
         _cacheAndRender(imageProvider);

+ 2 - 3
lib/ui/viewer/file/video_widget.dart

@@ -5,9 +5,9 @@ import 'package:chewie/chewie.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:logging/logging.dart';
-import 'package:photos/core/configuration.dart';
 import 'package:photos/core/constants.dart';
 import "package:photos/generated/l10n.dart";
+import "package:photos/models/file/extensions/file_props.dart";
 import 'package:photos/models/file/file.dart';
 import "package:photos/services/feature_flag_service.dart";
 import 'package:photos/services/files_service.dart';
@@ -76,8 +76,7 @@ class _VideoWidgetState extends State<VideoWidget> {
   }
 
   void _setFileSizeIfNull() {
-    if (widget.file.fileSize == null &&
-        widget.file.ownerID == Configuration.instance.getUserID()) {
+    if (widget.file.fileSize == null && widget.file.canEditMetaInfo) {
       FilesService.instance
           .getFileSize(widget.file.uploadedFileID!)
           .then((value) {

+ 1 - 19
lib/ui/viewer/file/zoomable_live_image.dart

@@ -4,8 +4,6 @@ import 'package:chewie/chewie.dart';
 import 'package:flutter/material.dart';
 import 'package:logging/logging.dart';
 import 'package:motion_photos/motion_photos.dart';
-import "package:photos/core/configuration.dart";
-import 'package:photos/core/constants.dart';
 import "package:photos/generated/l10n.dart";
 import "package:photos/models/file/extensions/file_props.dart";
 import 'package:photos/models/file/file.dart';
@@ -14,7 +12,6 @@ import "package:photos/services/file_magic_service.dart";
 import 'package:photos/ui/viewer/file/zoomable_image.dart';
 import 'package:photos/utils/file_util.dart';
 import 'package:photos/utils/toast_util.dart';
-import 'package:shared_preferences/shared_preferences.dart';
 import 'package:video_player/video_player.dart';
 
 class ZoomableLiveImage extends StatefulWidget {
@@ -48,7 +45,6 @@ class _ZoomableLiveImageState extends State<ZoomableLiveImage>
   @override
   void initState() {
     _enteFile = widget.enteFile;
-    Future.microtask(() => _showHintForMotionPhotoPlay).ignore();
     super.initState();
   }
 
@@ -183,9 +179,7 @@ class _ZoomableLiveImageState extends State<ZoomableLiveImage>
       final index = await motionPhoto.getMotionVideoIndex();
       if (index != null) {
         // Update the metadata if it is not updated
-        if (!_enteFile.isMotionPhoto &&
-            _enteFile
-                .canEditMetaInfo(Configuration.instance.getUserID()!)) {
+        if (!_enteFile.isMotionPhoto && _enteFile.canEditMetaInfo) {
           FileMagicService.instance.updatePublicMagicMetadata(
             [_enteFile],
             {motionVideoIndexKey: index.start},
@@ -196,7 +190,6 @@ class _ZoomableLiveImageState extends State<ZoomableLiveImage>
         );
       }
     }
-
     return null;
   }
 
@@ -212,15 +205,4 @@ class _ZoomableLiveImageState extends State<ZoomableLiveImage>
       });
   }
 
-  void _showHintForMotionPhotoPlay() async {
-    if (!_enteFile.isLiveOrMotionPhoto) {
-      return;
-    }
-    final preferences = await SharedPreferences.getInstance();
-    final int promptTillNow = preferences.getInt(livePhotoToastCounterKey) ?? 0;
-    if (promptTillNow < maxLivePhotoToastCount && mounted) {
-      showShortToast(context, S.of(context).pressAndHoldToPlayVideo);
-      preferences.setInt(livePhotoToastCounterKey, promptTillNow + 1);
-    }
-  }
 }

+ 6 - 7
lib/ui/viewer/file_details/added_by_widget.dart

@@ -1,24 +1,23 @@
 import "package:flutter/material.dart";
 import "package:photos/generated/l10n.dart";
+import "package:photos/models/file/extensions/file_props.dart";
 import 'package:photos/models/file/file.dart';
 import "package:photos/services/collections_service.dart";
 import "package:photos/theme/ente_theme.dart";
 
 class AddedByWidget extends StatelessWidget {
   final EnteFile file;
-  final int currentUserID;
-  const AddedByWidget(this.file, this.currentUserID, {super.key});
+
+  const AddedByWidget(this.file, {super.key});
 
   @override
   Widget build(BuildContext context) {
-    if (file.uploadedFileID == null) {
+    if (file.isUploaded) {
       return const SizedBox.shrink();
     }
     String? addedBy;
-    if (file.ownerID == currentUserID) {
-      if (file.pubMagicMetadata!.uploaderName != null) {
-        addedBy = file.pubMagicMetadata!.uploaderName;
-      }
+    if (file.isOwner && file.isCollect) {
+      addedBy = file.uploaderName;
     } else {
       final fileOwner = CollectionsService.instance
           .getFileOwner(file.ownerID!, file.collectionID);

+ 1 - 1
lib/ui/viewer/search/result/search_thumbnail_widget.dart

@@ -24,7 +24,7 @@ class SearchThumbnailWidget extends StatelessWidget {
           borderRadius: BorderRadius.circular(3),
           child: file != null
               ? ThumbnailWidget(
-                  file,
+                  file!,
                 )
               : const NoThumbnailWidget(),
         ),