diff --git a/app/lib/widget/app_bar_circular_progress_indicator.dart b/app/lib/widget/app_bar_circular_progress_indicator.dart new file mode 100644 index 00000000..0e805533 --- /dev/null +++ b/app/lib/widget/app_bar_circular_progress_indicator.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +class AppBarCircularProgressIndicator extends StatelessWidget { + const AppBarCircularProgressIndicator({super.key}); + + @override + Widget build(BuildContext context) { + return const Center( + child: SizedBox.square( + dimension: 24, + child: CircularProgressIndicator( + strokeWidth: 2, + ), + ), + ); + } +} diff --git a/app/lib/widget/home_albums.dart b/app/lib/widget/home_albums.dart index fd2189da..6b9e59ad 100644 --- a/app/lib/widget/home_albums.dart +++ b/app/lib/widget/home_albums.dart @@ -140,24 +140,17 @@ class _HomeAlbumsState extends State ), Align( alignment: Alignment.bottomCenter, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (state is ListAlbumBlocLoading) - const LinearProgressIndicator(), - SizedBox( - width: double.infinity, - height: _calcBottomAppBarExtent(context), - child: ClipRect( - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 4, sigmaY: 4), - child: const ColoredBox( - color: Colors.transparent, - ), - ), + child: SizedBox( + width: double.infinity, + height: _calcBottomAppBarExtent(context), + child: ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 4, sigmaY: 4), + child: const ColoredBox( + color: Colors.transparent, ), ), - ], + ), ), ), ], @@ -193,28 +186,36 @@ class _HomeAlbumsState extends State } Widget _buildNormalAppBar(BuildContext context) { - return HomeSliverAppBar( - account: widget.account, - menuActions: [ - PopupMenuItem( - value: _menuValueSort, - child: Text(L10n.global().sortTooltip), - ), - PopupMenuItem( - value: _menuValueImport, - child: Text(L10n.global().importFoldersTooltip), - ), - ], - onSelectedMenuActions: (option) { - switch (option) { - case _menuValueSort: - _onSortPressed(context); - break; + return BlocBuilder( + bloc: _bloc, + buildWhen: (previous, current) => + previous is ListAlbumBlocLoading != current is ListAlbumBlocLoading, + builder: (context, state) { + return HomeSliverAppBar( + account: widget.account, + isShowProgressIcon: state is ListAlbumBlocLoading, + menuActions: [ + PopupMenuItem( + value: _menuValueSort, + child: Text(L10n.global().sortTooltip), + ), + PopupMenuItem( + value: _menuValueImport, + child: Text(L10n.global().importFoldersTooltip), + ), + ], + onSelectedMenuActions: (option) { + switch (option) { + case _menuValueSort: + _onSortPressed(context); + break; - case _menuValueImport: - _onImportPressed(context); - break; - } + case _menuValueImport: + _onImportPressed(context); + break; + } + }, + ); }, ); } diff --git a/app/lib/widget/home_app_bar.dart b/app/lib/widget/home_app_bar.dart index 0e04db78..ac3a0f4c 100644 --- a/app/lib/widget/home_app_bar.dart +++ b/app/lib/widget/home_app_bar.dart @@ -9,6 +9,7 @@ import 'package:nc_photos/pref.dart'; import 'package:nc_photos/theme.dart'; import 'package:nc_photos/url_launcher_util.dart'; import 'package:nc_photos/widget/account_picker_dialog.dart'; +import 'package:nc_photos/widget/app_bar_circular_progress_indicator.dart'; import 'package:nc_photos/widget/app_bar_title_container.dart'; import 'package:nc_photos/widget/settings.dart'; @@ -20,6 +21,7 @@ class HomeSliverAppBar extends StatelessWidget { this.actions, this.menuActions, this.onSelectedMenuActions, + this.isShowProgressIcon = false, }) : super(key: key); @override @@ -38,15 +40,17 @@ class HomeSliverAppBar extends StatelessWidget { child: AppBarTitleContainer( title: Text(accountLabel ?? account.address), subtitle: accountLabel == null ? Text(account.username2) : null, - icon: account.scheme == "http" - ? Icon( - Icons.no_encryption_outlined, - color: Theme.of(context).colorScheme.error, - ) - : Icon( - Icons.https, - color: Theme.of(context).colorScheme.primary, - ), + icon: isShowProgressIcon + ? const AppBarCircularProgressIndicator() + : (account.scheme == "http" + ? Icon( + Icons.no_encryption_outlined, + color: Theme.of(context).colorScheme.error, + ) + : Icon( + Icons.https, + color: Theme.of(context).colorScheme.primary, + )), ), ), floating: true, @@ -103,6 +107,7 @@ class HomeSliverAppBar extends StatelessWidget { /// much >= 0 final List>? menuActions; final void Function(int)? onSelectedMenuActions; + final bool isShowProgressIcon; static const _menuValueAbout = -1; static const _menuValueHelp = -2; diff --git a/app/lib/widget/home_photos.dart b/app/lib/widget/home_photos.dart index e7eef270..f2d59241 100644 --- a/app/lib/widget/home_photos.dart +++ b/app/lib/widget/home_photos.dart @@ -205,25 +205,17 @@ class _HomePhotosState extends State ), Align( alignment: Alignment.bottomCenter, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (state is ScanAccountDirBlocLoading || - _buildItemQueue.isProcessing) - const LinearProgressIndicator(), - SizedBox( - width: double.infinity, - height: _calcBottomAppBarExtent(context), - child: ClipRect( - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 4, sigmaY: 4), - child: const ColoredBox( - color: Colors.transparent, - ), - ), + child: SizedBox( + width: double.infinity, + height: _calcBottomAppBarExtent(context), + child: ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 4, sigmaY: 4), + child: const ColoredBox( + color: Colors.transparent, ), ), - ], + ), ), ), ], @@ -287,31 +279,42 @@ class _HomePhotosState extends State } Widget _buildNormalAppBar(BuildContext context) { - return HomeSliverAppBar( - account: widget.account, - actions: [ - ZoomMenuButton( - initialZoom: _thumbZoomLevel, - minZoom: -1, - maxZoom: 2, - onZoomChanged: (value) { - _setThumbZoomLevel(value.round()); - Pref().setHomePhotosZoomLevel(_thumbZoomLevel); + return BlocBuilder( + bloc: _bloc, + buildWhen: (previous, current) => + previous is ScanAccountDirBlocLoading != + current is ScanAccountDirBlocLoading, + builder: (context, state) { + return HomeSliverAppBar( + account: widget.account, + isShowProgressIcon: (state is ScanAccountDirBlocLoading || + _buildItemQueue.isProcessing) && + !_isRefreshIndicatorActive, + actions: [ + ZoomMenuButton( + initialZoom: _thumbZoomLevel, + minZoom: -1, + maxZoom: 2, + onZoomChanged: (value) { + _setThumbZoomLevel(value.round()); + Pref().setHomePhotosZoomLevel(_thumbZoomLevel); + }, + ), + ], + menuActions: [ + PopupMenuItem( + value: _menuValueRefresh, + child: Text(L10n.global().refreshMenuLabel), + ), + ], + onSelectedMenuActions: (option) { + switch (option) { + case _menuValueRefresh: + _onRefreshSelected(); + break; + } }, - ), - ], - menuActions: [ - PopupMenuItem( - value: _menuValueRefresh, - child: Text(L10n.global().refreshMenuLabel), - ), - ], - onSelectedMenuActions: (option) { - switch (option) { - case _menuValueRefresh: - _onRefreshSelected(); - break; - } + ); }, ); } @@ -625,11 +628,25 @@ class _HomePhotosState extends State } Future _waitRefresh() async { - while (true) { - await Future.delayed(const Duration(seconds: 1)); - if (_bloc.state is! ScanAccountDirBlocLoading) { - return; + setState(() { + _isRefreshIndicatorActive = true; + }); + try { + while (true) { + await Future.delayed(const Duration(seconds: 1)); + if (_bloc.state is! ScanAccountDirBlocLoading) { + return; + } } + } finally { + // To prevent the app bar icon appearing for a very short while + unawaited(Future.delayed(const Duration(seconds: 2)).then((_) { + if (mounted) { + setState(() { + _isRefreshIndicatorActive = false; + }); + } + })); } } @@ -745,6 +762,7 @@ class _HomePhotosState extends State late final _Web? _web = platform_k.isWeb ? _Web(this) : null; var _isScrollbarVisible = false; + var _isRefreshIndicatorActive = false; static final _log = Logger("widget.home_photos._HomePhotosState"); static const _menuValueRefresh = 0;