@@ -16,101 +16,112 @@ import '../model/narrow.dart';
16
16
import 'dialog.dart' ;
17
17
import 'store.dart' ;
18
18
19
- Future <void > markNarrowAsRead (
19
+ /// Returns true if mark as read process is completed successfully.
20
+ Future <bool > markNarrowAsRead (
20
21
BuildContext context,
21
22
Narrow narrow,
22
23
bool useLegacy, // TODO(server-6)
23
24
) async {
24
- final store = PerAccountStoreWidget .of (context);
25
- final connection = store.connection;
26
- if (useLegacy) {
27
- return await _legacyMarkNarrowAsRead (context, narrow);
28
- }
25
+ try {
26
+ final store = PerAccountStoreWidget .of (context);
27
+ final connection = store.connection;
28
+ if (useLegacy) {
29
+ await _legacyMarkNarrowAsRead (context, narrow);
30
+ return true ;
31
+ }
29
32
30
- // Compare web's `mark_all_as_read` in web/src/unread_ops.js
31
- // and zulip-mobile's `markAsUnreadFromMessage` in src/action-sheets/index.js .
32
- final zulipLocalizations = ZulipLocalizations .of (context);
33
- final scaffoldMessenger = ScaffoldMessenger .of (context);
34
- // Use [AnchorCode.oldest], because [AnchorCode.firstUnread]
35
- // will be the oldest non-muted unread message, which would
36
- // result in muted unreads older than the first unread not
37
- // being processed.
38
- Anchor anchor = AnchorCode .oldest;
39
- int responseCount = 0 ;
40
- int updatedCount = 0 ;
33
+ // Compare web's `mark_all_as_read` in web/src/unread_ops.js
34
+ // and zulip-mobile's `markAsUnreadFromMessage` in src/action-sheets/index.js .
35
+ final zulipLocalizations = ZulipLocalizations .of (context);
36
+ final scaffoldMessenger = ScaffoldMessenger .of (context);
37
+ // Use [AnchorCode.oldest], because [AnchorCode.firstUnread]
38
+ // will be the oldest non-muted unread message, which would
39
+ // result in muted unreads older than the first unread not
40
+ // being processed.
41
+ Anchor anchor = AnchorCode .oldest;
42
+ int responseCount = 0 ;
43
+ int updatedCount = 0 ;
41
44
42
- // Include `is:unread` in the narrow. That has a database index, so
43
- // this can be an important optimization in narrows with a lot of history.
44
- // The server applies the same optimization within the (deprecated)
45
- // specialized endpoints for marking messages as read; see
46
- // `do_mark_stream_messages_as_read` in `zulip:zerver/actions/message_flags.py`.
47
- final apiNarrow = narrow.apiEncode ()..add (ApiNarrowIsUnread ());
45
+ // Include `is:unread` in the narrow. That has a database index, so
46
+ // this can be an important optimization in narrows with a lot of history.
47
+ // The server applies the same optimization within the (deprecated)
48
+ // specialized endpoints for marking messages as read; see
49
+ // `do_mark_stream_messages_as_read` in `zulip:zerver/actions/message_flags.py`.
50
+ final apiNarrow = narrow.apiEncode ()..add (ApiNarrowIsUnread ());
48
51
49
- while (true ) {
50
- final result = await updateMessageFlagsForNarrow (connection,
51
- anchor: anchor,
52
- // [AnchorCode.oldest] is an anchor ID lower than any valid
53
- // message ID; and follow-up requests will have already
54
- // processed the anchor ID, so we just want this to be
55
- // unconditionally false.
56
- includeAnchor: false ,
57
- // There is an upper limit of 5000 messages per batch
58
- // (numBefore + numAfter <= 5000) enforced on the server.
59
- // See `update_message_flags_in_narrow` in zerver/views/message_flags.py .
60
- // zulip-mobile uses `numAfter` of 5000, but web uses 1000
61
- // for more responsive feedback. See zulip@f0d87fcf6.
62
- numBefore: 0 ,
63
- numAfter: 1000 ,
64
- narrow: apiNarrow,
65
- op: UpdateMessageFlagsOp .add,
66
- flag: MessageFlag .read);
67
- if (! context.mounted) {
68
- scaffoldMessenger.clearSnackBars ();
69
- return ;
70
- }
71
- responseCount++ ;
72
- updatedCount += result.updatedCount;
52
+ while (true ) {
53
+ final result = await updateMessageFlagsForNarrow (connection,
54
+ anchor: anchor,
55
+ // [AnchorCode.oldest] is an anchor ID lower than any valid
56
+ // message ID; and follow-up requests will have already
57
+ // processed the anchor ID, so we just want this to be
58
+ // unconditionally false.
59
+ includeAnchor: false ,
60
+ // There is an upper limit of 5000 messages per batch
61
+ // (numBefore + numAfter <= 5000) enforced on the server.
62
+ // See `update_message_flags_in_narrow` in zerver/views/message_flags.py .
63
+ // zulip-mobile uses `numAfter` of 5000, but web uses 1000
64
+ // for more responsive feedback. See zulip@f0d87fcf6.
65
+ numBefore: 0 ,
66
+ numAfter: 1000 ,
67
+ narrow: apiNarrow,
68
+ op: UpdateMessageFlagsOp .add,
69
+ flag: MessageFlag .read);
70
+ if (! context.mounted) {
71
+ scaffoldMessenger.clearSnackBars ();
72
+ return false ;
73
+ }
74
+ responseCount++ ;
75
+ updatedCount += result.updatedCount;
73
76
74
- if (result.foundNewest) {
75
- if (responseCount > 1 ) {
76
- // We previously showed an in-progress [SnackBar], so say we're done.
77
- // There may be a backlog of [SnackBar]s accumulated in the queue
78
- // so be sure to clear them out here.
79
- scaffoldMessenger
80
- ..clearSnackBars ()
81
- ..showSnackBar (SnackBar (behavior: SnackBarBehavior .floating,
82
- content: Text (zulipLocalizations.markAsReadComplete (updatedCount))));
77
+ if (result.foundNewest) {
78
+ if (responseCount > 1 ) {
79
+ // We previously showed an in-progress [SnackBar], so say we're done.
80
+ // There may be a backlog of [SnackBar]s accumulated in the queue
81
+ // so be sure to clear them out here.
82
+ scaffoldMessenger
83
+ ..clearSnackBars ()
84
+ ..showSnackBar (SnackBar (behavior: SnackBarBehavior .floating,
85
+ content: Text (zulipLocalizations.markAsReadComplete (updatedCount))));
86
+ }
87
+ return true ;
83
88
}
84
- return ;
85
- }
86
89
87
- if (result.lastProcessedId == null ) {
88
- // No messages were in the range of the request.
89
- // This should be impossible given that `foundNewest` was false
90
- // (and that our `numAfter` was positive.)
91
- await showErrorDialog (context: context,
92
- title: zulipLocalizations.errorMarkAsReadFailedTitle,
93
- message: zulipLocalizations.errorInvalidResponse);
94
- return ;
95
- }
96
- anchor = NumericAnchor (result.lastProcessedId! );
90
+ if (result.lastProcessedId == null ) {
91
+ // No messages were in the range of the request.
92
+ // This should be impossible given that `foundNewest` was false
93
+ // (and that our `numAfter` was positive.)
94
+ showErrorDialog (context: context,
95
+ title: zulipLocalizations.errorMarkAsReadFailedTitle,
96
+ message: zulipLocalizations.errorInvalidResponse);
97
+ return false ;
98
+ }
99
+ anchor = NumericAnchor (result.lastProcessedId! );
97
100
98
- // The task is taking a while, so tell the user we're working on it.
99
- // No need to say how many messages, as the [MarkAsUnread] widget
100
- // should follow along.
101
- // TODO: Ideally we'd have a progress widget here that showed up based
102
- // on actual time elapsed -- so it could appear before the first
103
- // batch returns, if that takes a while -- and that then stuck
104
- // around continuously until the task ends. For now we use a
105
- // series of [SnackBar]s, which may feel a bit janky.
106
- // There is complexity in tracking the status of each [SnackBar],
107
- // due to having no way to determine which is currently active,
108
- // or if there is an active one at all. Resetting the [SnackBar] here
109
- // results in the same message popping in and out and the user experience
110
- // is better for now if we allow them to run their timer through
111
- // and clear the backlog later.
112
- scaffoldMessenger.showSnackBar (SnackBar (behavior: SnackBarBehavior .floating,
113
- content: Text (zulipLocalizations.markAsReadInProgress)));
101
+ // The task is taking a while, so tell the user we're working on it.
102
+ // No need to say how many messages, as the [MarkAsUnread] widget
103
+ // should follow along.
104
+ // TODO: Ideally we'd have a progress widget here that showed up based
105
+ // on actual time elapsed -- so it could appear before the first
106
+ // batch returns, if that takes a while -- and that then stuck
107
+ // around continuously until the task ends. For now we use a
108
+ // series of [SnackBar]s, which may feel a bit janky.
109
+ // There is complexity in tracking the status of each [SnackBar],
110
+ // due to having no way to determine which is currently active,
111
+ // or if there is an active one at all. Resetting the [SnackBar] here
112
+ // results in the same message popping in and out and the user experience
113
+ // is better for now if we allow them to run their timer through
114
+ // and clear the backlog later.
115
+ scaffoldMessenger.showSnackBar (SnackBar (behavior: SnackBarBehavior .floating,
116
+ content: Text (zulipLocalizations.markAsReadInProgress)));
117
+ }
118
+ } catch (e) {
119
+ if (! context.mounted) return false ;
120
+ final zulipLocalizations = ZulipLocalizations .of (context);
121
+ showErrorDialog (context: context,
122
+ title: zulipLocalizations.errorMarkAsReadFailedTitle,
123
+ message: e.toString ()); // TODO(#741): extract user-facing message better
124
+ return false ;
114
125
}
115
126
}
116
127
0 commit comments