Skip to content

api: Mark messages as read (markAllAsRead, markStreamAsRead, markTopicAsRead) #363

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions lib/api/route/messages.dart
Original file line number Diff line number Diff line change
Expand Up @@ -373,3 +373,63 @@ class UpdateMessageFlagsForNarrowResult {

Map<String, dynamic> toJson() => _$UpdateMessageFlagsForNarrowResultToJson(this);
}

/// https://zulip.com/api/mark-all-as-read
///
/// This binding is deprecated, in FL 155+ use
/// [updateMessageFlagsForNarrow] instead.
// TODO(server-6): Remove as deprecated by updateMessageFlagsForNarrow
//
// For FL < 153 this call was atomic on the server and would
// not mark any messages as read if it timed out.
// From FL 153 and onward the server started processing
// in batches so progress could still be made in the event
// of a timeout interruption. Thus, in FL 153 this call
// started returning `result: partially_completed` and
// `code: REQUEST_TIMEOUT` for timeouts.
//
// In FL 211 the `partially_completed` variant of
// `result` was removed, the string `code` field also
// removed, and a boolean `complete` field introduced.
//
// For full support of this endpoint we would need three
// variants of the return structure based on feature
// level (`{}`, `{code: string}`, and `{complete: bool}`)
// as well as handling of `partially_completed` variant
// of `result` in `lib/api/core.dart`. For simplicity we
// ignore these return values.
//
// We don't use this method for FL 155+ (it is replaced
// by `updateMessageFlagsForNarrow`) so there are only
// two versions (FL 153 and FL 154) affected.
Future<void> markAllAsRead(ApiConnection connection) {
return connection.post('markAllAsRead', (_) {}, 'mark_all_as_read', {});
}

/// https://zulip.com/api/mark-stream-as-read
///
/// This binding is deprecated, in FL 155+ use
/// [updateMessageFlagsForNarrow] instead.
// TODO(server-6): Remove as deprecated by updateMessageFlagsForNarrow
Future<void> markStreamAsRead(ApiConnection connection, {
required int streamId,
}) {
return connection.post('markStreamAsRead', (_) {}, 'mark_stream_as_read', {
'stream_id': streamId,
});
}

/// https://zulip.com/api/mark-topic-as-read
///
/// This binding is deprecated, in FL 155+ use
/// [updateMessageFlagsForNarrow] instead.
// TODO(server-6): Remove as deprecated by updateMessageFlagsForNarrow
Future<void> markTopicAsRead(ApiConnection connection, {
required int streamId,
required String topicName,
}) {
return connection.post('markTopicAsRead', (_) {}, 'mark_topic_as_read', {
'stream_id': streamId,
'topic_name': RawParameter(topicName),
});
}
72 changes: 72 additions & 0 deletions test/api/route/messages_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -571,4 +571,76 @@ void main() {
});
});
});

group('markAllAsRead', () {
Future<void> checkMarkAllAsRead(
FakeApiConnection connection, {
required Map<String, String> expected,
}) async {
connection.prepare(json: {});
await markAllAsRead(connection);
check(connection.lastRequest).isA<http.Request>()
..method.equals('POST')
..url.path.equals('/api/v1/mark_all_as_read')
..bodyFields.deepEquals(expected);
}

test('smoke', () {
return FakeApiConnection.with_((connection) async {
await checkMarkAllAsRead(connection, expected: {});
});
});
});

group('markStreamAsRead', () {
Future<void> checkMarkStreamAsRead(
FakeApiConnection connection, {
required int streamId,
required Map<String, String> expected,
}) async {
connection.prepare(json: {});
await markStreamAsRead(connection, streamId: streamId);
check(connection.lastRequest).isA<http.Request>()
..method.equals('POST')
..url.path.equals('/api/v1/mark_stream_as_read')
..bodyFields.deepEquals(expected);
}

test('smoke', () {
return FakeApiConnection.with_((connection) async {
await checkMarkStreamAsRead(connection,
streamId: 10,
expected: {'stream_id': '10'});
});
});
});

group('markTopicAsRead', () {
Future<void> checkMarkTopicAsRead(
FakeApiConnection connection, {
required int streamId,
required String topicName,
required Map<String, String> expected,
}) async {
connection.prepare(json: {});
await markTopicAsRead(connection,
streamId: streamId, topicName: topicName);
check(connection.lastRequest).isA<http.Request>()
..method.equals('POST')
..url.path.equals('/api/v1/mark_topic_as_read')
..bodyFields.deepEquals(expected);
}

test('smoke', () {
return FakeApiConnection.with_((connection) async {
await checkMarkTopicAsRead(connection,
streamId: 10,
topicName: 'topic',
expected: {
'stream_id': '10',
'topic_name': 'topic',
});
});
});
});
}