Merge pull request #817 from ente-io/redesign-add-to-album
Redesign add/move to album
This commit is contained in:
commit
cc23be733c
13 changed files with 749 additions and 550 deletions
|
@ -81,6 +81,8 @@ class Collection {
|
|||
return (owner?.id ?? 0) == userID;
|
||||
}
|
||||
|
||||
String get collectionName => name ?? "Unnamed collection";
|
||||
|
||||
void updateSharees(List<User> newSharees) {
|
||||
sharees?.clear();
|
||||
sharees?.addAll(newSharees);
|
||||
|
|
104
lib/ui/components/album_list_item_widget.dart
Normal file
104
lib/ui/components/album_list_item_widget.dart
Normal file
|
@ -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
lib/ui/components/bottom_of_title_bar_widget.dart
Normal file
37
lib/ui/components/bottom_of_title_bar_widget.dart
Normal file
|
@ -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
lib/ui/components/new_album_list_widget.dart
Normal file
73
lib/ui/components/new_album_list_widget.dart
Normal file
|
@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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
lib/ui/create_collection_sheet.dart
Normal file
465
lib/ui/create_collection_sheet.dart
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@ import 'package:photos/theme/colors.dart';
|
|||
import 'package:photos/theme/ente_theme.dart';
|
||||
import 'package:photos/ui/collections_gallery_widget.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/home/grant_permissions_widget.dart';
|
||||
import 'package:photos/ui/home/header_widget.dart';
|
||||
|
@ -72,6 +72,7 @@ class _HomeWidgetState extends State<HomeWidget> {
|
|||
// ignore: unused_field
|
||||
StreamSubscription? _intentDataStreamSubscription;
|
||||
List<SharedMediaFile>? _sharedFiles;
|
||||
bool _shouldRenderCreateCollectionSheet = false;
|
||||
|
||||
late StreamSubscription<TabChangedEvent> _tabChangedEventSubscription;
|
||||
late StreamSubscription<SubscriptionPurchasedEvent>
|
||||
|
@ -236,6 +237,7 @@ class _HomeWidgetState extends State<HomeWidget> {
|
|||
ReceiveSharingIntent.getMediaStream().listen(
|
||||
(List<SharedMediaFile> value) {
|
||||
setState(() {
|
||||
_shouldRenderCreateCollectionSheet = true;
|
||||
_sharedFiles = value;
|
||||
});
|
||||
},
|
||||
|
@ -317,9 +319,22 @@ class _HomeWidgetState extends State<HomeWidget> {
|
|||
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();
|
||||
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;
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import 'package:fast_base58/fast_base58.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:page_transition/page_transition.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/models/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/button_widget.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/utils/delete_file_util.dart';
|
||||
import 'package:photos/utils/magic_util.dart';
|
||||
|
@ -255,7 +254,12 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
|
|||
widget.selectedFiles
|
||||
.unSelectAll(split.ownedByOtherUsers.toSet(), skipNotify: true);
|
||||
}
|
||||
await _selectionCollectionForAction(CollectionActionType.moveFiles);
|
||||
createCollectionSheet(
|
||||
widget.selectedFiles,
|
||||
null,
|
||||
context,
|
||||
actionType: CollectionActionType.moveFiles,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _addToAlbum() async {
|
||||
|
@ -263,7 +267,11 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
|
|||
widget.selectedFiles
|
||||
.unSelectAll(split.ownedByOtherUsers.toSet(), skipNotify: true);
|
||||
}
|
||||
await _selectionCollectionForAction(CollectionActionType.addFiles);
|
||||
createCollectionSheet(
|
||||
widget.selectedFiles,
|
||||
null,
|
||||
context,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onDeleteClick() async {
|
||||
|
@ -339,7 +347,12 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
|
|||
widget.selectedFiles
|
||||
.unSelectAll(split.ownedByOtherUsers.toSet(), skipNotify: true);
|
||||
}
|
||||
await _selectionCollectionForAction(CollectionActionType.unHide);
|
||||
createCollectionSheet(
|
||||
widget.selectedFiles,
|
||||
null,
|
||||
context,
|
||||
actionType: CollectionActionType.unHide,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onCreatedSharedLinkClicked() async {
|
||||
|
@ -407,20 +420,4 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
|
|||
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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:page_transition/page_transition.dart';
|
||||
import 'package:photos/models/collection.dart';
|
||||
import 'package:photos/models/device_collection.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/ui/components/bottom_action_bar/bottom_action_bar_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/utils/delete_file_util.dart';
|
||||
import 'package:photos/utils/magic_util.dart';
|
||||
|
@ -85,9 +84,14 @@ class _FileSelectionOverlayBarState extends State<FileSelectionOverlayBar> {
|
|||
icon: Icons.visibility_off_outlined,
|
||||
iconButtonType: IconButtonType.primary,
|
||||
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();
|
||||
}
|
||||
|
||||
Future<Object?> _selectionCollectionForAction(
|
||||
CollectionActionType type,
|
||||
) async {
|
||||
return Navigator.push(
|
||||
context,
|
||||
PageTransition(
|
||||
type: PageTransitionType.bottomToTop,
|
||||
child: CreateCollectionPage(
|
||||
widget.selectedFiles,
|
||||
null,
|
||||
actionType: type,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_selectedFilesListener() {
|
||||
widget.selectedFiles.files.isNotEmpty
|
||||
? _bottomPosition.value = 0.0
|
||||
|
|
|
@ -6,7 +6,6 @@ import 'package:flutter/material.dart';
|
|||
import 'package:like_button/like_button.dart';
|
||||
import 'package:logging/logging.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:photo_manager/photo_manager.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/button_widget.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/utils/delete_file_util.dart';
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
|
@ -272,16 +271,11 @@ class FadingAppBarState extends State<FadingAppBar> {
|
|||
Future<void> _handleUnHideRequest(BuildContext context) async {
|
||||
final s = SelectedFiles();
|
||||
s.files.add(widget.file);
|
||||
Navigator.push(
|
||||
createCollectionSheet(
|
||||
s,
|
||||
null,
|
||||
context,
|
||||
PageTransition(
|
||||
type: PageTransitionType.bottomToTop,
|
||||
child: CreateCollectionPage(
|
||||
s,
|
||||
null,
|
||||
actionType: CollectionActionType.unHide,
|
||||
),
|
||||
),
|
||||
actionType: CollectionActionType.unHide,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ import 'dart:io';
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.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/models/file.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/theme/colors.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/utils/delete_file_util.dart';
|
||||
import 'package:photos/utils/magic_util.dart';
|
||||
|
@ -238,16 +237,11 @@ class FadingBottomBarState extends State<FadingBottomBar> {
|
|||
onPressed: () {
|
||||
final selectedFiles = SelectedFiles();
|
||||
selectedFiles.toggleSelection(widget.file);
|
||||
Navigator.push(
|
||||
createCollectionSheet(
|
||||
selectedFiles,
|
||||
null,
|
||||
context,
|
||||
PageTransition(
|
||||
type: PageTransitionType.bottomToTop,
|
||||
child: CreateCollectionPage(
|
||||
selectedFiles,
|
||||
null,
|
||||
actionType: CollectionActionType.restoreFiles,
|
||||
),
|
||||
),
|
||||
actionType: CollectionActionType.restoreFiles,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
@ -2,7 +2,8 @@ import 'package:flutter/material.dart';
|
|||
import 'package:photos/theme/ente_theme.dart';
|
||||
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -10,10 +11,12 @@ class NoThumbnailWidget extends StatelessWidget {
|
|||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
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,
|
||||
),
|
||||
child: Center(
|
||||
|
|
|
@ -5,7 +5,6 @@ import 'dart:ui';
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:page_transition/page_transition.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/core/event_bus.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/services/collections_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/dialog_util.dart';
|
||||
import 'package:photos/utils/magic_util.dart';
|
||||
|
@ -232,36 +231,6 @@ class _OverlayWidgetState extends State<OverlayWidget> {
|
|||
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) {
|
||||
final List<Widget> actions = <Widget>[];
|
||||
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() &&
|
||||
widget.type == GalleryType.ownedCollection &&
|
||||
widget.collection!.type != CollectionType.favorites) {
|
||||
|
@ -437,12 +391,6 @@ class _OverlayWidgetState extends State<OverlayWidget> {
|
|||
case 'hide':
|
||||
await _handleHideRequest(context);
|
||||
break;
|
||||
case 'add':
|
||||
await _createCollectionAction(CollectionActionType.addFiles);
|
||||
break;
|
||||
case 'move':
|
||||
await _moveFiles();
|
||||
break;
|
||||
case 'archive':
|
||||
await _handleVisibilityChangeRequest(context, visibilityArchive);
|
||||
break;
|
||||
|
@ -464,16 +412,11 @@ class _OverlayWidgetState extends State<OverlayWidget> {
|
|||
Icons.restore,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
createCollectionSheet(
|
||||
widget.selectedFiles,
|
||||
null,
|
||||
context,
|
||||
PageTransition(
|
||||
type: PageTransitionType.bottomToTop,
|
||||
child: CreateCollectionPage(
|
||||
widget.selectedFiles,
|
||||
null,
|
||||
actionType: CollectionActionType.restoreFiles,
|
||||
),
|
||||
),
|
||||
actionType: CollectionActionType.restoreFiles,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
Loading…
Add table
Reference in a new issue