Set cover photo (#1234)

This commit is contained in:
Neeraj Gupta 2023-06-22 22:20:15 +05:30 committed by GitHub
commit bfebc6d051
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 284 additions and 20 deletions

View file

@ -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 ?? "",

View file

@ -25,4 +25,5 @@ enum EventType {
unarchived,
hide,
unhide,
coverChanged,
}

View file

@ -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"),

View file

@ -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(

View file

@ -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",

View file

@ -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) {

View 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();
},
),
],
),
),
)
],
),
),
),
],
),
);
}
}

View file

@ -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,