Regression: import album shared with you

This commit is contained in:
Ming Ming 2023-05-15 21:23:27 +08:00
parent f20900f2a9
commit fb02f3c9b0
22 changed files with 160 additions and 3 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

2
app/lib/asset.dart Normal file
View file

@ -0,0 +1,2 @@
const icAddCollectionsOutlined24 =
"assets/ic_add_collections_outlined_24dp.png";

View file

@ -61,6 +61,9 @@ class Collection with EquatableMixin {
/// See [CollectionContentProvider.isDynamicCollection] /// See [CollectionContentProvider.isDynamicCollection]
bool get isDynamicCollection => contentProvider.isDynamicCollection; bool get isDynamicCollection => contentProvider.isDynamicCollection;
/// See [CollectionContentProvider.isPendingSharedAlbum]
bool get isPendingSharedAlbum => contentProvider.isPendingSharedAlbum;
@override @override
List<Object?> get props => [ List<Object?> get props => [
name, name,
@ -136,4 +139,10 @@ abstract class CollectionContentProvider with EquatableMixin {
/// A collection is defined as a dynamic one when the items are not specified /// A collection is defined as a dynamic one when the items are not specified
/// explicitly by the user, but rather derived from some conditions /// explicitly by the user, but rather derived from some conditions
bool get isDynamicCollection; bool get isDynamicCollection;
/// Return whether this is a shared album pending to be added
///
/// In some implementation, shared album does not immediately get added to the
/// collections list
bool get isPendingSharedAlbum;
} }

View file

@ -86,6 +86,9 @@ abstract class CollectionAdapter {
required ValueChanged<Collection> onCollectionUpdated, required ValueChanged<Collection> onCollectionUpdated,
}); });
/// Import a pending shared collection and return the resulting collection
Future<Collection> importPendingShared();
/// Convert a [NewCollectionItem] to an adapted one /// Convert a [NewCollectionItem] to an adapted one
Future<CollectionItem> adaptToNewItem(NewCollectionItem original); Future<CollectionItem> adaptToNewItem(NewCollectionItem original);

View file

@ -75,4 +75,9 @@ mixin CollectionAdapterUnshareableTag implements CollectionAdapter {
}) { }) {
throw UnsupportedError("Operation not supported"); throw UnsupportedError("Operation not supported");
} }
@override
Future<Collection> importPendingShared() {
throw UnsupportedError("Operation not supported");
}
} }

View file

@ -28,6 +28,7 @@ import 'package:nc_photos/use_case/album/remove_album.dart';
import 'package:nc_photos/use_case/album/remove_from_album.dart'; import 'package:nc_photos/use_case/album/remove_from_album.dart';
import 'package:nc_photos/use_case/album/share_album_with_user.dart'; import 'package:nc_photos/use_case/album/share_album_with_user.dart';
import 'package:nc_photos/use_case/album/unshare_album_with_user.dart'; import 'package:nc_photos/use_case/album/unshare_album_with_user.dart';
import 'package:nc_photos/use_case/import_pending_shared_album.dart';
import 'package:nc_photos/use_case/preprocess_album.dart'; import 'package:nc_photos/use_case/preprocess_album.dart';
import 'package:nc_photos/use_case/unimport_shared_album.dart'; import 'package:nc_photos/use_case/unimport_shared_album.dart';
import 'package:nc_photos/use_case/update_album_with_actual_items.dart'; import 'package:nc_photos/use_case/update_album_with_actual_items.dart';
@ -224,6 +225,13 @@ class CollectionAlbumAdapter implements CollectionAdapter {
: CollectionShareResult.ok; : CollectionShareResult.ok;
} }
@override
Future<Collection> importPendingShared() async {
final newAlbum =
await ImportPendingSharedAlbum(_c)(account, _provider.album);
return CollectionBuilder.byAlbum(account, newAlbum);
}
@override @override
Future<CollectionItem> adaptToNewItem(NewCollectionItem original) async { Future<CollectionItem> adaptToNewItem(NewCollectionItem original) async {
if (original is NewCollectionFileItem) { if (original is NewCollectionFileItem) {

View file

@ -7,6 +7,7 @@ import 'package:nc_photos/entity/album/provider.dart';
import 'package:nc_photos/entity/collection.dart'; import 'package:nc_photos/entity/collection.dart';
import 'package:nc_photos/entity/collection/util.dart'; import 'package:nc_photos/entity/collection/util.dart';
import 'package:nc_photos/entity/collection_item/util.dart'; import 'package:nc_photos/entity/collection_item/util.dart';
import 'package:nc_photos/remote_storage_util.dart' as remote_storage_util;
import 'package:to_string/to_string.dart'; import 'package:to_string/to_string.dart';
part 'album.g.dart'; part 'album.g.dart';
@ -102,6 +103,12 @@ class CollectionAlbumProvider
@override @override
bool get isDynamicCollection => album.provider is! AlbumStaticProvider; bool get isDynamicCollection => album.provider is! AlbumStaticProvider;
@override
bool get isPendingSharedAlbum =>
album.albumFile?.path.startsWith(
remote_storage_util.getRemotePendingSharedAlbumsDir(account)) ==
true;
@override @override
List<Object?> get props => [account, album]; List<Object?> get props => [account, album];

View file

@ -53,6 +53,9 @@ class CollectionLocationGroupProvider
@override @override
bool get isDynamicCollection => true; bool get isDynamicCollection => true;
@override
bool get isPendingSharedAlbum => false;
@override @override
List<Object?> get props => [account, location]; List<Object?> get props => [account, location];

View file

@ -61,6 +61,9 @@ class CollectionMemoryProvider
@override @override
bool get isDynamicCollection => true; bool get isDynamicCollection => true;
@override
bool get isPendingSharedAlbum => false;
@override @override
String toString() => _$toString(); String toString() => _$toString();

View file

@ -76,6 +76,9 @@ class CollectionNcAlbumProvider
@override @override
bool get isDynamicCollection => false; bool get isDynamicCollection => false;
@override
bool get isPendingSharedAlbum => false;
@override @override
List<Object?> get props => [account, album]; List<Object?> get props => [account, album];

View file

@ -51,6 +51,9 @@ class CollectionPersonProvider
@override @override
bool get isDynamicCollection => true; bool get isDynamicCollection => true;
@override
bool get isPendingSharedAlbum => false;
@override @override
List<Object?> get props => [account, person]; List<Object?> get props => [account, person];

View file

@ -46,6 +46,9 @@ class CollectionTagProvider
@override @override
bool get isDynamicCollection => true; bool get isDynamicCollection => true;
@override
bool get isPendingSharedAlbum => false;
@override @override
List<Object?> get props => [account, tags]; List<Object?> get props => [account, tags];

View file

@ -0,0 +1,18 @@
import 'package:nc_photos/account.dart';
import 'package:nc_photos/di_container.dart';
import 'package:nc_photos/entity/collection.dart';
import 'package:nc_photos/entity/collection/adapter.dart';
class ImportPendingSharedCollection {
const ImportPendingSharedCollection(this._c);
/// Import a pending shared collection to the app
///
/// For some implementations, shared collection may live in a temporary
/// state before being accepted by the user. This use case will accept the
/// share and import the collection to the collections view
Future<Collection> call(Account account, Collection collection) =>
CollectionAdapter.of(_c, account, collection).importPendingShared();
final DiContainer _c;
}

View file

@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
class AssetIcon extends StatelessWidget {
const AssetIcon(
this.assetName, {
super.key,
this.size,
this.color,
});
@override
Widget build(BuildContext context) {
return ImageIcon(
AssetImage(assetName),
size: size,
color: color,
);
}
final String assetName;
final double? size;
final Color? color;
}

View file

@ -13,6 +13,7 @@ import 'package:logging/logging.dart';
import 'package:nc_photos/account.dart'; import 'package:nc_photos/account.dart';
import 'package:nc_photos/api/api_util.dart' as api_util; import 'package:nc_photos/api/api_util.dart' as api_util;
import 'package:nc_photos/app_localizations.dart'; import 'package:nc_photos/app_localizations.dart';
import 'package:nc_photos/asset.dart' as asset;
import 'package:nc_photos/bloc_util.dart'; import 'package:nc_photos/bloc_util.dart';
import 'package:nc_photos/cache_manager_util.dart'; import 'package:nc_photos/cache_manager_util.dart';
import 'package:nc_photos/controller/account_controller.dart'; import 'package:nc_photos/controller/account_controller.dart';
@ -39,8 +40,10 @@ import 'package:nc_photos/object_extension.dart';
import 'package:nc_photos/or_null.dart'; import 'package:nc_photos/or_null.dart';
import 'package:nc_photos/snack_bar_manager.dart'; import 'package:nc_photos/snack_bar_manager.dart';
import 'package:nc_photos/use_case/archive_file.dart'; import 'package:nc_photos/use_case/archive_file.dart';
import 'package:nc_photos/use_case/collection/import_pending_shared_collection.dart';
import 'package:nc_photos/use_case/inflate_file_descriptor.dart'; import 'package:nc_photos/use_case/inflate_file_descriptor.dart';
import 'package:nc_photos/use_case/remove.dart'; import 'package:nc_photos/use_case/remove.dart';
import 'package:nc_photos/widget/asset_icon.dart';
import 'package:nc_photos/widget/collection_picker.dart'; import 'package:nc_photos/widget/collection_picker.dart';
import 'package:nc_photos/widget/draggable_item_list.dart'; import 'package:nc_photos/widget/draggable_item_list.dart';
import 'package:nc_photos/widget/export_collection_dialog.dart'; import 'package:nc_photos/widget/export_collection_dialog.dart';
@ -164,6 +167,18 @@ class _WrappedCollectionBrowserState extends State<_WrappedCollectionBrowser>
} }
}, },
), ),
BlocListener<_Bloc, _State>(
listenWhen: (previous, current) =>
previous.importResult != current.importResult,
listener: (context, state) {
if (state.importResult != null) {
Navigator.of(context).pushReplacementNamed(
CollectionBrowser.routeName,
arguments: CollectionBrowserArguments(state.importResult!),
);
}
},
),
BlocListener<_Bloc, _State>( BlocListener<_Bloc, _State>(
listenWhen: (previous, current) => listenWhen: (previous, current) =>
previous.error != current.error, previous.error != current.error,

View file

@ -29,6 +29,7 @@ abstract class $_StateCopyWithWorker {
List<_Item>? editTransformedItems, List<_Item>? editTransformedItems,
CollectionItemSort? editSort, CollectionItemSort? editSort,
bool? isDragging, bool? isDragging,
Collection? importResult,
ExceptionEvent? error, ExceptionEvent? error,
String? message}); String? message});
} }
@ -53,6 +54,7 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
dynamic editTransformedItems = copyWithNull, dynamic editTransformedItems = copyWithNull,
dynamic editSort = copyWithNull, dynamic editSort = copyWithNull,
dynamic isDragging, dynamic isDragging,
dynamic importResult = copyWithNull,
dynamic error = copyWithNull, dynamic error = copyWithNull,
dynamic message = copyWithNull}) { dynamic message = copyWithNull}) {
return _State( return _State(
@ -82,6 +84,9 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
? that.editSort ? that.editSort
: editSort as CollectionItemSort?, : editSort as CollectionItemSort?,
isDragging: isDragging as bool? ?? that.isDragging, isDragging: isDragging as bool? ?? that.isDragging,
importResult: importResult == copyWithNull
? that.importResult
: importResult as Collection?,
error: error == copyWithNull ? that.error : error as ExceptionEvent?, error: error == copyWithNull ? that.error : error as ExceptionEvent?,
message: message == copyWithNull ? that.message : message as String?); message: message == copyWithNull ? that.message : message as String?);
} }
@ -128,7 +133,7 @@ extension _$_BlocNpLog on _Bloc {
extension _$_StateToString on _State { extension _$_StateToString on _State {
String _$toString() { String _$toString() {
// ignore: unnecessary_string_interpolations // ignore: unnecessary_string_interpolations
return "_State {collection: $collection, coverUrl: $coverUrl, items: [length: ${items.length}], isLoading: $isLoading, transformedItems: [length: ${transformedItems.length}], selectedItems: $selectedItems, isSelectionRemovable: $isSelectionRemovable, isSelectionManageableFile: $isSelectionManageableFile, isEditMode: $isEditMode, isEditBusy: $isEditBusy, editName: $editName, editItems: ${editItems == null ? null : "[length: ${editItems!.length}]"}, editTransformedItems: ${editTransformedItems == null ? null : "[length: ${editTransformedItems!.length}]"}, editSort: ${editSort == null ? null : "${editSort!.name}"}, isDragging: $isDragging, error: $error, message: $message}"; return "_State {collection: $collection, coverUrl: $coverUrl, items: [length: ${items.length}], isLoading: $isLoading, transformedItems: [length: ${transformedItems.length}], selectedItems: $selectedItems, isSelectionRemovable: $isSelectionRemovable, isSelectionManageableFile: $isSelectionManageableFile, isEditMode: $isEditMode, isEditBusy: $isEditBusy, editName: $editName, editItems: ${editItems == null ? null : "[length: ${editItems!.length}]"}, editTransformedItems: ${editTransformedItems == null ? null : "[length: ${editTransformedItems!.length}]"}, editSort: ${editSort == null ? null : "${editSort!.name}"}, isDragging: $isDragging, importResult: $importResult, error: $error, message: $message}";
} }
} }
@ -153,6 +158,14 @@ extension _$_TransformItemsToString on _TransformItems {
} }
} }
extension _$_ImportPendingSharedCollectionToString
on _ImportPendingSharedCollection {
String _$toString() {
// ignore: unnecessary_string_interpolations
return "_ImportPendingSharedCollection {}";
}
}
extension _$_DownloadToString on _Download { extension _$_DownloadToString on _Download {
String _$toString() { String _$toString() {
// ignore: unnecessary_string_interpolations // ignore: unnecessary_string_interpolations

View file

@ -33,6 +33,12 @@ class _AppBar extends StatelessWidget {
icon: const Icon(Icons.share), icon: const Icon(Icons.share),
tooltip: L10n.global().shareTooltip, tooltip: L10n.global().shareTooltip,
), ),
if (state.collection.isPendingSharedAlbum)
IconButton(
onPressed: () => _onAddToCollectionsViewPressed(context),
icon: const AssetIcon(asset.icAddCollectionsOutlined24),
tooltip: L10n.global().addToCollectionsViewTooltip,
),
]; ];
if (state.items.isNotEmpty || canRename) { if (state.items.isNotEmpty || canRename) {
actions.add(PopupMenuButton<_MenuOption>( actions.add(PopupMenuButton<_MenuOption>(
@ -125,6 +131,10 @@ class _AppBar extends StatelessWidget {
), ),
); );
} }
Future<void> _onAddToCollectionsViewPressed(BuildContext context) async {
context.read<_Bloc>().add(const _ImportPendingSharedCollection());
}
} }
class _AppBarCover extends StatelessWidget { class _AppBarCover extends StatelessWidget {

View file

@ -19,6 +19,7 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
on<_UpdateCollection>(_onUpdateCollection); on<_UpdateCollection>(_onUpdateCollection);
on<_LoadItems>(_onLoad); on<_LoadItems>(_onLoad);
on<_TransformItems>(_onTransformItems); on<_TransformItems>(_onTransformItems);
on<_ImportPendingSharedCollection>(_onImportPendingSharedCollection);
on<_Download>(_onDownload); on<_Download>(_onDownload);
@ -56,6 +57,8 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
add(_UpdateCollection(c.collection)); add(_UpdateCollection(c.collection));
} }
}); });
} else {
_log.info("[_Bloc] Ad hoc collection");
} }
_itemsControllerSubscription = itemsController.stream.listen( _itemsControllerSubscription = itemsController.stream.listen(
(_) {}, (_) {},
@ -121,6 +124,15 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
} }
} }
Future<void> _onImportPendingSharedCollection(
_ImportPendingSharedCollection ev, Emitter<_State> emit) async {
_log.info(ev);
// pending collections are always ad hoc
final newCollection =
await ImportPendingSharedCollection(_c)(account, state.collection);
emit(state.copyWith(importResult: newCollection));
}
void _onBeginEdit(_BeginEdit ev, Emitter<_State> emit) { void _onBeginEdit(_BeginEdit ev, Emitter<_State> emit) {
_log.info("$ev"); _log.info("$ev");
emit(state.copyWith(isEditMode: true)); emit(state.copyWith(isEditMode: true));

View file

@ -19,6 +19,7 @@ class _State {
this.editTransformedItems, this.editTransformedItems,
this.editSort, this.editSort,
required this.isDragging, required this.isDragging,
this.importResult,
this.error, this.error,
this.message, this.message,
}); });
@ -66,6 +67,8 @@ class _State {
final bool isDragging; final bool isDragging;
final Collection? importResult;
final ExceptionEvent? error; final ExceptionEvent? error;
final String? message; final String? message;
} }
@ -104,6 +107,14 @@ class _TransformItems implements _Event {
final List<CollectionItem> items; final List<CollectionItem> items;
} }
@toString
class _ImportPendingSharedCollection implements _Event {
const _ImportPendingSharedCollection();
@override
String toString() => _$toString();
}
@toString @toString
class _Download implements _Event { class _Download implements _Event {
const _Download(); const _Download();

View file

@ -10,6 +10,7 @@ import 'package:nc_photos/bloc/list_sharing.dart';
import 'package:nc_photos/di_container.dart'; import 'package:nc_photos/di_container.dart';
import 'package:nc_photos/entity/album.dart'; import 'package:nc_photos/entity/album.dart';
import 'package:nc_photos/entity/album/data_source.dart'; import 'package:nc_photos/entity/album/data_source.dart';
import 'package:nc_photos/entity/collection/builder.dart';
import 'package:nc_photos/entity/file.dart'; import 'package:nc_photos/entity/file.dart';
import 'package:nc_photos/entity/file/data_source.dart'; import 'package:nc_photos/entity/file/data_source.dart';
import 'package:nc_photos/entity/share.dart'; import 'package:nc_photos/entity/share.dart';
@ -20,7 +21,7 @@ import 'package:nc_photos/or_null.dart';
import 'package:nc_photos/pref.dart'; import 'package:nc_photos/pref.dart';
import 'package:nc_photos/snack_bar_manager.dart'; import 'package:nc_photos/snack_bar_manager.dart';
import 'package:nc_photos/use_case/import_potential_shared_album.dart'; import 'package:nc_photos/use_case/import_potential_shared_album.dart';
import 'package:nc_photos/widget/album_browser_util.dart' as album_browser_util; import 'package:nc_photos/widget/collection_browser.dart';
import 'package:nc_photos/widget/empty_list_indicator.dart'; import 'package:nc_photos/widget/empty_list_indicator.dart';
import 'package:nc_photos/widget/network_thumbnail.dart'; import 'package:nc_photos/widget/network_thumbnail.dart';
import 'package:nc_photos/widget/shared_file_viewer.dart'; import 'package:nc_photos/widget/shared_file_viewer.dart';
@ -252,7 +253,12 @@ class _SharingBrowserState extends State<SharingBrowser> {
} }
void _onAlbumShareItemTap(BuildContext context, ListSharingAlbum share) { void _onAlbumShareItemTap(BuildContext context, ListSharingAlbum share) {
album_browser_util.push(context, widget.account, share.album); Navigator.of(context).pushNamed(
CollectionBrowser.routeName,
arguments: CollectionBrowserArguments(
CollectionBuilder.byAlbum(widget.account, share.album),
),
);
} }
void _transformItems(List<ListSharingItem> items) { void _transformItems(List<ListSharingItem> items) {