feat(ui): add support for translated message in MessageText
Signed-off-by: xsahil03x <[email protected]>
This commit is contained in:
@@ -29,61 +29,71 @@ class MessageText extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
//print(StreamChat.of(context).user);
|
final streamChat = StreamChat.of(context);
|
||||||
final text = _replaceMentions(message.text ?? '').replaceAll('\n', '\n\n');
|
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);
|
if (onMentionTap != null) {
|
||||||
return MarkdownBody(
|
onMentionTap!(mentionedUser);
|
||||||
data: text,
|
} else {
|
||||||
onTapLink: (
|
print('tap on ${mentionedUser.name}');
|
||||||
String link,
|
}
|
||||||
String? href,
|
} else {
|
||||||
String title,
|
if (onLinkTap != null) {
|
||||||
) {
|
onLinkTap!(link);
|
||||||
if (link.startsWith('@')) {
|
} else {
|
||||||
final mentionedUser = message.mentionedUsers.firstWhereOrNull(
|
launchURL(context, link);
|
||||||
(u) => '@${u.name}' == link,
|
}
|
||||||
);
|
}
|
||||||
if (mentionedUser == null) {
|
},
|
||||||
return;
|
styleSheet: MarkdownStyleSheet.fromTheme(
|
||||||
}
|
themeData.copyWith(
|
||||||
|
textTheme: themeData.textTheme.apply(
|
||||||
if (onMentionTap != null) {
|
bodyColor: messageTheme.messageText?.color,
|
||||||
onMentionTap!(mentionedUser);
|
decoration: messageTheme.messageText?.decoration,
|
||||||
} else {
|
decorationColor: messageTheme.messageText?.decorationColor,
|
||||||
print('tap on ${mentionedUser.name}');
|
decorationStyle: messageTheme.messageText?.decorationStyle,
|
||||||
}
|
fontFamily: messageTheme.messageText?.fontFamily,
|
||||||
} else {
|
),
|
||||||
if (onLinkTap != null) {
|
),
|
||||||
onLinkTap!(link);
|
).copyWith(
|
||||||
} else {
|
a: messageTheme.messageLinks,
|
||||||
launchURL(context, link);
|
p: messageTheme.messageText,
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
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,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String _replaceMentions(String text) {
|
String _replaceMentions(String text) {
|
||||||
message.mentionedUsers.map((u) => u.name).toSet().forEach((userName) {
|
var messageTextToRender = text;
|
||||||
// ignore: parameter_assignments
|
for (final user in message.mentionedUsers.toSet()) {
|
||||||
text = text.replaceAll(
|
final userName = user.name;
|
||||||
|
messageTextToRender = messageTextToRender.replaceAll(
|
||||||
'@$userName', '[@$userName](@${userName.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 'mocks.dart';
|
||||||
import 'simple_frame.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() {
|
void main() {
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'it should show correct message text',
|
'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(
|
testGoldens(
|
||||||
'control test',
|
'control test',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
|
|||||||
Reference in New Issue
Block a user