Skip to content

Commit

Permalink
api: Add route setTypingStatus.
Browse files Browse the repository at this point in the history
The three legacy cases will need to be dropped separately at different
server versions. The tests will fail accordingly as we do that.

Signed-off-by: Zixuan James Li <[email protected]>
  • Loading branch information
PIG208 committed Sep 7, 2024
1 parent c994ad2 commit 21ff417
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 2 deletions.
4 changes: 3 additions & 1 deletion lib/api/model/events.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1039,7 +1039,9 @@ class TypingEvent extends Event {
@JsonEnum(fieldRename: FieldRename.snake)
enum TypingOp {
start,
stop
stop;

String toJson() => _$TypingOpEnumMap[this]!;
}

/// A Zulip event of type `reaction`, with op `add` or `remove`.
Expand Down
2 changes: 1 addition & 1 deletion lib/api/model/events.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions lib/api/route/typing.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import '../core.dart';
import '../model/events.dart';
import 'messages.dart';


/// https://zulip.com/api/set-typing-status
Future<void> setTypingStatus(ApiConnection connection, {
required TypingOp op,
required MessageDestination destination,
}) {
switch (destination) {
case StreamDestination():
final supportsTypeChannel = connection.zulipFeatureLevel! >= 248; // TODO(server-9)
final supportsStreamId = connection.zulipFeatureLevel! >= 215; // TODO(server-8)
return connection.post('setTypingStatus', (_) {}, 'typing', {
'type': RawParameter((supportsTypeChannel) ? 'channel' : 'stream'),
'op': RawParameter(op.toJson()),
'topic': RawParameter(destination.topic),
...(supportsStreamId) ? {'stream_id': destination.streamId}
: {'to': [destination.streamId]}
});
case DmDestination():
final supportsDirect = connection.zulipFeatureLevel! >= 174; // TODO(server-7)
return connection.post('setTypingStatus', (_) {}, 'typing', {
'type': RawParameter((supportsDirect) ? 'direct' : 'private'),
'op': RawParameter(op.toJson()),
'to': destination.userIds,
});
}
}
94 changes: 94 additions & 0 deletions test/api/route/typing_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import 'dart:convert';

import 'package:http/http.dart' as http;
import 'package:checks/checks.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:zulip/api/model/events.dart';
import 'package:zulip/api/route/messages.dart';
import 'package:zulip/api/route/typing.dart';

import '../../stdlib_checks.dart';
import '../fake_api.dart';

void main() {
const streamId = 123;
const topic = 'topic';
const userIds = [101, 102, 103];

Future<void> checkSetTypingStatus(FakeApiConnection connection, TypingOp op, {
required MessageDestination destination,
required Map<String, String> expectedBodyFields,
}) async {
connection.prepare(json: {});
await setTypingStatus(connection, op: op, destination: destination);
check(connection.lastRequest).isA<http.Request>()
..method.equals('POST')
..url.path.equals('/api/v1/typing')
..bodyFields.deepEquals(expectedBodyFields);
}

Future<void> checkTopicExpectedOp(TypingOp op, String expectedOp) =>
FakeApiConnection.with_((connection) =>
checkSetTypingStatus(connection, op,
destination: const StreamDestination(streamId, topic),
expectedBodyFields: {
'type': 'channel',
'op': expectedOp,
'stream_id': streamId.toString(),
'topic': topic,
}));

test('send typing status start to topic', () =>
checkTopicExpectedOp(TypingOp.start, 'start'));

test('send typing status stop to topic', () =>
checkTopicExpectedOp(TypingOp.stop, 'stop'));

Future<void> checkDmExpectedOp(TypingOp op, String expectedOp) =>
FakeApiConnection.with_((connection) =>
checkSetTypingStatus(connection, op,
destination: const DmDestination(userIds: userIds),
expectedBodyFields: {
'type': 'direct',
'op': expectedOp,
'to': jsonEncode(userIds),
}));

test('send typing status start to dm', () =>
checkDmExpectedOp(TypingOp.start, 'start'));

test('send typing status stop to dm', () =>
checkDmExpectedOp(TypingOp.stop, 'stop'));

test('use stream on legacy server', () =>
FakeApiConnection.with_(zulipFeatureLevel: 247, (connection) =>
checkSetTypingStatus(connection, TypingOp.start,
destination: const StreamDestination(streamId, topic),
expectedBodyFields: {
'type': 'stream',
'op': 'start',
'stream_id': streamId.toString(),
'topic': topic,
})));

test('use to=[streamId] on legacy server', () =>
FakeApiConnection.with_(zulipFeatureLevel: 214, (connection) =>
checkSetTypingStatus(connection, TypingOp.start,
destination: const StreamDestination(streamId, topic),
expectedBodyFields: {
'type': 'stream',
'op': 'start',
'to': jsonEncode([streamId]),
'topic': topic,
})));

test('use "private" on legacy server', () =>
FakeApiConnection.with_(zulipFeatureLevel: 173, (connection) =>
checkSetTypingStatus(connection, TypingOp.start,
destination: const DmDestination(userIds: userIds),
expectedBodyFields: {
'type': 'private',
'op': 'start',
'to': jsonEncode(userIds),
})));
}

0 comments on commit 21ff417

Please sign in to comment.