Skip to content

Commit 898d907

Browse files
committed
api [nfc]: Remove backward-compat code for mark-read protocol in FL <155
This code had a switch/case on the Narrow type, so I discovered it while implementing keyword-search narrows. We support Zulip Server 7 and later (see README) and refuse to connect to older servers. Since we haven't been using this protocol for servers FL 155+, this is NFC. Related: #992
1 parent 80dcd47 commit 898d907

File tree

5 files changed

+3
-293
lines changed

5 files changed

+3
-293
lines changed

lib/api/route/messages.dart

Lines changed: 0 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -392,9 +392,6 @@ class UpdateMessageFlagsResult {
392392
}
393393

394394
/// https://zulip.com/api/update-message-flags-for-narrow
395-
///
396-
/// This binding only supports feature levels 155+.
397-
// TODO(server-6) remove FL 155+ mention in doc, and the related `assert`
398395
Future<UpdateMessageFlagsForNarrowResult> updateMessageFlagsForNarrow(ApiConnection connection, {
399396
required Anchor anchor,
400397
bool? includeAnchor,
@@ -404,7 +401,6 @@ Future<UpdateMessageFlagsForNarrowResult> updateMessageFlagsForNarrow(ApiConnect
404401
required UpdateMessageFlagsOp op,
405402
required MessageFlag flag,
406403
}) {
407-
assert(connection.zulipFeatureLevel! >= 155);
408404
return connection.post('updateMessageFlagsForNarrow', UpdateMessageFlagsForNarrowResult.fromJson, 'messages/flags/narrow', {
409405
'anchor': RawParameter(anchor.toJson()),
410406
if (includeAnchor != null) 'include_anchor': includeAnchor,
@@ -439,63 +435,3 @@ class UpdateMessageFlagsForNarrowResult {
439435

440436
Map<String, dynamic> toJson() => _$UpdateMessageFlagsForNarrowResultToJson(this);
441437
}
442-
443-
/// https://zulip.com/api/mark-all-as-read
444-
///
445-
/// This binding is deprecated, in FL 155+ use
446-
/// [updateMessageFlagsForNarrow] instead.
447-
// TODO(server-6): Remove as deprecated by updateMessageFlagsForNarrow
448-
//
449-
// For FL < 153 this call was atomic on the server and would
450-
// not mark any messages as read if it timed out.
451-
// From FL 153 and onward the server started processing
452-
// in batches so progress could still be made in the event
453-
// of a timeout interruption. Thus, in FL 153 this call
454-
// started returning `result: partially_completed` and
455-
// `code: REQUEST_TIMEOUT` for timeouts.
456-
//
457-
// In FL 211 the `partially_completed` variant of
458-
// `result` was removed, the string `code` field also
459-
// removed, and a boolean `complete` field introduced.
460-
//
461-
// For full support of this endpoint we would need three
462-
// variants of the return structure based on feature
463-
// level (`{}`, `{code: string}`, and `{complete: bool}`)
464-
// as well as handling of `partially_completed` variant
465-
// of `result` in `lib/api/core.dart`. For simplicity we
466-
// ignore these return values.
467-
//
468-
// We don't use this method for FL 155+ (it is replaced
469-
// by `updateMessageFlagsForNarrow`) so there are only
470-
// two versions (FL 153 and FL 154) affected.
471-
Future<void> markAllAsRead(ApiConnection connection) {
472-
return connection.post('markAllAsRead', (_) {}, 'mark_all_as_read', {});
473-
}
474-
475-
/// https://zulip.com/api/mark-stream-as-read
476-
///
477-
/// This binding is deprecated, in FL 155+ use
478-
/// [updateMessageFlagsForNarrow] instead.
479-
// TODO(server-6): Remove as deprecated by updateMessageFlagsForNarrow
480-
Future<void> markStreamAsRead(ApiConnection connection, {
481-
required int streamId,
482-
}) {
483-
return connection.post('markStreamAsRead', (_) {}, 'mark_stream_as_read', {
484-
'stream_id': streamId,
485-
});
486-
}
487-
488-
/// https://zulip.com/api/mark-topic-as-read
489-
///
490-
/// This binding is deprecated, in FL 155+ use
491-
/// [updateMessageFlagsForNarrow] instead.
492-
// TODO(server-6): Remove as deprecated by updateMessageFlagsForNarrow
493-
Future<void> markTopicAsRead(ApiConnection connection, {
494-
required int streamId,
495-
required TopicName topicName,
496-
}) {
497-
return connection.post('markTopicAsRead', (_) {}, 'mark_topic_as_read', {
498-
'stream_id': streamId,
499-
'topic_name': RawParameter(topicName.apiName),
500-
});
501-
}

lib/model/unreads.dart

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -441,22 +441,20 @@ class Unreads extends PerAccountStoreBase with ChangeNotifier {
441441
notifyListeners();
442442
}
443443

444-
/// To be called on success of a mark-all-as-read task in the modern protocol.
444+
/// To be called on success of a mark-all-as-read task.
445445
///
446446
/// When the user successfully marks all messages as read,
447447
/// there can't possibly be ancient unreads we don't know about.
448448
/// So this updates [oldUnreadsMissing] to false and calls [notifyListeners].
449449
///
450-
/// When we use POST /messages/flags/narrow (FL 155+) for mark-all-as-read,
451-
/// we don't expect to get a mark-as-read event with `all: true`,
450+
/// We don't expect to get a mark-as-read event with `all: true`,
452451
/// even on completion of the last batch of unreads.
453-
/// If we did get an event with `all: true` (as we do in the legacy mark-all-
452+
/// If we did get an event with `all: true` (as we did in a legacy mark-all-
454453
/// as-read protocol), this would be handled naturally, in
455454
/// [handleUpdateMessageFlagsEvent].
456455
///
457456
/// Discussion:
458457
/// <https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/flutter.3A.20Mark-as-read/near/1680275>
459-
// TODO(server-6) Delete mentions of legacy protocol.
460458
void handleAllMessagesReadSuccess() {
461459
oldUnreadsMissing = false;
462460

lib/widgets/actions.dart

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,7 @@ abstract final class ZulipAction {
2626
/// This is mostly a wrapper around [updateMessageFlagsStartingFromAnchor];
2727
/// for details on the UI feedback, see there.
2828
static Future<void> markNarrowAsRead(BuildContext context, Narrow narrow) async {
29-
final store = PerAccountStoreWidget.of(context);
3029
final zulipLocalizations = ZulipLocalizations.of(context);
31-
final useLegacy = store.zulipFeatureLevel < 155; // TODO(server-6)
32-
if (useLegacy) {
33-
try {
34-
await _legacyMarkNarrowAsRead(context, narrow);
35-
return;
36-
} catch (e) {
37-
if (!context.mounted) return;
38-
final message = switch (e) {
39-
ZulipApiException() => zulipLocalizations.errorServerMessage(e.message),
40-
_ => e.toString(), // TODO(#741): extract user-facing message better
41-
};
42-
showErrorDialog(context: context,
43-
title: zulipLocalizations.errorMarkAsReadFailedTitle,
44-
message: message);
45-
return;
46-
}
47-
}
4830

4931
final didPass = await updateMessageFlagsStartingFromAnchor(
5032
context: context,
@@ -208,39 +190,6 @@ abstract final class ZulipAction {
208190
}
209191
}
210192

211-
static Future<void> _legacyMarkNarrowAsRead(BuildContext context, Narrow narrow) async {
212-
final store = PerAccountStoreWidget.of(context);
213-
final connection = store.connection;
214-
switch (narrow) {
215-
case CombinedFeedNarrow():
216-
await markAllAsRead(connection);
217-
case ChannelNarrow(:final streamId):
218-
await markStreamAsRead(connection, streamId: streamId);
219-
case TopicNarrow(:final streamId, :final topic):
220-
await markTopicAsRead(connection, streamId: streamId, topicName: topic);
221-
case DmNarrow():
222-
final unreadDms = store.unreads.dms[narrow];
223-
// Silently ignore this race-condition as the outcome
224-
// (no unreads in this narrow) was the desired end-state
225-
// of pushing the button.
226-
if (unreadDms == null) return;
227-
await updateMessageFlags(connection,
228-
messages: unreadDms,
229-
op: UpdateMessageFlagsOp.add,
230-
flag: MessageFlag.read);
231-
case MentionsNarrow():
232-
final unreadMentions = store.unreads.mentions.toList();
233-
if (unreadMentions.isEmpty) return;
234-
await updateMessageFlags(connection,
235-
messages: unreadMentions,
236-
op: UpdateMessageFlagsOp.add,
237-
flag: MessageFlag.read);
238-
case StarredMessagesNarrow():
239-
// TODO: Implement unreads handling.
240-
return;
241-
}
242-
}
243-
244193
/// Fetch and return the raw Markdown content for [messageId],
245194
/// showing an error dialog on failure.
246195
static Future<String?> fetchRawContentWithFeedback({

test/api/route/messages_test.dart

Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -829,76 +829,4 @@ void main() {
829829
});
830830
});
831831
});
832-
833-
group('markAllAsRead', () {
834-
Future<void> checkMarkAllAsRead(
835-
FakeApiConnection connection, {
836-
required Map<String, String> expected,
837-
}) async {
838-
connection.prepare(json: {});
839-
await markAllAsRead(connection);
840-
check(connection.lastRequest).isA<http.Request>()
841-
..method.equals('POST')
842-
..url.path.equals('/api/v1/mark_all_as_read')
843-
..bodyFields.deepEquals(expected);
844-
}
845-
846-
test('smoke', () {
847-
return FakeApiConnection.with_((connection) async {
848-
await checkMarkAllAsRead(connection, expected: {});
849-
});
850-
});
851-
});
852-
853-
group('markStreamAsRead', () {
854-
Future<void> checkMarkStreamAsRead(
855-
FakeApiConnection connection, {
856-
required int streamId,
857-
required Map<String, String> expected,
858-
}) async {
859-
connection.prepare(json: {});
860-
await markStreamAsRead(connection, streamId: streamId);
861-
check(connection.lastRequest).isA<http.Request>()
862-
..method.equals('POST')
863-
..url.path.equals('/api/v1/mark_stream_as_read')
864-
..bodyFields.deepEquals(expected);
865-
}
866-
867-
test('smoke', () {
868-
return FakeApiConnection.with_((connection) async {
869-
await checkMarkStreamAsRead(connection,
870-
streamId: 10,
871-
expected: {'stream_id': '10'});
872-
});
873-
});
874-
});
875-
876-
group('markTopicAsRead', () {
877-
Future<void> checkMarkTopicAsRead(
878-
FakeApiConnection connection, {
879-
required int streamId,
880-
required String topicName,
881-
required Map<String, String> expected,
882-
}) async {
883-
connection.prepare(json: {});
884-
await markTopicAsRead(connection,
885-
streamId: streamId, topicName: eg.t(topicName));
886-
check(connection.lastRequest).isA<http.Request>()
887-
..method.equals('POST')
888-
..url.path.equals('/api/v1/mark_topic_as_read')
889-
..bodyFields.deepEquals(expected);
890-
}
891-
892-
test('smoke', () {
893-
return FakeApiConnection.with_((connection) async {
894-
await checkMarkTopicAsRead(connection,
895-
streamId: 10,
896-
topicName: 'topic',
897-
expected: {
898-
'stream_id': '10',
899-
'topic_name': 'topic',
900-
});
901-
});
902-
});
903-
});
904832
}

test/widgets/actions_test.dart

Lines changed: 0 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import '../api/fake_api.dart';
2121
import '../example_data.dart' as eg;
2222
import '../flutter_checks.dart';
2323
import '../model/binding.dart';
24-
import '../model/unreads_checks.dart';
2524
import '../stdlib_checks.dart';
2625
import '../test_clipboard.dart';
2726
import 'dialog_checks.dart';
@@ -119,106 +118,6 @@ void main() {
119118
await future;
120119
check(store.unreads.oldUnreadsMissing).isFalse();
121120
});
122-
123-
testWidgets('CombinedFeedNarrow on legacy server', (tester) async {
124-
const narrow = CombinedFeedNarrow();
125-
await prepare(tester);
126-
// Might as well test with oldUnreadsMissing: true.
127-
store.unreads.oldUnreadsMissing = true;
128-
129-
connection.zulipFeatureLevel = 154;
130-
connection.prepare(json: {});
131-
final future = ZulipAction.markNarrowAsRead(context, narrow);
132-
await tester.pump(Duration.zero);
133-
await future;
134-
check(connection.lastRequest).isA<http.Request>()
135-
..method.equals('POST')
136-
..url.path.equals('/api/v1/mark_all_as_read')
137-
..bodyFields.deepEquals({});
138-
139-
// Check that [Unreads.handleAllMessagesReadSuccess] wasn't called;
140-
// in the legacy protocol, that'd be redundant with the mark-read event.
141-
check(store.unreads).oldUnreadsMissing.isTrue();
142-
});
143-
144-
testWidgets('ChannelNarrow on legacy server', (tester) async {
145-
final stream = eg.stream();
146-
final narrow = ChannelNarrow(stream.streamId);
147-
await prepare(tester);
148-
connection.zulipFeatureLevel = 154;
149-
connection.prepare(json: {});
150-
final future = ZulipAction.markNarrowAsRead(context, narrow);
151-
await tester.pump(Duration.zero);
152-
await future;
153-
check(connection.lastRequest).isA<http.Request>()
154-
..method.equals('POST')
155-
..url.path.equals('/api/v1/mark_stream_as_read')
156-
..bodyFields.deepEquals({
157-
'stream_id': stream.streamId.toString(),
158-
});
159-
});
160-
161-
testWidgets('TopicNarrow on legacy server', (tester) async {
162-
final narrow = TopicNarrow.ofMessage(eg.streamMessage());
163-
await prepare(tester);
164-
connection.zulipFeatureLevel = 154;
165-
connection.prepare(json: {});
166-
final future = ZulipAction.markNarrowAsRead(context, narrow);
167-
await tester.pump(Duration.zero);
168-
await future;
169-
check(connection.lastRequest).isA<http.Request>()
170-
..method.equals('POST')
171-
..url.path.equals('/api/v1/mark_topic_as_read')
172-
..bodyFields.deepEquals({
173-
'stream_id': narrow.streamId.toString(),
174-
'topic_name': narrow.topic,
175-
});
176-
});
177-
178-
testWidgets('DmNarrow on legacy server', (tester) async {
179-
final message = eg.dmMessage(from: eg.otherUser, to: [eg.selfUser]);
180-
final narrow = DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId);
181-
final unreadMsgs = eg.unreadMsgs(dms: [
182-
UnreadDmSnapshot(otherUserId: eg.otherUser.userId,
183-
unreadMessageIds: [message.id]),
184-
]);
185-
await prepare(tester, unreadMsgs: unreadMsgs);
186-
connection.zulipFeatureLevel = 154;
187-
connection.prepare(json:
188-
UpdateMessageFlagsResult(messages: [message.id]).toJson());
189-
final future = ZulipAction.markNarrowAsRead(context, narrow);
190-
await tester.pump(Duration.zero);
191-
await future;
192-
check(connection.lastRequest).isA<http.Request>()
193-
..method.equals('POST')
194-
..url.path.equals('/api/v1/messages/flags')
195-
..bodyFields.deepEquals({
196-
'messages': jsonEncode([message.id]),
197-
'op': 'add',
198-
'flag': 'read',
199-
});
200-
});
201-
202-
testWidgets('MentionsNarrow on legacy server', (tester) async {
203-
const narrow = MentionsNarrow();
204-
final message = eg.streamMessage(flags: [MessageFlag.mentioned]);
205-
final unreadMsgs = eg.unreadMsgs(mentions: [message.id]);
206-
await prepare(tester, unreadMsgs: unreadMsgs);
207-
connection.zulipFeatureLevel = 154;
208-
connection.prepare(json:
209-
UpdateMessageFlagsResult(messages: [message.id]).toJson());
210-
final future = ZulipAction.markNarrowAsRead(context, narrow);
211-
await tester.pump(Duration.zero);
212-
await future;
213-
check(connection.lastRequest).isA<http.Request>()
214-
..method.equals('POST')
215-
..url.path.equals('/api/v1/messages/flags')
216-
..bodyFields.deepEquals({
217-
'messages': jsonEncode([message.id]),
218-
'op': 'add',
219-
'flag': 'read',
220-
});
221-
});
222121
});
223122

224123
group('updateMessageFlagsStartingFromAnchor', () {

0 commit comments

Comments
 (0)