@@ -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 isLoading = 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 (() => isLoading = 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
467
await 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 (() => isLoading = 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,15 +481,14 @@ 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
- child: AnimatedOpacity (
479
- opacity: areMessagesRead ? 0 : 1 ,
480
- duration: Duration (milliseconds: areMessagesRead ? 2000 : 300 ),
481
- curve: Curves .easeOut,
489
+ child: MarkAsReadAnimation (
490
+ visible: ! areMessagesRead,
491
+ loading: isLoading,
482
492
child: SizedBox (width: double .infinity,
483
493
// Design referenced from:
484
494
// https://www.figma.com/file/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?type=design&node-id=132-9684&mode=design&t=jJwHzloKJ0TMOG4M-0
@@ -503,10 +513,9 @@ class MarkAsReadWidget extends StatelessWidget {
503
513
.merge (weightVariableTextStyle (context, wght: 400 ))),
504
514
shape: RoundedRectangleBorder (borderRadius: BorderRadius .circular (7 )),
505
515
),
506
- onPressed: () => _handlePress (context),
516
+ onPressed: () => isLoading ? null : _handlePress (context),
507
517
icon: const Icon (Icons .playlist_add_check),
508
- label: Text (zulipLocalizations.markAllAsReadLabel))))),
509
- );
518
+ label: Text (zulipLocalizations.markAllAsReadLabel))))));
510
519
}
511
520
}
512
521
@@ -643,6 +652,36 @@ class _UnreadMarker extends StatelessWidget {
643
652
}
644
653
}
645
654
655
+ class MarkAsReadAnimation extends StatelessWidget {
656
+ const MarkAsReadAnimation ({
657
+ super .key,
658
+ required this .visible,
659
+ required this .loading,
660
+ required this .child});
661
+
662
+ final bool visible;
663
+ final bool loading;
664
+ final Widget child;
665
+
666
+ @override
667
+ Widget build (BuildContext context) {
668
+ final (opacity, scale) = loading
669
+ ? (0.5 , 0.95 )
670
+ : visible ? (1.0 , 1.0 ) : (0.0 , 0.0 );
671
+ const duration = Duration (milliseconds: 500 );
672
+ const curve = Curves .easeOut;
673
+ return AnimatedScale (
674
+ scale: scale,
675
+ duration: duration,
676
+ curve: curve,
677
+ child: AnimatedOpacity (
678
+ opacity: opacity,
679
+ duration: duration,
680
+ curve: curve,
681
+ child: child));
682
+ }
683
+ }
684
+
646
685
class StreamMessageRecipientHeader extends StatelessWidget {
647
686
const StreamMessageRecipientHeader ({
648
687
super .key,
0 commit comments