import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.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_share.dart'; import 'package:nc_photos/bloc/list_sharee.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/entity/share.dart'; import 'package:nc_photos/entity/share/data_source.dart'; import 'package:nc_photos/entity/sharee.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/snack_bar_manager.dart'; import 'package:nc_photos/theme.dart'; import 'package:nc_photos/use_case/share_album_with_user.dart'; import 'package:nc_photos/use_case/unshare_album_with_user.dart'; import 'package:nc_photos/widget/album_share_outlier_browser.dart'; class ShareAlbumDialog extends StatefulWidget { ShareAlbumDialog({ Key? key, required this.account, required this.album, }) : assert(album.albumFile != null), super(key: key); @override createState() => _ShareAlbumDialogState(); final Account account; final Album album; } class _ShareAlbumDialogState extends State { @override initState() { super.initState(); _shareeBloc.add(ListShareeBlocQuery(widget.account)); _shareBloc.add(ListShareBlocQuery(widget.account, widget.album.albumFile!)); } @override build(BuildContext context) { return AppTheme( child: GestureDetector( onTap: () { Navigator.of(context).pop(); }, child: Scaffold( backgroundColor: Colors.transparent, body: BlocListener( bloc: _shareeBloc, listener: (context, shareeState) => _onListShareeBlocStateChanged(context, shareeState), child: BlocBuilder( bloc: _shareeBloc, builder: (_, shareeState) => BlocBuilder( bloc: _shareBloc, builder: (context, shareState) => _buildContent(context, shareeState, shareState), ), ), ), ), ), ); } Widget _buildContent(BuildContext context, ListShareeBlocState shareeState, ListShareBlocState shareState) { final List children; if (shareeState is ListShareeBlocLoading || shareState is ListShareBlocLoading) { children = [ Padding( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 24), child: Row( mainAxisSize: MainAxisSize.min, children: [ const CircularProgressIndicator(), const SizedBox(width: 24), Text(L10n.global().genericProcessingDialogContent), ], ), ), ]; } else { children = shareeState.items .where((element) => element.type == ShareeType.user) .sorted((a, b) => a.label.compareTo(b.label)) .map((sharee) => _buildItem(context, shareState, sharee)) .toList(); } return GestureDetector( onTap: () {}, child: SimpleDialog( title: Text(L10n.global().shareAlbumDialogTitle), children: children, ), ); } Widget _buildItem( BuildContext context, ListShareBlocState shareState, Sharee sharee) { final bool isShared; if (_overrideSharee.containsKey(sharee.shareWith)) { isShared = _overrideSharee[sharee.shareWith]!; } else { isShared = shareState.items .any((element) => element.shareWith == sharee.shareWith); } final isProcessing = _processingSharee.any((element) => element == sharee.shareWith); final Widget trailing; if (isProcessing) { trailing = Padding( padding: const EdgeInsetsDirectional.only(end: 12), child: SizedBox( width: 24, height: 24, child: CircularProgressIndicator( color: AppTheme.getUnfocusedIconColor(context), ), ), ); } else { trailing = Checkbox( value: isShared, onChanged: (value) {}, ); } return SimpleDialogOption( child: ListTile( dense: true, title: Text(sharee.label), // pass through the tap event trailing: IgnorePointer( child: trailing, ), ), onPressed: isProcessing ? () {} : () => _onShareePressed(sharee, isShared), ); } void _onListShareeBlocStateChanged( BuildContext context, ListShareeBlocState state) { if (state is ListShareeBlocFailure) { SnackBarManager().showSnackBar(SnackBar( content: Text(exception_util.toUserString(state.exception)), duration: k.snackBarDurationNormal, )); } } void _onShareePressed(Sharee sharee, bool isShared) async { setState(() { _processingSharee.add(sharee.shareWith); }); if (!isShared) { // create new share await _createShare(sharee); } else { // remove share await _removeShare(sharee); } setState(() { _processingSharee.remove(sharee.shareWith); }); } void _onFixPressed() { Navigator.of(context).pushNamed(AlbumShareOutlierBrowser.routeName, arguments: AlbumShareOutlierBrowserArguments(widget.account, widget.album)); } Future _createShare(Sharee sharee) async { final shareRepo = ShareRepo(ShareRemoteDataSource()); var hasFailure = false; try { await ShareAlbumWithUser(shareRepo)( widget.account, widget.album, sharee.shareWith, onShareFileFailed: (_) { hasFailure = true; }, ); _overrideSharee[sharee.shareWith] = true; SnackBarManager().showSnackBar(SnackBar( content: Text(hasFailure ? L10n.global() .shareAlbumSuccessWithErrorNotification(sharee.shareWith) : L10n.global().shareAlbumSuccessNotification(sharee.shareWith)), action: hasFailure ? SnackBarAction( label: L10n.global().fixButtonLabel, textColor: Theme.of(context).colorScheme.secondaryVariant, onPressed: _onFixPressed, ) : null, duration: k.snackBarDurationNormal, )); } catch (e, stackTrace) { _log.shout( "[_createShare] Failed while ShareAlbumWithUser", e, stackTrace); SnackBarManager().showSnackBar(SnackBar( content: Text(exception_util.toUserString(e)), duration: k.snackBarDurationNormal, )); } } Future _removeShare(Sharee sharee) async { final shareRepo = ShareRepo(ShareRemoteDataSource()); final fileRepo = FileRepo(FileCachedDataSource()); final albumRepo = AlbumRepo(AlbumCachedDataSource()); var hasFailure = false; try { await UnshareAlbumWithUser(shareRepo, fileRepo, albumRepo)( widget.account, widget.album, sharee.shareWith, onUnshareFileFailed: (_) { hasFailure = true; }, ); _overrideSharee[sharee.shareWith] = false; SnackBarManager().showSnackBar(SnackBar( content: Text(hasFailure ? L10n.global() .unshareAlbumSuccessWithErrorNotification(sharee.shareWith) : L10n.global().unshareAlbumSuccessNotification(sharee.shareWith)), action: hasFailure ? SnackBarAction( label: L10n.global().fixButtonLabel, textColor: Theme.of(context).colorScheme.secondaryVariant, onPressed: _onFixPressed, ) : null, duration: k.snackBarDurationNormal, )); } catch (e, stackTrace) { _log.shout( "[_removeShare] Failed while UnshareAlbumWithUser", e, stackTrace); SnackBarManager().showSnackBar(SnackBar( content: Text(exception_util.toUserString(e)), duration: k.snackBarDurationNormal, )); } } final _shareeBloc = ListShareeBloc(); final _shareBloc = ListShareBloc(); final _processingSharee = []; /// Store the modified value of each sharee final _overrideSharee = {}; static final _log = Logger("widget.share_album_dialog._ShareAlbumDialogState"); }