diff --git a/packages/neon/neon_talk/lib/src/blocs/room.dart b/packages/neon/neon_talk/lib/src/blocs/room.dart index a04bd86e02e..4cbecccc1dc 100644 --- a/packages/neon/neon_talk/lib/src/blocs/room.dart +++ b/packages/neon/neon_talk/lib/src/blocs/room.dart @@ -28,9 +28,12 @@ abstract class TalkRoomBloc implements InteractiveBloc { /// Adds an emoji [reaction] to the [message]. void addReaction(spreed.$ChatMessageInterface message, String reaction); - /// Removes an emoji [reaction] to the [message]. + /// Removes an emoji [reaction] from the [message]. void removeReaction(spreed.$ChatMessageInterface message, String reaction); + /// Loads the emoji reactions for the [message]. + void loadReactions(spreed.$ChatMessageInterface message); + /// The current room data. BehaviorSubject> get room; @@ -41,6 +44,9 @@ abstract class TalkRoomBloc implements InteractiveBloc { /// The last message ID that was read by everyone with read-privacy set to public. /// {@endtemplate} BehaviorSubject get lastCommonRead; + + /// Map of emoji reactions for the [messages]. + BehaviorSubject>>> get reactions; } class _TalkRoomBloc extends InteractiveBloc implements TalkRoomBloc { @@ -150,6 +156,9 @@ class _TalkRoomBloc extends InteractiveBloc implements TalkRoomBloc { @override final lastCommonRead = BehaviorSubject(); + @override + final reactions = BehaviorSubject.seeded(BuiltMap()); + @override void dispose() { pollLoop = false; @@ -158,6 +167,7 @@ class _TalkRoomBloc extends InteractiveBloc implements TalkRoomBloc { unawaited(room.close()); unawaited(messages.close()); unawaited(lastCommonRead.close()); + unawaited(reactions.close()); super.dispose(); } @@ -252,6 +262,28 @@ class _TalkRoomBloc extends InteractiveBloc implements TalkRoomBloc { ); } + @override + Future loadReactions(spreed.$ChatMessageInterface message) async { + if (reactions.value.containsKey(message.id)) { + return; + } + + await wrapAction( + () async { + final response = await account.client.spreed.reaction.getReactions( + token: token, + messageId: message.id, + ); + + updateReactions( + message.id, + response.body.ocs.data, + ); + }, + refresh: () async {}, + ); + } + void updateLastCommonRead(String? header) { if (header != null) { lastCommonRead.add(int.parse(header)); @@ -284,6 +316,12 @@ class _TalkRoomBloc extends InteractiveBloc implements TalkRoomBloc { } void updateReactions(int messageId, BuiltMap> reactions) { + this.reactions.add( + this.reactions.value.rebuild((b) { + b[messageId] = reactions; + }), + ); + updateMessage( messageId, (b) => b diff --git a/packages/neon/neon_talk/lib/src/widgets/reactions.dart b/packages/neon/neon_talk/lib/src/widgets/reactions.dart index f31a0d41da2..87d1939f989 100644 --- a/packages/neon/neon_talk/lib/src/widgets/reactions.dart +++ b/packages/neon/neon_talk/lib/src/widgets/reactions.dart @@ -18,82 +18,95 @@ class TalkReactions extends StatelessWidget { @override Widget build(BuildContext context) { + final bloc = NeonProvider.of(context); + const shape = RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(50)), ); - final children = []; - for (final reaction in chatMessage.reactions.entries) { - final isSelf = chatMessage.reactionsSelf?.contains(reaction.key) ?? false; - - children.add( - ActionChip( - backgroundColor: isSelf ? Theme.of(context).colorScheme.primary : null, - shape: shape, - avatar: Text(reaction.key), - label: Text( - reaction.value.toString(), - style: const TextStyle( - fontSize: 12, - fontFamily: 'monospace', - ), - ), - padding: EdgeInsets.zero, - labelPadding: const EdgeInsets.only( - top: -2.5, - bottom: -2.5, - right: 10, - ), - onPressed: () { - final bloc = NeonProvider.of(context); + return MouseRegion( + onEnter: (_) { + bloc.loadReactions(chatMessage); + }, + child: StreamBuilder( + stream: bloc.reactions, + builder: (context, reactionsSnapshot) { + final reactions = reactionsSnapshot.data?[chatMessage.id]; - if (isSelf) { - bloc.removeReaction(chatMessage, reaction.key); - } else { - bloc.addReaction(chatMessage, reaction.key); - } - }, - ), - ); - } + final children = []; + for (final reaction in chatMessage.reactions.entries) { + final isSelf = chatMessage.reactionsSelf?.contains(reaction.key) ?? false; - children.add( - ActionChip( - shape: shape, - avatar: Icon( - Icons.add_reaction_outlined, - color: Theme.of(context).colorScheme.onSurfaceVariant, - size: 16, - ), - label: const SizedBox(), - padding: EdgeInsets.zero, - labelPadding: const EdgeInsets.symmetric(vertical: -2.5), - onPressed: () async { - final reaction = await showDialog( - context: context, - builder: (context) => const NeonEmojiPickerDialog(), - ); - if (reaction == null) { - return; + children.add( + ActionChip( + backgroundColor: isSelf ? Theme.of(context).colorScheme.primary : null, + shape: shape, + avatar: Text(reaction.key), + label: Text( + reaction.value.toString(), + style: const TextStyle( + fontSize: 12, + fontFamily: 'monospace', + ), + ), + tooltip: reactions?[reaction.key]?.map((r) => r.actorDisplayName).join(', '), + padding: EdgeInsets.zero, + labelPadding: const EdgeInsets.only( + top: -2.5, + bottom: -2.5, + right: 10, + ), + onPressed: () { + if (isSelf) { + bloc.removeReaction(chatMessage, reaction.key); + } else { + bloc.addReaction(chatMessage, reaction.key); + } + }, + ), + ); } - if (!context.mounted) { - return; - } + children.add( + ActionChip( + shape: shape, + avatar: Icon( + Icons.add_reaction_outlined, + color: Theme.of(context).colorScheme.onSurfaceVariant, + size: 16, + ), + label: const SizedBox(), + padding: EdgeInsets.zero, + labelPadding: const EdgeInsets.symmetric(vertical: -2.5), + onPressed: () async { + final reaction = await showDialog( + context: context, + builder: (context) => const NeonEmojiPickerDialog(), + ); + if (reaction == null) { + return; + } - NeonProvider.of(context).addReaction(chatMessage, reaction); - }, - ), - ); + if (!context.mounted) { + return; + } - return Row( - children: children - .intersperse( - const SizedBox( - width: 5, + bloc.addReaction(chatMessage, reaction); + }, ), - ) - .toList(), + ); + + return Row( + children: children + .intersperse( + const SizedBox( + width: 5, + ), + ) + .toList(), + ); + }, + ), ); } } diff --git a/packages/neon/neon_talk/test/goldens/reactions.png b/packages/neon/neon_talk/test/goldens/reactions.png index f197244c778..7b2432bf9ac 100644 Binary files a/packages/neon/neon_talk/test/goldens/reactions.png and b/packages/neon/neon_talk/test/goldens/reactions.png differ diff --git a/packages/neon/neon_talk/test/message_test.dart b/packages/neon/neon_talk/test/message_test.dart index c7cfc0388f7..10312db5885 100644 --- a/packages/neon/neon_talk/test/message_test.dart +++ b/packages/neon/neon_talk/test/message_test.dart @@ -8,6 +8,7 @@ import 'package:neon_framework/theme.dart'; import 'package:neon_framework/utils.dart'; import 'package:neon_talk/l10n/localizations.dart'; import 'package:neon_talk/l10n/localizations_en.dart'; +import 'package:neon_talk/src/blocs/room.dart'; import 'package:neon_talk/src/widgets/actor_avatar.dart'; import 'package:neon_talk/src/widgets/message.dart'; import 'package:neon_talk/src/widgets/reactions.dart'; @@ -22,12 +23,6 @@ import 'package:rxdart/rxdart.dart'; import 'testing.dart'; -Widget wrapWidget(Widget child) => TestApp( - localizationsDelegates: TalkLocalizations.localizationsDelegates, - supportedLocales: TalkLocalizations.supportedLocales, - child: child, - ); - void main() { setUpAll(() { FakeNeonStorage.setup(); @@ -63,8 +58,10 @@ void main() { when(() => chatMessage.messageParameters).thenReturn(BuiltMap()); await tester.pumpWidget( - wrapWidget( - TalkMessagePreview( + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + child: TalkMessagePreview( actorId: 'test', roomType: spreed.RoomType.group, chatMessage: chatMessage, @@ -83,8 +80,10 @@ void main() { when(() => chatMessage.messageParameters).thenReturn(BuiltMap()); await tester.pumpWidget( - wrapWidget( - TalkMessagePreview( + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + child: TalkMessagePreview( actorId: 'abc', roomType: spreed.RoomType.group, chatMessage: chatMessage, @@ -102,8 +101,10 @@ void main() { when(() => chatMessage.messageParameters).thenReturn(BuiltMap()); await tester.pumpWidget( - wrapWidget( - TalkMessagePreview( + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + child: TalkMessagePreview( actorId: 'test', roomType: spreed.RoomType.oneToOne, chatMessage: chatMessage, @@ -121,8 +122,10 @@ void main() { when(() => chatMessage.messageParameters).thenReturn(BuiltMap()); await tester.pumpWidget( - wrapWidget( - TalkMessagePreview( + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + child: TalkMessagePreview( actorId: 'abc', roomType: spreed.RoomType.oneToOne, chatMessage: chatMessage, @@ -140,8 +143,10 @@ void main() { when(() => chatMessage.messageParameters).thenReturn(BuiltMap()); await tester.pumpWidget( - wrapWidget( - TalkMessagePreview( + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + child: TalkMessagePreview( actorId: 'abc', roomType: spreed.RoomType.group, chatMessage: chatMessage, @@ -159,8 +164,10 @@ void main() { when(() => chatMessage.messageParameters).thenReturn(BuiltMap()); await tester.pumpWidget( - wrapWidget( - TalkMessagePreview( + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + child: TalkMessagePreview( actorId: 'abc', roomType: spreed.RoomType.oneToOne, chatMessage: chatMessage, @@ -180,8 +187,10 @@ void main() { when(() => chatMessage.messageParameters).thenReturn(BuiltMap()); await tester.pumpWidget( - wrapWidget( - TalkMessage( + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + child: TalkMessage( chatMessage: chatMessage, lastCommonRead: null, ), @@ -208,14 +217,20 @@ void main() { when(() => chatMessage.reactions).thenReturn(BuiltMap()); when(() => chatMessage.messageParameters).thenReturn(BuiltMap()); + final roomBloc = MockRoomBloc(); + when(() => roomBloc.reactions).thenAnswer((_) => BehaviorSubject.seeded(BuiltMap())); + await tester.pumpWidget( - wrapWidget( - NeonProvider.value( - value: accountsBloc, - child: TalkMessage( - chatMessage: chatMessage, - lastCommonRead: null, - ), + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + providers: [ + NeonProvider.value(value: accountsBloc), + NeonProvider.value(value: roomBloc), + ], + child: TalkMessage( + chatMessage: chatMessage, + lastCommonRead: null, ), ), ); @@ -229,8 +244,10 @@ void main() { when(() => chatMessage.systemMessage).thenReturn('reaction'); await tester.pumpWidget( - wrapWidget( - TalkSystemMessage( + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + child: TalkSystemMessage( chatMessage: chatMessage, previousChatMessage: null, ), @@ -248,8 +265,10 @@ void main() { when(() => chatMessage.messageParameters).thenReturn(BuiltMap()); await tester.pumpWidget( - wrapWidget( - TalkSystemMessage( + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + child: TalkSystemMessage( chatMessage: chatMessage, previousChatMessage: null, ), @@ -270,8 +289,10 @@ void main() { when(() => chatMessage.messageParameters).thenReturn(BuiltMap()); await tester.pumpWidget( - wrapWidget( - TalkSystemMessage( + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + child: TalkSystemMessage( chatMessage: chatMessage, previousChatMessage: previousChatMessage, ), @@ -305,13 +326,15 @@ void main() { when(() => chatMessage.messageParameters).thenReturn(BuiltMap()); await tester.pumpWidget( - wrapWidget( - NeonProvider.value( - value: accountsBloc, - child: TalkParentMessage( - parentChatMessage: chatMessage, - lastCommonRead: null, - ), + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + providers: [ + NeonProvider.value(value: accountsBloc), + ], + child: TalkParentMessage( + parentChatMessage: chatMessage, + lastCommonRead: null, ), ), ); @@ -345,15 +368,21 @@ void main() { when(() => chatMessage.messageParameters).thenReturn(BuiltMap()); when(() => chatMessage.id).thenReturn(0); + final roomBloc = MockRoomBloc(); + when(() => roomBloc.reactions).thenAnswer((_) => BehaviorSubject.seeded(BuiltMap())); + await tester.pumpWidget( - wrapWidget( - NeonProvider.value( - value: accountsBloc, - child: TalkCommentMessage( - chatMessage: chatMessage, - lastCommonRead: 0, - previousChatMessage: previousChatMessage, - ), + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + providers: [ + NeonProvider.value(value: accountsBloc), + NeonProvider.value(value: roomBloc), + ], + child: TalkCommentMessage( + chatMessage: chatMessage, + lastCommonRead: 0, + previousChatMessage: previousChatMessage, ), ), ); @@ -394,15 +423,21 @@ void main() { when(() => chatMessage.messageParameters).thenReturn(BuiltMap()); when(() => chatMessage.id).thenReturn(0); + final roomBloc = MockRoomBloc(); + when(() => roomBloc.reactions).thenAnswer((_) => BehaviorSubject.seeded(BuiltMap())); + await tester.pumpWidget( - wrapWidget( - NeonProvider.value( - value: accountsBloc, - child: TalkCommentMessage( - chatMessage: chatMessage, - lastCommonRead: 0, - previousChatMessage: previousChatMessage, - ), + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + providers: [ + NeonProvider.value(value: accountsBloc), + NeonProvider.value(value: roomBloc), + ], + child: TalkCommentMessage( + chatMessage: chatMessage, + lastCommonRead: 0, + previousChatMessage: previousChatMessage, ), ), ); @@ -442,14 +477,16 @@ void main() { when(() => chatMessage.messageParameters).thenReturn(BuiltMap()); await tester.pumpWidget( - wrapWidget( - NeonProvider.value( - value: accountsBloc, - child: TalkCommentMessage( - chatMessage: chatMessage, - lastCommonRead: null, - previousChatMessage: previousChatMessage, - ), + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + providers: [ + NeonProvider.value(value: accountsBloc), + ], + child: TalkCommentMessage( + chatMessage: chatMessage, + lastCommonRead: null, + previousChatMessage: previousChatMessage, ), ), ); @@ -479,14 +516,16 @@ void main() { when(() => chatMessage.messageParameters).thenReturn(BuiltMap()); await tester.pumpWidget( - wrapWidget( - NeonProvider.value( - value: accountsBloc, - child: TalkCommentMessage( - chatMessage: chatMessage, - lastCommonRead: null, - isParent: true, - ), + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + providers: [ + NeonProvider.value(value: accountsBloc), + ], + child: TalkCommentMessage( + chatMessage: chatMessage, + lastCommonRead: null, + isParent: true, ), ), ); @@ -533,15 +572,21 @@ void main() { when(() => chatMessage.parent).thenReturn(parentChatMessage); when(() => chatMessage.messageParameters).thenReturn(BuiltMap()); + final roomBloc = MockRoomBloc(); + when(() => roomBloc.reactions).thenAnswer((_) => BehaviorSubject.seeded(BuiltMap())); + await tester.pumpWidget( - wrapWidget( - NeonProvider.value( - value: accountsBloc, - child: TalkCommentMessage( - chatMessage: chatMessage, - lastCommonRead: null, - previousChatMessage: previousChatMessage, - ), + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + providers: [ + NeonProvider.value(value: accountsBloc), + NeonProvider.value(value: roomBloc), + ], + child: TalkCommentMessage( + chatMessage: chatMessage, + lastCommonRead: null, + previousChatMessage: previousChatMessage, ), ), ); @@ -577,15 +622,21 @@ void main() { when(() => chatMessage.reactions).thenReturn(BuiltMap({'😀': 1, '😊': 23})); when(() => chatMessage.messageParameters).thenReturn(BuiltMap()); + final roomBloc = MockRoomBloc(); + when(() => roomBloc.reactions).thenAnswer((_) => BehaviorSubject.seeded(BuiltMap())); + await tester.pumpWidget( - wrapWidget( - NeonProvider.value( - value: accountsBloc, - child: TalkCommentMessage( - chatMessage: chatMessage, - lastCommonRead: null, - previousChatMessage: previousChatMessage, - ), + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + providers: [ + NeonProvider.value(value: accountsBloc), + NeonProvider.value(value: roomBloc), + ], + child: TalkCommentMessage( + chatMessage: chatMessage, + lastCommonRead: null, + previousChatMessage: previousChatMessage, ), ), ); @@ -623,15 +674,21 @@ void main() { when(() => chatMessage.reactions).thenReturn(BuiltMap({'😀': 1, '😊': 23})); when(() => chatMessage.messageParameters).thenReturn(BuiltMap()); + final roomBloc = MockRoomBloc(); + when(() => roomBloc.reactions).thenAnswer((_) => BehaviorSubject.seeded(BuiltMap())); + await tester.pumpWidget( - wrapWidget( - NeonProvider.value( - value: accountsBloc, - child: TalkCommentMessage( - chatMessage: chatMessage, - lastCommonRead: null, - previousChatMessage: previousChatMessage, - ), + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + providers: [ + NeonProvider.value(value: accountsBloc), + NeonProvider.value(value: roomBloc), + ], + child: TalkCommentMessage( + chatMessage: chatMessage, + lastCommonRead: null, + previousChatMessage: previousChatMessage, ), ), ); @@ -669,15 +726,21 @@ void main() { when(() => chatMessage.reactions).thenReturn(BuiltMap({'😀': 1, '😊': 23})); when(() => chatMessage.messageParameters).thenReturn(BuiltMap()); + final roomBloc = MockRoomBloc(); + when(() => roomBloc.reactions).thenAnswer((_) => BehaviorSubject.seeded(BuiltMap())); + await tester.pumpWidget( - wrapWidget( - NeonProvider.value( - value: accountsBloc, - child: TalkCommentMessage( - chatMessage: chatMessage, - lastCommonRead: null, - previousChatMessage: previousChatMessage, - ), + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + providers: [ + NeonProvider.value(value: accountsBloc), + NeonProvider.value(value: roomBloc), + ], + child: TalkCommentMessage( + chatMessage: chatMessage, + lastCommonRead: null, + previousChatMessage: previousChatMessage, ), ), ); diff --git a/packages/neon/neon_talk/test/reactions_test.dart b/packages/neon/neon_talk/test/reactions_test.dart index 64e88672db9..d9b7b1d4252 100644 --- a/packages/neon/neon_talk/test/reactions_test.dart +++ b/packages/neon/neon_talk/test/reactions_test.dart @@ -1,4 +1,5 @@ import 'package:built_collection/built_collection.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; @@ -7,30 +8,56 @@ import 'package:neon_framework/utils.dart'; import 'package:neon_talk/src/blocs/room.dart'; import 'package:neon_talk/src/widgets/reactions.dart'; import 'package:nextcloud/spreed.dart' as spreed; +import 'package:rxdart/rxdart.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'testing.dart'; +spreed.Reaction getReaction() { + final reaction = MockReaction(); + when(() => reaction.actorDisplayName).thenReturn('actorDisplayName'); + + return reaction; +} + void main() { late spreed.ChatMessage chatMessage; late TalkRoomBloc bloc; setUp(() { chatMessage = MockChatMessage(); - when(() => chatMessage.reactions).thenReturn(BuiltMap({'😀': 1, '😊': 23})); + when(() => chatMessage.id).thenReturn(0); + when(() => chatMessage.reactions).thenReturn(BuiltMap({'😀': 1, '😊': 2})); when(() => chatMessage.reactionsSelf).thenReturn(BuiltList(['😊'])); bloc = MockRoomBloc(); + when(() => bloc.reactions).thenAnswer( + (_) => BehaviorSubject.seeded( + BuiltMap({ + 0: BuiltMap>({ + '😀': BuiltList([getReaction()]), + '😊': BuiltList([getReaction(), getReaction()]), + }), + }), + ), + ); }); testWidgets('Reactions', (tester) async { await tester.pumpWidget( TestApp( + providers: [ + NeonProvider.value(value: bloc), + ], child: TalkReactions( chatMessage: chatMessage, ), ), ); + await tester.pumpAndSettle(); + + expect(find.byTooltip('actorDisplayName'), findsOne); + expect(find.byTooltip('actorDisplayName, actorDisplayName'), findsOne); await expectLater(find.byType(TalkReactions), matchesGoldenFile('goldens/reactions.png')); }); @@ -93,4 +120,26 @@ void main() { verify(() => bloc.addReaction(chatMessage, '😂')).called(1); }); }); + + testWidgets('Load reactions on hover', (tester) async { + await tester.pumpWidget( + TestApp( + providers: [ + NeonProvider.value(value: bloc), + ], + child: TalkReactions( + chatMessage: chatMessage, + ), + ), + ); + + final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); + await gesture.addPointer(location: Offset.zero); + addTearDown(gesture.removePointer); + await tester.pump(); + await gesture.moveTo(tester.getCenter(find.byType(TalkReactions))); + await tester.pumpAndSettle(); + + verify(() => bloc.loadReactions(chatMessage)).called(1); + }); } diff --git a/packages/neon/neon_talk/test/room_bloc_test.dart b/packages/neon/neon_talk/test/room_bloc_test.dart index 5c4dd1abe50..ff67d199161 100644 --- a/packages/neon/neon_talk/test/room_bloc_test.dart +++ b/packages/neon/neon_talk/test/room_bloc_test.dart @@ -147,6 +147,33 @@ Account mockTalkAccount() { }, ); }, + 'get': (match, queryParameters) => Response( + json.encode({ + 'ocs': { + 'meta': {'status': '', 'statuscode': 0}, + 'data': { + '😀': [ + { + 'actorDisplayName': '', + 'actorId': 'test', + 'actorType': 'users', + 'timestamp': 0, + }, + { + 'actorDisplayName': '', + 'actorId': 'other', + 'actorType': 'users', + 'timestamp': 0, + }, + ], + }, + }, + }), + 200, + headers: { + 'content-type': 'application/json; charset=utf-8', + }, + ), }, }); } @@ -291,6 +318,17 @@ void main() { ), ]), ); + expect( + roomBloc.reactions.map((a) => a.map((k, v) => MapEntry(k, v.map((k, v) => MapEntry(k, v.length))))), + emitsInOrder(>>[ + BuiltMap(), + BuiltMap({ + 0: BuiltMap({ + '😀': 2, + }), + }), + ]), + ); final message = MockChatMessage(); when(() => message.id).thenReturn(0); @@ -339,6 +377,17 @@ void main() { ), ]), ); + expect( + roomBloc.reactions.map((a) => a.map((k, v) => MapEntry(k, v.map((k, v) => MapEntry(k, v.length))))), + emitsInOrder(>>[ + BuiltMap(), + BuiltMap({ + 2: BuiltMap({ + '😀': 2, + }), + }), + ]), + ); final message = MockChatMessage(); when(() => message.id).thenReturn(2); @@ -346,6 +395,65 @@ void main() { roomBloc.removeReaction(message, '😀'); }); + test('loadReactions', () async { + expect( + roomBloc.messages.transformResult((e) => BuiltList>(e.map((m) => m.reactions))), + emitsInOrder([ + Result>>.loading(), + Result.success( + BuiltList>([ + BuiltMap(), + BuiltMap(), + BuiltMap(), + ]), + ), + Result.success( + BuiltList>([ + BuiltMap(), + BuiltMap({'😀': 2}), + BuiltMap(), + ]), + ), + ]), + ); + expect( + roomBloc.messages.transformResult((e) => BuiltList?>(e.map((m) => m.reactionsSelf))), + emitsInOrder([ + Result?>>.loading(), + Result.success( + BuiltList?>([ + null, + null, + null, + ]), + ), + Result.success( + BuiltList?>([ + null, + BuiltList(['😀']), + null, + ]), + ), + ]), + ); + expect( + roomBloc.reactions.map((a) => a.map((k, v) => MapEntry(k, v.map((k, v) => MapEntry(k, v.length))))), + emitsInOrder(>>[ + BuiltMap(), + BuiltMap({ + 1: BuiltMap({ + '😀': 2, + }), + }), + ]), + ); + + final message = MockChatMessage(); + when(() => message.id).thenReturn(1); + + roomBloc.loadReactions(message); + }); + test('polling', () async { expect( roomBloc.messages.transformResult((e) => BuiltList(e.map((m) => m.id))), diff --git a/packages/neon/neon_talk/test/room_page_test.dart b/packages/neon/neon_talk/test/room_page_test.dart index 4ddd857c6d0..3a95c123205 100644 --- a/packages/neon/neon_talk/test/room_page_test.dart +++ b/packages/neon/neon_talk/test/room_page_test.dart @@ -138,6 +138,8 @@ void main() { ), ); + when(() => bloc.reactions).thenAnswer((_) => BehaviorSubject.seeded(BuiltMap())); + final account = MockAccount(); when(() => account.client).thenReturn(NextcloudClient(Uri.parse(''))); diff --git a/packages/neon/neon_talk/test/testing.dart b/packages/neon/neon_talk/test/testing.dart index 72f96f3dbbf..30d92d4f9f0 100644 --- a/packages/neon/neon_talk/test/testing.dart +++ b/packages/neon/neon_talk/test/testing.dart @@ -9,6 +9,8 @@ class MockChatMessage extends Mock implements spreed.ChatMessage {} class MockChatMessageWithParent extends Mock implements spreed.ChatMessageWithParent {} +class MockReaction extends Mock implements spreed.Reaction {} + class MockRoomBloc extends Mock implements TalkRoomBloc {} class MockTalkBloc extends Mock implements TalkBloc {} diff --git a/packages/nextcloud/test/fixtures/spreed/reaction/get.regexp b/packages/nextcloud/test/fixtures/spreed/reaction/get.regexp new file mode 100644 index 00000000000..b4252b33092 --- /dev/null +++ b/packages/nextcloud/test/fixtures/spreed/reaction/get.regexp @@ -0,0 +1,16 @@ +POST http://localhost/ocs/v2\.php/apps/spreed/api/v4/room\?roomType=3&invite=&roomName=Test&source=&objectType=&objectId= +accept: application/json +authorization: Bearer mock +ocs-apirequest: true +POST http://localhost/ocs/v2\.php/apps/spreed/api/v1/chat/[a-z0-9]{8}\?message=bla&actorDisplayName=&referenceId=&replyTo=0&silent=0 +accept: application/json +authorization: Bearer mock +ocs-apirequest: true +POST http://localhost/ocs/v2\.php/apps/spreed/api/v1/reaction/[a-z0-9]{8}/[0-9]+\?reaction=%F0%9F%98%80 +accept: application/json +authorization: Bearer mock +ocs-apirequest: true +GET http://localhost/ocs/v2\.php/apps/spreed/api/v1/reaction/[a-z0-9]{8}/[0-9]+ +accept: application/json +authorization: Bearer mock +ocs-apirequest: true \ No newline at end of file diff --git a/packages/nextcloud/test/spreed_test.dart b/packages/nextcloud/test/spreed_test.dart index bc172fd33b2..6d2aa6cc437 100644 --- a/packages/nextcloud/test/spreed_test.dart +++ b/packages/nextcloud/test/spreed_test.dart @@ -503,6 +503,30 @@ void main() { expect(message.reactions, isEmpty); expect(message.reactionsSelf, isNull); }); + + test('Get', () async { + final room = await createTestRoom(); + + final sendMessageResponse = await client1.spreed.chat.sendMessage( + token: room.token, + message: 'bla', + ); + + final addResponse = await client1.spreed.reaction.react( + reaction: '😀', + token: room.token, + messageId: sendMessageResponse.body.ocs.data!.id, + ); + expect(addResponse.body.ocs.data, hasLength(1)); + expect(addResponse.body.ocs.data['😀'], isNotNull); + + final getResponse = await client1.spreed.reaction.getReactions( + token: room.token, + messageId: sendMessageResponse.body.ocs.data!.id, + ); + expect(getResponse.body.ocs.data, hasLength(1)); + expect(getResponse.body.ocs.data['😀'], isNotNull); + }); }); }, retry: retryCount,