feat(ui): add support for translated message in MessageText

Signed-off-by: xsahil03x <[email protected]>
This commit is contained in:
Sahil Kumar
2021-07-27 16:49:04 +05:30
committed by xsahil03x
parent aad60bbc66
commit 467e892079
2 changed files with 178 additions and 49 deletions
@@ -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;
}
}
@@ -8,6 +8,28 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'mocks.dart';
import 'simple_frame.dart';
void expectTextStrings(Iterable<Widget> widgets, List<String> 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<TextSpan>) {
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, <String>['नमस्ते']);
},
);
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, <String>['Hello']);
},
);
});
testGoldens(
'control test',
(WidgetTester tester) async {