@@ -458,30 +458,40 @@ class ScrollToBottomButton extends StatelessWidget {
458458 }
459459}
460460
461- class MarkAsReadWidget extends StatelessWidget {
461+ class MarkAsReadWidget extends StatefulWidget {
462462 const MarkAsReadWidget ({super .key, required this .narrow});
463463
464464 final Narrow narrow;
465465
466+ @override
467+ State <MarkAsReadWidget > createState () => _MarkAsReadWidgetState ();
468+ }
469+
470+ class _MarkAsReadWidgetState extends State <MarkAsReadWidget > {
471+ bool _loading = false ;
472+
466473 void _handlePress (BuildContext context) async {
467474 if (! context.mounted) return ;
468475
469476 final store = PerAccountStoreWidget .of (context);
470477 final connection = store.connection;
471478 final useLegacy = connection.zulipFeatureLevel! < 155 ;
479+ setState (() => _loading = true );
472480
473481 try {
474- await markNarrowAsRead (context, narrow, useLegacy);
482+ await markNarrowAsRead (context, widget. narrow, useLegacy);
475483 } catch (e) {
476484 if (! context.mounted) return ;
477485 final zulipLocalizations = ZulipLocalizations .of (context);
478- await showErrorDialog (context: context,
486+ showErrorDialog (context: context,
479487 title: zulipLocalizations.errorMarkAsReadFailedTitle,
480488 message: e.toString ()); // TODO(#741): extract user-facing message better
481489 return ;
490+ } finally {
491+ setState (() => _loading = false );
482492 }
483493 if (! context.mounted) return ;
484- if (narrow is CombinedFeedNarrow && ! useLegacy) {
494+ if (widget. narrow is CombinedFeedNarrow && ! useLegacy) {
485495 PerAccountStoreWidget .of (context).unreads.handleAllMessagesReadSuccess ();
486496 }
487497 }
@@ -490,13 +500,13 @@ class MarkAsReadWidget extends StatelessWidget {
490500 Widget build (BuildContext context) {
491501 final zulipLocalizations = ZulipLocalizations .of (context);
492502 final store = PerAccountStoreWidget .of (context);
493- final unreadCount = store.unreads.countInNarrow (narrow);
503+ final unreadCount = store.unreads.countInNarrow (widget. narrow);
494504 final areMessagesRead = unreadCount == 0 ;
495505
496506 return IgnorePointer (
497507 ignoring: areMessagesRead,
498508 child: AnimatedOpacity (
499- opacity: areMessagesRead ? 0 : 1 ,
509+ opacity: areMessagesRead ? 0 : _loading ? 0.5 : 1 ,
500510 duration: Duration (milliseconds: areMessagesRead ? 2000 : 300 ),
501511 curve: Curves .easeOut,
502512 child: SizedBox (width: double .infinity,
@@ -507,8 +517,6 @@ class MarkAsReadWidget extends StatelessWidget {
507517 padding: const EdgeInsets .symmetric (horizontal: 10 , vertical: 10 - ((48 - 38 ) / 2 )),
508518 child: FilledButton .icon (
509519 style: FilledButton .styleFrom (
510- // TODO(#95) need dark-theme colors (foreground and background)
511- backgroundColor: _UnreadMarker .color,
512520 minimumSize: const Size .fromHeight (38 ),
513521 textStyle:
514522 // Restate [FilledButton]'s default, which inherits from
@@ -522,8 +530,15 @@ class MarkAsReadWidget extends StatelessWidget {
522530 height: (23 / 18 ))
523531 .merge (weightVariableTextStyle (context, wght: 400 ))),
524532 shape: RoundedRectangleBorder (borderRadius: BorderRadius .circular (7 )),
533+ ).copyWith (
534+ // Give the buttons a constant color regardless of whether their
535+ // state is disabled, pressed, etc. We handle those states
536+ // separately, via MarkAsReadAnimation.
537+ // TODO(#95) need dark-theme colors (foreground and background)
538+ foregroundColor: WidgetStateColor .resolveWith ((_) => Colors .white),
539+ backgroundColor: WidgetStateColor .resolveWith ((_) => _UnreadMarker .color),
525540 ),
526- onPressed: () => _handlePress (context),
541+ onPressed: _loading ? null : () => _handlePress (context),
527542 icon: const Icon (Icons .playlist_add_check),
528543 label: Text (zulipLocalizations.markAllAsReadLabel))))));
529544 }
0 commit comments