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 authored and gnprice committed Sep 16, 2024
1 parent 7fe8048 commit e974ed3
Show file tree
Hide file tree
Showing 4 changed files with 135 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', {
'op': RawParameter(op.toJson()),
'type': RawParameter(supportsTypeChannel ? 'channel' : 'stream'),
if (supportsStreamId) 'stream_id': destination.streamId
else 'to': [destination.streamId],
'topic': RawParameter(destination.topic),
});
case DmDestination():
final supportsDirect = connection.zulipFeatureLevel! >= 174; // TODO(server-7)
return connection.post('setTypingStatus', (_) {}, 'typing', {
'op': RawParameter(op.toJson()),
'type': RawParameter(supportsDirect ? 'direct' : 'private'),
'to': destination.userIds,
});
}
}
101 changes: 101 additions & 0 deletions test/api/route/typing_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
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> checkSetTypingStatusForTopic(TypingOp op, String expectedOp) {
return FakeApiConnection.with_((connection) {
return checkSetTypingStatus(connection, op,
destination: const StreamDestination(streamId, topic),
expectedBodyFields: {
'op': expectedOp,
'type': 'channel',
'stream_id': streamId.toString(),
'topic': topic,
});
});
}

test('send typing status start for topic', () {
return checkSetTypingStatusForTopic(TypingOp.start, 'start');
});

test('send typing status stop for topic', () {
return checkSetTypingStatusForTopic(TypingOp.stop, 'stop');
});

test('send typing status start for dm', () {
return FakeApiConnection.with_((connection) {
return checkSetTypingStatus(connection, TypingOp.start,
destination: const DmDestination(userIds: userIds),
expectedBodyFields: {
'op': 'start',
'type': 'direct',
'to': jsonEncode(userIds),
});
});
});

test('legacy: use "stream" instead of "channel"', () {
return FakeApiConnection.with_(zulipFeatureLevel: 247, (connection) {
return checkSetTypingStatus(connection, TypingOp.start,
destination: const StreamDestination(streamId, topic),
expectedBodyFields: {
'op': 'start',
'type': 'stream',
'stream_id': streamId.toString(),
'topic': topic,
});
});
});

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

test('legacy: use "private" instead of "direct"', () {
return FakeApiConnection.with_(zulipFeatureLevel: 173, (connection) {
return checkSetTypingStatus(connection, TypingOp.start,
destination: const DmDestination(userIds: userIds),
expectedBodyFields: {
'op': 'start',
'type': 'private',
'to': jsonEncode(userIds),
});
});
});
}

0 comments on commit e974ed3

Please sign in to comment.