Set/unset favorite in viewer

This commit is contained in:
Ming Ming 2022-01-25 18:17:19 +08:00
parent 3b8f0f09d4
commit abe997f274
7 changed files with 192 additions and 8 deletions

View file

@ -575,6 +575,7 @@ class FileRepo {
OrNull<Metadata>? metadata, OrNull<Metadata>? metadata,
OrNull<bool>? isArchived, OrNull<bool>? isArchived,
OrNull<DateTime>? overrideDateTime, OrNull<DateTime>? overrideDateTime,
bool? favorite,
}) => }) =>
dataSrc.updateProperty( dataSrc.updateProperty(
account, account,
@ -582,6 +583,7 @@ class FileRepo {
metadata: metadata, metadata: metadata,
isArchived: isArchived, isArchived: isArchived,
overrideDateTime: overrideDateTime, overrideDateTime: overrideDateTime,
favorite: favorite,
); );
/// See [FileDataSource.copy] /// See [FileDataSource.copy]
@ -642,6 +644,7 @@ abstract class FileDataSource {
OrNull<Metadata>? metadata, OrNull<Metadata>? metadata,
OrNull<bool>? isArchived, OrNull<bool>? isArchived,
OrNull<DateTime>? overrideDateTime, OrNull<DateTime>? overrideDateTime,
bool? favorite,
}); });
/// Copy [f] to [destination] /// Copy [f] to [destination]

View file

@ -148,6 +148,7 @@ class FileWebdavDataSource implements FileDataSource {
OrNull<Metadata>? metadata, OrNull<Metadata>? metadata,
OrNull<bool>? isArchived, OrNull<bool>? isArchived,
OrNull<DateTime>? overrideDateTime, OrNull<DateTime>? overrideDateTime,
bool? favorite,
}) async { }) async {
_log.info("[updateProperty] ${f.path}"); _log.info("[updateProperty] ${f.path}");
if (metadata?.obj != null && metadata!.obj!.fileEtag != f.etag) { if (metadata?.obj != null && metadata!.obj!.fileEtag != f.etag) {
@ -161,6 +162,7 @@ class FileWebdavDataSource implements FileDataSource {
if (overrideDateTime?.obj != null) if (overrideDateTime?.obj != null)
"app:override-date-time": "app:override-date-time":
overrideDateTime!.obj!.toUtc().toIso8601String(), overrideDateTime!.obj!.toUtc().toIso8601String(),
if (favorite != null) "oc:favorite": favorite ? 1 : 0,
}; };
final removeProps = [ final removeProps = [
if (OrNull.isSetNull(metadata)) "app:metadata", if (OrNull.isSetNull(metadata)) "app:metadata",
@ -171,6 +173,7 @@ class FileWebdavDataSource implements FileDataSource {
path: f.path, path: f.path,
namespaces: { namespaces: {
"com.nkming.nc_photos": "app", "com.nkming.nc_photos": "app",
"http://owncloud.org/ns": "oc",
}, },
set: setProps.isNotEmpty ? setProps : null, set: setProps.isNotEmpty ? setProps : null,
remove: removeProps.isNotEmpty ? removeProps : null, remove: removeProps.isNotEmpty ? removeProps : null,
@ -373,6 +376,7 @@ class FileAppDbDataSource implements FileDataSource {
OrNull<Metadata>? metadata, OrNull<Metadata>? metadata,
OrNull<bool>? isArchived, OrNull<bool>? isArchived,
OrNull<DateTime>? overrideDateTime, OrNull<DateTime>? overrideDateTime,
bool? favorite,
}) { }) {
_log.info("[updateProperty] ${f.path}"); _log.info("[updateProperty] ${f.path}");
return appDb.use((db) async { return appDb.use((db) async {
@ -384,6 +388,7 @@ class FileAppDbDataSource implements FileDataSource {
metadata: metadata, metadata: metadata,
isArchived: isArchived, isArchived: isArchived,
overrideDateTime: overrideDateTime, overrideDateTime: overrideDateTime,
isFavorite: favorite,
); );
final fileStore = transaction.objectStore(AppDb.file2StoreName); final fileStore = transaction.objectStore(AppDb.file2StoreName);
await fileStore.put(AppDbFile2Entry.fromFile(account, newFile).toJson(), await fileStore.put(AppDbFile2Entry.fromFile(account, newFile).toJson(),
@ -501,6 +506,7 @@ class FileCachedDataSource implements FileDataSource {
OrNull<Metadata>? metadata, OrNull<Metadata>? metadata,
OrNull<bool>? isArchived, OrNull<bool>? isArchived,
OrNull<DateTime>? overrideDateTime, OrNull<DateTime>? overrideDateTime,
bool? favorite,
}) async { }) async {
await _remoteSrc await _remoteSrc
.updateProperty( .updateProperty(
@ -509,6 +515,7 @@ class FileCachedDataSource implements FileDataSource {
metadata: metadata, metadata: metadata,
isArchived: isArchived, isArchived: isArchived,
overrideDateTime: overrideDateTime, overrideDateTime: overrideDateTime,
favorite: favorite,
) )
.then((_) => _appDbSrc.updateProperty( .then((_) => _appDbSrc.updateProperty(
account, account,
@ -516,6 +523,7 @@ class FileCachedDataSource implements FileDataSource {
metadata: metadata, metadata: metadata,
isArchived: isArchived, isArchived: isArchived,
overrideDateTime: overrideDateTime, overrideDateTime: overrideDateTime,
favorite: favorite,
)); ));
// generate a new random token // generate a new random token

View file

@ -1127,6 +1127,30 @@
"@collectionFavoritesLabel": { "@collectionFavoritesLabel": {
"description": "Browse photos added to favorites" "description": "Browse photos added to favorites"
}, },
"favoriteTooltip": "Favorite",
"@favoriteTooltip": {
"description": "Add photo to favorites"
},
"favoriteSuccessNotification": "Added to favorites",
"@favoriteSuccessNotification": {
"description": "Successfully added photos to favorites"
},
"favoriteFailureNotification": "Failed adding to favorites",
"@favoriteFailureNotification": {
"description": "Failed adding photos to favorites"
},
"unfavoriteTooltip": "Unfavorite",
"@unfavoriteTooltip": {
"description": "Remove photo to favorites"
},
"unfavoriteSuccessNotification": "Removed from favorites",
"@unfavoriteSuccessNotification": {
"description": "Successfully removed photos from favorites"
},
"unfavoriteFailureNotification": "Failed removing from favorites",
"@unfavoriteFailureNotification": {
"description": "Failed removing photos from favorites"
},
"errorUnauthenticated": "Unauthenticated access. Please sign-in again if the problem continues", "errorUnauthenticated": "Unauthenticated access. Please sign-in again if the problem continues",
"@errorUnauthenticated": { "@errorUnauthenticated": {

View file

@ -72,6 +72,12 @@
"convertAlbumConfirmationDialogContent", "convertAlbumConfirmationDialogContent",
"convertAlbumSuccessNotification", "convertAlbumSuccessNotification",
"collectionFavoritesLabel", "collectionFavoritesLabel",
"favoriteTooltip",
"favoriteSuccessNotification",
"favoriteFailureNotification",
"unfavoriteTooltip",
"unfavoriteSuccessNotification",
"unfavoriteFailureNotification",
"errorAlbumDowngrade" "errorAlbumDowngrade"
], ],
@ -162,6 +168,12 @@
"convertAlbumConfirmationDialogContent", "convertAlbumConfirmationDialogContent",
"convertAlbumSuccessNotification", "convertAlbumSuccessNotification",
"collectionFavoritesLabel", "collectionFavoritesLabel",
"favoriteTooltip",
"favoriteSuccessNotification",
"favoriteFailureNotification",
"unfavoriteTooltip",
"unfavoriteSuccessNotification",
"unfavoriteFailureNotification",
"errorAlbumDowngrade" "errorAlbumDowngrade"
], ],
@ -307,6 +319,12 @@
"convertAlbumConfirmationDialogContent", "convertAlbumConfirmationDialogContent",
"convertAlbumSuccessNotification", "convertAlbumSuccessNotification",
"collectionFavoritesLabel", "collectionFavoritesLabel",
"favoriteTooltip",
"favoriteSuccessNotification",
"favoriteFailureNotification",
"unfavoriteTooltip",
"unfavoriteSuccessNotification",
"unfavoriteFailureNotification",
"errorAlbumDowngrade" "errorAlbumDowngrade"
], ],
@ -335,6 +353,12 @@
"convertAlbumConfirmationDialogContent", "convertAlbumConfirmationDialogContent",
"convertAlbumSuccessNotification", "convertAlbumSuccessNotification",
"collectionFavoritesLabel", "collectionFavoritesLabel",
"favoriteTooltip",
"favoriteSuccessNotification",
"favoriteFailureNotification",
"unfavoriteTooltip",
"unfavoriteSuccessNotification",
"unfavoriteFailureNotification",
"errorAlbumDowngrade" "errorAlbumDowngrade"
], ],
@ -347,7 +371,13 @@
"convertAlbumTooltip", "convertAlbumTooltip",
"convertAlbumConfirmationDialogContent", "convertAlbumConfirmationDialogContent",
"convertAlbumSuccessNotification", "convertAlbumSuccessNotification",
"collectionFavoritesLabel" "collectionFavoritesLabel",
"favoriteTooltip",
"favoriteSuccessNotification",
"favoriteFailureNotification",
"unfavoriteTooltip",
"unfavoriteSuccessNotification",
"unfavoriteFailureNotification"
], ],
"fr": [ "fr": [
@ -472,6 +502,12 @@
"convertAlbumConfirmationDialogContent", "convertAlbumConfirmationDialogContent",
"convertAlbumSuccessNotification", "convertAlbumSuccessNotification",
"collectionFavoritesLabel", "collectionFavoritesLabel",
"favoriteTooltip",
"favoriteSuccessNotification",
"favoriteFailureNotification",
"unfavoriteTooltip",
"unfavoriteSuccessNotification",
"unfavoriteFailureNotification",
"errorAlbumDowngrade" "errorAlbumDowngrade"
], ],
@ -484,7 +520,13 @@
"convertAlbumTooltip", "convertAlbumTooltip",
"convertAlbumConfirmationDialogContent", "convertAlbumConfirmationDialogContent",
"convertAlbumSuccessNotification", "convertAlbumSuccessNotification",
"collectionFavoritesLabel" "collectionFavoritesLabel",
"favoriteTooltip",
"favoriteSuccessNotification",
"favoriteFailureNotification",
"unfavoriteTooltip",
"unfavoriteSuccessNotification",
"unfavoriteFailureNotification"
], ],
"ru": [ "ru": [
@ -582,6 +624,12 @@
"convertAlbumConfirmationDialogContent", "convertAlbumConfirmationDialogContent",
"convertAlbumSuccessNotification", "convertAlbumSuccessNotification",
"collectionFavoritesLabel", "collectionFavoritesLabel",
"favoriteTooltip",
"favoriteSuccessNotification",
"favoriteFailureNotification",
"unfavoriteTooltip",
"unfavoriteSuccessNotification",
"unfavoriteFailureNotification",
"errorAlbumDowngrade" "errorAlbumDowngrade"
] ]
} }

View file

@ -15,8 +15,12 @@ class UpdateProperty {
OrNull<Metadata>? metadata, OrNull<Metadata>? metadata,
OrNull<bool>? isArchived, OrNull<bool>? isArchived,
OrNull<DateTime>? overrideDateTime, OrNull<DateTime>? overrideDateTime,
bool? favorite,
}) async { }) async {
if (metadata == null && isArchived == null && overrideDateTime == null) { if (metadata == null &&
isArchived == null &&
overrideDateTime == null &&
favorite == null) {
// ? // ?
_log.warning("[call] Nothing to update"); _log.warning("[call] Nothing to update");
return; return;
@ -32,6 +36,7 @@ class UpdateProperty {
metadata: metadata, metadata: metadata,
isArchived: isArchived, isArchived: isArchived,
overrideDateTime: overrideDateTime, overrideDateTime: overrideDateTime,
favorite: favorite,
); );
int properties = 0; int properties = 0;
@ -44,6 +49,9 @@ class UpdateProperty {
if (overrideDateTime != null) { if (overrideDateTime != null) {
properties |= FilePropertyUpdatedEvent.propOverrideDateTime; properties |= FilePropertyUpdatedEvent.propOverrideDateTime;
} }
if (favorite != null) {
properties |= FilePropertyUpdatedEvent.propFavorite;
}
assert(properties != 0); assert(properties != 0);
KiwiContainer() KiwiContainer()
.resolve<EventBus>() .resolve<EventBus>()

View file

@ -5,17 +5,21 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:kiwi/kiwi.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:nc_photos/account.dart'; import 'package:nc_photos/account.dart';
import 'package:nc_photos/app_localizations.dart'; import 'package:nc_photos/app_localizations.dart';
import 'package:nc_photos/di_container.dart';
import 'package:nc_photos/download_handler.dart'; import 'package:nc_photos/download_handler.dart';
import 'package:nc_photos/entity/album.dart'; import 'package:nc_photos/entity/album.dart';
import 'package:nc_photos/entity/file.dart'; import 'package:nc_photos/entity/file.dart';
import 'package:nc_photos/entity/file_util.dart' as file_util; import 'package:nc_photos/entity/file_util.dart' as file_util;
import 'package:nc_photos/k.dart' as k; import 'package:nc_photos/k.dart' as k;
import 'package:nc_photos/notified_action.dart';
import 'package:nc_photos/pref.dart'; import 'package:nc_photos/pref.dart';
import 'package:nc_photos/share_handler.dart'; import 'package:nc_photos/share_handler.dart';
import 'package:nc_photos/theme.dart'; import 'package:nc_photos/theme.dart';
import 'package:nc_photos/use_case/update_property.dart';
import 'package:nc_photos/widget/animated_visibility.dart'; import 'package:nc_photos/widget/animated_visibility.dart';
import 'package:nc_photos/widget/disposable.dart'; import 'package:nc_photos/widget/disposable.dart';
import 'package:nc_photos/widget/handler/remove_selection_handler.dart'; import 'package:nc_photos/widget/handler/remove_selection_handler.dart';
@ -125,6 +129,9 @@ class _ViewerState extends State<Viewer>
} }
Widget _buildAppBar(BuildContext context) { Widget _buildAppBar(BuildContext context) {
final index =
_isViewerLoaded ? _viewerController.currentPage : widget.startIndex;
final file = widget.streamFiles[index];
return Wrap( return Wrap(
children: [ children: [
AnimatedVisibility( AnimatedVisibility(
@ -151,12 +158,25 @@ class _ViewerState extends State<Viewer>
shadowColor: Colors.transparent, shadowColor: Colors.transparent,
foregroundColor: Colors.white.withOpacity(.87), foregroundColor: Colors.white.withOpacity(.87),
actions: [ actions: [
if (!_isDetailPaneActive && _canOpenDetailPane()) if (!_isDetailPaneActive && _canOpenDetailPane()) ...[
(_pageStates[index]?.favoriteOverride ?? file.isFavorite) ==
true
? IconButton(
icon: const Icon(Icons.star),
tooltip: L10n.global().unfavoriteTooltip,
onPressed: () => _onUnfavoritePressed(index),
)
: IconButton(
icon: const Icon(Icons.star_border),
tooltip: L10n.global().favoriteTooltip,
onPressed: () => _onFavoritePressed(index),
),
IconButton( IconButton(
icon: const Icon(Icons.more_vert), icon: const Icon(Icons.more_vert),
tooltip: L10n.global().detailsTooltip, tooltip: L10n.global().detailsTooltip,
onPressed: _onDetailsPressed, onPressed: _onDetailsPressed,
), ),
],
], ],
), ),
], ],
@ -454,6 +474,72 @@ class _ViewerState extends State<Viewer>
} }
} }
Future<void> _onFavoritePressed(int index) async {
if (_pageStates[index]!.isProcessingFavorite) {
_log.fine("[_onFavoritePressed] Process ongoing, ignored");
return;
}
final file = widget.streamFiles[_viewerController.currentPage];
final c = KiwiContainer().resolve<DiContainer>();
setState(() {
_pageStates[index]!.favoriteOverride = true;
});
_pageStates[index]!.isProcessingFavorite = true;
try {
await NotifiedAction(
() => UpdateProperty(c.fileRepo)(
widget.account,
file,
favorite: true,
),
null,
L10n.global().favoriteSuccessNotification,
failureText: L10n.global().favoriteFailureNotification,
)();
} catch (e, stackTrace) {
_log.shout(
"[_onFavoritePressed] Failed while UpdateProperty", e, stackTrace);
setState(() {
_pageStates[index]!.favoriteOverride = false;
});
}
_pageStates[index]!.isProcessingFavorite = false;
}
Future<void> _onUnfavoritePressed(int index) async {
if (_pageStates[index]!.isProcessingFavorite) {
_log.fine("[_onUnfavoritePressed] Process ongoing, ignored");
return;
}
final file = widget.streamFiles[_viewerController.currentPage];
final c = KiwiContainer().resolve<DiContainer>();
setState(() {
_pageStates[index]!.favoriteOverride = false;
});
_pageStates[index]!.isProcessingFavorite = true;
try {
await NotifiedAction(
() => UpdateProperty(c.fileRepo)(
widget.account,
file,
favorite: false,
),
null,
L10n.global().unfavoriteSuccessNotification,
failureText: L10n.global().unfavoriteFailureNotification,
)();
} catch (e, stackTrace) {
_log.shout(
"[_onUnfavoritePressed] Failed while UpdateProperty", e, stackTrace);
setState(() {
_pageStates[index]!.favoriteOverride = true;
});
}
_pageStates[index]!.isProcessingFavorite = false;
}
void _onDetailsPressed() { void _onDetailsPressed() {
if (!_isDetailPaneActive) { if (!_isDetailPaneActive) {
setState(() { setState(() {
@ -614,4 +700,7 @@ class _PageState {
ScrollController scrollController; ScrollController scrollController;
double? itemHeight; double? itemHeight;
bool hasLoaded = false; bool hasLoaded = false;
bool isProcessingFavorite = false;
bool? favoriteOverride;
} }

View file

@ -242,10 +242,14 @@ class MockFileRepo implements FileRepo {
} }
@override @override
Future<void> updateProperty(Account account, File file, Future<void> updateProperty(
{OrNull<Metadata>? metadata, Account account,
OrNull<bool>? isArchived, File file, {
OrNull<DateTime>? overrideDateTime}) { OrNull<Metadata>? metadata,
OrNull<bool>? isArchived,
OrNull<DateTime>? overrideDateTime,
bool? favorite,
}) {
throw UnimplementedError(); throw UnimplementedError();
} }
} }