mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-02-02 14:56:20 +01:00
Set album cover manually
This commit is contained in:
parent
7ae54b9d7e
commit
f7d8a2533a
7 changed files with 168 additions and 6 deletions
|
@ -17,6 +17,9 @@ abstract class AlbumCoverProvider with EquatableMixin {
|
|||
switch (type) {
|
||||
case AlbumAutoCoverProvider._type:
|
||||
return AlbumAutoCoverProvider.fromJson(content.cast<String, dynamic>());
|
||||
case AlbumManualCoverProvider._type:
|
||||
return AlbumManualCoverProvider.fromJson(
|
||||
content.cast<String, dynamic>());
|
||||
default:
|
||||
_log.shout("[fromJson] Unknown type: $type");
|
||||
throw ArgumentError.value(type, "type");
|
||||
|
@ -27,6 +30,8 @@ abstract class AlbumCoverProvider with EquatableMixin {
|
|||
String getType() {
|
||||
if (this is AlbumAutoCoverProvider) {
|
||||
return AlbumAutoCoverProvider._type;
|
||||
} else if (this is AlbumManualCoverProvider) {
|
||||
return AlbumManualCoverProvider._type;
|
||||
} else {
|
||||
throw StateError("Unknwon subtype");
|
||||
}
|
||||
|
@ -107,3 +112,42 @@ class AlbumAutoCoverProvider extends AlbumCoverProvider {
|
|||
|
||||
static const _type = "auto";
|
||||
}
|
||||
|
||||
/// Cover picked by user
|
||||
class AlbumManualCoverProvider extends AlbumCoverProvider {
|
||||
AlbumManualCoverProvider({
|
||||
required this.coverFile,
|
||||
});
|
||||
|
||||
factory AlbumManualCoverProvider.fromJson(JsonObj json) {
|
||||
return AlbumManualCoverProvider(
|
||||
coverFile: File.fromJson(json["coverFile"].cast<String, dynamic>()),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
toString() {
|
||||
return "$runtimeType {"
|
||||
"coverFile: '${coverFile.path}', "
|
||||
"}";
|
||||
}
|
||||
|
||||
@override
|
||||
getCover(Album album) => coverFile;
|
||||
|
||||
@override
|
||||
get props => [
|
||||
coverFile,
|
||||
];
|
||||
|
||||
@override
|
||||
_toContentJson() {
|
||||
return {
|
||||
"coverFile": coverFile.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
final File coverFile;
|
||||
|
||||
static const _type = "manual";
|
||||
}
|
||||
|
|
|
@ -631,6 +631,18 @@
|
|||
"@albumSharedLabel": {
|
||||
"description": "A small label placed next to a shared album"
|
||||
},
|
||||
"setAlbumCoverProcessingNotification": "Setting photo as album cover",
|
||||
"@setAlbumCoverProcessingNotification": {
|
||||
"description": "Setting the opened item as the album cover"
|
||||
},
|
||||
"setAlbumCoverSuccessNotification": "Set album cover successfully",
|
||||
"@setAlbumCoverSuccessNotification": {
|
||||
"description": "Set the opened item as the album cover successfully"
|
||||
},
|
||||
"setAlbumCoverFailureNotification": "Failed setting album cover",
|
||||
"@setAlbumCoverFailureNotification": {
|
||||
"description": "Cannot set the opened item as the album cover"
|
||||
},
|
||||
|
||||
"changelogTitle": "Changelog",
|
||||
"@changelogTitle": {
|
||||
|
|
|
@ -12,11 +12,17 @@
|
|||
"deletePermanentlyTooltip",
|
||||
"deletePermanentlyConfirmationDialogTitle",
|
||||
"deletePermanentlyConfirmationDialogContent",
|
||||
"albumSharedLabel"
|
||||
"albumSharedLabel",
|
||||
"setAlbumCoverProcessingNotification",
|
||||
"setAlbumCoverSuccessNotification",
|
||||
"setAlbumCoverFailureNotification"
|
||||
],
|
||||
|
||||
"es": [
|
||||
"albumSharedLabel"
|
||||
"albumSharedLabel",
|
||||
"setAlbumCoverProcessingNotification",
|
||||
"setAlbumCoverSuccessNotification",
|
||||
"setAlbumCoverFailureNotification"
|
||||
],
|
||||
|
||||
"fr": [
|
||||
|
@ -32,6 +38,9 @@
|
|||
"deletePermanentlyTooltip",
|
||||
"deletePermanentlyConfirmationDialogTitle",
|
||||
"deletePermanentlyConfirmationDialogContent",
|
||||
"albumSharedLabel"
|
||||
"albumSharedLabel",
|
||||
"setAlbumCoverProcessingNotification",
|
||||
"setAlbumCoverSuccessNotification",
|
||||
"setAlbumCoverFailureNotification"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import 'package:nc_photos/entity/album/provider.dart';
|
|||
import 'package:nc_photos/entity/album/sort_provider.dart';
|
||||
import 'package:nc_photos/entity/file.dart';
|
||||
import 'package:nc_photos/entity/file_util.dart' as file_util;
|
||||
import 'package:nc_photos/event/event.dart';
|
||||
import 'package:nc_photos/exception_util.dart' as exception_util;
|
||||
import 'package:nc_photos/iterable_extension.dart';
|
||||
import 'package:nc_photos/k.dart' as k;
|
||||
|
@ -75,6 +76,16 @@ class _AlbumBrowserState extends State<AlbumBrowser>
|
|||
initState() {
|
||||
super.initState();
|
||||
_initAlbum();
|
||||
|
||||
_albumUpdatedListener =
|
||||
AppEventListener<AlbumUpdatedEvent>(_onAlbumUpdatedEvent);
|
||||
_albumUpdatedListener.begin();
|
||||
}
|
||||
|
||||
@override
|
||||
dispose() {
|
||||
super.dispose();
|
||||
_albumUpdatedListener.end();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -273,7 +284,8 @@ class _AlbumBrowserState extends State<AlbumBrowser>
|
|||
}
|
||||
}
|
||||
Navigator.pushNamed(context, Viewer.routeName,
|
||||
arguments: ViewerArguments(widget.account, _backingFiles, fileIndex));
|
||||
arguments: ViewerArguments(widget.account, _backingFiles, fileIndex,
|
||||
album: widget.album));
|
||||
}
|
||||
|
||||
void _onSelectionAppBarSharePressed(BuildContext context) {
|
||||
|
@ -492,6 +504,15 @@ class _AlbumBrowserState extends State<AlbumBrowser>
|
|||
});
|
||||
}
|
||||
|
||||
void _onAlbumUpdatedEvent(AlbumUpdatedEvent ev) {
|
||||
if (ev.album.albumFile!.path == _album?.albumFile?.path) {
|
||||
setState(() {
|
||||
_album = ev.album;
|
||||
initCover(widget.account, ev.album);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _transformItems() {
|
||||
if (_editAlbum != null) {
|
||||
// edit mode
|
||||
|
@ -662,6 +683,8 @@ class _AlbumBrowserState extends State<AlbumBrowser>
|
|||
final _editFormKey = GlobalKey<FormState>();
|
||||
Album? _editAlbum;
|
||||
|
||||
late AppEventListener<AlbumUpdatedEvent> _albumUpdatedListener;
|
||||
|
||||
static final _log = Logger("widget.album_browser._AlbumBrowserState");
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import 'package:nc_photos/entity/album/sort_provider.dart';
|
|||
import 'package:nc_photos/entity/file.dart';
|
||||
import 'package:nc_photos/entity/file/data_source.dart';
|
||||
import 'package:nc_photos/entity/file_util.dart' as file_util;
|
||||
import 'package:nc_photos/event/event.dart';
|
||||
import 'package:nc_photos/exception_util.dart' as exception_util;
|
||||
import 'package:nc_photos/iterable_extension.dart';
|
||||
import 'package:nc_photos/k.dart' as k;
|
||||
|
@ -76,6 +77,16 @@ class _DynamicAlbumBrowserState extends State<DynamicAlbumBrowser>
|
|||
initState() {
|
||||
super.initState();
|
||||
_initAlbum();
|
||||
|
||||
_albumUpdatedListener =
|
||||
AppEventListener<AlbumUpdatedEvent>(_onAlbumUpdatedEvent);
|
||||
_albumUpdatedListener.begin();
|
||||
}
|
||||
|
||||
@override
|
||||
dispose() {
|
||||
super.dispose();
|
||||
_albumUpdatedListener.end();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -318,7 +329,8 @@ class _DynamicAlbumBrowserState extends State<DynamicAlbumBrowser>
|
|||
}
|
||||
}
|
||||
Navigator.pushNamed(context, Viewer.routeName,
|
||||
arguments: ViewerArguments(widget.account, _backingFiles, fileIndex));
|
||||
arguments: ViewerArguments(widget.account, _backingFiles, fileIndex,
|
||||
album: widget.album));
|
||||
}
|
||||
|
||||
void _onAppBarConvertBasicPressed(BuildContext context) {
|
||||
|
@ -491,6 +503,15 @@ class _DynamicAlbumBrowserState extends State<DynamicAlbumBrowser>
|
|||
});
|
||||
}
|
||||
|
||||
void _onAlbumUpdatedEvent(AlbumUpdatedEvent ev) {
|
||||
if (ev.album.albumFile!.path == _album?.albumFile?.path) {
|
||||
setState(() {
|
||||
_album = ev.album;
|
||||
initCover(widget.account, ev.album);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _transformItems(List<AlbumItem> items) {
|
||||
if (_editAlbum != null) {
|
||||
// edit mode
|
||||
|
@ -547,6 +568,8 @@ class _DynamicAlbumBrowserState extends State<DynamicAlbumBrowser>
|
|||
final _editFormKey = GlobalKey<FormState>();
|
||||
Album? _editAlbum;
|
||||
|
||||
late AppEventListener<AlbumUpdatedEvent> _albumUpdatedListener;
|
||||
|
||||
static final _log =
|
||||
Logger("widget.dynamic_album_browser._DynamicAlbumBrowserState");
|
||||
static const _menuValueConvertBasic = 0;
|
||||
|
|
|
@ -30,11 +30,17 @@ import 'package:nc_photos/widget/viewer_bottom_app_bar.dart';
|
|||
import 'package:nc_photos/widget/viewer_detail_pane.dart';
|
||||
|
||||
class ViewerArguments {
|
||||
ViewerArguments(this.account, this.streamFiles, this.startIndex);
|
||||
ViewerArguments(
|
||||
this.account,
|
||||
this.streamFiles,
|
||||
this.startIndex, {
|
||||
this.album,
|
||||
});
|
||||
|
||||
final Account account;
|
||||
final List<File> streamFiles;
|
||||
final int startIndex;
|
||||
final Album? album;
|
||||
}
|
||||
|
||||
class Viewer extends StatefulWidget {
|
||||
|
@ -49,6 +55,7 @@ class Viewer extends StatefulWidget {
|
|||
required this.account,
|
||||
required this.streamFiles,
|
||||
required this.startIndex,
|
||||
this.album,
|
||||
}) : super(key: key);
|
||||
|
||||
Viewer.fromArgs(ViewerArguments args, {Key? key})
|
||||
|
@ -57,6 +64,7 @@ class Viewer extends StatefulWidget {
|
|||
account: args.account,
|
||||
streamFiles: args.streamFiles,
|
||||
startIndex: args.startIndex,
|
||||
album: args.album,
|
||||
);
|
||||
|
||||
@override
|
||||
|
@ -65,6 +73,9 @@ class Viewer extends StatefulWidget {
|
|||
final Account account;
|
||||
final List<File> streamFiles;
|
||||
final int startIndex;
|
||||
|
||||
/// The album these files belongs to, or null
|
||||
final Album? album;
|
||||
}
|
||||
|
||||
class _ViewerState extends State<Viewer> {
|
||||
|
@ -260,6 +271,7 @@ class _ViewerState extends State<Viewer> {
|
|||
child: ViewerDetailPane(
|
||||
account: widget.account,
|
||||
file: widget.streamFiles[index],
|
||||
album: widget.album,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -11,6 +11,7 @@ import 'package:nc_photos/account.dart';
|
|||
import 'package:nc_photos/app_localizations.dart';
|
||||
import 'package:nc_photos/double_extension.dart';
|
||||
import 'package:nc_photos/entity/album.dart';
|
||||
import 'package:nc_photos/entity/album/cover_provider.dart';
|
||||
import 'package:nc_photos/entity/album/item.dart';
|
||||
import 'package:nc_photos/entity/album/provider.dart';
|
||||
import 'package:nc_photos/entity/file.dart';
|
||||
|
@ -36,6 +37,7 @@ class ViewerDetailPane extends StatefulWidget {
|
|||
Key? key,
|
||||
required this.account,
|
||||
required this.file,
|
||||
this.album,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -43,6 +45,9 @@ class ViewerDetailPane extends StatefulWidget {
|
|||
|
||||
final Account account;
|
||||
final File file;
|
||||
|
||||
/// The album this file belongs to, or null
|
||||
final Album? album;
|
||||
}
|
||||
|
||||
class _ViewerDetailPaneState extends State<ViewerDetailPane> {
|
||||
|
@ -110,6 +115,12 @@ class _ViewerDetailPaneState extends State<ViewerDetailPane> {
|
|||
children: [
|
||||
Row(
|
||||
children: [
|
||||
if (widget.album != null)
|
||||
_DetailPaneButton(
|
||||
icon: Icons.photo_album_outlined,
|
||||
label: "Use as album cover",
|
||||
onPressed: () => _onSetAlbumCoverPressed(context),
|
||||
),
|
||||
_DetailPaneButton(
|
||||
icon: Icons.playlist_add_outlined,
|
||||
label: L10n.of(context).addToAlbumTooltip,
|
||||
|
@ -232,6 +243,34 @@ class _ViewerDetailPaneState extends State<ViewerDetailPane> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _onSetAlbumCoverPressed(BuildContext context) async {
|
||||
assert(widget.album != null);
|
||||
_log.info(
|
||||
"[_onSetAlbumCoverPressed] Set '${widget.file.path}' as album cover for '${widget.album!.name}'");
|
||||
await _onAction(
|
||||
context,
|
||||
L10n.of(context).setAlbumCoverProcessingNotification,
|
||||
L10n.of(context).setAlbumCoverSuccessNotification,
|
||||
() async {
|
||||
final albumRepo = AlbumRepo(AlbumCachedDataSource());
|
||||
try {
|
||||
await UpdateAlbum(albumRepo).call(
|
||||
widget.account,
|
||||
widget.album!.copyWith(
|
||||
coverProvider: AlbumManualCoverProvider(
|
||||
coverFile: widget.file,
|
||||
),
|
||||
));
|
||||
} catch (e, stackTrace) {
|
||||
_log.shout("[_onSetAlbumCoverPressed] Failed while updating album", e,
|
||||
stackTrace);
|
||||
rethrow;
|
||||
}
|
||||
},
|
||||
failureText: L10n.of(context).setAlbumCoverFailureNotification,
|
||||
);
|
||||
}
|
||||
|
||||
void _onAddToAlbumPressed(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
|
|
Loading…
Reference in a new issue