mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-01-22 16:56:19 +01:00
Regression: import album shared with you
This commit is contained in:
parent
f20900f2a9
commit
fb02f3c9b0
22 changed files with 160 additions and 3 deletions
BIN
app/assets/2.0x/ic_add_collections_outlined_24dp.png
Normal file
BIN
app/assets/2.0x/ic_add_collections_outlined_24dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 309 B |
BIN
app/assets/3.0x/ic_add_collections_outlined_24dp.png
Normal file
BIN
app/assets/3.0x/ic_add_collections_outlined_24dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 362 B |
BIN
app/assets/ic_add_collections_outlined_24dp.png
Normal file
BIN
app/assets/ic_add_collections_outlined_24dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 286 B |
2
app/lib/asset.dart
Normal file
2
app/lib/asset.dart
Normal file
|
@ -0,0 +1,2 @@
|
|||
const icAddCollectionsOutlined24 =
|
||||
"assets/ic_add_collections_outlined_24dp.png";
|
|
@ -61,6 +61,9 @@ class Collection with EquatableMixin {
|
|||
/// See [CollectionContentProvider.isDynamicCollection]
|
||||
bool get isDynamicCollection => contentProvider.isDynamicCollection;
|
||||
|
||||
/// See [CollectionContentProvider.isPendingSharedAlbum]
|
||||
bool get isPendingSharedAlbum => contentProvider.isPendingSharedAlbum;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
name,
|
||||
|
@ -136,4 +139,10 @@ abstract class CollectionContentProvider with EquatableMixin {
|
|||
/// A collection is defined as a dynamic one when the items are not specified
|
||||
/// explicitly by the user, but rather derived from some conditions
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -86,6 +86,9 @@ abstract class CollectionAdapter {
|
|||
required ValueChanged<Collection> onCollectionUpdated,
|
||||
});
|
||||
|
||||
/// Import a pending shared collection and return the resulting collection
|
||||
Future<Collection> importPendingShared();
|
||||
|
||||
/// Convert a [NewCollectionItem] to an adapted one
|
||||
Future<CollectionItem> adaptToNewItem(NewCollectionItem original);
|
||||
|
||||
|
|
|
@ -75,4 +75,9 @@ mixin CollectionAdapterUnshareableTag implements CollectionAdapter {
|
|||
}) {
|
||||
throw UnsupportedError("Operation not supported");
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Collection> importPendingShared() {
|
||||
throw UnsupportedError("Operation not supported");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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/share_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/unimport_shared_album.dart';
|
||||
import 'package:nc_photos/use_case/update_album_with_actual_items.dart';
|
||||
|
@ -224,6 +225,13 @@ class CollectionAlbumAdapter implements CollectionAdapter {
|
|||
: CollectionShareResult.ok;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Collection> importPendingShared() async {
|
||||
final newAlbum =
|
||||
await ImportPendingSharedAlbum(_c)(account, _provider.album);
|
||||
return CollectionBuilder.byAlbum(account, newAlbum);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<CollectionItem> adaptToNewItem(NewCollectionItem original) async {
|
||||
if (original is NewCollectionFileItem) {
|
||||
|
|
|
@ -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/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';
|
||||
|
||||
part 'album.g.dart';
|
||||
|
@ -102,6 +103,12 @@ class CollectionAlbumProvider
|
|||
@override
|
||||
bool get isDynamicCollection => album.provider is! AlbumStaticProvider;
|
||||
|
||||
@override
|
||||
bool get isPendingSharedAlbum =>
|
||||
album.albumFile?.path.startsWith(
|
||||
remote_storage_util.getRemotePendingSharedAlbumsDir(account)) ==
|
||||
true;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [account, album];
|
||||
|
||||
|
|
|
@ -53,6 +53,9 @@ class CollectionLocationGroupProvider
|
|||
@override
|
||||
bool get isDynamicCollection => true;
|
||||
|
||||
@override
|
||||
bool get isPendingSharedAlbum => false;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [account, location];
|
||||
|
||||
|
|
|
@ -61,6 +61,9 @@ class CollectionMemoryProvider
|
|||
@override
|
||||
bool get isDynamicCollection => true;
|
||||
|
||||
@override
|
||||
bool get isPendingSharedAlbum => false;
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
|
|
|
@ -76,6 +76,9 @@ class CollectionNcAlbumProvider
|
|||
@override
|
||||
bool get isDynamicCollection => false;
|
||||
|
||||
@override
|
||||
bool get isPendingSharedAlbum => false;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [account, album];
|
||||
|
||||
|
|
|
@ -51,6 +51,9 @@ class CollectionPersonProvider
|
|||
@override
|
||||
bool get isDynamicCollection => true;
|
||||
|
||||
@override
|
||||
bool get isPendingSharedAlbum => false;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [account, person];
|
||||
|
||||
|
|
|
@ -46,6 +46,9 @@ class CollectionTagProvider
|
|||
@override
|
||||
bool get isDynamicCollection => true;
|
||||
|
||||
@override
|
||||
bool get isPendingSharedAlbum => false;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [account, tags];
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
23
app/lib/widget/asset_icon.dart
Normal file
23
app/lib/widget/asset_icon.dart
Normal 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;
|
||||
}
|
|
@ -13,6 +13,7 @@ import 'package:logging/logging.dart';
|
|||
import 'package:nc_photos/account.dart';
|
||||
import 'package:nc_photos/api/api_util.dart' as api_util;
|
||||
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/cache_manager_util.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/snack_bar_manager.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/remove.dart';
|
||||
import 'package:nc_photos/widget/asset_icon.dart';
|
||||
import 'package:nc_photos/widget/collection_picker.dart';
|
||||
import 'package:nc_photos/widget/draggable_item_list.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>(
|
||||
listenWhen: (previous, current) =>
|
||||
previous.error != current.error,
|
||||
|
|
|
@ -29,6 +29,7 @@ abstract class $_StateCopyWithWorker {
|
|||
List<_Item>? editTransformedItems,
|
||||
CollectionItemSort? editSort,
|
||||
bool? isDragging,
|
||||
Collection? importResult,
|
||||
ExceptionEvent? error,
|
||||
String? message});
|
||||
}
|
||||
|
@ -53,6 +54,7 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
|
|||
dynamic editTransformedItems = copyWithNull,
|
||||
dynamic editSort = copyWithNull,
|
||||
dynamic isDragging,
|
||||
dynamic importResult = copyWithNull,
|
||||
dynamic error = copyWithNull,
|
||||
dynamic message = copyWithNull}) {
|
||||
return _State(
|
||||
|
@ -82,6 +84,9 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
|
|||
? that.editSort
|
||||
: editSort as CollectionItemSort?,
|
||||
isDragging: isDragging as bool? ?? that.isDragging,
|
||||
importResult: importResult == copyWithNull
|
||||
? that.importResult
|
||||
: importResult as Collection?,
|
||||
error: error == copyWithNull ? that.error : error as ExceptionEvent?,
|
||||
message: message == copyWithNull ? that.message : message as String?);
|
||||
}
|
||||
|
@ -128,7 +133,7 @@ extension _$_BlocNpLog on _Bloc {
|
|||
extension _$_StateToString on _State {
|
||||
String _$toString() {
|
||||
// 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 {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
|
|
|
@ -33,6 +33,12 @@ class _AppBar extends StatelessWidget {
|
|||
icon: const Icon(Icons.share),
|
||||
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) {
|
||||
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 {
|
||||
|
|
|
@ -19,6 +19,7 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
|
|||
on<_UpdateCollection>(_onUpdateCollection);
|
||||
on<_LoadItems>(_onLoad);
|
||||
on<_TransformItems>(_onTransformItems);
|
||||
on<_ImportPendingSharedCollection>(_onImportPendingSharedCollection);
|
||||
|
||||
on<_Download>(_onDownload);
|
||||
|
||||
|
@ -56,6 +57,8 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
|
|||
add(_UpdateCollection(c.collection));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
_log.info("[_Bloc] Ad hoc collection");
|
||||
}
|
||||
_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) {
|
||||
_log.info("$ev");
|
||||
emit(state.copyWith(isEditMode: true));
|
||||
|
|
|
@ -19,6 +19,7 @@ class _State {
|
|||
this.editTransformedItems,
|
||||
this.editSort,
|
||||
required this.isDragging,
|
||||
this.importResult,
|
||||
this.error,
|
||||
this.message,
|
||||
});
|
||||
|
@ -66,6 +67,8 @@ class _State {
|
|||
|
||||
final bool isDragging;
|
||||
|
||||
final Collection? importResult;
|
||||
|
||||
final ExceptionEvent? error;
|
||||
final String? message;
|
||||
}
|
||||
|
@ -104,6 +107,14 @@ class _TransformItems implements _Event {
|
|||
final List<CollectionItem> items;
|
||||
}
|
||||
|
||||
@toString
|
||||
class _ImportPendingSharedCollection implements _Event {
|
||||
const _ImportPendingSharedCollection();
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
}
|
||||
|
||||
@toString
|
||||
class _Download implements _Event {
|
||||
const _Download();
|
||||
|
|
|
@ -10,6 +10,7 @@ import 'package:nc_photos/bloc/list_sharing.dart';
|
|||
import 'package:nc_photos/di_container.dart';
|
||||
import 'package:nc_photos/entity/album.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/data_source.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/snack_bar_manager.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/network_thumbnail.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) {
|
||||
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) {
|
||||
|
|
Loading…
Reference in a new issue