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