diff --git a/packages/neon_framework/packages/talk_app/lib/src/pages/room.dart b/packages/neon_framework/packages/talk_app/lib/src/pages/room.dart index 09f8f7094fe..470397152e8 100644 --- a/packages/neon_framework/packages/talk_app/lib/src/pages/room.dart +++ b/packages/neon_framework/packages/talk_app/lib/src/pages/room.dart @@ -123,6 +123,19 @@ class _TalkRoomPageState extends State { previousChatMessage: previousMessage, ); + if (canReplyToMessage(room, message)) { + child = Dismissible( + key: Key(message.id.toString()), + confirmDismiss: (_) async { + bloc.setReplyChatMessage(message); + + // We don't use the real dismiss feature as we don't want the widget to be removed from the list + return false; + }, + child: child, + ); + } + if (previousMessage == null || (tz.local.translate(previousMessage.timestamp * 1000) ~/ _millisecondsPerDay) != (tz.local.translate(message.timestamp * 1000) ~/ _millisecondsPerDay)) { diff --git a/packages/neon_framework/packages/talk_app/test/room_page_test.dart b/packages/neon_framework/packages/talk_app/test/room_page_test.dart index ba124538f0f..114be2dba46 100644 --- a/packages/neon_framework/packages/talk_app/test/room_page_test.dart +++ b/packages/neon_framework/packages/talk_app/test/room_page_test.dart @@ -236,4 +236,108 @@ void main() { expect(find.byType(TalkMessageInput), findsNothing); await expectLater(find.byType(TestApp), matchesGoldenFile('goldens/room_page_no_chat_permission.png')); }); + + group('Swipe-to-reply', () { + late spreed.ChatMessageWithParent chatMessage; + + setUp(() { + chatMessage = MockChatMessageWithParent(); + when(() => chatMessage.timestamp).thenReturn(0); + when(() => chatMessage.actorId).thenReturn('test'); + when(() => chatMessage.actorType).thenReturn(spreed.ActorType.users); + when(() => chatMessage.actorDisplayName).thenReturn('test'); + when(() => chatMessage.messageType).thenReturn(spreed.MessageType.comment); + when(() => chatMessage.message).thenReturn('abc'); + when(() => chatMessage.reactions).thenReturn(BuiltMap()); + when(() => chatMessage.messageParameters).thenReturn(BuiltMap()); + when(() => chatMessage.id).thenReturn(0); + when(() => chatMessage.isReplyable).thenReturn(true); + + when(() => bloc.messages).thenAnswer( + (_) => BehaviorSubject.seeded( + Result.success( + BuiltList([ + chatMessage, + ]), + ), + ), + ); + + when(() => bloc.reactions).thenAnswer((_) => BehaviorSubject.seeded(BuiltMap())); + }); + + testWidgets('Allowed', (tester) async { + final account = MockAccount(); + + await tester.pumpWidgetWithAccessibility( + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + appThemes: const [ + TalkTheme(), + ], + providers: [ + Provider.value(value: account), + NeonProvider.value(value: bloc), + NeonProvider.value(value: referencesBloc), + ], + child: const TalkRoomPage(), + ), + ); + + expect(find.byType(Dismissible), findsOne); + + await tester.drag(find.byType(TalkCommentMessage), const Offset(1000, 0)); + await tester.pumpAndSettle(); + verify(() => bloc.setReplyChatMessage(chatMessage)).called(1); + }); + + testWidgets('Read-only', (tester) async { + when(() => room.readOnly).thenReturn(1); + + final account = MockAccount(); + + await tester.pumpWidgetWithAccessibility( + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + appThemes: const [ + TalkTheme(), + ], + providers: [ + Provider.value(value: account), + NeonProvider.value(value: bloc), + NeonProvider.value(value: referencesBloc), + ], + child: const TalkRoomPage(), + ), + ); + + expect(find.byType(Dismissible), findsNothing); + }); + + testWidgets('No permission', (tester) async { + when(() => room.permissions).thenReturn(0); + + final account = MockAccount(); + + await tester.pumpWidgetWithAccessibility( + TestApp( + localizationsDelegates: TalkLocalizations.localizationsDelegates, + supportedLocales: TalkLocalizations.supportedLocales, + appThemes: const [ + TalkTheme(), + ], + providers: [ + Provider.value(value: account), + NeonProvider.value(value: bloc), + NeonProvider.value(value: referencesBloc), + ], + child: const TalkRoomPage(), + ), + ); + + expect(find.byType(Dismissible), findsNothing); + }); + }); }