@@ -459,30 +459,41 @@ class ScrollToBottomButton extends StatelessWidget {
459
459
}
460
460
}
461
461
462
- class MarkAsReadWidget extends StatelessWidget {
462
+ class MarkAsReadWidget extends StatefulWidget {
463
463
const MarkAsReadWidget ({super .key, required this .narrow});
464
464
465
465
final Narrow narrow;
466
466
467
+ @override
468
+ State <MarkAsReadWidget > createState () => MarkAsReadWidgetState ();
469
+ }
470
+
471
+ class MarkAsReadWidgetState extends State <MarkAsReadWidget > {
472
+ @visibleForTesting
473
+ bool loading = false ;
474
+
467
475
void _handlePress (BuildContext context) async {
468
476
if (! context.mounted) return ;
469
477
470
478
final store = PerAccountStoreWidget .of (context);
471
479
final connection = store.connection;
472
480
final useLegacy = connection.zulipFeatureLevel! < 155 ;
481
+ setState (() => loading = true );
473
482
474
483
try {
475
- await markNarrowAsRead (context, narrow, useLegacy);
484
+ await markNarrowAsRead (context, widget. narrow, useLegacy);
476
485
} catch (e) {
477
486
if (! context.mounted) return ;
478
487
final zulipLocalizations = ZulipLocalizations .of (context);
479
- await showErrorDialog (context: context,
488
+ showErrorDialog (context: context,
480
489
title: zulipLocalizations.errorMarkAsReadFailedTitle,
481
490
message: e.toString ()); // TODO(#741): extract user-facing message better
482
491
return ;
492
+ } finally {
493
+ setState (() => loading = false );
483
494
}
484
495
if (! context.mounted) return ;
485
- if (narrow is CombinedFeedNarrow && ! useLegacy) {
496
+ if (widget. narrow is CombinedFeedNarrow && ! useLegacy) {
486
497
PerAccountStoreWidget .of (context).unreads.handleAllMessagesReadSuccess ();
487
498
}
488
499
}
@@ -491,13 +502,13 @@ class MarkAsReadWidget extends StatelessWidget {
491
502
Widget build (BuildContext context) {
492
503
final zulipLocalizations = ZulipLocalizations .of (context);
493
504
final store = PerAccountStoreWidget .of (context);
494
- final unreadCount = store.unreads.countInNarrow (narrow);
505
+ final unreadCount = store.unreads.countInNarrow (widget. narrow);
495
506
final areMessagesRead = unreadCount == 0 ;
496
507
497
508
return IgnorePointer (
498
509
ignoring: areMessagesRead,
499
510
child: AnimatedOpacity (
500
- opacity: areMessagesRead ? 0 : 1 ,
511
+ opacity: areMessagesRead ? 0 : loading ? 0.5 : 1 ,
501
512
duration: Duration (milliseconds: areMessagesRead ? 2000 : 300 ),
502
513
curve: Curves .easeOut,
503
514
child: SizedBox (width: double .infinity,
@@ -508,8 +519,6 @@ class MarkAsReadWidget extends StatelessWidget {
508
519
padding: const EdgeInsets .symmetric (horizontal: 10 , vertical: 10 - ((48 - 38 ) / 2 )),
509
520
child: FilledButton .icon (
510
521
style: FilledButton .styleFrom (
511
- // TODO(#95) need dark-theme colors (foreground and background)
512
- backgroundColor: _UnreadMarker .color,
513
522
minimumSize: const Size .fromHeight (38 ),
514
523
textStyle:
515
524
// Restate [FilledButton]'s default, which inherits from
@@ -523,8 +532,13 @@ class MarkAsReadWidget extends StatelessWidget {
523
532
height: (23 / 18 ))
524
533
.merge (weightVariableTextStyle (context, wght: 400 ))),
525
534
shape: RoundedRectangleBorder (borderRadius: BorderRadius .circular (7 )),
535
+ ).copyWith (
536
+ // clobber `FilledButton`'s default disabled state
537
+ // TODO(#95) need dark-theme colors (foreground and background)
538
+ foregroundColor: WidgetStateColor .resolveWith ((_) => Colors .white),
539
+ backgroundColor: WidgetStateColor .resolveWith ((_) => _UnreadMarker .color),
526
540
),
527
- onPressed: () => _handlePress (context),
541
+ onPressed: loading ? null : () => _handlePress (context),
528
542
icon: const Icon (Icons .playlist_add_check),
529
543
label: Text (zulipLocalizations.markAllAsReadLabel))))));
530
544
}
0 commit comments