Set cover photo (#1234)
This commit is contained in:
commit
bfebc6d051
|
@ -1,10 +1,15 @@
|
|||
import 'package:photos/events/files_updated_event.dart';
|
||||
import "package:photos/models/file.dart";
|
||||
|
||||
class CollectionUpdatedEvent extends FilesUpdatedEvent {
|
||||
final int? collectionID;
|
||||
|
||||
CollectionUpdatedEvent(this.collectionID, updatedFiles, source, {type})
|
||||
: super(
|
||||
CollectionUpdatedEvent(
|
||||
this.collectionID,
|
||||
List<File> updatedFiles,
|
||||
String? source, {
|
||||
EventType? type,
|
||||
}) : super(
|
||||
updatedFiles,
|
||||
type: type ?? EventType.addedOrUpdated,
|
||||
source: source ?? "",
|
||||
|
|
|
@ -25,4 +25,5 @@ enum EventType {
|
|||
unarchived,
|
||||
hide,
|
||||
unhide,
|
||||
coverChanged,
|
||||
}
|
||||
|
|
1
lib/generated/intl/messages_en.dart
generated
1
lib/generated/intl/messages_en.dart
generated
|
@ -1050,6 +1050,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
MessageLookupByLibrary.simpleMessage("Session expired"),
|
||||
"setAPassword": MessageLookupByLibrary.simpleMessage("Set a password"),
|
||||
"setAs": MessageLookupByLibrary.simpleMessage("Set as"),
|
||||
"setCover": MessageLookupByLibrary.simpleMessage("Set cover"),
|
||||
"setLabel": MessageLookupByLibrary.simpleMessage("Set"),
|
||||
"setPasswordTitle":
|
||||
MessageLookupByLibrary.simpleMessage("Set password"),
|
||||
|
|
10
lib/generated/l10n.dart
generated
10
lib/generated/l10n.dart
generated
|
@ -5283,6 +5283,16 @@ class S {
|
|||
);
|
||||
}
|
||||
|
||||
/// `Set cover`
|
||||
String get setCover {
|
||||
return Intl.message(
|
||||
'Set cover',
|
||||
name: 'setCover',
|
||||
desc: 'Text to set cover photo for an album',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Sort by`
|
||||
String get sortAlbumsBy {
|
||||
return Intl.message(
|
||||
|
|
|
@ -753,6 +753,10 @@
|
|||
},
|
||||
"deleteAll": "Delete All",
|
||||
"renameAlbum": "Rename album",
|
||||
"setCover" : "Set cover",
|
||||
"@setCover": {
|
||||
"description": "Text to set cover photo for an album"
|
||||
},
|
||||
"sortAlbumsBy": "Sort by",
|
||||
"sortNewestFirst": "Newest first",
|
||||
"sortOldestFirst": "Oldest first",
|
||||
|
|
|
@ -27,6 +27,7 @@ import "package:photos/ui/map/map_screen.dart";
|
|||
import 'package:photos/ui/sharing/album_participants_page.dart';
|
||||
import 'package:photos/ui/sharing/share_collection_page.dart';
|
||||
import 'package:photos/ui/tools/free_space_page.dart';
|
||||
import "package:photos/ui/viewer/gallery/pick_cover_photo.dart";
|
||||
import 'package:photos/utils/data_util.dart';
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
import 'package:photos/utils/magic_util.dart';
|
||||
|
@ -53,6 +54,18 @@ class GalleryAppBarWidget extends StatefulWidget {
|
|||
State<GalleryAppBarWidget> createState() => _GalleryAppBarWidgetState();
|
||||
}
|
||||
|
||||
enum AlbumPopupAction {
|
||||
rename,
|
||||
delete,
|
||||
map,
|
||||
ownedArchive,
|
||||
sharedArchive,
|
||||
sort,
|
||||
leave,
|
||||
freeUpSpace,
|
||||
setCover,
|
||||
}
|
||||
|
||||
class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
||||
final _logger = Logger("GalleryAppBar");
|
||||
late StreamSubscription _userAuthEventSubscription;
|
||||
|
@ -266,12 +279,12 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
),
|
||||
);
|
||||
}
|
||||
final List<PopupMenuItem> items = [];
|
||||
final List<PopupMenuItem<AlbumPopupAction>> items = [];
|
||||
if (widget.type == GalleryType.ownedCollection) {
|
||||
if (widget.collection!.type != CollectionType.favorites) {
|
||||
items.add(
|
||||
PopupMenuItem(
|
||||
value: 1,
|
||||
value: AlbumPopupAction.rename,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.edit),
|
||||
|
@ -283,12 +296,26 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
),
|
||||
),
|
||||
);
|
||||
items.add(
|
||||
PopupMenuItem(
|
||||
value: AlbumPopupAction.setCover,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.image_outlined),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
),
|
||||
Text(S.of(context).setCover),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (widget.type == GalleryType.ownedCollection ||
|
||||
widget.type == GalleryType.sharedCollection) {
|
||||
items.add(
|
||||
PopupMenuItem(
|
||||
value: 8,
|
||||
value: AlbumPopupAction.map,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.map_outlined),
|
||||
|
@ -307,7 +334,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
if (isArchived || widget.collection!.type != CollectionType.favorites) {
|
||||
items.add(
|
||||
PopupMenuItem(
|
||||
value: 6,
|
||||
value: AlbumPopupAction.sort,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.sort_outlined),
|
||||
|
@ -323,7 +350,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
);
|
||||
items.add(
|
||||
PopupMenuItem(
|
||||
value: 2,
|
||||
value: AlbumPopupAction.ownedArchive,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(isArchived ? Icons.unarchive : Icons.archive_outlined),
|
||||
|
@ -343,7 +370,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
if (widget.collection!.type != CollectionType.favorites) {
|
||||
items.add(
|
||||
PopupMenuItem(
|
||||
value: 3,
|
||||
value: AlbumPopupAction.delete,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.delete_outline),
|
||||
|
@ -362,7 +389,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
final bool hasShareeArchived = widget.collection!.hasShareeArchived();
|
||||
items.add(
|
||||
PopupMenuItem(
|
||||
value: 4,
|
||||
value: AlbumPopupAction.leave,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.logout),
|
||||
|
@ -376,7 +403,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
);
|
||||
items.add(
|
||||
PopupMenuItem(
|
||||
value: 7,
|
||||
value: AlbumPopupAction.sharedArchive,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
|
@ -398,7 +425,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
if (widget.type == GalleryType.localFolder) {
|
||||
items.add(
|
||||
PopupMenuItem(
|
||||
value: 5,
|
||||
value: AlbumPopupAction.freeUpSpace,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.delete_sweep_outlined),
|
||||
|
@ -417,10 +444,10 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
itemBuilder: (context) {
|
||||
return items;
|
||||
},
|
||||
onSelected: (dynamic value) async {
|
||||
if (value == 1) {
|
||||
onSelected: (AlbumPopupAction value) async {
|
||||
if (value == AlbumPopupAction.rename) {
|
||||
await _renameAlbum(context);
|
||||
} else if (value == 2) {
|
||||
} else if (value == AlbumPopupAction.ownedArchive) {
|
||||
await changeCollectionVisibility(
|
||||
context,
|
||||
widget.collection!,
|
||||
|
@ -428,15 +455,17 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
? visibleVisibility
|
||||
: archiveVisibility,
|
||||
);
|
||||
} else if (value == 3) {
|
||||
} else if (value == AlbumPopupAction.delete) {
|
||||
await _trashCollection();
|
||||
} else if (value == 4) {
|
||||
} else if (value == AlbumPopupAction.leave) {
|
||||
await _leaveAlbum(context);
|
||||
} else if (value == 5) {
|
||||
} else if (value == AlbumPopupAction.freeUpSpace) {
|
||||
await _deleteBackedUpFiles(context);
|
||||
} else if (value == 6) {
|
||||
} else if (value == AlbumPopupAction.setCover) {
|
||||
await setCoverPhoto(context);
|
||||
} else if (value == AlbumPopupAction.sort) {
|
||||
await _showSortOption(context);
|
||||
} else if (value == 7) {
|
||||
} else if (value == AlbumPopupAction.sharedArchive) {
|
||||
await changeCollectionVisibility(
|
||||
context,
|
||||
widget.collection!,
|
||||
|
@ -448,7 +477,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
} else if (value == 8) {
|
||||
} else if (value == AlbumPopupAction.map) {
|
||||
await showOnMap();
|
||||
} else {
|
||||
showToast(context, S.of(context).somethingWentWrong);
|
||||
|
@ -461,6 +490,13 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
return actions;
|
||||
}
|
||||
|
||||
Future<void> setCoverPhoto(BuildContext context) async {
|
||||
final file = await showPickCoverPhotoSheet(context, widget.collection!);
|
||||
if (file != null) {
|
||||
changeCoverPhoto(context, widget.collection!, file);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> showOnMap() async {
|
||||
final bool result = await requestForMapEnable(context);
|
||||
if (result) {
|
||||
|
|
181
lib/ui/viewer/gallery/pick_cover_photo.dart
Normal file
181
lib/ui/viewer/gallery/pick_cover_photo.dart
Normal file
|
@ -0,0 +1,181 @@
|
|||
import "dart:math";
|
||||
|
||||
import "package:flutter/material.dart";
|
||||
import "package:modal_bottom_sheet/modal_bottom_sheet.dart";
|
||||
import "package:photos/core/event_bus.dart";
|
||||
import "package:photos/db/files_db.dart";
|
||||
import "package:photos/events/collection_updated_event.dart";
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import "package:photos/models/collection.dart";
|
||||
import "package:photos/models/file.dart";
|
||||
import "package:photos/models/file_load_result.dart";
|
||||
import "package:photos/models/selected_files.dart";
|
||||
import "package:photos/services/ignored_files_service.dart";
|
||||
import "package:photos/theme/colors.dart";
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
import "package:photos/ui/components/bottom_of_title_bar_widget.dart";
|
||||
import "package:photos/ui/components/buttons/button_widget.dart";
|
||||
import "package:photos/ui/components/models/button_type.dart";
|
||||
import "package:photos/ui/components/title_bar_title_widget.dart";
|
||||
import "package:photos/ui/viewer/gallery/gallery.dart";
|
||||
|
||||
Future<File?> showPickCoverPhotoSheet(
|
||||
BuildContext context,
|
||||
Collection collection,
|
||||
) async {
|
||||
return await showBarModalBottomSheet(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return PickCoverPhotoWidget(collection);
|
||||
},
|
||||
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 PickCoverPhotoWidget extends StatelessWidget {
|
||||
final Collection collection;
|
||||
|
||||
const PickCoverPhotoWidget(
|
||||
this.collection, {
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ValueNotifier<bool> isFileSelected = ValueNotifier(false);
|
||||
final selectedFiles = SelectedFiles();
|
||||
selectedFiles.addListener(() {
|
||||
isFileSelected.value = selectedFiles.files.isNotEmpty;
|
||||
});
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: 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.max,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
BottomOfTitleBarWidget(
|
||||
title: const TitleBarTitleWidget(
|
||||
title: "Select cover photo",
|
||||
),
|
||||
caption: collection.displayName,
|
||||
),
|
||||
Expanded(
|
||||
child: Gallery(
|
||||
asyncLoader: (
|
||||
creationStartTime,
|
||||
creationEndTime, {
|
||||
limit,
|
||||
asc,
|
||||
}) async {
|
||||
final FileLoadResult result =
|
||||
await FilesDB.instance.getFilesInCollection(
|
||||
collection.id,
|
||||
creationStartTime,
|
||||
creationEndTime,
|
||||
limit: limit,
|
||||
asc: asc,
|
||||
);
|
||||
// hide ignored files from home page UI
|
||||
final ignoredIDs =
|
||||
await IgnoredFilesService.instance.ignoredIDs;
|
||||
result.files.removeWhere(
|
||||
(f) =>
|
||||
f.uploadedFileID == null &&
|
||||
IgnoredFilesService.instance
|
||||
.shouldSkipUpload(ignoredIDs, f),
|
||||
);
|
||||
return result;
|
||||
},
|
||||
reloadEvent:
|
||||
Bus.instance.on<CollectionUpdatedEvent>().where(
|
||||
(event) =>
|
||||
event.collectionID == collection.id,
|
||||
),
|
||||
tagPrefix: "pick_center_point_gallery",
|
||||
selectedFiles: selectedFiles,
|
||||
limitSelectionToOne: true,
|
||||
showSelectAllByDefault: false,
|
||||
sortAsyncFn: () =>
|
||||
collection.pubMagicMetadata.asc ?? false,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
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: Column(
|
||||
children: [
|
||||
ValueListenableBuilder(
|
||||
valueListenable: isFileSelected,
|
||||
builder: (context, bool value, _) {
|
||||
return AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
switchInCurve: Curves.easeInOutExpo,
|
||||
switchOutCurve: Curves.easeInOutExpo,
|
||||
child: ButtonWidget(
|
||||
key: ValueKey(value),
|
||||
isDisabled: !value,
|
||||
buttonType: ButtonType.neutral,
|
||||
labelText: S.of(context).useSelectedPhoto,
|
||||
onTap: () async {
|
||||
final selectedFile =
|
||||
selectedFiles.files.first;
|
||||
Navigator.pop(context, selectedFile);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
ButtonWidget(
|
||||
buttonType: ButtonType.secondary,
|
||||
buttonAction: ButtonAction.cancel,
|
||||
labelText: S.of(context).cancel,
|
||||
onTap: () async {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@ import 'package:logging/logging.dart';
|
|||
import 'package:path/path.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
import "package:photos/events/collection_meta_event.dart";
|
||||
import "package:photos/events/collection_updated_event.dart";
|
||||
import "package:photos/events/files_updated_event.dart";
|
||||
import 'package:photos/events/force_reload_home_gallery_event.dart';
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import 'package:photos/models/collection.dart';
|
||||
|
@ -103,6 +105,30 @@ Future<void> changeSortOrder(
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> changeCoverPhoto(
|
||||
BuildContext context,
|
||||
Collection collection,
|
||||
File file,
|
||||
) async {
|
||||
try {
|
||||
final Map<String, dynamic> update = {"coverID": file.uploadedFileID};
|
||||
await CollectionsService.instance
|
||||
.updatePublicMagicMetadata(collection, update);
|
||||
Bus.instance.fire(
|
||||
CollectionUpdatedEvent(
|
||||
collection.id,
|
||||
<File>[],
|
||||
"cover_change",
|
||||
type: EventType.coverChanged,
|
||||
),
|
||||
);
|
||||
} catch (e, s) {
|
||||
_logger.severe("failed to update cover", e, s);
|
||||
showShortToast(context, S.of(context).somethingWentWrong);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> editTime(
|
||||
BuildContext context,
|
||||
List<File> files,
|
||||
|
|
Loading…
Reference in a new issue