From abe997f274025ebaffd2493fa91143be8a1684e5 Mon Sep 17 00:00:00 2001 From: Ming Ming Date: Tue, 25 Jan 2022 18:17:19 +0800 Subject: [PATCH] Set/unset favorite in viewer --- lib/entity/file.dart | 3 + lib/entity/file/data_source.dart | 8 +++ lib/l10n/app_en.arb | 24 ++++++++ lib/l10n/untranslated-messages.txt | 52 ++++++++++++++++- lib/use_case/update_property.dart | 10 +++- lib/widget/viewer.dart | 91 +++++++++++++++++++++++++++++- test/mock_type.dart | 12 ++-- 7 files changed, 192 insertions(+), 8 deletions(-) diff --git a/lib/entity/file.dart b/lib/entity/file.dart index 6582e4bd..32c80280 100644 --- a/lib/entity/file.dart +++ b/lib/entity/file.dart @@ -575,6 +575,7 @@ class FileRepo { OrNull? metadata, OrNull? isArchived, OrNull? overrideDateTime, + bool? favorite, }) => dataSrc.updateProperty( account, @@ -582,6 +583,7 @@ class FileRepo { metadata: metadata, isArchived: isArchived, overrideDateTime: overrideDateTime, + favorite: favorite, ); /// See [FileDataSource.copy] @@ -642,6 +644,7 @@ abstract class FileDataSource { OrNull? metadata, OrNull? isArchived, OrNull? overrideDateTime, + bool? favorite, }); /// Copy [f] to [destination] diff --git a/lib/entity/file/data_source.dart b/lib/entity/file/data_source.dart index 25e621eb..e7a9e1d9 100644 --- a/lib/entity/file/data_source.dart +++ b/lib/entity/file/data_source.dart @@ -148,6 +148,7 @@ class FileWebdavDataSource implements FileDataSource { OrNull? metadata, OrNull? isArchived, OrNull? overrideDateTime, + bool? favorite, }) async { _log.info("[updateProperty] ${f.path}"); if (metadata?.obj != null && metadata!.obj!.fileEtag != f.etag) { @@ -161,6 +162,7 @@ class FileWebdavDataSource implements FileDataSource { if (overrideDateTime?.obj != null) "app:override-date-time": overrideDateTime!.obj!.toUtc().toIso8601String(), + if (favorite != null) "oc:favorite": favorite ? 1 : 0, }; final removeProps = [ if (OrNull.isSetNull(metadata)) "app:metadata", @@ -171,6 +173,7 @@ class FileWebdavDataSource implements FileDataSource { path: f.path, namespaces: { "com.nkming.nc_photos": "app", + "http://owncloud.org/ns": "oc", }, set: setProps.isNotEmpty ? setProps : null, remove: removeProps.isNotEmpty ? removeProps : null, @@ -373,6 +376,7 @@ class FileAppDbDataSource implements FileDataSource { OrNull? metadata, OrNull? isArchived, OrNull? overrideDateTime, + bool? favorite, }) { _log.info("[updateProperty] ${f.path}"); return appDb.use((db) async { @@ -384,6 +388,7 @@ class FileAppDbDataSource implements FileDataSource { metadata: metadata, isArchived: isArchived, overrideDateTime: overrideDateTime, + isFavorite: favorite, ); final fileStore = transaction.objectStore(AppDb.file2StoreName); await fileStore.put(AppDbFile2Entry.fromFile(account, newFile).toJson(), @@ -501,6 +506,7 @@ class FileCachedDataSource implements FileDataSource { OrNull? metadata, OrNull? isArchived, OrNull? overrideDateTime, + bool? favorite, }) async { await _remoteSrc .updateProperty( @@ -509,6 +515,7 @@ class FileCachedDataSource implements FileDataSource { metadata: metadata, isArchived: isArchived, overrideDateTime: overrideDateTime, + favorite: favorite, ) .then((_) => _appDbSrc.updateProperty( account, @@ -516,6 +523,7 @@ class FileCachedDataSource implements FileDataSource { metadata: metadata, isArchived: isArchived, overrideDateTime: overrideDateTime, + favorite: favorite, )); // generate a new random token diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 83585c7a..08494bba 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1127,6 +1127,30 @@ "@collectionFavoritesLabel": { "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": { diff --git a/lib/l10n/untranslated-messages.txt b/lib/l10n/untranslated-messages.txt index ba9a37da..c0c41b81 100644 --- a/lib/l10n/untranslated-messages.txt +++ b/lib/l10n/untranslated-messages.txt @@ -72,6 +72,12 @@ "convertAlbumConfirmationDialogContent", "convertAlbumSuccessNotification", "collectionFavoritesLabel", + "favoriteTooltip", + "favoriteSuccessNotification", + "favoriteFailureNotification", + "unfavoriteTooltip", + "unfavoriteSuccessNotification", + "unfavoriteFailureNotification", "errorAlbumDowngrade" ], @@ -162,6 +168,12 @@ "convertAlbumConfirmationDialogContent", "convertAlbumSuccessNotification", "collectionFavoritesLabel", + "favoriteTooltip", + "favoriteSuccessNotification", + "favoriteFailureNotification", + "unfavoriteTooltip", + "unfavoriteSuccessNotification", + "unfavoriteFailureNotification", "errorAlbumDowngrade" ], @@ -307,6 +319,12 @@ "convertAlbumConfirmationDialogContent", "convertAlbumSuccessNotification", "collectionFavoritesLabel", + "favoriteTooltip", + "favoriteSuccessNotification", + "favoriteFailureNotification", + "unfavoriteTooltip", + "unfavoriteSuccessNotification", + "unfavoriteFailureNotification", "errorAlbumDowngrade" ], @@ -335,6 +353,12 @@ "convertAlbumConfirmationDialogContent", "convertAlbumSuccessNotification", "collectionFavoritesLabel", + "favoriteTooltip", + "favoriteSuccessNotification", + "favoriteFailureNotification", + "unfavoriteTooltip", + "unfavoriteSuccessNotification", + "unfavoriteFailureNotification", "errorAlbumDowngrade" ], @@ -347,7 +371,13 @@ "convertAlbumTooltip", "convertAlbumConfirmationDialogContent", "convertAlbumSuccessNotification", - "collectionFavoritesLabel" + "collectionFavoritesLabel", + "favoriteTooltip", + "favoriteSuccessNotification", + "favoriteFailureNotification", + "unfavoriteTooltip", + "unfavoriteSuccessNotification", + "unfavoriteFailureNotification" ], "fr": [ @@ -472,6 +502,12 @@ "convertAlbumConfirmationDialogContent", "convertAlbumSuccessNotification", "collectionFavoritesLabel", + "favoriteTooltip", + "favoriteSuccessNotification", + "favoriteFailureNotification", + "unfavoriteTooltip", + "unfavoriteSuccessNotification", + "unfavoriteFailureNotification", "errorAlbumDowngrade" ], @@ -484,7 +520,13 @@ "convertAlbumTooltip", "convertAlbumConfirmationDialogContent", "convertAlbumSuccessNotification", - "collectionFavoritesLabel" + "collectionFavoritesLabel", + "favoriteTooltip", + "favoriteSuccessNotification", + "favoriteFailureNotification", + "unfavoriteTooltip", + "unfavoriteSuccessNotification", + "unfavoriteFailureNotification" ], "ru": [ @@ -582,6 +624,12 @@ "convertAlbumConfirmationDialogContent", "convertAlbumSuccessNotification", "collectionFavoritesLabel", + "favoriteTooltip", + "favoriteSuccessNotification", + "favoriteFailureNotification", + "unfavoriteTooltip", + "unfavoriteSuccessNotification", + "unfavoriteFailureNotification", "errorAlbumDowngrade" ] } diff --git a/lib/use_case/update_property.dart b/lib/use_case/update_property.dart index 46555bfe..702be236 100644 --- a/lib/use_case/update_property.dart +++ b/lib/use_case/update_property.dart @@ -15,8 +15,12 @@ class UpdateProperty { OrNull? metadata, OrNull? isArchived, OrNull? overrideDateTime, + bool? favorite, }) async { - if (metadata == null && isArchived == null && overrideDateTime == null) { + if (metadata == null && + isArchived == null && + overrideDateTime == null && + favorite == null) { // ? _log.warning("[call] Nothing to update"); return; @@ -32,6 +36,7 @@ class UpdateProperty { metadata: metadata, isArchived: isArchived, overrideDateTime: overrideDateTime, + favorite: favorite, ); int properties = 0; @@ -44,6 +49,9 @@ class UpdateProperty { if (overrideDateTime != null) { properties |= FilePropertyUpdatedEvent.propOverrideDateTime; } + if (favorite != null) { + properties |= FilePropertyUpdatedEvent.propFavorite; + } assert(properties != 0); KiwiContainer() .resolve() diff --git a/lib/widget/viewer.dart b/lib/widget/viewer.dart index e7d387b8..7c73eece 100644 --- a/lib/widget/viewer.dart +++ b/lib/widget/viewer.dart @@ -5,17 +5,21 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; +import 'package:kiwi/kiwi.dart'; import 'package:logging/logging.dart'; import 'package:nc_photos/account.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/entity/album.dart'; import 'package:nc_photos/entity/file.dart'; import 'package:nc_photos/entity/file_util.dart' as file_util; 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/share_handler.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/disposable.dart'; import 'package:nc_photos/widget/handler/remove_selection_handler.dart'; @@ -125,6 +129,9 @@ class _ViewerState extends State } Widget _buildAppBar(BuildContext context) { + final index = + _isViewerLoaded ? _viewerController.currentPage : widget.startIndex; + final file = widget.streamFiles[index]; return Wrap( children: [ AnimatedVisibility( @@ -151,12 +158,25 @@ class _ViewerState extends State shadowColor: Colors.transparent, foregroundColor: Colors.white.withOpacity(.87), 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( icon: const Icon(Icons.more_vert), tooltip: L10n.global().detailsTooltip, onPressed: _onDetailsPressed, ), + ], ], ), ], @@ -454,6 +474,72 @@ class _ViewerState extends State } } + Future _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(); + 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 _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(); + 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() { if (!_isDetailPaneActive) { setState(() { @@ -614,4 +700,7 @@ class _PageState { ScrollController scrollController; double? itemHeight; bool hasLoaded = false; + + bool isProcessingFavorite = false; + bool? favoriteOverride; } diff --git a/test/mock_type.dart b/test/mock_type.dart index 87b7f179..f942b2c5 100644 --- a/test/mock_type.dart +++ b/test/mock_type.dart @@ -242,10 +242,14 @@ class MockFileRepo implements FileRepo { } @override - Future updateProperty(Account account, File file, - {OrNull? metadata, - OrNull? isArchived, - OrNull? overrideDateTime}) { + Future updateProperty( + Account account, + File file, { + OrNull? metadata, + OrNull? isArchived, + OrNull? overrideDateTime, + bool? favorite, + }) { throw UnimplementedError(); } }