@@ -29,30 +29,77 @@ Future<void> markNarrowAsRead(BuildContext context, Narrow narrow) async {
29
29
// Compare web's `mark_all_as_read` in web/src/unread_ops.js
30
30
// and zulip-mobile's `markAsUnreadFromMessage` in src/action-sheets/index.js .
31
31
final zulipLocalizations = ZulipLocalizations .of (context);
32
+ final didPass = await updateMessageFlagsStartingFromAnchor (
33
+ context: context,
34
+ // Include `is:unread` in the narrow. That has a database index, so
35
+ // this can be an important optimization in narrows with a lot of history.
36
+ // The server applies the same optimization within the (deprecated)
37
+ // specialized endpoints for marking messages as read; see
38
+ // `do_mark_stream_messages_as_read` in `zulip:zerver/actions/message_flags.py`.
39
+ apiNarrow: narrow.apiEncode ()..add (ApiNarrowIsUnread ()),
40
+ // Use [AnchorCode.oldest], because [AnchorCode.firstUnread]
41
+ // will be the oldest non-muted unread message, which would
42
+ // result in muted unreads older than the first unread not
43
+ // being processed.
44
+ startingAnchor: AnchorCode .oldest,
45
+ // [AnchorCode.oldest] is an anchor ID lower than any valid
46
+ // message ID; and follow-up requests will have already
47
+ // processed the anchor ID, so we just want this to be
48
+ // unconditionally false.
49
+ includeAnchor: false ,
50
+ op: UpdateMessageFlagsOp .add,
51
+ flag: MessageFlag .read,
52
+ onCompletedMessage: zulipLocalizations.markAsReadComplete,
53
+ progressMessage: zulipLocalizations.markAsReadInProgress,
54
+ onFailedTitle: zulipLocalizations.errorMarkAsReadFailedTitle);
55
+
56
+ if (! didPass || ! context.mounted) return ;
57
+ if (narrow is CombinedFeedNarrow && ! useLegacy) {
58
+ PerAccountStoreWidget .of (context).unreads.handleAllMessagesReadSuccess ();
59
+ }
60
+ } catch (e) {
61
+ if (! context.mounted) return ;
62
+ final zulipLocalizations = ZulipLocalizations .of (context);
63
+ await showErrorDialog (context: context,
64
+ title: zulipLocalizations.errorMarkAsReadFailedTitle,
65
+ message: e.toString ()); // TODO(#741): extract user-facing message better
66
+ return ;
67
+ }
68
+ }
69
+
70
+ /// Updates message flags by applying given operation `op` using given `flag`
71
+ /// the update happens on given `apiNarrow` starting from given `startingAnchor`
72
+ ///
73
+ /// This also handles interactions with the user as it shows a `Snackbar` with
74
+ /// `progressMessage` while performing the update, shows an error dialog when
75
+ /// update fails with the given title using `onFailedTitle` and shows
76
+ /// a `Snackbar` with computed message using given `onCompletedMessage` .
77
+ ///
78
+ /// Returns true in case the process is completed with no exceptions
79
+ /// otherwise shows an error dialog and returns false.
80
+ Future <bool > updateMessageFlagsStartingFromAnchor ({
81
+ required BuildContext context,
82
+ required List <ApiNarrowElement > apiNarrow,
83
+ required Anchor startingAnchor,
84
+ required bool includeAnchor,
85
+ required UpdateMessageFlagsOp op,
86
+ required MessageFlag flag,
87
+ required String Function (int ) onCompletedMessage,
88
+ required String progressMessage,
89
+ required String onFailedTitle,
90
+ }) async {
91
+ try {
92
+ final store = PerAccountStoreWidget .of (context);
93
+ final connection = store.connection;
32
94
final scaffoldMessenger = ScaffoldMessenger .of (context);
33
- // Use [AnchorCode.oldest], because [AnchorCode.firstUnread]
34
- // will be the oldest non-muted unread message, which would
35
- // result in muted unreads older than the first unread not
36
- // being processed.
37
- Anchor anchor = AnchorCode .oldest;
95
+ Anchor anchor = startingAnchor;
38
96
int responseCount = 0 ;
39
97
int updatedCount = 0 ;
40
98
41
- // Include `is:unread` in the narrow. That has a database index, so
42
- // this can be an important optimization in narrows with a lot of history.
43
- // The server applies the same optimization within the (deprecated)
44
- // specialized endpoints for marking messages as read; see
45
- // `do_mark_stream_messages_as_read` in `zulip:zerver/actions/message_flags.py`.
46
- final apiNarrow = narrow.apiEncode ()..add (ApiNarrowIsUnread ());
47
-
48
99
while (true ) {
49
100
final result = await updateMessageFlagsForNarrow (connection,
50
101
anchor: anchor,
51
- // [AnchorCode.oldest] is an anchor ID lower than any valid
52
- // message ID; and follow-up requests will have already
53
- // processed the anchor ID, so we just want this to be
54
- // unconditionally false.
55
- includeAnchor: false ,
102
+ includeAnchor: includeAnchor,
56
103
// There is an upper limit of 5000 messages per batch
57
104
// (numBefore + numAfter <= 5000) enforced on the server.
58
105
// See `update_message_flags_in_narrow` in zerver/views/message_flags.py .
@@ -61,11 +108,11 @@ Future<void> markNarrowAsRead(BuildContext context, Narrow narrow) async {
61
108
numBefore: 0 ,
62
109
numAfter: 1000 ,
63
110
narrow: apiNarrow,
64
- op: UpdateMessageFlagsOp .add ,
65
- flag: MessageFlag .read );
111
+ op: op ,
112
+ flag: flag );
66
113
if (! context.mounted) {
67
114
scaffoldMessenger.clearSnackBars ();
68
- return ;
115
+ return false ;
69
116
}
70
117
responseCount++ ;
71
118
updatedCount += result.updatedCount;
@@ -78,25 +125,24 @@ Future<void> markNarrowAsRead(BuildContext context, Narrow narrow) async {
78
125
scaffoldMessenger
79
126
..clearSnackBars ()
80
127
..showSnackBar (SnackBar (behavior: SnackBarBehavior .floating,
81
- content: Text (zulipLocalizations. markAsReadComplete (updatedCount))));
128
+ content: Text (onCompletedMessage (updatedCount))));
82
129
}
83
- break ;
130
+ return true ;
84
131
}
85
132
86
133
if (result.lastProcessedId == null ) {
134
+ final zulipLocalizations = ZulipLocalizations .of (context);
87
135
// No messages were in the range of the request.
88
136
// This should be impossible given that `foundNewest` was false
89
137
// (and that our `numAfter` was positive.)
90
138
showErrorDialog (context: context,
91
- title: zulipLocalizations.errorMarkAsReadFailedTitle ,
139
+ title: onFailedTitle ,
92
140
message: zulipLocalizations.errorInvalidResponse);
93
- return ;
141
+ return false ;
94
142
}
95
143
anchor = NumericAnchor (result.lastProcessedId! );
96
144
97
145
// The task is taking a while, so tell the user we're working on it.
98
- // No need to say how many messages, as the [MarkAsUnread] widget
99
- // should follow along.
100
146
// TODO: Ideally we'd have a progress widget here that showed up based
101
147
// on actual time elapsed -- so it could appear before the first
102
148
// batch returns, if that takes a while -- and that then stuck
@@ -109,19 +155,14 @@ Future<void> markNarrowAsRead(BuildContext context, Narrow narrow) async {
109
155
// is better for now if we allow them to run their timer through
110
156
// and clear the backlog later.
111
157
scaffoldMessenger.showSnackBar (SnackBar (behavior: SnackBarBehavior .floating,
112
- content: Text (zulipLocalizations.markAsReadInProgress)));
113
- }
114
- if (! context.mounted) return ;
115
- if (narrow is CombinedFeedNarrow && ! useLegacy) {
116
- PerAccountStoreWidget .of (context).unreads.handleAllMessagesReadSuccess ();
158
+ content: Text (progressMessage)));
117
159
}
118
160
} catch (e) {
119
- if (! context.mounted) return ;
120
- final zulipLocalizations = ZulipLocalizations .of (context);
161
+ if (! context.mounted) return false ;
121
162
showErrorDialog (context: context,
122
- title: zulipLocalizations.errorMarkAsReadFailedTitle ,
163
+ title: onFailedTitle ,
123
164
message: e.toString ()); // TODO(#741): extract user-facing message better
124
- return ;
165
+ return false ;
125
166
}
126
167
}
127
168
0 commit comments