diff --git a/packages/stream_chat_flutter/lib/src/message_text.dart b/packages/stream_chat_flutter/lib/src/message_text.dart index 7e86fed2..a1e3a4f8 100644 --- a/packages/stream_chat_flutter/lib/src/message_text.dart +++ b/packages/stream_chat_flutter/lib/src/message_text.dart @@ -29,61 +29,71 @@ class MessageText extends StatelessWidget { @override Widget build(BuildContext context) { - //print(StreamChat.of(context).user); - final text = _replaceMentions(message.text ?? '').replaceAll('\n', '\n\n'); + final streamChat = StreamChat.of(context); + assert(streamChat.currentUser != null, ''); + return BetterStreamBuilder( + stream: streamChat.currentUserStream.map((it) => it!.language), + initialData: streamChat.currentUser!.language, + builder: (context, language) { + final translatedText = + message.i18n?['${language}_text'] ?? message.text; + final messageText = + _replaceMentions(translatedText ?? '').replaceAll('\n', '\n\n'); + final themeData = Theme.of(context); + return MarkdownBody( + data: messageText, + onTapLink: ( + String link, + String? href, + String title, + ) { + if (link.startsWith('@')) { + final mentionedUser = message.mentionedUsers.firstWhereOrNull( + (u) => '@${u.name}' == link, + ); + if (mentionedUser == null) { + return; + } - final themeData = Theme.of(context); - return MarkdownBody( - data: text, - onTapLink: ( - String link, - String? href, - String title, - ) { - if (link.startsWith('@')) { - final mentionedUser = message.mentionedUsers.firstWhereOrNull( - (u) => '@${u.name}' == link, - ); - if (mentionedUser == null) { - return; - } - - if (onMentionTap != null) { - onMentionTap!(mentionedUser); - } else { - print('tap on ${mentionedUser.name}'); - } - } else { - if (onLinkTap != null) { - onLinkTap!(link); - } else { - launchURL(context, link); - } - } - }, - styleSheet: MarkdownStyleSheet.fromTheme( - themeData.copyWith( - textTheme: themeData.textTheme.apply( - bodyColor: messageTheme.messageText?.color, - decoration: messageTheme.messageText?.decoration, - decorationColor: messageTheme.messageText?.decorationColor, - decorationStyle: messageTheme.messageText?.decorationStyle, - fontFamily: messageTheme.messageText?.fontFamily, + if (onMentionTap != null) { + onMentionTap!(mentionedUser); + } else { + print('tap on ${mentionedUser.name}'); + } + } else { + if (onLinkTap != null) { + onLinkTap!(link); + } else { + launchURL(context, link); + } + } + }, + styleSheet: MarkdownStyleSheet.fromTheme( + themeData.copyWith( + textTheme: themeData.textTheme.apply( + bodyColor: messageTheme.messageText?.color, + decoration: messageTheme.messageText?.decoration, + decorationColor: messageTheme.messageText?.decorationColor, + decorationStyle: messageTheme.messageText?.decorationStyle, + fontFamily: messageTheme.messageText?.fontFamily, + ), + ), + ).copyWith( + a: messageTheme.messageLinks, + p: messageTheme.messageText, ), - ), - ).copyWith( - a: messageTheme.messageLinks, - p: messageTheme.messageText, - ), + ); + }, ); } String _replaceMentions(String text) { - message.mentionedUsers.map((u) => u.name).toSet().forEach((userName) { - // ignore: parameter_assignments - text = text.replaceAll( + var messageTextToRender = text; + for (final user in message.mentionedUsers.toSet()) { + final userName = user.name; + messageTextToRender = messageTextToRender.replaceAll( '@$userName', '[@$userName](@${userName.replaceAll(' ', '')})'); - }); - return text; + } + return messageTextToRender; } } diff --git a/packages/stream_chat_flutter/test/src/message_text_test.dart b/packages/stream_chat_flutter/test/src/message_text_test.dart index 5daa94ba..80ba5a3c 100644 --- a/packages/stream_chat_flutter/test/src/message_text_test.dart +++ b/packages/stream_chat_flutter/test/src/message_text_test.dart @@ -8,6 +8,28 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'mocks.dart'; import 'simple_frame.dart'; +void expectTextStrings(Iterable widgets, List strings) { + var currentString = 0; + for (final widget in widgets) { + if (widget is RichText) { + final span = widget.text as TextSpan; + final text = _extractTextFromTextSpan(span); + expect(text, equals(strings[currentString])); + currentString += 1; + } + } +} + +String _extractTextFromTextSpan(TextSpan span) { + var text = span.text ?? ''; + if (span.children != null) { + for (final child in span.children! as Iterable) { + text += _extractTextFromTextSpan(child); + } + } + return text; +} + void main() { testWidgets( 'it should show correct message text', @@ -54,6 +76,103 @@ void main() { }, ); + group('Message with i18n field', () { + final client = MockClient(); + final clientState = MockClientState(); + final channel = MockChannel(); + final channelState = MockChannelState(); + const messageTheme = MessageTheme(); + + final currentUser = OwnUser( + id: 'sahil', + language: 'hi', + ); + + setUp(() { + when(() => client.state).thenReturn(clientState); + when(() => clientState.currentUser).thenReturn(currentUser); + when(() => clientState.currentUserStream) + .thenAnswer((_) => Stream.value(currentUser)); + + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.isMuted).thenReturn(false); + when(() => channel.isMutedStream).thenAnswer((_) => Stream.value(false)); + }); + + testWidgets( + 'should show correct translated message text as per user language', + (WidgetTester tester) async { + final message = Message( + text: 'Hello', + i18n: const { + 'en_text': 'Hello', + 'hi_text': 'नमस्ते', + 'language': 'en', + }, + ); + + await tester.pumpWidget( + MaterialApp( + home: StreamChat( + client: client, + child: StreamChannel( + channel: channel, + child: Scaffold( + body: MessageText( + message: message, + messageTheme: messageTheme, + ), + ), + ), + ), + ), + ); + + expect(find.byType(MarkdownBody), findsOneWidget); + + final widgets = tester.allWidgets; + expectTextStrings(widgets, ['नमस्ते']); + }, + ); + + testWidgets( + '''should show default text if i18n does not contain translations as per user language''', + (WidgetTester tester) async { + final message = Message( + text: 'Hello', + i18n: const { + 'en_text': 'Hello', + 'fr_text': 'Bonjour', + 'language': 'en', + }, + ); + + await tester.pumpWidget( + MaterialApp( + home: StreamChat( + client: client, + child: StreamChannel( + channel: channel, + child: Scaffold( + body: MessageText( + message: message, + messageTheme: messageTheme, + ), + ), + ), + ), + ), + ); + + expect(find.byType(MarkdownBody), findsOneWidget); + + final widgets = tester.allWidgets; + expectTextStrings(widgets, ['Hello']); + }, + ); + }); + testGoldens( 'control test', (WidgetTester tester) async {