mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-02-25 02:48:54 +01:00
List pending shard albums
This commit is contained in:
parent
6d2bfb2831
commit
c0f65745f7
7 changed files with 505 additions and 10 deletions
183
lib/bloc/list_pending_shared_album.dart
Normal file
183
lib/bloc/list_pending_shared_album.dart
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:nc_photos/account.dart';
|
||||||
|
import 'package:nc_photos/entity/album.dart';
|
||||||
|
import 'package:nc_photos/entity/file.dart';
|
||||||
|
import 'package:nc_photos/entity/file/data_source.dart';
|
||||||
|
import 'package:nc_photos/event/event.dart';
|
||||||
|
import 'package:nc_photos/remote_storage_util.dart' as remote_storage_util;
|
||||||
|
import 'package:nc_photos/use_case/list_pending_shared_album.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
class ListPendingSharedAlbumBlocItem {
|
||||||
|
ListPendingSharedAlbumBlocItem(this.album);
|
||||||
|
|
||||||
|
final Album album;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class ListPendingSharedAlbumBlocEvent {
|
||||||
|
const ListPendingSharedAlbumBlocEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
class ListPendingSharedAlbumBlocQuery
|
||||||
|
extends ListPendingSharedAlbumBlocEvent {
|
||||||
|
const ListPendingSharedAlbumBlocQuery(
|
||||||
|
this.account,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
toString() {
|
||||||
|
return "$runtimeType {"
|
||||||
|
"account: $account, "
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
final Account account;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An external event has happened and may affect the state of this bloc
|
||||||
|
class _ListPendingSharedAlbumBlocExternalEvent
|
||||||
|
extends ListPendingSharedAlbumBlocEvent {
|
||||||
|
const _ListPendingSharedAlbumBlocExternalEvent();
|
||||||
|
|
||||||
|
@override
|
||||||
|
toString() {
|
||||||
|
return "$runtimeType {"
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class ListPendingSharedAlbumBlocState {
|
||||||
|
const ListPendingSharedAlbumBlocState(this.items);
|
||||||
|
|
||||||
|
@override
|
||||||
|
toString() {
|
||||||
|
return "$runtimeType {"
|
||||||
|
"items: List {length: ${items.length}}, "
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<ListPendingSharedAlbumBlocItem> items;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ListPendingSharedAlbumBlocInit
|
||||||
|
extends ListPendingSharedAlbumBlocState {
|
||||||
|
ListPendingSharedAlbumBlocInit() : super(const []);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ListPendingSharedAlbumBlocLoading
|
||||||
|
extends ListPendingSharedAlbumBlocState {
|
||||||
|
const ListPendingSharedAlbumBlocLoading(
|
||||||
|
List<ListPendingSharedAlbumBlocItem> items)
|
||||||
|
: super(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ListPendingSharedAlbumBlocSuccess
|
||||||
|
extends ListPendingSharedAlbumBlocState {
|
||||||
|
const ListPendingSharedAlbumBlocSuccess(
|
||||||
|
List<ListPendingSharedAlbumBlocItem> items)
|
||||||
|
: super(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ListPendingSharedAlbumBlocFailure
|
||||||
|
extends ListPendingSharedAlbumBlocState {
|
||||||
|
const ListPendingSharedAlbumBlocFailure(
|
||||||
|
List<ListPendingSharedAlbumBlocItem> items, this.exception)
|
||||||
|
: super(items);
|
||||||
|
|
||||||
|
@override
|
||||||
|
toString() {
|
||||||
|
return "$runtimeType {"
|
||||||
|
"super: ${super.toString()}, "
|
||||||
|
"exception: $exception, "
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
final dynamic exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The state of this bloc is inconsistent. This typically means that the data
|
||||||
|
/// may have been changed externally
|
||||||
|
class ListPendingSharedAlbumBlocInconsistent
|
||||||
|
extends ListPendingSharedAlbumBlocState {
|
||||||
|
const ListPendingSharedAlbumBlocInconsistent(
|
||||||
|
List<ListPendingSharedAlbumBlocItem> items)
|
||||||
|
: super(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a list of importable shared albums in the pending dir
|
||||||
|
class ListPendingSharedAlbumBloc extends Bloc<
|
||||||
|
ListPendingSharedAlbumBlocEvent, ListPendingSharedAlbumBlocState> {
|
||||||
|
ListPendingSharedAlbumBloc() : super(ListPendingSharedAlbumBlocInit()) {
|
||||||
|
_fileMovedEventListener.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
mapEventToState(ListPendingSharedAlbumBlocEvent event) async* {
|
||||||
|
_log.info("[mapEventToState] $event");
|
||||||
|
if (event is ListPendingSharedAlbumBlocQuery) {
|
||||||
|
yield* _onEventQuery(event);
|
||||||
|
} else if (event is _ListPendingSharedAlbumBlocExternalEvent) {
|
||||||
|
yield* _onExternalEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
close() {
|
||||||
|
_fileMovedEventListener.end();
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<ListPendingSharedAlbumBlocState> _onEventQuery(
|
||||||
|
ListPendingSharedAlbumBlocQuery ev) async* {
|
||||||
|
yield ListPendingSharedAlbumBlocLoading([]);
|
||||||
|
try {
|
||||||
|
final fileRepo = FileRepo(FileCachedDataSource());
|
||||||
|
final albumRepo = AlbumRepo(AlbumCachedDataSource());
|
||||||
|
final albums = <Album>[];
|
||||||
|
final errors = <dynamic>[];
|
||||||
|
await for (final result
|
||||||
|
in ListPendingSharedAlbum(fileRepo, albumRepo)(ev.account)) {
|
||||||
|
if (result is Tuple2) {
|
||||||
|
_log.severe("[_onEventQuery] Exception while ListPendingSharedAlbum",
|
||||||
|
result.item1, result.item2);
|
||||||
|
errors.add(result.item1);
|
||||||
|
} else if (result is Album) {
|
||||||
|
albums.add(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final items =
|
||||||
|
albums.map((e) => ListPendingSharedAlbumBlocItem(e)).toList();
|
||||||
|
if (errors.isEmpty) {
|
||||||
|
yield ListPendingSharedAlbumBlocSuccess(items);
|
||||||
|
} else {
|
||||||
|
yield ListPendingSharedAlbumBlocFailure(items, errors.first);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
_log.severe("[_onEventQuery] Exception", e);
|
||||||
|
yield ListPendingSharedAlbumBlocFailure(state.items, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<ListPendingSharedAlbumBlocState> _onExternalEvent(
|
||||||
|
_ListPendingSharedAlbumBlocExternalEvent ev) async* {
|
||||||
|
yield ListPendingSharedAlbumBlocInconsistent(state.items);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onFileMovedEvent(FileMovedEvent ev) {
|
||||||
|
if (state is ListPendingSharedAlbumBlocInit) {
|
||||||
|
// no data in this bloc, ignore
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ev.file.path.startsWith(
|
||||||
|
remote_storage_util.getRemotePendingSharedAlbumsDir(ev.account))) {
|
||||||
|
add(_ListPendingSharedAlbumBlocExternalEvent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _fileMovedEventListener =
|
||||||
|
AppEventListener<FileMovedEvent>(_onFileMovedEvent);
|
||||||
|
|
||||||
|
static final _log =
|
||||||
|
Logger("bloc.list_pending_shared_album.ListPendingSharedAlbumBloc");
|
||||||
|
}
|
59
lib/use_case/list_pending_shared_album.dart
Normal file
59
lib/use_case/list_pending_shared_album.dart
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:nc_photos/account.dart';
|
||||||
|
import 'package:nc_photos/entity/album.dart';
|
||||||
|
import 'package:nc_photos/entity/file.dart';
|
||||||
|
import 'package:nc_photos/exception.dart';
|
||||||
|
import 'package:nc_photos/remote_storage_util.dart' as remote_storage_util;
|
||||||
|
import 'package:nc_photos/use_case/ls.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
class ListPendingSharedAlbum {
|
||||||
|
ListPendingSharedAlbum(this.fileRepo, this.albumRepo);
|
||||||
|
|
||||||
|
/// Return shared albums that are known to us (in pending dir) but not added
|
||||||
|
/// to the user library
|
||||||
|
///
|
||||||
|
/// The returned stream would emit either Album data or a tuple of exception
|
||||||
|
/// and stacktrace
|
||||||
|
Stream<dynamic> call(Account account) async* {
|
||||||
|
List<File> ls;
|
||||||
|
try {
|
||||||
|
ls = await Ls(fileRepo)(
|
||||||
|
account,
|
||||||
|
File(
|
||||||
|
path: remote_storage_util.getRemotePendingSharedAlbumsDir(account),
|
||||||
|
));
|
||||||
|
} catch (e, stacktrace) {
|
||||||
|
if (e is ApiException && e.response.statusCode == 404) {
|
||||||
|
// no albums
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
yield Tuple2(e, stacktrace);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final albumFiles =
|
||||||
|
ls.where((element) => element.isCollection != true).toList();
|
||||||
|
for (final f in albumFiles) {
|
||||||
|
try {
|
||||||
|
yield await albumRepo.get(account, f);
|
||||||
|
} catch (e, stacktrace) {
|
||||||
|
yield Tuple2(e, stacktrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
albumRepo.cleanUp(
|
||||||
|
account,
|
||||||
|
remote_storage_util.getRemotePendingSharedAlbumsDir(account),
|
||||||
|
albumFiles);
|
||||||
|
} catch (e, stacktrace) {
|
||||||
|
// not important, log and ignore
|
||||||
|
_log.shout("[_call] Failed while cleanUp", e, stacktrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final FileRepo fileRepo;
|
||||||
|
final AlbumRepo albumRepo;
|
||||||
|
|
||||||
|
static final _log =
|
||||||
|
Logger("user_case.list_pending_shared_album.ListPendingSharedAlbum");
|
||||||
|
}
|
|
@ -109,7 +109,7 @@ class _AlbumBrowserState extends State<AlbumBrowser>
|
||||||
}
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
get canEdit => _album != null;
|
get canEdit => _album?.albumFile?.isOwned(widget.account.username) == true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
enterEditMode() {
|
enterEditMode() {
|
||||||
|
|
|
@ -110,7 +110,7 @@ class _DynamicAlbumBrowserState extends State<DynamicAlbumBrowser>
|
||||||
}
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
get canEdit => _album != null;
|
get canEdit => _album?.albumFile?.isOwned(widget.account.username) == true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
enterEditMode() {
|
enterEditMode() {
|
||||||
|
@ -261,12 +261,14 @@ class _DynamicAlbumBrowserState extends State<DynamicAlbumBrowser>
|
||||||
context,
|
context,
|
||||||
widget.account,
|
widget.account,
|
||||||
_album!,
|
_album!,
|
||||||
menuItemBuilder: (context) => [
|
menuItemBuilder: canEdit
|
||||||
PopupMenuItem(
|
? (context) => [
|
||||||
value: _menuValueConvertBasic,
|
PopupMenuItem(
|
||||||
child: Text(L10n.of(context).convertBasicAlbumMenuLabel),
|
value: _menuValueConvertBasic,
|
||||||
),
|
child: Text(L10n.of(context).convertBasicAlbumMenuLabel),
|
||||||
],
|
),
|
||||||
|
]
|
||||||
|
: null,
|
||||||
onSelectedMenuItem: (option) {
|
onSelectedMenuItem: (option) {
|
||||||
switch (option) {
|
switch (option) {
|
||||||
case _menuValueConvertBasic:
|
case _menuValueConvertBasic:
|
||||||
|
|
|
@ -15,6 +15,8 @@ import 'package:nc_photos/entity/file/data_source.dart';
|
||||||
import 'package:nc_photos/exception_util.dart' as exception_util;
|
import 'package:nc_photos/exception_util.dart' as exception_util;
|
||||||
import 'package:nc_photos/iterable_extension.dart';
|
import 'package:nc_photos/iterable_extension.dart';
|
||||||
import 'package:nc_photos/k.dart' as k;
|
import 'package:nc_photos/k.dart' as k;
|
||||||
|
import 'package:nc_photos/lab.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/theme.dart';
|
import 'package:nc_photos/theme.dart';
|
||||||
import 'package:nc_photos/use_case/remove.dart';
|
import 'package:nc_photos/use_case/remove.dart';
|
||||||
|
@ -27,6 +29,7 @@ import 'package:nc_photos/widget/dynamic_album_browser.dart';
|
||||||
import 'package:nc_photos/widget/home_app_bar.dart';
|
import 'package:nc_photos/widget/home_app_bar.dart';
|
||||||
import 'package:nc_photos/widget/new_album_dialog.dart';
|
import 'package:nc_photos/widget/new_album_dialog.dart';
|
||||||
import 'package:nc_photos/widget/page_visibility_mixin.dart';
|
import 'package:nc_photos/widget/page_visibility_mixin.dart';
|
||||||
|
import 'package:nc_photos/widget/pending_albums.dart';
|
||||||
import 'package:nc_photos/widget/selection_app_bar.dart';
|
import 'package:nc_photos/widget/selection_app_bar.dart';
|
||||||
import 'package:nc_photos/widget/trashbin_browser.dart';
|
import 'package:nc_photos/widget/trashbin_browser.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
@ -192,7 +195,9 @@ class _HomeAlbumsState extends State<HomeAlbums>
|
||||||
return _buildArchiveItem(context);
|
return _buildArchiveItem(context);
|
||||||
} else if (index == 1) {
|
} else if (index == 1) {
|
||||||
return _buildTrashbinItem(context);
|
return _buildTrashbinItem(context);
|
||||||
} else if (index == 2) {
|
} else if (index == 2 && Lab().enableSharedAlbum) {
|
||||||
|
return _buildShareItem(context);
|
||||||
|
} else if (index == 2 + (Lab().enableSharedAlbum ? 1 : 0)) {
|
||||||
return _buildNewAlbumItem(context);
|
return _buildNewAlbumItem(context);
|
||||||
} else if (index == _extraGridItemCount) {
|
} else if (index == _extraGridItemCount) {
|
||||||
return Container();
|
return Container();
|
||||||
|
@ -239,6 +244,20 @@ class _HomeAlbumsState extends State<HomeAlbums>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildShareItem(BuildContext context) {
|
||||||
|
return _NonAlbumGridItem(
|
||||||
|
icon: Icons.share_outlined,
|
||||||
|
label: "Sharing",
|
||||||
|
isShowIndicator: Pref.inst().hasNewSharedAlbumOr(false),
|
||||||
|
onTap: _isSelectionMode
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
Navigator.of(context).pushNamed(PendingAlbums.routeName,
|
||||||
|
arguments: PendingAlbumsArguments(widget.account));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildNewAlbumItem(BuildContext context) {
|
Widget _buildNewAlbumItem(BuildContext context) {
|
||||||
return _NonAlbumGridItem(
|
return _NonAlbumGridItem(
|
||||||
icon: Icons.add,
|
icon: Icons.add,
|
||||||
|
@ -437,7 +456,7 @@ class _HomeAlbumsState extends State<HomeAlbums>
|
||||||
static final _log = Logger("widget.home_albums._HomeAlbumsState");
|
static final _log = Logger("widget.home_albums._HomeAlbumsState");
|
||||||
static const _menuValueImport = 0;
|
static const _menuValueImport = 0;
|
||||||
|
|
||||||
static const _extraGridItemCount = 3;
|
static final _extraGridItemCount = 3 + (Lab().enableSharedAlbum ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _GridItem {
|
class _GridItem {
|
||||||
|
@ -454,6 +473,7 @@ class _NonAlbumGridItem extends StatelessWidget {
|
||||||
required this.icon,
|
required this.icon,
|
||||||
required this.label,
|
required this.label,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
|
this.isShowIndicator = false,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -486,6 +506,12 @@ class _NonAlbumGridItem extends StatelessWidget {
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(label),
|
child: Text(label),
|
||||||
),
|
),
|
||||||
|
if (isShowIndicator)
|
||||||
|
Icon(
|
||||||
|
Icons.circle,
|
||||||
|
color: Colors.red,
|
||||||
|
size: 8,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -498,4 +524,5 @@ class _NonAlbumGridItem extends StatelessWidget {
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
final String label;
|
final String label;
|
||||||
final VoidCallback? onTap;
|
final VoidCallback? onTap;
|
||||||
|
final bool isShowIndicator;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import 'package:nc_photos/widget/connect.dart';
|
||||||
import 'package:nc_photos/widget/dynamic_album_browser.dart';
|
import 'package:nc_photos/widget/dynamic_album_browser.dart';
|
||||||
import 'package:nc_photos/widget/home.dart';
|
import 'package:nc_photos/widget/home.dart';
|
||||||
import 'package:nc_photos/widget/lab_settings.dart';
|
import 'package:nc_photos/widget/lab_settings.dart';
|
||||||
|
import 'package:nc_photos/widget/pending_albums.dart';
|
||||||
import 'package:nc_photos/widget/root_picker.dart';
|
import 'package:nc_photos/widget/root_picker.dart';
|
||||||
import 'package:nc_photos/widget/settings.dart';
|
import 'package:nc_photos/widget/settings.dart';
|
||||||
import 'package:nc_photos/widget/setup.dart';
|
import 'package:nc_photos/widget/setup.dart';
|
||||||
|
@ -115,6 +116,7 @@ class _MyAppState extends State<MyApp> implements SnackBarHandler {
|
||||||
route ??= _handleAlbumImporterRoute(settings);
|
route ??= _handleAlbumImporterRoute(settings);
|
||||||
route ??= _handleTrashbinBrowserRoute(settings);
|
route ??= _handleTrashbinBrowserRoute(settings);
|
||||||
route ??= _handleTrashbinViewerRoute(settings);
|
route ??= _handleTrashbinViewerRoute(settings);
|
||||||
|
route ??= _handlePendingAlbumsRoute(settings);
|
||||||
return route;
|
return route;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,6 +295,19 @@ class _MyAppState extends State<MyApp> implements SnackBarHandler {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Route<dynamic>? _handlePendingAlbumsRoute(RouteSettings settings) {
|
||||||
|
try {
|
||||||
|
if (settings.name == PendingAlbums.routeName &&
|
||||||
|
settings.arguments != null) {
|
||||||
|
final args = settings.arguments as PendingAlbumsArguments;
|
||||||
|
return PendingAlbums.buildRoute(args);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
_log.severe("[_handlePendingAlbumsRoute] Failed while handling route", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
final _scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
|
final _scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
|
||||||
|
|
||||||
late AppEventListener<ThemeChangedEvent> _themeChangedListener;
|
late AppEventListener<ThemeChangedEvent> _themeChangedListener;
|
||||||
|
|
209
lib/widget/pending_albums.dart
Normal file
209
lib/widget/pending_albums.dart
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:nc_photos/account.dart';
|
||||||
|
import 'package:nc_photos/app_localizations.dart';
|
||||||
|
import 'package:nc_photos/bloc/list_pending_shared_album.dart';
|
||||||
|
import 'package:nc_photos/entity/album.dart';
|
||||||
|
import 'package:nc_photos/entity/file.dart';
|
||||||
|
import 'package:nc_photos/entity/file/data_source.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;
|
||||||
|
import 'package:nc_photos/pref.dart';
|
||||||
|
import 'package:nc_photos/snack_bar_manager.dart';
|
||||||
|
import 'package:nc_photos/theme.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/builder/album_grid_item_builder.dart';
|
||||||
|
import 'package:nc_photos/widget/empty_list_indicator.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
class PendingAlbumsArguments {
|
||||||
|
PendingAlbumsArguments(this.account);
|
||||||
|
|
||||||
|
final Account account;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PendingAlbums extends StatefulWidget {
|
||||||
|
static const routeName = "/pending-albums";
|
||||||
|
|
||||||
|
static Route buildRoute(PendingAlbumsArguments args) =>
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => PendingAlbums.fromArgs(args),
|
||||||
|
);
|
||||||
|
|
||||||
|
PendingAlbums({
|
||||||
|
Key? key,
|
||||||
|
required this.account,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
PendingAlbums.fromArgs(PendingAlbumsArguments args, {Key? key})
|
||||||
|
: this(
|
||||||
|
key: key,
|
||||||
|
account: args.account,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
createState() => _PendingAlbumsState();
|
||||||
|
|
||||||
|
final Account account;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PendingAlbumsState extends State<PendingAlbums> {
|
||||||
|
@override
|
||||||
|
initState() {
|
||||||
|
super.initState();
|
||||||
|
_importPotentialSharedAlbum().then((_) {
|
||||||
|
_bloc.add(ListPendingSharedAlbumBlocQuery(widget.account));
|
||||||
|
});
|
||||||
|
Pref.inst().setNewSharedAlbum(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
build(BuildContext context) {
|
||||||
|
return AppTheme(
|
||||||
|
child: Scaffold(
|
||||||
|
body: BlocListener<ListPendingSharedAlbumBloc,
|
||||||
|
ListPendingSharedAlbumBlocState>(
|
||||||
|
bloc: _bloc,
|
||||||
|
listener: (context, state) => _onStateChange(context, state),
|
||||||
|
child: BlocBuilder<ListPendingSharedAlbumBloc,
|
||||||
|
ListPendingSharedAlbumBlocState>(
|
||||||
|
bloc: _bloc,
|
||||||
|
builder: (context, state) => _buildContent(context, state),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildContent(
|
||||||
|
BuildContext context, ListPendingSharedAlbumBlocState state) {
|
||||||
|
if (state is ListPendingSharedAlbumBlocSuccess && _items.isEmpty) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
AppBar(
|
||||||
|
title: Text("Sharing with you"),
|
||||||
|
elevation: 0,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: EmptyListIndicator(
|
||||||
|
icon: Icons.share_outlined,
|
||||||
|
text: L10n.of(context).listEmptyText,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
Theme(
|
||||||
|
data: Theme.of(context).copyWith(
|
||||||
|
accentColor: AppTheme.getOverscrollIndicatorColor(context),
|
||||||
|
),
|
||||||
|
child: CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
SliverAppBar(
|
||||||
|
title: Text("Sharing with you"),
|
||||||
|
),
|
||||||
|
SliverPadding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
sliver: SliverStaggeredGrid.extentBuilder(
|
||||||
|
maxCrossAxisExtent: 256,
|
||||||
|
mainAxisSpacing: 8,
|
||||||
|
itemCount: _items.length,
|
||||||
|
itemBuilder: _buildItem,
|
||||||
|
staggeredTileBuilder: (_) =>
|
||||||
|
const StaggeredTile.count(1, 1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (!_isReady || state is ListPendingSharedAlbumBlocLoading)
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: const LinearProgressIndicator(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildItem(BuildContext context, int index) {
|
||||||
|
final item = _items[index];
|
||||||
|
return AlbumGridItemBuilder(
|
||||||
|
account: widget.account,
|
||||||
|
album: item.album,
|
||||||
|
onTap: () => _onItemTap(context, item),
|
||||||
|
).build(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onStateChange(
|
||||||
|
BuildContext context, ListPendingSharedAlbumBlocState state) {
|
||||||
|
if (state is ListPendingSharedAlbumBlocSuccess ||
|
||||||
|
state is ListPendingSharedAlbumBlocLoading) {
|
||||||
|
_transformItems(state.items);
|
||||||
|
} else if (state is ListPendingSharedAlbumBlocFailure) {
|
||||||
|
SnackBarManager().showSnackBar(SnackBar(
|
||||||
|
content: Text(exception_util.toUserString(state.exception, context)),
|
||||||
|
duration: k.snackBarDurationNormal,
|
||||||
|
));
|
||||||
|
} else if (state is ListPendingSharedAlbumBlocInconsistent) {
|
||||||
|
_bloc.add(ListPendingSharedAlbumBlocQuery(widget.account));
|
||||||
|
}
|
||||||
|
_isReady = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onItemTap(BuildContext context, _GridItem item) {
|
||||||
|
album_browser_util.open(context, widget.account, item.album);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _transformItems(List<ListPendingSharedAlbumBlocItem> items) {
|
||||||
|
final sortedAlbums = items
|
||||||
|
.map((e) => Tuple2(
|
||||||
|
e.album.provider.latestItemTime ?? e.album.lastUpdated, e.album))
|
||||||
|
.sorted((a, b) {
|
||||||
|
// then sort in descending order
|
||||||
|
final tmp = b.item1.compareTo(a.item1);
|
||||||
|
if (tmp != 0) {
|
||||||
|
return tmp;
|
||||||
|
} else {
|
||||||
|
return a.item2.name.compareTo(b.item2.name);
|
||||||
|
}
|
||||||
|
}).map((e) => e.item2);
|
||||||
|
_items.clear();
|
||||||
|
_items.addAll(sortedAlbums.map((e) => _GridItem(e)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _importPotentialSharedAlbum() async {
|
||||||
|
final fileRepo = FileRepo(FileWebdavDataSource());
|
||||||
|
// don't want the potential albums to be cached at this moment
|
||||||
|
final albumRepo = AlbumRepo(AlbumRemoteDataSource());
|
||||||
|
try {
|
||||||
|
await ImportPotentialSharedAlbum(fileRepo, albumRepo)(widget.account);
|
||||||
|
} catch (e, stacktrace) {
|
||||||
|
_log.shout(
|
||||||
|
"[_importPotentialSharedAlbum] Failed while ImportPotentialSharedAlbum",
|
||||||
|
e,
|
||||||
|
stacktrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final _bloc = ListPendingSharedAlbumBloc();
|
||||||
|
bool _isReady = false;
|
||||||
|
|
||||||
|
var _items = <_GridItem>[];
|
||||||
|
|
||||||
|
static final _log =
|
||||||
|
Logger("widget.pending_albums._PendingAlbumsState");
|
||||||
|
}
|
||||||
|
|
||||||
|
class _GridItem {
|
||||||
|
_GridItem(this.album);
|
||||||
|
|
||||||
|
Album album;
|
||||||
|
}
|
Loading…
Reference in a new issue