Skip to content

Commit ed3268c

Browse files
committed
api: Add add-reaction and remove-reaction routes
1 parent 4d00d23 commit ed3268c

File tree

4 files changed

+191
-0
lines changed

4 files changed

+191
-0
lines changed

lib/api/route/messages.dart

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,3 +263,60 @@ class UploadFileResult {
263263

264264
Map<String, dynamic> toJson() => _$UploadFileResultToJson(this);
265265
}
266+
267+
/// https://zulip.com/api/add-reaction
268+
Future<AddReactionResult> addReaction(ApiConnection connection, {
269+
required int messageId,
270+
required ReactionType reactionType,
271+
required String emojiCode,
272+
required String emojiName,
273+
}) {
274+
return connection.post('addReaction', AddReactionResult.fromJson, 'messages/$messageId/reactions', {
275+
'emoji_name': RawParameter(emojiName),
276+
'emoji_code': RawParameter(emojiCode),
277+
'reaction_type': RawParameter(switch (reactionType) {
278+
ReactionType.unicodeEmoji => 'unicode_emoji',
279+
ReactionType.realmEmoji => 'realm_emoji',
280+
ReactionType.zulipExtraEmoji => 'zulip_extra_emoji',
281+
}),
282+
});
283+
}
284+
285+
@JsonSerializable(fieldRename: FieldRename.snake)
286+
class AddReactionResult {
287+
AddReactionResult();
288+
289+
factory AddReactionResult.fromJson(Map<String, dynamic> json) =>
290+
_$AddReactionResultFromJson(json);
291+
292+
Map<String, dynamic> toJson() => _$AddReactionResultToJson(this);
293+
}
294+
295+
296+
/// https://zulip.com/api/remove-reaction
297+
Future<RemoveReactionResult> removeReaction(ApiConnection connection, {
298+
required int messageId,
299+
required ReactionType reactionType,
300+
required String emojiCode,
301+
required String emojiName,
302+
}) {
303+
return connection.delete('removeReaction', RemoveReactionResult.fromJson, 'messages/$messageId/reactions', {
304+
'emoji_name': RawParameter(emojiName),
305+
'emoji_code': RawParameter(emojiCode),
306+
'reaction_type': RawParameter(switch (reactionType) {
307+
ReactionType.unicodeEmoji => 'unicode_emoji',
308+
ReactionType.realmEmoji => 'realm_emoji',
309+
ReactionType.zulipExtraEmoji => 'zulip_extra_emoji',
310+
}),
311+
});
312+
}
313+
314+
@JsonSerializable(fieldRename: FieldRename.snake)
315+
class RemoveReactionResult {
316+
RemoveReactionResult();
317+
318+
factory RemoveReactionResult.fromJson(Map<String, dynamic> json) =>
319+
_$RemoveReactionResultFromJson(json);
320+
321+
Map<String, dynamic> toJson() => _$RemoveReactionResultToJson(this);
322+
}

lib/api/route/messages.g.dart

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/api/route/messages_test.dart

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,4 +334,110 @@ void main() {
334334
});
335335
});
336336
});
337+
338+
group('addReaction', () {
339+
Future<void> checkAddReaction(FakeApiConnection connection, {
340+
required int messageId,
341+
required Reaction reaction,
342+
required String expectedReactionType,
343+
}) async {
344+
connection.prepare(json: AddReactionResult().toJson());
345+
final result = await addReaction(connection,
346+
messageId: messageId,
347+
reactionType: reaction.reactionType,
348+
emojiCode: reaction.emojiCode,
349+
emojiName: reaction.emojiName,
350+
);
351+
// (If AddReactionResult grows any fields, check them here.)
352+
check(result).isA<AddReactionResult>();
353+
check(connection.lastRequest).isNotNull().isA<http.Request>()
354+
..method.equals('POST')
355+
..url.path.equals('/api/v1/messages/$messageId/reactions')
356+
..bodyFields.deepEquals({
357+
'reaction_type': expectedReactionType,
358+
'emoji_code': reaction.emojiCode,
359+
'emoji_name': reaction.emojiName,
360+
});
361+
}
362+
363+
test('unicode emoji', () {
364+
return FakeApiConnection.with_((connection) async {
365+
await checkAddReaction(connection,
366+
messageId: eg.streamMessage().id,
367+
reaction: eg.unicodeEmojiReaction,
368+
expectedReactionType: 'unicode_emoji');
369+
});
370+
});
371+
372+
test('realm emoji', () {
373+
return FakeApiConnection.with_((connection) async {
374+
await checkAddReaction(connection,
375+
messageId: eg.streamMessage().id,
376+
reaction: eg.realmEmoji,
377+
expectedReactionType: 'realm_emoji');
378+
});
379+
});
380+
381+
test('Zulip extra emoji', () {
382+
return FakeApiConnection.with_((connection) async {
383+
await checkAddReaction(connection,
384+
messageId: eg.streamMessage().id,
385+
reaction: eg.zulipExtraEmoji,
386+
expectedReactionType: 'zulip_extra_emoji');
387+
});
388+
});
389+
});
390+
391+
group('removeReaction', () {
392+
Future<void> checkRemoveReaction(FakeApiConnection connection, {
393+
required int messageId,
394+
required Reaction reaction,
395+
required String expectedReactionType,
396+
}) async {
397+
connection.prepare(json: RemoveReactionResult().toJson());
398+
final result = await removeReaction(connection,
399+
messageId: messageId,
400+
reactionType: reaction.reactionType,
401+
emojiCode: reaction.emojiCode,
402+
emojiName: reaction.emojiName,
403+
);
404+
// (If RemoveReactionResult grows any fields, check them here.)
405+
check(result).isA<RemoveReactionResult>();
406+
check(connection.lastRequest).isNotNull().isA<http.Request>()
407+
..method.equals('DELETE')
408+
..url.path.equals('/api/v1/messages/$messageId/reactions')
409+
..bodyFields.deepEquals({
410+
'reaction_type': expectedReactionType,
411+
'emoji_code': reaction.emojiCode,
412+
'emoji_name': reaction.emojiName,
413+
});
414+
}
415+
416+
test('unicode emoji', () {
417+
return FakeApiConnection.with_((connection) async {
418+
await checkRemoveReaction(connection,
419+
messageId: eg.streamMessage().id,
420+
reaction: eg.unicodeEmojiReaction,
421+
expectedReactionType: 'unicode_emoji');
422+
});
423+
});
424+
425+
test('realm emoji', () {
426+
return FakeApiConnection.with_((connection) async {
427+
await checkRemoveReaction(connection,
428+
messageId: eg.streamMessage().id,
429+
reaction: eg.realmEmoji,
430+
expectedReactionType: 'realm_emoji');
431+
});
432+
});
433+
434+
test('Zulip extra emoji', () {
435+
return FakeApiConnection.with_((connection) async {
436+
await checkRemoveReaction(connection,
437+
messageId: eg.streamMessage().id,
438+
reaction: eg.zulipExtraEmoji,
439+
expectedReactionType: 'zulip_extra_emoji');
440+
});
441+
});
442+
});
337443
}

test/example_data.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,20 @@ Reaction unicodeEmojiReaction = Reaction(
195195
userId: selfUser.userId,
196196
);
197197

198+
Reaction realmEmoji = Reaction(
199+
emojiName: 'twocents',
200+
emojiCode: '181',
201+
reactionType: ReactionType.realmEmoji,
202+
userId: selfUser.userId,
203+
);
204+
205+
Reaction zulipExtraEmoji = Reaction(
206+
emojiName: 'zulip',
207+
emojiCode: 'zulip',
208+
reactionType: ReactionType.zulipExtraEmoji,
209+
userId: selfUser.userId,
210+
);
211+
198212
// TODO example data for many more types
199213

200214
InitialSnapshot initialSnapshot({

0 commit comments

Comments
 (0)