Sfoglia il codice sorgente

Merge pull request #817 from ente-io/redesign-add-to-album

Redesign add/move to album
Ashil 2 anni fa
parent
commit
cc23be733c

+ 2 - 0
lib/models/collection.dart

@@ -81,6 +81,8 @@ class Collection {
     return (owner?.id ?? 0) == userID;
     return (owner?.id ?? 0) == userID;
   }
   }
 
 
+  String get collectionName => name ?? "Unnamed collection";
+
   void updateSharees(List<User> newSharees) {
   void updateSharees(List<User> newSharees) {
     sharees?.clear();
     sharees?.clear();
     sharees?.addAll(newSharees);
     sharees?.addAll(newSharees);

+ 104 - 0
lib/ui/components/album_list_item_widget.dart

@@ -0,0 +1,104 @@
+import 'package:flutter/material.dart';
+import 'package:logging/logging.dart';
+import 'package:photos/db/files_db.dart';
+import 'package:photos/models/collection_items.dart';
+import 'package:photos/theme/ente_theme.dart';
+import 'package:photos/ui/viewer/file/no_thumbnail_widget.dart';
+import 'package:photos/ui/viewer/file/thumbnail_widget.dart';
+
+///https://www.figma.com/file/SYtMyLBs5SAOkTbfMMzhqt/ente-Visual-Design?node-id=7480%3A33462&t=H5AvR79OYDnB9ekw-4
+class AlbumListItemWidget extends StatelessWidget {
+  final CollectionWithThumbnail item;
+  const AlbumListItemWidget(
+    this.item, {
+    super.key,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    final textTheme = getEnteTextTheme(context);
+    final colorScheme = getEnteColorScheme(context);
+    const sideOfThumbnail = 60.0;
+    return LayoutBuilder(
+      builder: (context, constraints) {
+        return Stack(
+          alignment: Alignment.center,
+          children: [
+            Row(
+              children: [
+                ClipRRect(
+                  borderRadius: const BorderRadius.horizontal(
+                    left: Radius.circular(4),
+                  ),
+                  child: SizedBox(
+                    height: sideOfThumbnail,
+                    width: sideOfThumbnail,
+                    child: item.thumbnail != null
+                        ? ThumbnailWidget(
+                            item.thumbnail,
+                            showFavForAlbumOnly: true,
+                          )
+                        : const NoThumbnailWidget(
+                            addBorder: false,
+                          ),
+                  ),
+                ),
+                Padding(
+                  padding: const EdgeInsets.only(left: 12),
+                  child: Column(
+                    crossAxisAlignment: CrossAxisAlignment.start,
+                    children: [
+                      Text(item.collection.collectionName),
+                      FutureBuilder<int>(
+                        future: FilesDB.instance.collectionFileCount(
+                          item.collection.id,
+                        ),
+                        builder: (context, snapshot) {
+                          if (snapshot.hasData) {
+                            final text =
+                                snapshot.data == 1 ? " memory" : " memories";
+                            return Text(
+                              snapshot.data.toString() + text,
+                              style: textTheme.small.copyWith(
+                                color: colorScheme.textMuted,
+                              ),
+                            );
+                          } else {
+                            if (snapshot.hasError) {
+                              Logger("AlbumListItemWidget").severe(
+                                "Failed to fetch file count of collection",
+                                snapshot.error,
+                              );
+                            }
+                            return Text(
+                              "",
+                              style: textTheme.small.copyWith(
+                                color: colorScheme.textMuted,
+                              ),
+                            );
+                          }
+                        },
+                      )
+                    ],
+                  ),
+                ),
+              ],
+            ),
+            IgnorePointer(
+              child: Container(
+                decoration: BoxDecoration(
+                  borderRadius: const BorderRadius.all(Radius.circular(4)),
+                  border: Border.all(
+                    color: colorScheme.strokeFainter,
+                  ),
+                ),
+                height: sideOfThumbnail,
+                width: constraints.maxWidth,
+              ),
+            ),
+          ],
+        );
+      },
+    );
+  }
+}

+ 37 - 0
lib/ui/components/bottom_of_title_bar_widget.dart

@@ -0,0 +1,37 @@
+import 'package:flutter/material.dart';
+import 'package:photos/theme/ente_theme.dart';
+import 'package:photos/ui/components/title_bar_title_widget.dart';
+
+///https://www.figma.com/file/SYtMyLBs5SAOkTbfMMzhqt/ente-Visual-Design?node-id=7309%3A29088&t=ReeZ2Big8xSsemZb-4
+class BottomOfTitleBarWidget extends StatelessWidget {
+  final TitleBarTitleWidget? title;
+  final String? caption;
+  const BottomOfTitleBarWidget({this.title, this.caption, super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Row(
+      children: [
+        Flexible(
+          child: Padding(
+            padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
+            child: Column(
+              crossAxisAlignment: CrossAxisAlignment.start,
+              children: [
+                title ?? const SizedBox.shrink(),
+                caption != null
+                    ? Text(
+                        caption!,
+                        style: getEnteTextTheme(context).small.copyWith(
+                              color: getEnteColorScheme(context).textMuted,
+                            ),
+                      )
+                    : const SizedBox.shrink(),
+              ],
+            ),
+          ),
+        ),
+      ],
+    );
+  }
+}

+ 73 - 0
lib/ui/components/new_album_list_widget.dart

@@ -0,0 +1,73 @@
+import 'package:dotted_border/dotted_border.dart';
+import 'package:flutter/material.dart';
+import 'package:photos/theme/ente_theme.dart';
+
+///https://www.figma.com/file/SYtMyLBs5SAOkTbfMMzhqt/ente-Visual-Design?node-id=10854%3A57947&t=H5AvR79OYDnB9ekw-4
+class NewAlbumListItemWidget extends StatelessWidget {
+  const NewAlbumListItemWidget({
+    super.key,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    final textTheme = getEnteTextTheme(context);
+    final colorScheme = getEnteColorScheme(context);
+    const sideOfThumbnail = 60.0;
+    return LayoutBuilder(
+      builder: (context, constraints) {
+        return Stack(
+          alignment: Alignment.center,
+          children: [
+            Row(
+              children: [
+                ClipRRect(
+                  borderRadius: const BorderRadius.horizontal(
+                    left: Radius.circular(4),
+                  ),
+                  child: SizedBox(
+                    height: sideOfThumbnail,
+                    width: sideOfThumbnail,
+                    child: Icon(
+                      Icons.add_outlined,
+                      color: colorScheme.strokeMuted,
+                    ),
+                  ),
+                ),
+                Padding(
+                  padding: const EdgeInsets.only(left: 12),
+                  child: Text(
+                    "New album",
+                    style:
+                        textTheme.body.copyWith(color: colorScheme.textMuted),
+                  ),
+                ),
+              ],
+            ),
+            IgnorePointer(
+              child: DottedBorder(
+                dashPattern: const [4],
+                color: colorScheme.strokeFainter,
+                strokeWidth: 1,
+                padding: const EdgeInsets.all(0),
+                borderType: BorderType.RRect,
+                radius: const Radius.circular(4),
+                child: SizedBox(
+                  //Have to decrease the height and width by 1 pt as the stroke
+                  //dotted border gives is of strokeAlign.center, so 0.5 inside and
+                  // outside. Here for the row, stroke should be inside so we
+                  //decrease the size of this sizedBox by 1 (so it shrinks 0.5 from
+                  //every side) so that the strokeAlign.center of this sizedBox
+                  //looks like a strokeAlign.inside in the row.
+                  height: sideOfThumbnail - 1,
+                  //This width will work for this only if the row widget takes up the
+                  //full size it's parent (stack).
+                  width: constraints.maxWidth - 1,
+                ),
+              ),
+            ),
+          ],
+        );
+      },
+    );
+  }
+}

+ 0 - 416
lib/ui/create_collection_page.dart

@@ -1,416 +0,0 @@
-import 'package:collection/collection.dart';
-import 'package:flutter/material.dart';
-import 'package:logging/logging.dart';
-import 'package:photos/db/files_db.dart';
-import 'package:photos/ente_theme_data.dart';
-import 'package:photos/models/collection.dart';
-import 'package:photos/models/collection_items.dart';
-import 'package:photos/models/file.dart';
-import 'package:photos/models/selected_files.dart';
-import 'package:photos/services/collections_service.dart';
-import 'package:photos/services/ignored_files_service.dart';
-import 'package:photos/services/remote_sync_service.dart';
-import 'package:photos/ui/common/gradient_button.dart';
-import 'package:photos/ui/common/loading_widget.dart';
-import 'package:photos/ui/viewer/file/no_thumbnail_widget.dart';
-import 'package:photos/ui/viewer/file/thumbnail_widget.dart';
-import 'package:photos/ui/viewer/gallery/collection_page.dart';
-import 'package:photos/utils/dialog_util.dart';
-import 'package:photos/utils/navigation_util.dart';
-import 'package:photos/utils/share_util.dart';
-import 'package:photos/utils/toast_util.dart';
-import 'package:receive_sharing_intent/receive_sharing_intent.dart';
-
-enum CollectionActionType { addFiles, moveFiles, restoreFiles, unHide }
-
-String _actionName(CollectionActionType type, bool plural) {
-  final titleSuffix = (plural ? "s" : "");
-  String text = "";
-  switch (type) {
-    case CollectionActionType.addFiles:
-      text = "Add file";
-      break;
-    case CollectionActionType.moveFiles:
-      text = "Move file";
-      break;
-    case CollectionActionType.restoreFiles:
-      text = "Restore file";
-      break;
-    case CollectionActionType.unHide:
-      text = "Unhide file";
-      break;
-  }
-  return text + titleSuffix;
-}
-
-class CreateCollectionPage extends StatefulWidget {
-  final SelectedFiles? selectedFiles;
-  final List<SharedMediaFile>? sharedFiles;
-  final CollectionActionType actionType;
-
-  const CreateCollectionPage(
-    this.selectedFiles,
-    this.sharedFiles, {
-    Key? key,
-    this.actionType = CollectionActionType.addFiles,
-  }) : super(key: key);
-
-  @override
-  State<CreateCollectionPage> createState() => _CreateCollectionPageState();
-}
-
-class _CreateCollectionPageState extends State<CreateCollectionPage> {
-  final _logger = Logger((_CreateCollectionPageState).toString());
-  late String _albumName;
-
-  @override
-  Widget build(BuildContext context) {
-    final filesCount = widget.sharedFiles != null
-        ? widget.sharedFiles!.length
-        : widget.selectedFiles!.files.length;
-    return Scaffold(
-      appBar: AppBar(
-        title: Text(_actionName(widget.actionType, filesCount > 1)),
-      ),
-      body: _getBody(context),
-    );
-  }
-
-  Widget _getBody(BuildContext context) {
-    return SingleChildScrollView(
-      child: Column(
-        mainAxisSize: MainAxisSize.min,
-        children: [
-          Row(
-            children: [
-              Expanded(
-                child: Padding(
-                  padding: const EdgeInsets.only(
-                    top: 30,
-                    bottom: 12,
-                    left: 40,
-                    right: 40,
-                  ),
-                  child: GradientButton(
-                    onTap: () async {
-                      _showNameAlbumDialog();
-                    },
-                    iconData: Icons.create_new_folder_outlined,
-                    text: "To a new album",
-                  ),
-                ),
-              ),
-            ],
-          ),
-          const Padding(
-            padding: EdgeInsets.fromLTRB(40, 24, 40, 20),
-            child: Align(
-              alignment: Alignment.centerLeft,
-              child: Text(
-                "To an existing album",
-                style: TextStyle(
-                  fontWeight: FontWeight.bold,
-                ),
-              ),
-            ),
-          ),
-          Padding(
-            padding: const EdgeInsets.fromLTRB(20, 4, 20, 0),
-            child: _getExistingCollectionsWidget(),
-          ),
-        ],
-      ),
-    );
-  }
-
-  Widget _getExistingCollectionsWidget() {
-    return FutureBuilder<List<CollectionWithThumbnail>>(
-      future: _getCollectionsWithThumbnail(),
-      builder: (context, snapshot) {
-        if (snapshot.hasError) {
-          return Text(snapshot.error.toString());
-        } else if (snapshot.hasData) {
-          return ListView.builder(
-            itemBuilder: (context, index) {
-              return _buildCollectionItem(snapshot.data![index]);
-            },
-            itemCount: snapshot.data!.length,
-            shrinkWrap: true,
-            physics: const NeverScrollableScrollPhysics(),
-          );
-        } else {
-          return const EnteLoadingWidget();
-        }
-      },
-    );
-  }
-
-  Widget _buildCollectionItem(CollectionWithThumbnail item) {
-    return Container(
-      padding: const EdgeInsets.only(left: 24, bottom: 16),
-      child: GestureDetector(
-        behavior: HitTestBehavior.translucent,
-        child: Row(
-          children: <Widget>[
-            ClipRRect(
-              borderRadius: BorderRadius.circular(2.0),
-              child: SizedBox(
-                height: 64,
-                width: 64,
-                key: Key("collection_item:" + (item.thumbnail?.tag ?? "")),
-                child: item.thumbnail != null
-                    ? ThumbnailWidget(
-                        item.thumbnail,
-                        showFavForAlbumOnly: true,
-                      )
-                    : const NoThumbnailWidget(),
-              ),
-            ),
-            const Padding(padding: EdgeInsets.all(8)),
-            Expanded(
-              child: Text(
-                item.collection.name!,
-                style: const TextStyle(
-                  fontSize: 16,
-                ),
-              ),
-            ),
-          ],
-        ),
-        onTap: () async {
-          if (await _runCollectionAction(item.collection.id)) {
-            showShortToast(
-              context,
-              widget.actionType == CollectionActionType.addFiles
-                  ? "Added successfully to " + item.collection.name!
-                  : "Moved successfully to " + item.collection.name!,
-            );
-            _navigateToCollection(item.collection);
-          }
-        },
-      ),
-    );
-  }
-
-  Future<List<CollectionWithThumbnail>> _getCollectionsWithThumbnail() async {
-    final List<CollectionWithThumbnail> collectionsWithThumbnail =
-        await CollectionsService.instance.getCollectionsWithThumbnails(
-      // in collections where user is a collaborator, only addTo and remove
-      // action can to be performed
-      includeCollabCollections:
-          widget.actionType == CollectionActionType.addFiles,
-    );
-    collectionsWithThumbnail.removeWhere(
-      (element) => (element.collection.type == CollectionType.favorites ||
-          element.collection.type == CollectionType.uncategorized ||
-          element.collection.isSharedFilesCollection()),
-    );
-    collectionsWithThumbnail.sort((first, second) {
-      return compareAsciiLowerCaseNatural(
-        first.collection.name ?? "",
-        second.collection.name ?? "",
-      );
-    });
-    return collectionsWithThumbnail;
-  }
-
-  void _showNameAlbumDialog() async {
-    final AlertDialog alert = AlertDialog(
-      title: const Text("Album title"),
-      content: TextFormField(
-        decoration: const InputDecoration(
-          hintText: "Christmas 2020 / Dinner at Alice's",
-          contentPadding: EdgeInsets.all(8),
-        ),
-        onChanged: (value) {
-          setState(() {
-            _albumName = value;
-          });
-        },
-        autofocus: true,
-        keyboardType: TextInputType.text,
-        textCapitalization: TextCapitalization.words,
-      ),
-      actions: [
-        TextButton(
-          child: Text(
-            "Ok",
-            style: TextStyle(
-              color: Theme.of(context).colorScheme.greenAlternative,
-            ),
-          ),
-          onPressed: () async {
-            Navigator.of(context, rootNavigator: true).pop('dialog');
-            final collection = await _createAlbum(_albumName);
-            if (collection != null) {
-              if (await _runCollectionAction(collection.id)) {
-                if (widget.actionType == CollectionActionType.restoreFiles) {
-                  showShortToast(
-                    context,
-                    'Restored files to album ' + _albumName,
-                  );
-                } else {
-                  showShortToast(
-                    context,
-                    "Album '" + _albumName + "' created.",
-                  );
-                }
-                _navigateToCollection(collection);
-              }
-            }
-          },
-        ),
-      ],
-    );
-
-    showDialog(
-      context: context,
-      builder: (BuildContext context) {
-        return alert;
-      },
-    );
-  }
-
-  void _navigateToCollection(Collection collection) {
-    Navigator.pop(context);
-    routeToPage(
-      context,
-      CollectionPage(
-        CollectionWithThumbnail(collection, null),
-      ),
-    );
-  }
-
-  Future<bool> _runCollectionAction(int collectionID) async {
-    switch (widget.actionType) {
-      case CollectionActionType.addFiles:
-        return _addToCollection(collectionID);
-      case CollectionActionType.moveFiles:
-        return _moveFilesToCollection(collectionID);
-      case CollectionActionType.unHide:
-        return _moveFilesToCollection(collectionID);
-      case CollectionActionType.restoreFiles:
-        return _restoreFilesToCollection(collectionID);
-    }
-  }
-
-  Future<bool> _moveFilesToCollection(int toCollectionID) async {
-    final String message = widget.actionType == CollectionActionType.moveFiles
-        ? "Moving files to album..."
-        : "Unhiding files to album";
-    final dialog = createProgressDialog(context, message);
-    await dialog.show();
-    try {
-      final int fromCollectionID =
-          widget.selectedFiles!.files.first.collectionID!;
-      await CollectionsService.instance.move(
-        toCollectionID,
-        fromCollectionID,
-        widget.selectedFiles!.files.toList(),
-      );
-      await dialog.hide();
-      RemoteSyncService.instance.sync(silently: true);
-      widget.selectedFiles?.clearAll();
-
-      return true;
-    } on AssertionError catch (e) {
-      await dialog.hide();
-      showErrorDialog(context, "Oops", e.message as String?);
-      return false;
-    } catch (e, s) {
-      _logger.severe("Could not move to album", e, s);
-      await dialog.hide();
-      showGenericErrorDialog(context: context);
-      return false;
-    }
-  }
-
-  Future<bool> _restoreFilesToCollection(int toCollectionID) async {
-    final dialog = createProgressDialog(context, "Restoring files...");
-    await dialog.show();
-    try {
-      await CollectionsService.instance
-          .restore(toCollectionID, widget.selectedFiles!.files.toList());
-      RemoteSyncService.instance.sync(silently: true);
-      widget.selectedFiles?.clearAll();
-      await dialog.hide();
-      return true;
-    } on AssertionError catch (e) {
-      await dialog.hide();
-      showErrorDialog(context, "Oops", e.message as String?);
-      return false;
-    } catch (e, s) {
-      _logger.severe("Could not move to album", e, s);
-      await dialog.hide();
-      showGenericErrorDialog(context: context);
-      return false;
-    }
-  }
-
-  Future<bool> _addToCollection(int collectionID) async {
-    final dialog = createProgressDialog(context, "Uploading files to album...");
-    await dialog.show();
-    try {
-      final List<File> files = [];
-      final List<File> filesPendingUpload = [];
-      if (widget.sharedFiles != null) {
-        filesPendingUpload.addAll(
-          await convertIncomingSharedMediaToFile(
-            widget.sharedFiles!,
-            collectionID,
-          ),
-        );
-      } else {
-        for (final file in widget.selectedFiles!.files) {
-          final File? currentFile =
-              await (FilesDB.instance.getFile(file.generatedID!));
-          if (currentFile == null) {
-            _logger.severe("Failed to find fileBy genID");
-            continue;
-          }
-          if (currentFile.uploadedFileID == null) {
-            currentFile.collectionID = collectionID;
-            filesPendingUpload.add(currentFile);
-          } else {
-            files.add(currentFile);
-          }
-        }
-      }
-      if (filesPendingUpload.isNotEmpty) {
-        // filesPendingUpload might be getting ignored during auto-upload
-        // because the user deleted these files from ente in the past.
-        await IgnoredFilesService.instance
-            .removeIgnoredMappings(filesPendingUpload);
-        await FilesDB.instance.insertMultiple(filesPendingUpload);
-      }
-      if (files.isNotEmpty) {
-        await CollectionsService.instance.addToCollection(collectionID, files);
-      }
-      RemoteSyncService.instance.sync(silently: true);
-      await dialog.hide();
-      widget.selectedFiles?.clearAll();
-      return true;
-    } catch (e, s) {
-      _logger.severe("Could not add to album", e, s);
-      await dialog.hide();
-      showGenericErrorDialog(context: context);
-    }
-    return false;
-  }
-
-  Future<Collection?> _createAlbum(String albumName) async {
-    Collection? collection;
-    final dialog = createProgressDialog(context, "Creating album...");
-    await dialog.show();
-    try {
-      collection = await CollectionsService.instance.createAlbum(albumName);
-    } catch (e, s) {
-      _logger.severe(e, s);
-      await dialog.hide();
-      showGenericErrorDialog(context: context);
-    } finally {
-      await dialog.hide();
-    }
-    return collection;
-  }
-}

+ 465 - 0
lib/ui/create_collection_sheet.dart

@@ -0,0 +1,465 @@
+import 'dart:math';
+
+import 'package:collection/collection.dart';
+import 'package:flutter/material.dart';
+import 'package:logging/logging.dart';
+import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
+import 'package:photos/db/files_db.dart';
+import 'package:photos/models/collection.dart';
+import 'package:photos/models/collection_items.dart';
+import 'package:photos/models/file.dart';
+import 'package:photos/models/selected_files.dart';
+import 'package:photos/services/collections_service.dart';
+import 'package:photos/services/ignored_files_service.dart';
+import 'package:photos/services/remote_sync_service.dart';
+import 'package:photos/theme/colors.dart';
+import 'package:photos/theme/ente_theme.dart';
+import 'package:photos/ui/common/loading_widget.dart';
+import 'package:photos/ui/components/album_list_item_widget.dart';
+import 'package:photos/ui/components/bottom_of_title_bar_widget.dart';
+import 'package:photos/ui/components/button_widget.dart';
+import 'package:photos/ui/components/models/button_type.dart';
+import 'package:photos/ui/components/new_album_list_widget.dart';
+import 'package:photos/ui/components/title_bar_title_widget.dart';
+import 'package:photos/ui/viewer/gallery/collection_page.dart';
+import 'package:photos/utils/dialog_util.dart';
+import 'package:photos/utils/navigation_util.dart';
+import 'package:photos/utils/share_util.dart';
+import 'package:photos/utils/toast_util.dart';
+import 'package:receive_sharing_intent/receive_sharing_intent.dart';
+
+enum CollectionActionType { addFiles, moveFiles, restoreFiles, unHide }
+
+String _actionName(CollectionActionType type, bool plural) {
+  bool addTitleSuffix = false;
+  final titleSuffix = (plural ? "s" : "");
+  String text = "";
+  switch (type) {
+    case CollectionActionType.addFiles:
+      text = "Add item";
+      addTitleSuffix = true;
+      break;
+    case CollectionActionType.moveFiles:
+      text = "Move item";
+      addTitleSuffix = true;
+      break;
+    case CollectionActionType.restoreFiles:
+      text = "Restore to album";
+      break;
+    case CollectionActionType.unHide:
+      text = "Unhide to album";
+      break;
+  }
+  return addTitleSuffix ? text + titleSuffix : text;
+}
+
+void createCollectionSheet(
+  SelectedFiles? selectedFiles,
+  List<SharedMediaFile>? sharedFiles,
+  BuildContext context, {
+  CollectionActionType actionType = CollectionActionType.addFiles,
+  bool showOptionToCreateNewAlbum = true,
+}) {
+  showBarModalBottomSheet(
+    context: context,
+    builder: (context) {
+      return CreateCollectionSheet(
+        selectedFiles: selectedFiles,
+        sharedFiles: sharedFiles,
+        actionType: actionType,
+        showOptionToCreateNewAlbum: showOptionToCreateNewAlbum,
+      );
+    },
+    shape: const RoundedRectangleBorder(
+      side: BorderSide(width: 0),
+      borderRadius: BorderRadius.vertical(
+        top: Radius.circular(5),
+      ),
+    ),
+    topControl: const SizedBox.shrink(),
+    backgroundColor: getEnteColorScheme(context).backgroundElevated,
+    barrierColor: backdropFaintDark,
+    enableDrag: false,
+  );
+}
+
+class CreateCollectionSheet extends StatefulWidget {
+  final SelectedFiles? selectedFiles;
+  final List<SharedMediaFile>? sharedFiles;
+  final CollectionActionType actionType;
+  final bool showOptionToCreateNewAlbum;
+  const CreateCollectionSheet({
+    required this.selectedFiles,
+    required this.sharedFiles,
+    required this.actionType,
+    required this.showOptionToCreateNewAlbum,
+    super.key,
+  });
+
+  @override
+  State<CreateCollectionSheet> createState() => _CreateCollectionSheetState();
+}
+
+class _CreateCollectionSheetState extends State<CreateCollectionSheet> {
+  final _logger = Logger((_CreateCollectionSheetState).toString());
+
+  @override
+  Widget build(BuildContext context) {
+    final filesCount = widget.sharedFiles != null
+        ? widget.sharedFiles!.length
+        : widget.selectedFiles!.files.length;
+    return Row(
+      mainAxisAlignment: MainAxisAlignment.center,
+      children: [
+        ConstrainedBox(
+          constraints: BoxConstraints(
+            maxWidth: min(428, MediaQuery.of(context).size.width),
+          ),
+          child: Padding(
+            padding: const EdgeInsets.fromLTRB(0, 32, 0, 8),
+            child: Column(
+              mainAxisSize: MainAxisSize.min,
+              children: [
+                BottomOfTitleBarWidget(
+                  title: TitleBarTitleWidget(
+                    title: _actionName(widget.actionType, filesCount > 1),
+                  ),
+                  caption: "Create or select album",
+                ),
+                Flexible(
+                  child: Column(
+                    mainAxisSize: MainAxisSize.min,
+                    children: [
+                      Flexible(
+                        child: Padding(
+                          padding: const EdgeInsets.fromLTRB(16, 24, 4, 0),
+                          child: Scrollbar(
+                            radius: const Radius.circular(2),
+                            child: Padding(
+                              padding: const EdgeInsets.only(right: 12),
+                              child: FutureBuilder(
+                                future: _getCollectionsWithThumbnail(),
+                                builder: (context, snapshot) {
+                                  if (snapshot.hasError) {
+                                    //Need to show an error on the UI here
+                                    return const SizedBox.shrink();
+                                  } else if (snapshot.hasData) {
+                                    final collectionsWithThumbnail = snapshot
+                                        .data as List<CollectionWithThumbnail>;
+                                    return ListView.separated(
+                                      itemBuilder: (context, index) {
+                                        if (index == 0 &&
+                                            widget.showOptionToCreateNewAlbum) {
+                                          return GestureDetector(
+                                            onTap: () {
+                                              _showNameAlbumDialog();
+                                            },
+                                            behavior: HitTestBehavior.opaque,
+                                            child:
+                                                const NewAlbumListItemWidget(),
+                                          );
+                                        }
+                                        final item = collectionsWithThumbnail[
+                                            index -
+                                                (widget.showOptionToCreateNewAlbum
+                                                    ? 1
+                                                    : 0)];
+                                        return GestureDetector(
+                                          behavior: HitTestBehavior.opaque,
+                                          onTap: () =>
+                                              _albumListItemOnTap(item),
+                                          child: AlbumListItemWidget(
+                                            item,
+                                          ),
+                                        );
+                                      },
+                                      separatorBuilder: (context, index) =>
+                                          const SizedBox(
+                                        height: 8,
+                                      ),
+                                      itemCount:
+                                          collectionsWithThumbnail.length +
+                                              (widget.showOptionToCreateNewAlbum
+                                                  ? 1
+                                                  : 0),
+                                      shrinkWrap: true,
+                                      physics: const BouncingScrollPhysics(),
+                                    );
+                                  } else {
+                                    return const EnteLoadingWidget();
+                                  }
+                                },
+                              ),
+                            ),
+                          ),
+                        ),
+                      ),
+                      SafeArea(
+                        child: Container(
+                          //inner stroke of 1pt + 15 pts of top padding = 16 pts
+                          padding: const EdgeInsets.fromLTRB(16, 15, 16, 8),
+                          decoration: BoxDecoration(
+                            border: Border(
+                              top: BorderSide(
+                                color: getEnteColorScheme(context).strokeFaint,
+                              ),
+                            ),
+                          ),
+                          child: const ButtonWidget(
+                            buttonType: ButtonType.secondary,
+                            buttonAction: ButtonAction.cancel,
+                            isInAlert: true,
+                            labelText: "Cancel",
+                          ),
+                        ),
+                      )
+                    ],
+                  ),
+                ),
+              ],
+            ),
+          ),
+        ),
+      ],
+    );
+  }
+
+  void _showNameAlbumDialog() async {
+    String? albumName;
+    final AlertDialog alert = AlertDialog(
+      title: const Text("Album title"),
+      content: TextFormField(
+        decoration: const InputDecoration(
+          hintText: "Christmas 2020 / Dinner at Alice's",
+          contentPadding: EdgeInsets.all(8),
+        ),
+        onChanged: (value) {
+          albumName = value;
+        },
+        autofocus: true,
+        keyboardType: TextInputType.text,
+        textCapitalization: TextCapitalization.words,
+      ),
+      actions: [
+        TextButton(
+          child: Text(
+            "Ok",
+            style: TextStyle(
+              color: getEnteColorScheme(context).primary500,
+            ),
+          ),
+          onPressed: () async {
+            if (albumName != null && albumName!.isNotEmpty) {
+              Navigator.of(context, rootNavigator: true).pop('dialog');
+              final collection = await _createAlbum(albumName!);
+              if (collection != null) {
+                if (await _runCollectionAction(collection.id)) {
+                  if (widget.actionType == CollectionActionType.restoreFiles) {
+                    showShortToast(
+                      context,
+                      'Restored files to album ' + albumName!,
+                    );
+                  } else {
+                    showShortToast(
+                      context,
+                      "Album '" + albumName! + "' created.",
+                    );
+                  }
+                  _navigateToCollection(collection);
+                }
+              }
+            }
+          },
+        ),
+      ],
+    );
+
+    showDialog(
+      context: context,
+      builder: (BuildContext context) {
+        return alert;
+      },
+    );
+  }
+
+  Future<Collection?> _createAlbum(String albumName) async {
+    Collection? collection;
+    final dialog = createProgressDialog(context, "Creating album...");
+    await dialog.show();
+    try {
+      collection = await CollectionsService.instance.createAlbum(albumName);
+    } catch (e, s) {
+      _logger.severe(e, s);
+      await dialog.hide();
+      showGenericErrorDialog(context: context);
+    } finally {
+      await dialog.hide();
+    }
+    return collection;
+  }
+
+  Future<void> _albumListItemOnTap(CollectionWithThumbnail item) async {
+    if (await _runCollectionAction(
+      item.collection.id,
+    )) {
+      showShortToast(
+        context,
+        widget.actionType == CollectionActionType.addFiles
+            ? "Added successfully to " + item.collection.name!
+            : "Moved successfully to " + item.collection.name!,
+      );
+      _navigateToCollection(
+        item.collection,
+      );
+    }
+  }
+
+  Future<List<CollectionWithThumbnail>> _getCollectionsWithThumbnail() async {
+    final List<CollectionWithThumbnail> collectionsWithThumbnail =
+        await CollectionsService.instance.getCollectionsWithThumbnails(
+      // in collections where user is a collaborator, only addTo and remove
+      // action can to be performed
+      includeCollabCollections:
+          widget.actionType == CollectionActionType.addFiles,
+    );
+    collectionsWithThumbnail.removeWhere(
+      (element) => (element.collection.type == CollectionType.favorites ||
+          element.collection.type == CollectionType.uncategorized ||
+          element.collection.isSharedFilesCollection()),
+    );
+    collectionsWithThumbnail.sort((first, second) {
+      return compareAsciiLowerCaseNatural(
+        first.collection.name ?? "",
+        second.collection.name ?? "",
+      );
+    });
+    return collectionsWithThumbnail;
+  }
+
+  void _navigateToCollection(Collection collection) {
+    Navigator.pop(context);
+    routeToPage(
+      context,
+      CollectionPage(
+        CollectionWithThumbnail(collection, null),
+      ),
+    );
+  }
+
+  Future<bool> _runCollectionAction(int collectionID) async {
+    switch (widget.actionType) {
+      case CollectionActionType.addFiles:
+        return _addToCollection(collectionID);
+      case CollectionActionType.moveFiles:
+        return _moveFilesToCollection(collectionID);
+      case CollectionActionType.unHide:
+        return _moveFilesToCollection(collectionID);
+      case CollectionActionType.restoreFiles:
+        return _restoreFilesToCollection(collectionID);
+    }
+  }
+
+  Future<bool> _addToCollection(int collectionID) async {
+    final dialog = createProgressDialog(context, "Uploading files to album...");
+    await dialog.show();
+    try {
+      final List<File> files = [];
+      final List<File> filesPendingUpload = [];
+      if (widget.sharedFiles != null) {
+        filesPendingUpload.addAll(
+          await convertIncomingSharedMediaToFile(
+            widget.sharedFiles!,
+            collectionID,
+          ),
+        );
+      } else {
+        for (final file in widget.selectedFiles!.files) {
+          final File? currentFile =
+              await (FilesDB.instance.getFile(file.generatedID!));
+          if (currentFile == null) {
+            _logger.severe("Failed to find fileBy genID");
+            continue;
+          }
+          if (currentFile.uploadedFileID == null) {
+            currentFile.collectionID = collectionID;
+            filesPendingUpload.add(currentFile);
+          } else {
+            files.add(currentFile);
+          }
+        }
+      }
+      if (filesPendingUpload.isNotEmpty) {
+        // filesPendingUpload might be getting ignored during auto-upload
+        // because the user deleted these files from ente in the past.
+        await IgnoredFilesService.instance
+            .removeIgnoredMappings(filesPendingUpload);
+        await FilesDB.instance.insertMultiple(filesPendingUpload);
+      }
+      if (files.isNotEmpty) {
+        await CollectionsService.instance.addToCollection(collectionID, files);
+      }
+      RemoteSyncService.instance.sync(silently: true);
+      await dialog.hide();
+      widget.selectedFiles?.clearAll();
+      return true;
+    } catch (e, s) {
+      _logger.severe("Could not add to album", e, s);
+      await dialog.hide();
+      showGenericErrorDialog(context: context);
+    }
+    return false;
+  }
+
+  Future<bool> _moveFilesToCollection(int toCollectionID) async {
+    final String message = widget.actionType == CollectionActionType.moveFiles
+        ? "Moving files to album..."
+        : "Unhiding files to album";
+    final dialog = createProgressDialog(context, message);
+    await dialog.show();
+    try {
+      final int fromCollectionID =
+          widget.selectedFiles!.files.first.collectionID!;
+      await CollectionsService.instance.move(
+        toCollectionID,
+        fromCollectionID,
+        widget.selectedFiles!.files.toList(),
+      );
+      await dialog.hide();
+      RemoteSyncService.instance.sync(silently: true);
+      widget.selectedFiles?.clearAll();
+
+      return true;
+    } on AssertionError catch (e) {
+      await dialog.hide();
+      showErrorDialog(context, "Oops", e.message as String?);
+      return false;
+    } catch (e, s) {
+      _logger.severe("Could not move to album", e, s);
+      await dialog.hide();
+      showGenericErrorDialog(context: context);
+      return false;
+    }
+  }
+
+  Future<bool> _restoreFilesToCollection(int toCollectionID) async {
+    final dialog = createProgressDialog(context, "Restoring files...");
+    await dialog.show();
+    try {
+      await CollectionsService.instance
+          .restore(toCollectionID, widget.selectedFiles!.files.toList());
+      RemoteSyncService.instance.sync(silently: true);
+      widget.selectedFiles?.clearAll();
+      await dialog.hide();
+      return true;
+    } on AssertionError catch (e) {
+      await dialog.hide();
+      showErrorDialog(context, "Oops", e.message as String?);
+      return false;
+    } catch (e, s) {
+      _logger.severe("Could not move to album", e, s);
+      await dialog.hide();
+      showGenericErrorDialog(context: context);
+      return false;
+    }
+  }
+}

+ 18 - 3
lib/ui/home_widget.dart

@@ -28,7 +28,7 @@ import 'package:photos/theme/colors.dart';
 import 'package:photos/theme/ente_theme.dart';
 import 'package:photos/theme/ente_theme.dart';
 import 'package:photos/ui/collections_gallery_widget.dart';
 import 'package:photos/ui/collections_gallery_widget.dart';
 import 'package:photos/ui/common/bottom_shadow.dart';
 import 'package:photos/ui/common/bottom_shadow.dart';
-import 'package:photos/ui/create_collection_page.dart';
+import 'package:photos/ui/create_collection_sheet.dart';
 import 'package:photos/ui/extents_page_view.dart';
 import 'package:photos/ui/extents_page_view.dart';
 import 'package:photos/ui/home/grant_permissions_widget.dart';
 import 'package:photos/ui/home/grant_permissions_widget.dart';
 import 'package:photos/ui/home/header_widget.dart';
 import 'package:photos/ui/home/header_widget.dart';
@@ -72,6 +72,7 @@ class _HomeWidgetState extends State<HomeWidget> {
   // ignore: unused_field
   // ignore: unused_field
   StreamSubscription? _intentDataStreamSubscription;
   StreamSubscription? _intentDataStreamSubscription;
   List<SharedMediaFile>? _sharedFiles;
   List<SharedMediaFile>? _sharedFiles;
+  bool _shouldRenderCreateCollectionSheet = false;
 
 
   late StreamSubscription<TabChangedEvent> _tabChangedEventSubscription;
   late StreamSubscription<TabChangedEvent> _tabChangedEventSubscription;
   late StreamSubscription<SubscriptionPurchasedEvent>
   late StreamSubscription<SubscriptionPurchasedEvent>
@@ -236,6 +237,7 @@ class _HomeWidgetState extends State<HomeWidget> {
         ReceiveSharingIntent.getMediaStream().listen(
         ReceiveSharingIntent.getMediaStream().listen(
       (List<SharedMediaFile> value) {
       (List<SharedMediaFile> value) {
         setState(() {
         setState(() {
+          _shouldRenderCreateCollectionSheet = true;
           _sharedFiles = value;
           _sharedFiles = value;
         });
         });
       },
       },
@@ -317,9 +319,22 @@ class _HomeWidgetState extends State<HomeWidget> {
       return const LoadingPhotosWidget();
       return const LoadingPhotosWidget();
     }
     }
 
 
-    if (_sharedFiles != null && _sharedFiles!.isNotEmpty) {
+    if (_sharedFiles != null &&
+        _sharedFiles!.isNotEmpty &&
+        _shouldRenderCreateCollectionSheet) {
+      //The gallery is getting rebuilt for some reason when the keyboard is up.
+      //So to stop showing multiple CreateCollectionSheets, this flag
+      //needs to be set to false the first time it is rendered.
+      _shouldRenderCreateCollectionSheet = false;
       ReceiveSharingIntent.reset();
       ReceiveSharingIntent.reset();
-      return CreateCollectionPage(null, _sharedFiles);
+      Future.delayed(const Duration(milliseconds: 10), () {
+        createCollectionSheet(
+          null,
+          _sharedFiles,
+          context,
+          actionType: CollectionActionType.addFiles,
+        );
+      });
     }
     }
     final isBottomInsetPresent = MediaQuery.of(context).viewPadding.bottom != 0;
     final isBottomInsetPresent = MediaQuery.of(context).viewPadding.bottom != 0;
 
 

+ 18 - 21
lib/ui/viewer/actions/file_selection_actions_widget.dart

@@ -1,7 +1,6 @@
 import 'package:fast_base58/fast_base58.dart';
 import 'package:fast_base58/fast_base58.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter/services.dart';
-import 'package:page_transition/page_transition.dart';
 import 'package:photos/core/configuration.dart';
 import 'package:photos/core/configuration.dart';
 import 'package:photos/models/collection.dart';
 import 'package:photos/models/collection.dart';
 import 'package:photos/models/device_collection.dart';
 import 'package:photos/models/device_collection.dart';
@@ -20,7 +19,7 @@ import 'package:photos/ui/components/blur_menu_item_widget.dart';
 import 'package:photos/ui/components/bottom_action_bar/expanded_menu_widget.dart';
 import 'package:photos/ui/components/bottom_action_bar/expanded_menu_widget.dart';
 import 'package:photos/ui/components/button_widget.dart';
 import 'package:photos/ui/components/button_widget.dart';
 import 'package:photos/ui/components/models/button_type.dart';
 import 'package:photos/ui/components/models/button_type.dart';
-import 'package:photos/ui/create_collection_page.dart';
+import 'package:photos/ui/create_collection_sheet.dart';
 import 'package:photos/ui/sharing/manage_links_widget.dart';
 import 'package:photos/ui/sharing/manage_links_widget.dart';
 import 'package:photos/utils/delete_file_util.dart';
 import 'package:photos/utils/delete_file_util.dart';
 import 'package:photos/utils/magic_util.dart';
 import 'package:photos/utils/magic_util.dart';
@@ -255,7 +254,12 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
       widget.selectedFiles
       widget.selectedFiles
           .unSelectAll(split.ownedByOtherUsers.toSet(), skipNotify: true);
           .unSelectAll(split.ownedByOtherUsers.toSet(), skipNotify: true);
     }
     }
-    await _selectionCollectionForAction(CollectionActionType.moveFiles);
+    createCollectionSheet(
+      widget.selectedFiles,
+      null,
+      context,
+      actionType: CollectionActionType.moveFiles,
+    );
   }
   }
 
 
   Future<void> _addToAlbum() async {
   Future<void> _addToAlbum() async {
@@ -263,7 +267,11 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
       widget.selectedFiles
       widget.selectedFiles
           .unSelectAll(split.ownedByOtherUsers.toSet(), skipNotify: true);
           .unSelectAll(split.ownedByOtherUsers.toSet(), skipNotify: true);
     }
     }
-    await _selectionCollectionForAction(CollectionActionType.addFiles);
+    createCollectionSheet(
+      widget.selectedFiles,
+      null,
+      context,
+    );
   }
   }
 
 
   Future<void> _onDeleteClick() async {
   Future<void> _onDeleteClick() async {
@@ -339,7 +347,12 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
       widget.selectedFiles
       widget.selectedFiles
           .unSelectAll(split.ownedByOtherUsers.toSet(), skipNotify: true);
           .unSelectAll(split.ownedByOtherUsers.toSet(), skipNotify: true);
     }
     }
-    await _selectionCollectionForAction(CollectionActionType.unHide);
+    createCollectionSheet(
+      widget.selectedFiles,
+      null,
+      context,
+      actionType: CollectionActionType.unHide,
+    );
   }
   }
 
 
   Future<void> _onCreatedSharedLinkClicked() async {
   Future<void> _onCreatedSharedLinkClicked() async {
@@ -407,20 +420,4 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
       showShortToast(context, "Link copied to clipboard");
       showShortToast(context, "Link copied to clipboard");
     }
     }
   }
   }
-
-  Future<Object?> _selectionCollectionForAction(
-    CollectionActionType type,
-  ) async {
-    return Navigator.push(
-      context,
-      PageTransition(
-        type: PageTransitionType.bottomToTop,
-        child: CreateCollectionPage(
-          widget.selectedFiles,
-          null,
-          actionType: type,
-        ),
-      ),
-    );
-  }
 }
 }

+ 9 - 21
lib/ui/viewer/actions/file_selection_overlay_bar.dart

@@ -1,5 +1,4 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
-import 'package:page_transition/page_transition.dart';
 import 'package:photos/models/collection.dart';
 import 'package:photos/models/collection.dart';
 import 'package:photos/models/device_collection.dart';
 import 'package:photos/models/device_collection.dart';
 import 'package:photos/models/gallery_type.dart';
 import 'package:photos/models/gallery_type.dart';
@@ -8,7 +7,7 @@ import 'package:photos/models/selected_files.dart';
 import 'package:photos/theme/ente_theme.dart';
 import 'package:photos/theme/ente_theme.dart';
 import 'package:photos/ui/components/bottom_action_bar/bottom_action_bar_widget.dart';
 import 'package:photos/ui/components/bottom_action_bar/bottom_action_bar_widget.dart';
 import 'package:photos/ui/components/icon_button_widget.dart';
 import 'package:photos/ui/components/icon_button_widget.dart';
-import 'package:photos/ui/create_collection_page.dart';
+import 'package:photos/ui/create_collection_sheet.dart';
 import 'package:photos/ui/viewer/actions/file_selection_actions_widget.dart';
 import 'package:photos/ui/viewer/actions/file_selection_actions_widget.dart';
 import 'package:photos/utils/delete_file_util.dart';
 import 'package:photos/utils/delete_file_util.dart';
 import 'package:photos/utils/magic_util.dart';
 import 'package:photos/utils/magic_util.dart';
@@ -85,9 +84,14 @@ class _FileSelectionOverlayBarState extends State<FileSelectionOverlayBar> {
           icon: Icons.visibility_off_outlined,
           icon: Icons.visibility_off_outlined,
           iconButtonType: IconButtonType.primary,
           iconButtonType: IconButtonType.primary,
           iconColor: iconColor,
           iconColor: iconColor,
-          onTap: () => _selectionCollectionForAction(
-            CollectionActionType.unHide,
-          ),
+          onTap: () {
+            createCollectionSheet(
+              widget.selectedFiles,
+              null,
+              context,
+              actionType: CollectionActionType.unHide,
+            );
+          },
         ),
         ),
       );
       );
     }
     }
@@ -143,22 +147,6 @@ class _FileSelectionOverlayBarState extends State<FileSelectionOverlayBar> {
     widget.selectedFiles.clearAll();
     widget.selectedFiles.clearAll();
   }
   }
 
 
-  Future<Object?> _selectionCollectionForAction(
-    CollectionActionType type,
-  ) async {
-    return Navigator.push(
-      context,
-      PageTransition(
-        type: PageTransitionType.bottomToTop,
-        child: CreateCollectionPage(
-          widget.selectedFiles,
-          null,
-          actionType: type,
-        ),
-      ),
-    );
-  }
-
   _selectedFilesListener() {
   _selectedFilesListener() {
     widget.selectedFiles.files.isNotEmpty
     widget.selectedFiles.files.isNotEmpty
         ? _bottomPosition.value = 0.0
         ? _bottomPosition.value = 0.0

+ 5 - 11
lib/ui/viewer/file/fading_app_bar.dart

@@ -6,7 +6,6 @@ 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:media_extension/media_extension.dart';
-import 'package:page_transition/page_transition.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';
@@ -26,7 +25,7 @@ import 'package:photos/ui/common/progress_dialog.dart';
 import 'package:photos/ui/components/action_sheet_widget.dart';
 import 'package:photos/ui/components/action_sheet_widget.dart';
 import 'package:photos/ui/components/button_widget.dart';
 import 'package:photos/ui/components/button_widget.dart';
 import 'package:photos/ui/components/models/button_type.dart';
 import 'package:photos/ui/components/models/button_type.dart';
-import 'package:photos/ui/create_collection_page.dart';
+import 'package:photos/ui/create_collection_sheet.dart';
 import 'package:photos/ui/viewer/file/custom_app_bar.dart';
 import 'package:photos/ui/viewer/file/custom_app_bar.dart';
 import 'package:photos/utils/delete_file_util.dart';
 import 'package:photos/utils/delete_file_util.dart';
 import 'package:photos/utils/dialog_util.dart';
 import 'package:photos/utils/dialog_util.dart';
@@ -272,16 +271,11 @@ class FadingAppBarState extends State<FadingAppBar> {
   Future<void> _handleUnHideRequest(BuildContext context) async {
   Future<void> _handleUnHideRequest(BuildContext context) async {
     final s = SelectedFiles();
     final s = SelectedFiles();
     s.files.add(widget.file);
     s.files.add(widget.file);
-    Navigator.push(
+    createCollectionSheet(
+      s,
+      null,
       context,
       context,
-      PageTransition(
-        type: PageTransitionType.bottomToTop,
-        child: CreateCollectionPage(
-          s,
-          null,
-          actionType: CollectionActionType.unHide,
-        ),
-      ),
+      actionType: CollectionActionType.unHide,
     );
     );
   }
   }
 
 

+ 5 - 11
lib/ui/viewer/file/fading_bottom_bar.dart

@@ -3,7 +3,6 @@ import 'dart:io';
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
 import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
-import 'package:page_transition/page_transition.dart';
 import 'package:photos/core/configuration.dart';
 import 'package:photos/core/configuration.dart';
 import 'package:photos/models/file.dart';
 import 'package:photos/models/file.dart';
 import 'package:photos/models/file_type.dart';
 import 'package:photos/models/file_type.dart';
@@ -13,7 +12,7 @@ import 'package:photos/models/trash_file.dart';
 import 'package:photos/services/collections_service.dart';
 import 'package:photos/services/collections_service.dart';
 import 'package:photos/theme/colors.dart';
 import 'package:photos/theme/colors.dart';
 import 'package:photos/theme/ente_theme.dart';
 import 'package:photos/theme/ente_theme.dart';
-import 'package:photos/ui/create_collection_page.dart';
+import 'package:photos/ui/create_collection_sheet.dart';
 import 'package:photos/ui/viewer/file/file_info_widget.dart';
 import 'package:photos/ui/viewer/file/file_info_widget.dart';
 import 'package:photos/utils/delete_file_util.dart';
 import 'package:photos/utils/delete_file_util.dart';
 import 'package:photos/utils/magic_util.dart';
 import 'package:photos/utils/magic_util.dart';
@@ -238,16 +237,11 @@ class FadingBottomBarState extends State<FadingBottomBar> {
             onPressed: () {
             onPressed: () {
               final selectedFiles = SelectedFiles();
               final selectedFiles = SelectedFiles();
               selectedFiles.toggleSelection(widget.file);
               selectedFiles.toggleSelection(widget.file);
-              Navigator.push(
+              createCollectionSheet(
+                selectedFiles,
+                null,
                 context,
                 context,
-                PageTransition(
-                  type: PageTransitionType.bottomToTop,
-                  child: CreateCollectionPage(
-                    selectedFiles,
-                    null,
-                    actionType: CollectionActionType.restoreFiles,
-                  ),
-                ),
+                actionType: CollectionActionType.restoreFiles,
               );
               );
             },
             },
           ),
           ),

+ 8 - 5
lib/ui/viewer/file/no_thumbnail_widget.dart

@@ -2,7 +2,8 @@ import 'package:flutter/material.dart';
 import 'package:photos/theme/ente_theme.dart';
 import 'package:photos/theme/ente_theme.dart';
 
 
 class NoThumbnailWidget extends StatelessWidget {
 class NoThumbnailWidget extends StatelessWidget {
-  const NoThumbnailWidget({Key? key}) : super(key: key);
+  final bool addBorder;
+  const NoThumbnailWidget({this.addBorder = true, Key? key}) : super(key: key);
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
@@ -10,10 +11,12 @@ class NoThumbnailWidget extends StatelessWidget {
     return Container(
     return Container(
       decoration: BoxDecoration(
       decoration: BoxDecoration(
         borderRadius: BorderRadius.circular(1),
         borderRadius: BorderRadius.circular(1),
-        border: Border.all(
-          color: enteColorScheme.strokeFaint,
-          width: 1,
-        ),
+        border: addBorder
+            ? Border.all(
+                color: enteColorScheme.strokeFaint,
+                width: 1,
+              )
+            : null,
         color: enteColorScheme.fillFaint,
         color: enteColorScheme.fillFaint,
       ),
       ),
       child: Center(
       child: Center(

+ 5 - 62
lib/ui/viewer/gallery/gallery_overlay_widget.dart

@@ -5,7 +5,6 @@ import 'dart:ui';
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:logging/logging.dart';
 import 'package:logging/logging.dart';
-import 'package:page_transition/page_transition.dart';
 import 'package:photos/core/configuration.dart';
 import 'package:photos/core/configuration.dart';
 import 'package:photos/core/event_bus.dart';
 import 'package:photos/core/event_bus.dart';
 import 'package:photos/ente_theme_data.dart';
 import 'package:photos/ente_theme_data.dart';
@@ -16,7 +15,7 @@ import 'package:photos/models/magic_metadata.dart';
 import 'package:photos/models/selected_files.dart';
 import 'package:photos/models/selected_files.dart';
 import 'package:photos/services/collections_service.dart';
 import 'package:photos/services/collections_service.dart';
 import 'package:photos/services/hidden_service.dart';
 import 'package:photos/services/hidden_service.dart';
-import 'package:photos/ui/create_collection_page.dart';
+import 'package:photos/ui/create_collection_sheet.dart';
 import 'package:photos/utils/delete_file_util.dart';
 import 'package:photos/utils/delete_file_util.dart';
 import 'package:photos/utils/dialog_util.dart';
 import 'package:photos/utils/dialog_util.dart';
 import 'package:photos/utils/magic_util.dart';
 import 'package:photos/utils/magic_util.dart';
@@ -232,36 +231,6 @@ class _OverlayWidgetState extends State<OverlayWidget> {
     widget.selectedFiles.clearAll();
     widget.selectedFiles.clearAll();
   }
   }
 
 
-  Future<void> _createCollectionAction(CollectionActionType type) async {
-    Navigator.push(
-      context,
-      PageTransition(
-        type: PageTransitionType.bottomToTop,
-        child: CreateCollectionPage(
-          widget.selectedFiles,
-          null,
-          actionType: type,
-        ),
-      ),
-    );
-  }
-
-  Future<void> _moveFiles() async {
-    unawaited(
-      Navigator.push(
-        context,
-        PageTransition(
-          type: PageTransitionType.bottomToTop,
-          child: CreateCollectionPage(
-            widget.selectedFiles,
-            null,
-            actionType: CollectionActionType.moveFiles,
-          ),
-        ),
-      ),
-    );
-  }
-
   List<Widget> _getActions(BuildContext context) {
   List<Widget> _getActions(BuildContext context) {
     final List<Widget> actions = <Widget>[];
     final List<Widget> actions = <Widget>[];
     if (widget.type == GalleryType.trash) {
     if (widget.type == GalleryType.trash) {
@@ -291,21 +260,6 @@ class _OverlayWidgetState extends State<OverlayWidget> {
       );
       );
     }
     }
 
 
-    if (Configuration.instance.hasConfiguredAccount() &&
-        widget.type == GalleryType.hidden) {
-      actions.add(
-        Tooltip(
-          message: "Unhide",
-          child: IconButton(
-            color: Theme.of(context).colorScheme.iconColor,
-            icon: const Icon(Icons.visibility),
-            onPressed: () {
-              _createCollectionAction(CollectionActionType.unHide);
-            },
-          ),
-        ),
-      );
-    }
     if (Configuration.instance.hasConfiguredAccount() &&
     if (Configuration.instance.hasConfiguredAccount() &&
         widget.type == GalleryType.ownedCollection &&
         widget.type == GalleryType.ownedCollection &&
         widget.collection!.type != CollectionType.favorites) {
         widget.collection!.type != CollectionType.favorites) {
@@ -437,12 +391,6 @@ class _OverlayWidgetState extends State<OverlayWidget> {
       case 'hide':
       case 'hide':
         await _handleHideRequest(context);
         await _handleHideRequest(context);
         break;
         break;
-      case 'add':
-        await _createCollectionAction(CollectionActionType.addFiles);
-        break;
-      case 'move':
-        await _moveFiles();
-        break;
       case 'archive':
       case 'archive':
         await _handleVisibilityChangeRequest(context, visibilityArchive);
         await _handleVisibilityChangeRequest(context, visibilityArchive);
         break;
         break;
@@ -464,16 +412,11 @@ class _OverlayWidgetState extends State<OverlayWidget> {
             Icons.restore,
             Icons.restore,
           ),
           ),
           onPressed: () {
           onPressed: () {
-            Navigator.push(
+            createCollectionSheet(
+              widget.selectedFiles,
+              null,
               context,
               context,
-              PageTransition(
-                type: PageTransitionType.bottomToTop,
-                child: CreateCollectionPage(
-                  widget.selectedFiles,
-                  null,
-                  actionType: CollectionActionType.restoreFiles,
-                ),
-              ),
+              actionType: CollectionActionType.restoreFiles,
             );
             );
           },
           },
         ),
         ),