ente/lib/ui/gallery_app_bar_widget.dart

573 lines
17 KiB
Dart
Raw Normal View History

2020-05-05 12:56:24 +00:00
import 'dart:async';
2021-01-13 10:34:23 +00:00
import 'dart:io';
2020-04-18 18:46:38 +00:00
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
2020-10-28 12:03:28 +00:00
import 'package:page_transition/page_transition.dart';
import 'package:photos/core/configuration.dart';
2020-11-15 07:19:44 +00:00
import 'package:photos/core/event_bus.dart';
2021-02-02 16:35:38 +00:00
import 'package:photos/events/subscription_purchased_event.dart';
2020-10-29 12:56:30 +00:00
import 'package:photos/models/collection.dart';
2021-09-20 06:41:38 +00:00
import 'package:photos/models/magic_metadata.dart';
import 'package:photos/models/selected_files.dart';
2020-10-13 05:22:20 +00:00
import 'package:photos/services/collections_service.dart';
import 'package:photos/ui/change_collection_name_dialog.dart';
2020-10-28 12:03:28 +00:00
import 'package:photos/ui/create_collection_page.dart';
import 'package:photos/ui/share_collection_widget.dart';
import 'package:photos/utils/archive_util.dart';
import 'package:photos/utils/delete_file_util.dart';
import 'package:photos/utils/dialog_util.dart';
2020-05-01 18:20:12 +00:00
import 'package:photos/utils/share_util.dart';
import 'package:photos/utils/toast_util.dart';
2020-04-18 18:46:38 +00:00
enum GalleryAppBarType {
homepage,
2021-09-20 06:41:38 +00:00
archive,
2021-10-12 20:01:51 +00:00
trash,
local_folder,
// indicator for gallery view of collections shared with the user
2020-10-13 21:46:46 +00:00
shared_collection,
owned_collection,
search_results
}
2020-05-17 14:23:37 +00:00
class GalleryAppBarWidget extends StatefulWidget {
2020-05-17 14:23:37 +00:00
final GalleryAppBarType type;
2020-04-18 18:46:38 +00:00
final String title;
final SelectedFiles selectedFiles;
2020-05-17 12:39:38 +00:00
final String path;
2020-10-29 12:56:30 +00:00
final Collection collection;
2020-04-18 18:46:38 +00:00
GalleryAppBarWidget(
this.type,
this.title,
2020-10-29 12:56:30 +00:00
this.selectedFiles, {
this.path,
2020-10-29 12:56:30 +00:00
this.collection,
});
2020-04-18 18:46:38 +00:00
@override
_GalleryAppBarWidgetState createState() => _GalleryAppBarWidgetState();
}
class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
final _logger = Logger("GalleryAppBar");
2020-11-15 07:19:44 +00:00
StreamSubscription _userAuthEventSubscription;
2021-01-08 16:40:03 +00:00
Function() _selectedFilesListener;
2021-09-23 10:41:48 +00:00
String _appBarTitle;
2020-04-30 15:09:41 +00:00
@override
void initState() {
2021-01-08 16:40:03 +00:00
_selectedFilesListener = () {
setState(() {});
2021-01-08 16:40:03 +00:00
};
widget.selectedFiles.addListener(_selectedFilesListener);
2020-11-15 07:19:44 +00:00
_userAuthEventSubscription =
2021-02-02 16:35:38 +00:00
Bus.instance.on<SubscriptionPurchasedEvent>().listen((event) {
2020-11-15 07:19:44 +00:00
setState(() {});
});
2021-09-23 10:41:48 +00:00
_appBarTitle = widget.title;
2020-04-30 15:09:41 +00:00
super.initState();
}
2020-11-15 07:19:44 +00:00
@override
void dispose() {
_userAuthEventSubscription.cancel();
2021-01-08 16:40:03 +00:00
widget.selectedFiles.removeListener(_selectedFilesListener);
2020-11-15 07:19:44 +00:00
super.dispose();
}
2020-04-18 18:46:38 +00:00
@override
Widget build(BuildContext context) {
if (widget.selectedFiles.files.isEmpty) {
2020-04-30 15:09:41 +00:00
return AppBar(
backgroundColor: widget.type == GalleryAppBarType.homepage
? Color(0x00000000)
: null,
2020-12-12 00:31:06 +00:00
elevation: 0,
title: widget.type == GalleryAppBarType.homepage
? Container()
: TextButton(
child: Text(
2021-09-23 10:41:48 +00:00
_appBarTitle,
style: TextStyle(
color: Colors.white.withOpacity(0.80),
2021-10-26 13:39:52 +00:00
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
onPressed: () => _renameAlbum(context),
),
2020-04-30 15:09:41 +00:00
actions: _getDefaultActions(context),
);
2020-04-18 18:46:38 +00:00
}
return AppBar(
2021-09-23 09:52:59 +00:00
leading: Tooltip(
message: "clear selection",
child: IconButton(
icon: Icon(Platform.isAndroid ? Icons.clear : CupertinoIcons.clear),
onPressed: () {
_clearSelectedFiles();
},
),
2020-04-18 18:46:38 +00:00
),
title: Text(widget.selectedFiles.files.length.toString()),
2020-06-19 23:03:26 +00:00
actions: _getActions(context),
2020-04-18 18:46:38 +00:00
);
}
Future<dynamic> _renameAlbum(BuildContext context) async {
if (widget.type != GalleryAppBarType.owned_collection) {
return;
}
2021-09-23 10:41:48 +00:00
final result = await showDialog(
context: context,
builder: (BuildContext context) {
2021-09-23 10:41:48 +00:00
return ChangeCollectionNameDialog(name: _appBarTitle);
},
barrierColor: Colors.black.withOpacity(0.85),
);
2021-09-23 10:41:48 +00:00
// indicates user cancelled the rename request
if (result == null) {
return;
}
final dialog = createProgressDialog(context, "changing name...");
await dialog.show();
try {
2021-09-23 11:33:25 +00:00
await CollectionsService.instance.rename(widget.collection, result);
await dialog.hide();
if (mounted) {
2021-09-23 10:41:48 +00:00
_appBarTitle = result;
setState(() {});
}
} catch (e) {
await dialog.hide();
showGenericErrorDialog(context);
}
}
2020-04-30 15:09:41 +00:00
List<Widget> _getDefaultActions(BuildContext context) {
2021-09-05 09:27:07 +00:00
List<Widget> actions = <Widget>[];
if (Configuration.instance.hasConfiguredAccount() &&
(widget.type == GalleryAppBarType.local_folder ||
widget.type == GalleryAppBarType.owned_collection)) {
2021-09-23 09:52:59 +00:00
actions.add(
Tooltip(
message: "share",
child: IconButton(
icon: Icon(Icons.person_add),
onPressed: () {
_showShareCollectionDialog();
},
),
),
);
2020-04-30 15:09:41 +00:00
}
2021-09-23 11:33:25 +00:00
if (widget.type == GalleryAppBarType.owned_collection &&
widget.collection.type == CollectionType.album) {
2021-09-23 02:34:15 +00:00
actions.add(PopupMenuButton(
itemBuilder: (context) {
final List<PopupMenuItem> items = [];
items.add(
PopupMenuItem(
value: 1,
child: Row(
2021-09-23 11:33:25 +00:00
children: const [
Icon(Icons.edit),
2021-09-23 02:34:15 +00:00
Padding(
padding: EdgeInsets.all(8),
),
Text("rename"),
],
),
),
);
return items;
},
onSelected: (value) async {
if (value == 1) {
await _renameAlbum(context);
}
},
));
}
2021-10-26 13:32:33 +00:00
if (widget.type == GalleryAppBarType.trash) {
2021-10-25 14:57:41 +00:00
actions.add(
Tooltip(
message: "empty trash",
child: IconButton(
icon: Icon(Icons.delete_forever),
onPressed: () async {
await emptyTrash(context);
},
),
),
);
}
2020-04-30 15:09:41 +00:00
return actions;
}
2020-05-17 14:23:37 +00:00
Future<void> _showShareCollectionDialog() async {
2020-10-29 12:56:30 +00:00
var collection = widget.collection;
final dialog = createProgressDialog(context, "please wait...");
await dialog.show();
2020-10-29 12:56:30 +00:00
if (collection == null) {
if (widget.type == GalleryAppBarType.local_folder) {
collection =
CollectionsService.instance.getCollectionForPath(widget.path);
if (collection == null) {
try {
collection = await CollectionsService.instance
.getOrCreateForPath(widget.path);
} catch (e, s) {
_logger.severe(e, s);
await dialog.hide();
showGenericErrorDialog(context);
}
2020-10-29 12:56:30 +00:00
}
} else {
throw Exception(
"Cannot create a collection of type" + widget.type.toString());
}
} else {
final sharees =
await CollectionsService.instance.getSharees(collection.id);
collection = collection.copyWith(sharees: sharees);
2020-10-29 12:56:30 +00:00
}
await dialog.hide();
2020-12-03 22:41:01 +00:00
return showDialog<void>(
context: context,
builder: (BuildContext context) {
return SharingDialog(collection);
},
);
2020-05-17 12:39:38 +00:00
}
2020-10-28 12:03:28 +00:00
Future<void> _createAlbum() async {
Navigator.push(
context,
PageTransition(
type: PageTransitionType.bottomToTop,
child: CreateCollectionPage(
widget.selectedFiles,
null,
2020-10-28 12:03:28 +00:00
)));
}
Future<void> _moveFiles() async {
Navigator.push(
context,
PageTransition(
type: PageTransitionType.bottomToTop,
child: CreateCollectionPage(
widget.selectedFiles,
null,
actionType: CollectionActionType.moveFiles,
)));
}
2020-06-19 23:03:26 +00:00
List<Widget> _getActions(BuildContext context) {
2021-09-05 09:27:07 +00:00
List<Widget> actions = <Widget>[];
if (widget.type == GalleryAppBarType.trash) {
_addTrashAction(actions);
return actions;
}
2021-09-13 08:40:35 +00:00
// skip add button for incoming collection till this feature is implemented
if (Configuration.instance.hasConfiguredAccount() &&
2021-09-13 08:40:35 +00:00
widget.type != GalleryAppBarType.shared_collection) {
String msg = "add";
IconData iconData =
Platform.isAndroid ? Icons.add_outlined : CupertinoIcons.add;
// show upload icon instead of add for files selected in local gallery
if (widget.type == GalleryAppBarType.local_folder) {
msg = "upload";
iconData = Platform.isAndroid
? Icons.cloud_upload
: CupertinoIcons.cloud_upload;
}
2021-09-23 09:52:59 +00:00
actions.add(
Tooltip(
message: msg,
2021-09-23 09:52:59 +00:00
child: IconButton(
icon: Icon(iconData),
2021-09-23 09:52:59 +00:00
onPressed: () {
_createAlbum();
},
),
),
);
}
if (Configuration.instance.hasConfiguredAccount() &&
widget.type == GalleryAppBarType.owned_collection &&
widget.collection.type != CollectionType.favorites) {
2021-09-23 09:52:59 +00:00
actions.add(
Tooltip(
message: "move",
child: IconButton(
icon: Icon(Platform.isAndroid
? Icons.arrow_forward
: CupertinoIcons.arrow_right),
onPressed: () {
_moveFiles();
},
),
),
);
}
2021-09-23 09:52:59 +00:00
actions.add(
Tooltip(
message: "share",
child: IconButton(
icon: Icon(
Platform.isAndroid ? Icons.share_outlined : CupertinoIcons.share),
onPressed: () {
_shareSelected(context);
},
),
),
);
if (widget.type == GalleryAppBarType.homepage ||
2021-09-20 06:41:38 +00:00
widget.type == GalleryAppBarType.archive ||
widget.type == GalleryAppBarType.local_folder) {
2021-09-23 09:52:59 +00:00
actions.add(
Tooltip(
message: "delete",
child: IconButton(
icon: Icon(Platform.isAndroid
? Icons.delete_outline
: CupertinoIcons.delete),
onPressed: () {
_showDeleteSheet(context);
},
),
),
);
} else if (widget.type == GalleryAppBarType.owned_collection) {
2021-03-01 12:06:17 +00:00
if (widget.collection.type == CollectionType.folder) {
2021-09-23 09:52:59 +00:00
actions.add(
Tooltip(
message: "delete",
child: IconButton(
icon: Icon(Platform.isAndroid
? Icons.delete_outline
: CupertinoIcons.delete),
onPressed: () {
_showDeleteSheet(context);
},
),
),
);
2021-03-01 12:06:17 +00:00
} else {
2021-09-23 09:52:59 +00:00
actions.add(
Tooltip(
message: "remove",
child: IconButton(
icon: Icon(Icons.remove_circle_outline_rounded),
onPressed: () {
_showRemoveFromCollectionSheet(context);
},
),
),
);
2021-03-01 12:06:17 +00:00
}
2020-04-18 18:46:38 +00:00
}
if (widget.type == GalleryAppBarType.homepage ||
2021-09-20 06:41:38 +00:00
widget.type == GalleryAppBarType.archive) {
bool showArchive = widget.type == GalleryAppBarType.homepage;
2021-09-23 09:52:59 +00:00
actions.add(Tooltip(
message: showArchive ? "archive" : "unarchive",
child: IconButton(
icon: Icon(
Platform.isAndroid
2021-09-23 09:52:59 +00:00
? (showArchive
? Icons.archive_outlined
: Icons.unarchive_outlined)
2021-10-04 14:43:18 +00:00
: (showArchive
? CupertinoIcons.archivebox
: CupertinoIcons.archivebox_fill),
2021-09-23 09:52:59 +00:00
),
onPressed: () {
_handleVisibilityChangeRequest(
context, showArchive ? kVisibilityArchive : kVisibilityVisible);
},
),
));
}
2020-04-18 18:46:38 +00:00
return actions;
}
void _addTrashAction(List<Widget> actions) {
actions.add(Tooltip(
message: "restore",
child: IconButton(
icon: Icon(Icons.restore_outlined),
onPressed: () {
Navigator.push(
context,
PageTransition(
type: PageTransitionType.bottomToTop,
child: CreateCollectionPage(
widget.selectedFiles,
null,
actionType: CollectionActionType.restoreFiles,
)));
},
),
));
2021-10-26 13:32:33 +00:00
actions.add(
Tooltip(
message: "delete permanently",
child: IconButton(
icon: Icon(Icons.delete_forever_outlined),
onPressed: () async {
if (await deleteFromTrash(
context, widget.selectedFiles.files.toList())) {
_clearSelectedFiles();
}
},
),
),
2021-10-26 13:32:33 +00:00
);
}
2021-09-23 07:21:39 +00:00
Future<void> _handleVisibilityChangeRequest(
BuildContext context, int newVisibility) async {
try {
await changeVisibility(
context, widget.selectedFiles.files.toList(), newVisibility);
} catch (e, s) {
_logger.severe("failed to update file visibility", e, s);
await showGenericErrorDialog(context);
} finally {
_clearSelectedFiles();
}
}
2020-06-19 23:03:26 +00:00
void _shareSelected(BuildContext context) {
2021-01-13 10:16:31 +00:00
share(context, widget.selectedFiles.files.toList());
2020-04-18 18:46:38 +00:00
}
void _showRemoveFromCollectionSheet(BuildContext context) {
2020-11-01 05:33:53 +00:00
final count = widget.selectedFiles.files.length;
final action = CupertinoActionSheet(
2021-03-01 12:06:17 +00:00
title: Text("remove " +
2020-11-01 05:33:53 +00:00
count.toString() +
" file" +
(count == 1 ? "" : "s") +
" from " +
widget.collection.name +
"?"),
actions: <Widget>[
CupertinoActionSheetAction(
2021-01-08 17:11:32 +00:00
child: Text("remove"),
isDestructiveAction: true,
onPressed: () async {
2021-03-21 11:21:45 +00:00
Navigator.of(context, rootNavigator: true).pop();
2021-01-08 17:02:41 +00:00
final dialog = createProgressDialog(context, "removing files...");
await dialog.show();
try {
await CollectionsService.instance.removeFromCollection(
widget.collection.id, widget.selectedFiles.files.toList());
await dialog.hide();
widget.selectedFiles.clearAll();
} catch (e, s) {
_logger.severe(e, s);
await dialog.hide();
showGenericErrorDialog(context);
}
},
),
],
cancelButton: CupertinoActionSheetAction(
2021-01-08 17:11:32 +00:00
child: Text("cancel"),
onPressed: () {
Navigator.of(context, rootNavigator: true).pop();
},
),
);
showCupertinoModalPopup(context: context, builder: (_) => action);
}
2020-06-19 23:03:26 +00:00
void _showDeleteSheet(BuildContext context) {
2020-11-01 05:33:53 +00:00
final count = widget.selectedFiles.files.length;
bool containsUploadedFile = false, containsLocalFile = false;
for (final file in widget.selectedFiles.files) {
if (file.uploadedFileID != null) {
containsUploadedFile = true;
}
if (file.localID != null) {
containsLocalFile = true;
}
}
2021-09-05 09:27:07 +00:00
final actions = <Widget>[];
if (containsUploadedFile && containsLocalFile) {
actions.add(CupertinoActionSheetAction(
2021-10-26 13:36:15 +00:00
child: Text("device"),
isDestructiveAction: true,
onPressed: () async {
Navigator.of(context, rootNavigator: true).pop();
await deleteFilesOnDeviceOnly(
context, widget.selectedFiles.files.toList());
_clearSelectedFiles();
2021-01-08 17:11:32 +00:00
showToast("files deleted from device");
},
));
2021-10-17 18:44:47 +00:00
actions.add(CupertinoActionSheetAction(
child: Text("ente"),
isDestructiveAction: true,
onPressed: () async {
Navigator.of(context, rootNavigator: true).pop();
await deleteFilesFromRemoteOnly(
context, widget.selectedFiles.files.toList());
_clearSelectedFiles();
2021-10-26 13:32:33 +00:00
showToast("moved to trash");
2021-10-17 18:44:47 +00:00
},
));
actions.add(CupertinoActionSheetAction(
2021-01-08 17:11:32 +00:00
child: Text("everywhere"),
isDestructiveAction: true,
onPressed: () async {
Navigator.of(context, rootNavigator: true).pop();
await deleteFilesFromEverywhere(
context, widget.selectedFiles.files.toList());
_clearSelectedFiles();
},
));
} else {
actions.add(CupertinoActionSheetAction(
2021-10-26 13:32:33 +00:00
child: Text("delete"),
isDestructiveAction: true,
onPressed: () async {
Navigator.of(context, rootNavigator: true).pop();
await deleteFilesFromEverywhere(
context, widget.selectedFiles.files.toList());
_clearSelectedFiles();
},
));
}
2020-04-18 18:46:38 +00:00
final action = CupertinoActionSheet(
2021-01-08 17:11:32 +00:00
title: Text("delete " +
2020-11-01 05:33:53 +00:00
count.toString() +
" file" +
(count == 1 ? "" : "s") +
2021-03-08 09:29:59 +00:00
(containsUploadedFile && containsLocalFile ? " from" : "?")),
actions: actions,
2020-04-18 18:46:38 +00:00
cancelButton: CupertinoActionSheetAction(
2021-01-08 17:11:32 +00:00
child: Text("cancel"),
2020-04-18 18:46:38 +00:00
onPressed: () {
Navigator.of(context, rootNavigator: true).pop();
},
),
);
showCupertinoModalPopup(context: context, builder: (_) => action);
}
2020-06-19 23:03:26 +00:00
void _clearSelectedFiles() {
widget.selectedFiles.clearAll();
2020-04-18 18:46:38 +00:00
}
}