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