From c3312c2da21c04b60b76299103dc3795f9b03956 Mon Sep 17 00:00:00 2001 From: groovinchip Date: Tue, 20 Jul 2021 14:35:15 -0400 Subject: [PATCH 01/22] chore: remove the `TranslatedMessage` class and add the `i18n` field to the `Message` class Tests have been updated to reflect the removal of this class --- .../lib/src/core/api/responses.dart | 2 +- .../lib/src/core/api/responses.g.dart | 3 +- .../lib/src/core/models/message.dart | 41 +++++-------------- .../lib/src/core/models/message.g.dart | 17 ++------ .../lib/src/core/models/own_user.g.dart | 3 ++ .../test/src/api/channel_test.dart | 9 ++-- .../stream_chat/test/src/api/client_test.dart | 8 ++-- .../test/src/core/api/message_api_test.dart | 8 ++-- 8 files changed, 35 insertions(+), 56 deletions(-) diff --git a/packages/stream_chat/lib/src/core/api/responses.dart b/packages/stream_chat/lib/src/core/api/responses.dart index e06a213a..033e91b2 100644 --- a/packages/stream_chat/lib/src/core/api/responses.dart +++ b/packages/stream_chat/lib/src/core/api/responses.dart @@ -75,7 +75,7 @@ class QueryChannelsResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class TranslateMessageResponse extends _BaseResponse { /// Translated message - late TranslatedMessage message; + late Message message; /// Create a new instance from a json static TranslateMessageResponse fromJson(Map json) => diff --git a/packages/stream_chat/lib/src/core/api/responses.g.dart b/packages/stream_chat/lib/src/core/api/responses.g.dart index deba55f0..15b64a05 100644 --- a/packages/stream_chat/lib/src/core/api/responses.g.dart +++ b/packages/stream_chat/lib/src/core/api/responses.g.dart @@ -47,8 +47,7 @@ TranslateMessageResponse _$TranslateMessageResponseFromJson( Map json) { return TranslateMessageResponse() ..duration = json['duration'] as String? - ..message = - TranslatedMessage.fromJson(json['message'] as Map); + ..message = Message.fromJson(json['message'] as Map); } QueryMembersResponse _$QueryMembersResponseFromJson(Map json) { diff --git a/packages/stream_chat/lib/src/core/models/message.dart b/packages/stream_chat/lib/src/core/models/message.dart index d394dbdd..0d2094cb 100644 --- a/packages/stream_chat/lib/src/core/models/message.dart +++ b/packages/stream_chat/lib/src/core/models/message.dart @@ -2,8 +2,8 @@ import 'package:equatable/equatable.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:stream_chat/src/core/models/attachment.dart'; import 'package:stream_chat/src/core/models/reaction.dart'; -import 'package:stream_chat/src/core/util/serializer.dart'; import 'package:stream_chat/src/core/models/user.dart'; +import 'package:stream_chat/src/core/util/serializer.dart'; import 'package:uuid/uuid.dart'; part 'message.g.dart'; @@ -73,6 +73,7 @@ class Message extends Equatable { this.extraData = const {}, this.deletedAt, this.status = MessageSendingStatus.sent, + this.i18n, }) : id = id ?? const Uuid().v4(), pinExpires = pinExpires?.toUtc(), createdAt = createdAt ?? DateTime.now(), @@ -218,6 +219,9 @@ class Message extends Equatable { @JsonKey(includeIfNull: false, toJson: Serializer.readOnly) final DateTime? deletedAt; + /// A Map of + final Map? i18n; + /// Known top level fields. /// Useful for [Serializer] methods. static const topLevelFields = [ @@ -248,6 +252,7 @@ class Message extends Equatable { 'pinned_at', 'pin_expires', 'pinned_by', + 'i18n', ]; /// Serialize to json @@ -285,6 +290,7 @@ class Message extends Equatable { User? pinnedBy, Map? extraData, MessageSendingStatus? status, + Map? i18n, }) { assert(() { if (pinExpires is! DateTime && @@ -324,6 +330,7 @@ class Message extends Equatable { pinnedBy: pinnedBy ?? this.pinnedBy, pinExpires: pinExpires == _pinExpires ? this.pinExpires : pinExpires as DateTime?, + i18n: i18n ?? this.i18n, ); } @@ -358,6 +365,7 @@ class Message extends Equatable { pinnedAt: other.pinnedAt, pinExpires: other.pinExpires, pinnedBy: other.pinnedBy, + i18n: other.i18n, ); @override @@ -390,35 +398,6 @@ class Message extends Equatable { pinnedBy, extraData, status, + i18n, ]; } - -/// A translated message -/// It has an additional property called [i18n] -@JsonSerializable() -class TranslatedMessage extends Message { - /// Constructor used for json serialization - TranslatedMessage(this.i18n) : super(); - - /// Create a new instance from a json - factory TranslatedMessage.fromJson(Map json) => - _$TranslatedMessageFromJson( - Serializer.moveToExtraDataFromRoot(json, topLevelFields), - ); - - /// A Map of - final Map? i18n; - - /// Known top level fields. - /// Useful for [Serializer] methods. - static final topLevelFields = [ - 'i18n', - ...Message.topLevelFields, - ]; - - /// Serialize to json - @override - Map toJson() => Serializer.moveFromExtraDataToRoot( - _$TranslatedMessageToJson(this), - ); -} diff --git a/packages/stream_chat/lib/src/core/models/message.g.dart b/packages/stream_chat/lib/src/core/models/message.g.dart index c49e2a6e..8ad1b3e1 100644 --- a/packages/stream_chat/lib/src/core/models/message.g.dart +++ b/packages/stream_chat/lib/src/core/models/message.g.dart @@ -67,6 +67,9 @@ Message _$MessageFromJson(Map json) { deletedAt: json['deleted_at'] == null ? null : DateTime.parse(json['deleted_at'] as String), + i18n: (json['i18n'] as Map?)?.map( + (k, e) => MapEntry(k, e as String), + ), ); } @@ -107,18 +110,6 @@ Map _$MessageToJson(Message instance) { val['pinned_by'] = readonly(instance.pinnedBy); val['extra_data'] = instance.extraData; writeNotNull('deleted_at', readonly(instance.deletedAt)); + val['i18n'] = instance.i18n; return val; } - -TranslatedMessage _$TranslatedMessageFromJson(Map json) { - return TranslatedMessage( - (json['i18n'] as Map?)?.map( - (k, e) => MapEntry(k, e as String), - ), - ); -} - -Map _$TranslatedMessageToJson(TranslatedMessage instance) => - { - 'i18n': instance.i18n, - }; diff --git a/packages/stream_chat/lib/src/core/models/own_user.g.dart b/packages/stream_chat/lib/src/core/models/own_user.g.dart index 26e4786e..465faff2 100644 --- a/packages/stream_chat/lib/src/core/models/own_user.g.dart +++ b/packages/stream_chat/lib/src/core/models/own_user.g.dart @@ -36,5 +36,8 @@ OwnUser _$OwnUserFromJson(Map json) { online: json['online'] as bool? ?? false, extraData: json['extra_data'] as Map? ?? {}, banned: json['banned'] as bool? ?? false, + teams: + (json['teams'] as List?)?.map((e) => e as String).toList() ?? + [], ); } diff --git a/packages/stream_chat/test/src/api/channel_test.dart b/packages/stream_chat/test/src/api/channel_test.dart index 39003fb7..5be55491 100644 --- a/packages/stream_chat/test/src/api/channel_test.dart +++ b/packages/stream_chat/test/src/api/channel_test.dart @@ -1610,9 +1610,12 @@ void main() { const messageId = 'test-message-id'; const language = 'hi'; // Hindi const translatedMessageText = 'नमस्ते'; - final translatedMessage = TranslatedMessage(const { - language: translatedMessageText, - }); + + final translatedMessage = Message( + i18n: const { + language: translatedMessageText, + }, + ); when(() => client.translateMessage(messageId, language)).thenAnswer( (_) async => TranslateMessageResponse()..message = translatedMessage, diff --git a/packages/stream_chat/test/src/api/client_test.dart b/packages/stream_chat/test/src/api/client_test.dart index 423445ec..5df7406b 100644 --- a/packages/stream_chat/test/src/api/client_test.dart +++ b/packages/stream_chat/test/src/api/client_test.dart @@ -2109,9 +2109,11 @@ void main() { const messageId = 'test-message-id'; const language = 'hi'; // Hindi const translatedMessageText = 'नमस्ते'; - final translatedMessage = TranslatedMessage(const { - language: translatedMessageText, - }); + final translatedMessage = Message( + i18n: const { + language: translatedMessageText, + }, + ); when(() => api.message.translateMessage(messageId, language)).thenAnswer( (_) async => TranslateMessageResponse()..message = translatedMessage, diff --git a/packages/stream_chat/test/src/core/api/message_api_test.dart b/packages/stream_chat/test/src/core/api/message_api_test.dart index 1aacb6d2..e89491a2 100644 --- a/packages/stream_chat/test/src/core/api/message_api_test.dart +++ b/packages/stream_chat/test/src/core/api/message_api_test.dart @@ -371,9 +371,11 @@ void main() { final path = '/messages/${message.id}/translate'; const translatedMessageText = 'नमस्ते'; - final translatedMessage = TranslatedMessage(const { - language: translatedMessageText, - }); + final translatedMessage = Message( + i18n: const { + language: translatedMessageText, + }, + ); when(() => client.post( path, From 62b058e675bd7c44d3b20f528a9c10bb1538750a Mon Sep 17 00:00:00 2001 From: groovinchip Date: Tue, 20 Jul 2021 15:07:30 -0400 Subject: [PATCH 02/22] chore: add 'language' field to user.dart --- packages/stream_chat/lib/src/core/models/user.dart | 8 ++++++++ packages/stream_chat/lib/src/core/models/user.g.dart | 2 ++ 2 files changed, 10 insertions(+) diff --git a/packages/stream_chat/lib/src/core/models/user.dart b/packages/stream_chat/lib/src/core/models/user.dart index 1360d14e..87b4f9aa 100644 --- a/packages/stream_chat/lib/src/core/models/user.dart +++ b/packages/stream_chat/lib/src/core/models/user.dart @@ -18,6 +18,7 @@ class User extends Equatable { this.extraData = const {}, this.banned = false, this.teams = const [], + this.language = 'en', }) : createdAt = createdAt ?? DateTime.now(), updatedAt = updatedAt ?? DateTime.now(); @@ -82,6 +83,13 @@ class User extends Equatable { ) final Map extraData; + /// The language this user prefers. + /// + /// Defaults to 'en'. + @JsonKey( + includeIfNull: false, toJson: Serializer.readOnly, defaultValue: 'en') + final String? language; + @override int get hashCode => id.hashCode; diff --git a/packages/stream_chat/lib/src/core/models/user.g.dart b/packages/stream_chat/lib/src/core/models/user.g.dart index befcac03..7b193f04 100644 --- a/packages/stream_chat/lib/src/core/models/user.g.dart +++ b/packages/stream_chat/lib/src/core/models/user.g.dart @@ -25,6 +25,7 @@ User _$UserFromJson(Map json) { teams: (json['teams'] as List?)?.map((e) => e as String).toList() ?? [], + language: json['language'] as String? ?? 'en', ); } @@ -47,5 +48,6 @@ Map _$UserToJson(User instance) { writeNotNull('online', readonly(instance.online)); writeNotNull('banned', readonly(instance.banned)); val['extra_data'] = instance.extraData; + writeNotNull('language', readonly(instance.language)); return val; } From c6dd3b0cd7aa254fbdd353e0aaed8ddc60830717 Mon Sep 17 00:00:00 2001 From: groovinchip Date: Tue, 20 Jul 2021 15:13:58 -0400 Subject: [PATCH 03/22] test: add 'language' field test to user_test.dart --- packages/stream_chat/test/src/core/models/user_test.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/stream_chat/test/src/core/models/user_test.dart b/packages/stream_chat/test/src/core/models/user_test.dart index ce488cab..5319c02b 100644 --- a/packages/stream_chat/test/src/core/models/user_test.dart +++ b/packages/stream_chat/test/src/core/models/user_test.dart @@ -29,6 +29,7 @@ void main() { expect(newUser.id, user.id); expect(newUser.role, user.role); expect(newUser.name, user.name); + expect(newUser.language, user.language); newUser = user.copyWith( id: 'test', @@ -41,6 +42,7 @@ void main() { expect(newUser.id, 'test'); expect(newUser.role, 'test'); expect(newUser.name, 'test'); + expect(newUser.language, 'en'); }); }); } From 0943980b51ec265fbf4ef5de342295ace8eadda7 Mon Sep 17 00:00:00 2001 From: groovinchip Date: Tue, 20 Jul 2021 15:15:09 -0400 Subject: [PATCH 04/22] test: add 'i18n' field test to message_test.dart --- packages/stream_chat/test/src/core/models/message_test.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stream_chat/test/src/core/models/message_test.dart b/packages/stream_chat/test/src/core/models/message_test.dart index a5571430..b5e6ddcc 100644 --- a/packages/stream_chat/test/src/core/models/message_test.dart +++ b/packages/stream_chat/test/src/core/models/message_test.dart @@ -28,6 +28,7 @@ void main() { expect(message.pinnedAt, null); expect(message.pinExpires, null); expect(message.pinnedBy, null); + expect(message.i18n, null); }); test('should serialize to json correctly', () { From 0210e2d49f5ce6309b2f02b8a5cababb776ac253 Mon Sep 17 00:00:00 2001 From: groovinchip Date: Tue, 20 Jul 2021 15:19:37 -0400 Subject: [PATCH 05/22] chore: fill out message.i18n docs more --- packages/stream_chat/lib/src/core/models/message.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat/lib/src/core/models/message.dart b/packages/stream_chat/lib/src/core/models/message.dart index 0d2094cb..7244f617 100644 --- a/packages/stream_chat/lib/src/core/models/message.dart +++ b/packages/stream_chat/lib/src/core/models/message.dart @@ -219,7 +219,7 @@ class Message extends Equatable { @JsonKey(includeIfNull: false, toJson: Serializer.readOnly) final DateTime? deletedAt; - /// A Map of + /// A Map of translations. final Map? i18n; /// Known top level fields. From 247efb4e8f49b580036fd68a4a55c83011200cca Mon Sep 17 00:00:00 2001 From: groovinchip Date: Tue, 20 Jul 2021 15:22:15 -0400 Subject: [PATCH 06/22] chore: add missing JsonKey to Message.i18n --- packages/stream_chat/lib/src/core/models/message.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stream_chat/lib/src/core/models/message.dart b/packages/stream_chat/lib/src/core/models/message.dart index 7244f617..d5435222 100644 --- a/packages/stream_chat/lib/src/core/models/message.dart +++ b/packages/stream_chat/lib/src/core/models/message.dart @@ -220,6 +220,7 @@ class Message extends Equatable { final DateTime? deletedAt; /// A Map of translations. + @JsonKey(includeIfNull: false, toJson: Serializer.readOnly) final Map? i18n; /// Known top level fields. From 25aa6f2a905f60a54b893864772d7aec82329d9c Mon Sep 17 00:00:00 2001 From: groovinchip Date: Tue, 20 Jul 2021 15:23:25 -0400 Subject: [PATCH 07/22] chore: update CHANGELOG.md --- packages/stream_chat/CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index e05599e1..f4a6a136 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -1,3 +1,13 @@ +## Upcoming + +🛑️ Removed +- The `MessageTranslation` class has been removed. Use the new `i18n` field in the `Message` class instead. + +✅ Added + +- The `Message` class now has an `i18n` field for translations +- The `User` class now has a `language` field for the user's language preference. + ## 2.0.0 🛑️ Breaking Changes from `1.5.3` From f109a479091cb306a6fb53fffeacb642fac05736 Mon Sep 17 00:00:00 2001 From: groovinchip Date: Wed, 21 Jul 2021 12:28:04 -0400 Subject: [PATCH 08/22] fix: JsonKey for User.language --- packages/stream_chat/lib/src/core/models/user.dart | 3 +-- packages/stream_chat/lib/src/core/models/user.g.dart | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/stream_chat/lib/src/core/models/user.dart b/packages/stream_chat/lib/src/core/models/user.dart index 87b4f9aa..bd4bf226 100644 --- a/packages/stream_chat/lib/src/core/models/user.dart +++ b/packages/stream_chat/lib/src/core/models/user.dart @@ -86,8 +86,7 @@ class User extends Equatable { /// The language this user prefers. /// /// Defaults to 'en'. - @JsonKey( - includeIfNull: false, toJson: Serializer.readOnly, defaultValue: 'en') + @JsonKey(defaultValue: 'en') final String? language; @override diff --git a/packages/stream_chat/lib/src/core/models/user.g.dart b/packages/stream_chat/lib/src/core/models/user.g.dart index 7b193f04..85fdfef9 100644 --- a/packages/stream_chat/lib/src/core/models/user.g.dart +++ b/packages/stream_chat/lib/src/core/models/user.g.dart @@ -48,6 +48,6 @@ Map _$UserToJson(User instance) { writeNotNull('online', readonly(instance.online)); writeNotNull('banned', readonly(instance.banned)); val['extra_data'] = instance.extraData; - writeNotNull('language', readonly(instance.language)); + val['language'] = instance.language; return val; } From 4f768024c663e17876b8962fce9bb59910188c6e Mon Sep 17 00:00:00 2001 From: groovinchip Date: Thu, 22 Jul 2021 11:08:10 -0400 Subject: [PATCH 09/22] chore: Add language to OwnUser --- packages/stream_chat/lib/src/core/models/own_user.dart | 6 ++++++ packages/stream_chat/lib/src/core/models/user.dart | 4 ++++ packages/stream_chat_flutter/example/lib/main.dart | 6 +++++- packages/stream_chat_flutter/lib/src/message_text.dart | 1 + 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/stream_chat/lib/src/core/models/own_user.dart b/packages/stream_chat/lib/src/core/models/own_user.dart index 679abb47..0eada8d0 100644 --- a/packages/stream_chat/lib/src/core/models/own_user.dart +++ b/packages/stream_chat/lib/src/core/models/own_user.dart @@ -27,6 +27,7 @@ class OwnUser extends User { Map extraData = const {}, bool banned = false, List teams = const [], + String? language, }) : super( id: id, role: role, @@ -37,6 +38,7 @@ class OwnUser extends User { extraData: extraData, banned: banned, teams: teams, + language: language, ); /// Create a new instance from a json @@ -54,6 +56,7 @@ class OwnUser extends User { banned: user.banned, extraData: user.extraData, teams: user.teams, + language: user.language, ); /// Creates a copy of [OwnUser] with specified attributes overridden. @@ -73,6 +76,7 @@ class OwnUser extends User { List? mutes, int? totalUnreadCount, int? unreadChannels, + String? language, }) => OwnUser( id: id ?? this.id, @@ -89,6 +93,7 @@ class OwnUser extends User { mutes: mutes ?? this.mutes, totalUnreadCount: totalUnreadCount ?? this.totalUnreadCount, unreadChannels: unreadChannels ?? this.unreadChannels, + language: language ?? this.language, ); /// Returns a new [OwnUser] that is a combination of this ownUser @@ -113,6 +118,7 @@ class OwnUser extends User { totalUnreadCount: other.totalUnreadCount, unreadChannels: other.unreadChannels, updatedAt: other.updatedAt, + language: other.language, ); } diff --git a/packages/stream_chat/lib/src/core/models/user.dart b/packages/stream_chat/lib/src/core/models/user.dart index bd4bf226..12ab162d 100644 --- a/packages/stream_chat/lib/src/core/models/user.dart +++ b/packages/stream_chat/lib/src/core/models/user.dart @@ -37,6 +37,7 @@ class User extends Equatable { 'online', 'banned', 'teams', + 'language', ]; /// User id @@ -126,6 +127,7 @@ class User extends Equatable { Map? extraData, bool? banned, List? teams, + String? language, }) => User( id: id ?? this.id, @@ -137,6 +139,7 @@ class User extends Equatable { extraData: extraData ?? this.extraData, banned: banned ?? this.banned, teams: teams ?? this.teams, + language: language ?? this.language, ); @override @@ -150,5 +153,6 @@ class User extends Equatable { online, banned, extraData, + language, ]; } diff --git a/packages/stream_chat_flutter/example/lib/main.dart b/packages/stream_chat_flutter/example/lib/main.dart index 89362d81..42891aa7 100644 --- a/packages/stream_chat_flutter/example/lib/main.dart +++ b/packages/stream_chat_flutter/example/lib/main.dart @@ -24,11 +24,15 @@ void main() async { /// Please see the following for more information: /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/ await client.connectUser( - User(id: 'super-band-9'), + User(id: 'super-band-9', language: 'fr'), 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.' '0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A', ); + client.on().listen((e) { + print('e.user.language ${e.me?.language}'); + }); + final channel = client.channel('messaging', id: 'godevs'); await channel.watch(); diff --git a/packages/stream_chat_flutter/lib/src/message_text.dart b/packages/stream_chat_flutter/lib/src/message_text.dart index 6d006085..7e86fed2 100644 --- a/packages/stream_chat_flutter/lib/src/message_text.dart +++ b/packages/stream_chat_flutter/lib/src/message_text.dart @@ -29,6 +29,7 @@ class MessageText extends StatelessWidget { @override Widget build(BuildContext context) { + //print(StreamChat.of(context).user); final text = _replaceMentions(message.text ?? '').replaceAll('\n', '\n\n'); final themeData = Theme.of(context); From 9ebd8c7876be9e3e475d3f9da72859f1a81f0d09 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Fri, 23 Jul 2021 09:42:59 +0200 Subject: [PATCH 10/22] fix(llc): ignore healthcheck user.me objects --- .../stream_chat/lib/src/client/client.dart | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/stream_chat/lib/src/client/client.dart b/packages/stream_chat/lib/src/client/client.dart index 050d36f1..514dd086 100644 --- a/packages/stream_chat/lib/src/client/client.dart +++ b/packages/stream_chat/lib/src/client/client.dart @@ -388,9 +388,6 @@ class StreamChatClient { } void _handleHealthCheckEvent(Event event) { - final user = event.me; - if (user != null) state.user = user; - final connectionId = event.connectionId; if (connectionId != null) { _connectionIdManager.setConnectionId(connectionId); @@ -1333,16 +1330,15 @@ class ClientState { _subscriptions.addAll([ _client .on() - .where((event) => event.me != null) - .map((e) => e.me) + .where((event) => + event.me != null && event.type != EventType.healthCheck) + .map((e) => e.me!) .listen((user) { - _userController.add(user); - final totalUnreadCount = user?.totalUnreadCount; - if (totalUnreadCount != null) { - _totalUnreadCountController.add(totalUnreadCount); - } + this.user = user; + final totalUnreadCount = user.totalUnreadCount; + _totalUnreadCountController.add(totalUnreadCount); - final unreadChannels = user?.unreadChannels; + final unreadChannels = user.unreadChannels; if (unreadChannels != null) { _unreadChannelsController.add(unreadChannels); } From c5782e0275c2b03e1894ca3634b1461a42e57c7f Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 27 Jul 2021 16:47:24 +0530 Subject: [PATCH 11/22] chore(llc): re-generate model classes Signed-off-by: xsahil03x --- .../lib/src/core/models/message.g.dart | 2 +- .../lib/src/core/models/own_user.dart | 7 ++---- .../lib/src/core/models/own_user.g.dart | 1 + .../stream_chat/lib/src/core/models/user.dart | 23 ++----------------- 4 files changed, 6 insertions(+), 27 deletions(-) diff --git a/packages/stream_chat/lib/src/core/models/message.g.dart b/packages/stream_chat/lib/src/core/models/message.g.dart index 8ad1b3e1..5fb559f0 100644 --- a/packages/stream_chat/lib/src/core/models/message.g.dart +++ b/packages/stream_chat/lib/src/core/models/message.g.dart @@ -110,6 +110,6 @@ Map _$MessageToJson(Message instance) { val['pinned_by'] = readonly(instance.pinnedBy); val['extra_data'] = instance.extraData; writeNotNull('deleted_at', readonly(instance.deletedAt)); - val['i18n'] = instance.i18n; + writeNotNull('i18n', readonly(instance.i18n)); return val; } diff --git a/packages/stream_chat/lib/src/core/models/own_user.dart b/packages/stream_chat/lib/src/core/models/own_user.dart index 0eada8d0..116a2f84 100644 --- a/packages/stream_chat/lib/src/core/models/own_user.dart +++ b/packages/stream_chat/lib/src/core/models/own_user.dart @@ -27,7 +27,7 @@ class OwnUser extends User { Map extraData = const {}, bool banned = false, List teams = const [], - String? language, + String language = 'en', }) : super( id: id, role: role, @@ -99,10 +99,7 @@ class OwnUser extends User { /// Returns a new [OwnUser] that is a combination of this ownUser /// and the given [other] ownUser. OwnUser merge(OwnUser? other) { - if (other == null) { - return this; - } - + if (other == null) return this; return copyWith( banned: other.banned, channelMutes: other.channelMutes, diff --git a/packages/stream_chat/lib/src/core/models/own_user.g.dart b/packages/stream_chat/lib/src/core/models/own_user.g.dart index 465faff2..5d80cc1c 100644 --- a/packages/stream_chat/lib/src/core/models/own_user.g.dart +++ b/packages/stream_chat/lib/src/core/models/own_user.g.dart @@ -39,5 +39,6 @@ OwnUser _$OwnUserFromJson(Map json) { teams: (json['teams'] as List?)?.map((e) => e as String).toList() ?? [], + language: json['language'] as String? ?? 'en', ); } diff --git a/packages/stream_chat/lib/src/core/models/user.dart b/packages/stream_chat/lib/src/core/models/user.dart index 12ab162d..2cdb6dd6 100644 --- a/packages/stream_chat/lib/src/core/models/user.dart +++ b/packages/stream_chat/lib/src/core/models/user.dart @@ -88,10 +88,7 @@ class User extends Equatable { /// /// Defaults to 'en'. @JsonKey(defaultValue: 'en') - final String? language; - - @override - int get hashCode => id.hashCode; + final String language; /// Shortcut for user name String get name { @@ -106,11 +103,6 @@ class User extends Equatable { static List? toIds(List? users) => users?.map((u) => u.id).toList(); - @override - bool operator ==(Object other) => - identical(this, other) || - other is User && runtimeType == other.runtimeType && id == other.id; - /// Serialize to json Map toJson() => Serializer.moveFromExtraDataToRoot( _$UserToJson(this), @@ -143,16 +135,5 @@ class User extends Equatable { ); @override - List get props => [ - id, - role, - teams, - createdAt, - updatedAt, - lastActive, - online, - banned, - extraData, - language, - ]; + List get props => [id]; } From aad60bbc66edc5540b561e2ed5d8bee748f13b63 Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Tue, 27 Jul 2021 16:48:25 +0530 Subject: [PATCH 12/22] chore(llc): remove redundant `event.me` listener Signed-off-by: xsahil03x --- .../stream_chat/lib/src/client/client.dart | 22 +++---------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/packages/stream_chat/lib/src/client/client.dart b/packages/stream_chat/lib/src/client/client.dart index 3941beac..e1212e83 100644 --- a/packages/stream_chat/lib/src/client/client.dart +++ b/packages/stream_chat/lib/src/client/client.dart @@ -318,9 +318,7 @@ class StreamChatClient { final ownUser = OwnUser.fromUser(user); state.currentUser = ownUser; - if (!connectWebSocket) { - return ownUser; - } + if (!connectWebSocket) return ownUser; try { if (_originalChatPersistenceClient != null) { @@ -1336,21 +1334,6 @@ class ClientState { /// Creates a new instance listening to events and updating the state ClientState(this._client) { _subscriptions.addAll([ - _client - .on() - .where((event) => - event.me != null && event.type != EventType.healthCheck) - .map((e) => e.me!) - .listen((user) { - currentUser = user; - final totalUnreadCount = user.totalUnreadCount; - _totalUnreadCountController.add(totalUnreadCount); - - final unreadChannels = user.unreadChannels; - if (unreadChannels != null) { - _unreadChannelsController.add(unreadChannels); - } - }), _client .on() .map((event) => event.unreadChannels) @@ -1413,7 +1396,8 @@ class ClientState { final StreamChatClient _client; - /// Update user information + /// Sets the user currently interacting with the client + /// note: this fully overrides the [currentUser] set currentUser(OwnUser? user) { _currentUserController.add(user); } From 467e892079a043d34349b1423fd5f252dad00109 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 27 Jul 2021 16:49:04 +0530 Subject: [PATCH 13/22] feat(ui): add support for translated message in MessageText Signed-off-by: xsahil03x --- .../lib/src/message_text.dart | 108 ++++++++-------- .../test/src/message_text_test.dart | 119 ++++++++++++++++++ 2 files changed, 178 insertions(+), 49 deletions(-) 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 { From 7449540f3483219322e3eb062d3ce463422e86de Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 27 Jul 2021 18:35:15 +0530 Subject: [PATCH 14/22] fix(llc, ui): fix failing tests Signed-off-by: xsahil03x --- .../lib/src/core/models/message.dart | 2 +- .../lib/src/core/models/message.g.dart | 2 +- .../stream_chat/lib/src/core/models/user.dart | 6 +++- .../lib/src/core/models/user.g.dart | 2 +- .../test/src/message_text_test.dart | 28 +++++++++++++------ 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/packages/stream_chat/lib/src/core/models/message.dart b/packages/stream_chat/lib/src/core/models/message.dart index d5435222..6ad8721c 100644 --- a/packages/stream_chat/lib/src/core/models/message.dart +++ b/packages/stream_chat/lib/src/core/models/message.dart @@ -220,7 +220,7 @@ class Message extends Equatable { final DateTime? deletedAt; /// A Map of translations. - @JsonKey(includeIfNull: false, toJson: Serializer.readOnly) + @JsonKey(includeIfNull: false) final Map? i18n; /// Known top level fields. diff --git a/packages/stream_chat/lib/src/core/models/message.g.dart b/packages/stream_chat/lib/src/core/models/message.g.dart index 5fb559f0..25254e2c 100644 --- a/packages/stream_chat/lib/src/core/models/message.g.dart +++ b/packages/stream_chat/lib/src/core/models/message.g.dart @@ -110,6 +110,6 @@ Map _$MessageToJson(Message instance) { val['pinned_by'] = readonly(instance.pinnedBy); val['extra_data'] = instance.extraData; writeNotNull('deleted_at', readonly(instance.deletedAt)); - writeNotNull('i18n', readonly(instance.i18n)); + writeNotNull('i18n', instance.i18n); return val; } diff --git a/packages/stream_chat/lib/src/core/models/user.dart b/packages/stream_chat/lib/src/core/models/user.dart index 2cdb6dd6..dd4abcc6 100644 --- a/packages/stream_chat/lib/src/core/models/user.dart +++ b/packages/stream_chat/lib/src/core/models/user.dart @@ -87,7 +87,11 @@ class User extends Equatable { /// The language this user prefers. /// /// Defaults to 'en'. - @JsonKey(defaultValue: 'en') + @JsonKey( + includeIfNull: false, + toJson: Serializer.readOnly, + defaultValue: 'en', + ) final String language; /// Shortcut for user name diff --git a/packages/stream_chat/lib/src/core/models/user.g.dart b/packages/stream_chat/lib/src/core/models/user.g.dart index 85fdfef9..7b193f04 100644 --- a/packages/stream_chat/lib/src/core/models/user.g.dart +++ b/packages/stream_chat/lib/src/core/models/user.g.dart @@ -48,6 +48,6 @@ Map _$UserToJson(User instance) { writeNotNull('online', readonly(instance.online)); writeNotNull('banned', readonly(instance.banned)); val['extra_data'] = instance.extraData; - val['language'] = instance.language; + writeNotNull('language', readonly(instance.language)); return val; } 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 80ba5a3c..5bfe0a34 100644 --- a/packages/stream_chat_flutter/test/src/message_text_test.dart +++ b/packages/stream_chat_flutter/test/src/message_text_test.dart @@ -34,6 +34,7 @@ void main() { testWidgets( 'it should show correct message text', (WidgetTester tester) async { + final currentUser = OwnUser(id: 'user-id'); final client = MockClient(); final clientState = MockClientState(); final channel = MockChannel(); @@ -43,7 +44,9 @@ void main() { final streamTheme = StreamChatThemeData.fromTheme(themeData); when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); + when(() => clientState.currentUser).thenReturn(currentUser); + when(() => clientState.currentUserStream) + .thenAnswer((_) => Stream.value(currentUser)); when(() => channel.lastMessageAt).thenReturn(lastMessageAt); when(() => channel.state).thenReturn(channelState); when(() => channel.client).thenReturn(client); @@ -176,6 +179,7 @@ void main() { testGoldens( 'control test', (WidgetTester tester) async { + final currentUser = OwnUser(id: 'user-id'); final client = MockClient(); final clientState = MockClientState(); final channel = MockChannel(); @@ -185,7 +189,9 @@ void main() { final streamTheme = StreamChatThemeData.fromTheme(themeData); when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); + when(() => clientState.currentUser).thenReturn(currentUser); + when(() => clientState.currentUserStream) + .thenAnswer((_) => Stream.value(currentUser)); when(() => channel.lastMessageAt).thenReturn(lastMessageAt); when(() => channel.state).thenReturn(channelState); when(() => channel.client).thenReturn(client); @@ -209,14 +215,18 @@ cool.'''; await tester.pumpWidgetBuilder( materialAppWrapper()(SimpleFrame( - child: StreamChannel( - channel: channel, - child: Scaffold( - body: MessageText( - message: Message( - text: messageText, + child: StreamChat( + client: client, + connectivityStream: Stream.value(ConnectivityResult.wifi), + child: StreamChannel( + channel: channel, + child: Scaffold( + body: MessageText( + message: Message( + text: messageText, + ), + messageTheme: streamTheme.otherMessageTheme, ), - messageTheme: streamTheme.otherMessageTheme, ), ), ), From 658f7f2504d0674a89a276b885d92cfd627f2573 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 27 Jul 2021 20:06:06 +0530 Subject: [PATCH 15/22] feat(persistence): add support for message.i18n Signed-off-by: xsahil03x --- .../lib/src/db/moor_chat_database.g.dart | 159 +++++++++++++++--- .../lib/src/entity/messages.dart | 3 + .../lib/src/entity/users.dart | 3 + .../lib/src/mapper/message_mapper.dart | 2 + .../lib/src/mapper/pinned_message_mapper.dart | 4 + .../lib/src/mapper/user_mapper.dart | 2 + .../test/src/dao/message_dao_test.dart | 21 ++- .../test/src/dao/user_dao_test.dart | 1 + .../test/src/mapper/message_mapper_test.dart | 16 +- .../mapper/pinned_message_mapper_test.dart | 16 +- .../test/src/mapper/user_mapper_test.dart | 4 + 11 files changed, 205 insertions(+), 26 deletions(-) diff --git a/packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart b/packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart index 6a5bc38a..6274e932 100644 --- a/packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart +++ b/packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart @@ -647,6 +647,9 @@ class MessageEntity extends DataClass implements Insertable { /// The channel cid of which this message is part of final String? channelCid; + /// A Map of [messageText] translations. + final Map? i18n; + /// Message custom extraData final Map? extraData; MessageEntity( @@ -673,6 +676,7 @@ class MessageEntity extends DataClass implements Insertable { this.pinExpires, this.pinnedByUserId, this.channelCid, + this.i18n, this.extraData}); factory MessageEntity.fromData( Map data, GeneratedDatabase db, @@ -725,7 +729,9 @@ class MessageEntity extends DataClass implements Insertable { .mapFromDatabaseResponse(data['${effectivePrefix}pinned_by_user_id']), channelCid: const StringType() .mapFromDatabaseResponse(data['${effectivePrefix}channel_cid']), - extraData: $MessagesTable.$converter5.mapToDart(const StringType() + i18n: $MessagesTable.$converter5.mapToDart(const StringType() + .mapFromDatabaseResponse(data['${effectivePrefix}i18n'])), + extraData: $MessagesTable.$converter6.mapToDart(const StringType() .mapFromDatabaseResponse(data['${effectivePrefix}extra_data'])), ); } @@ -797,8 +803,12 @@ class MessageEntity extends DataClass implements Insertable { if (!nullToAbsent || channelCid != null) { map['channel_cid'] = Variable(channelCid); } - if (!nullToAbsent || extraData != null) { + if (!nullToAbsent || i18n != null) { final converter = $MessagesTable.$converter5; + map['i18n'] = Variable(converter.mapToSql(i18n)); + } + if (!nullToAbsent || extraData != null) { + final converter = $MessagesTable.$converter6; map['extra_data'] = Variable(converter.mapToSql(extraData)); } return map; @@ -833,6 +843,7 @@ class MessageEntity extends DataClass implements Insertable { pinExpires: serializer.fromJson(json['pinExpires']), pinnedByUserId: serializer.fromJson(json['pinnedByUserId']), channelCid: serializer.fromJson(json['channelCid']), + i18n: serializer.fromJson?>(json['i18n']), extraData: serializer.fromJson?>(json['extraData']), ); } @@ -863,6 +874,7 @@ class MessageEntity extends DataClass implements Insertable { 'pinExpires': serializer.toJson(pinExpires), 'pinnedByUserId': serializer.toJson(pinnedByUserId), 'channelCid': serializer.toJson(channelCid), + 'i18n': serializer.toJson?>(i18n), 'extraData': serializer.toJson?>(extraData), }; } @@ -891,6 +903,7 @@ class MessageEntity extends DataClass implements Insertable { Value pinExpires = const Value.absent(), Value pinnedByUserId = const Value.absent(), Value channelCid = const Value.absent(), + Value?> i18n = const Value.absent(), Value?> extraData = const Value.absent()}) => MessageEntity( id: id ?? this.id, @@ -922,6 +935,7 @@ class MessageEntity extends DataClass implements Insertable { pinnedByUserId: pinnedByUserId.present ? pinnedByUserId.value : this.pinnedByUserId, channelCid: channelCid.present ? channelCid.value : this.channelCid, + i18n: i18n.present ? i18n.value : this.i18n, extraData: extraData.present ? extraData.value : this.extraData, ); @override @@ -950,6 +964,7 @@ class MessageEntity extends DataClass implements Insertable { ..write('pinExpires: $pinExpires, ') ..write('pinnedByUserId: $pinnedByUserId, ') ..write('channelCid: $channelCid, ') + ..write('i18n: $i18n, ') ..write('extraData: $extraData') ..write(')')) .toString(); @@ -998,7 +1013,7 @@ class MessageEntity extends DataClass implements Insertable { .hashCode, $mrjc( pinned.hashCode, - $mrjc(pinnedAt.hashCode, $mrjc(pinExpires.hashCode, $mrjc(pinnedByUserId.hashCode, $mrjc(channelCid.hashCode, extraData.hashCode)))))))))))))))))))))))); + $mrjc(pinnedAt.hashCode, $mrjc(pinExpires.hashCode, $mrjc(pinnedByUserId.hashCode, $mrjc(channelCid.hashCode, $mrjc(i18n.hashCode, extraData.hashCode))))))))))))))))))))))))); @override bool operator ==(Object other) => identical(this, other) || @@ -1026,6 +1041,7 @@ class MessageEntity extends DataClass implements Insertable { other.pinExpires == this.pinExpires && other.pinnedByUserId == this.pinnedByUserId && other.channelCid == this.channelCid && + other.i18n == this.i18n && other.extraData == this.extraData); } @@ -1053,6 +1069,7 @@ class MessagesCompanion extends UpdateCompanion { final Value pinExpires; final Value pinnedByUserId; final Value channelCid; + final Value?> i18n; final Value?> extraData; const MessagesCompanion({ this.id = const Value.absent(), @@ -1078,6 +1095,7 @@ class MessagesCompanion extends UpdateCompanion { this.pinExpires = const Value.absent(), this.pinnedByUserId = const Value.absent(), this.channelCid = const Value.absent(), + this.i18n = const Value.absent(), this.extraData = const Value.absent(), }); MessagesCompanion.insert({ @@ -1104,6 +1122,7 @@ class MessagesCompanion extends UpdateCompanion { this.pinExpires = const Value.absent(), this.pinnedByUserId = const Value.absent(), this.channelCid = const Value.absent(), + this.i18n = const Value.absent(), this.extraData = const Value.absent(), }) : id = Value(id), attachments = Value(attachments), @@ -1132,6 +1151,7 @@ class MessagesCompanion extends UpdateCompanion { Expression? pinExpires, Expression? pinnedByUserId, Expression? channelCid, + Expression?>? i18n, Expression?>? extraData, }) { return RawValuesInsertable({ @@ -1158,6 +1178,7 @@ class MessagesCompanion extends UpdateCompanion { if (pinExpires != null) 'pin_expires': pinExpires, if (pinnedByUserId != null) 'pinned_by_user_id': pinnedByUserId, if (channelCid != null) 'channel_cid': channelCid, + if (i18n != null) 'i18n': i18n, if (extraData != null) 'extra_data': extraData, }); } @@ -1186,6 +1207,7 @@ class MessagesCompanion extends UpdateCompanion { Value? pinExpires, Value? pinnedByUserId, Value? channelCid, + Value?>? i18n, Value?>? extraData}) { return MessagesCompanion( id: id ?? this.id, @@ -1211,6 +1233,7 @@ class MessagesCompanion extends UpdateCompanion { pinExpires: pinExpires ?? this.pinExpires, pinnedByUserId: pinnedByUserId ?? this.pinnedByUserId, channelCid: channelCid ?? this.channelCid, + i18n: i18n ?? this.i18n, extraData: extraData ?? this.extraData, ); } @@ -1296,8 +1319,12 @@ class MessagesCompanion extends UpdateCompanion { if (channelCid.present) { map['channel_cid'] = Variable(channelCid.value); } - if (extraData.present) { + if (i18n.present) { final converter = $MessagesTable.$converter5; + map['i18n'] = Variable(converter.mapToSql(i18n.value)); + } + if (extraData.present) { + final converter = $MessagesTable.$converter6; map['extra_data'] = Variable(converter.mapToSql(extraData.value)); } @@ -1330,6 +1357,7 @@ class MessagesCompanion extends UpdateCompanion { ..write('pinExpires: $pinExpires, ') ..write('pinnedByUserId: $pinnedByUserId, ') ..write('channelCid: $channelCid, ') + ..write('i18n: $i18n, ') ..write('extraData: $extraData') ..write(')')) .toString(); @@ -1468,11 +1496,16 @@ class $MessagesTable extends Messages requiredDuringInsert: false, $customConstraints: 'NULLABLE REFERENCES channels(cid) ON DELETE CASCADE'); + final VerificationMeta _i18nMeta = const VerificationMeta('i18n'); + late final GeneratedColumnWithTypeConverter, String?> + i18n = GeneratedColumn('i18n', aliasedName, true, + typeName: 'TEXT', requiredDuringInsert: false) + .withConverter>($MessagesTable.$converter5); final VerificationMeta _extraDataMeta = const VerificationMeta('extraData'); late final GeneratedColumnWithTypeConverter, String?> extraData = GeneratedColumn('extra_data', aliasedName, true, typeName: 'TEXT', requiredDuringInsert: false) - .withConverter>($MessagesTable.$converter5); + .withConverter>($MessagesTable.$converter6); @override List get $columns => [ id, @@ -1498,6 +1531,7 @@ class $MessagesTable extends Messages pinExpires, pinnedByUserId, channelCid, + i18n, extraData ]; @override @@ -1601,6 +1635,7 @@ class $MessagesTable extends Messages channelCid.isAcceptableOrUnknown( data['channel_cid']!, _channelCidMeta)); } + context.handle(_i18nMeta, const VerificationResult.success()); context.handle(_extraDataMeta, const VerificationResult.success()); return context; } @@ -1628,7 +1663,9 @@ class $MessagesTable extends Messages MapConverter(); static TypeConverter, String> $converter4 = MapConverter(); - static TypeConverter, String> $converter5 = + static TypeConverter, String> $converter5 = + MapConverter(); + static TypeConverter, String> $converter6 = MapConverter(); } @@ -1704,6 +1741,9 @@ class PinnedMessageEntity extends DataClass /// The channel cid of which this message is part of final String? channelCid; + /// A Map of [messageText] translations. + final Map? i18n; + /// Message custom extraData final Map? extraData; PinnedMessageEntity( @@ -1730,6 +1770,7 @@ class PinnedMessageEntity extends DataClass this.pinExpires, this.pinnedByUserId, this.channelCid, + this.i18n, this.extraData}); factory PinnedMessageEntity.fromData( Map data, GeneratedDatabase db, @@ -1785,7 +1826,9 @@ class PinnedMessageEntity extends DataClass .mapFromDatabaseResponse(data['${effectivePrefix}pinned_by_user_id']), channelCid: const StringType() .mapFromDatabaseResponse(data['${effectivePrefix}channel_cid']), - extraData: $PinnedMessagesTable.$converter5.mapToDart(const StringType() + i18n: $PinnedMessagesTable.$converter5.mapToDart(const StringType() + .mapFromDatabaseResponse(data['${effectivePrefix}i18n'])), + extraData: $PinnedMessagesTable.$converter6.mapToDart(const StringType() .mapFromDatabaseResponse(data['${effectivePrefix}extra_data'])), ); } @@ -1857,8 +1900,12 @@ class PinnedMessageEntity extends DataClass if (!nullToAbsent || channelCid != null) { map['channel_cid'] = Variable(channelCid); } - if (!nullToAbsent || extraData != null) { + if (!nullToAbsent || i18n != null) { final converter = $PinnedMessagesTable.$converter5; + map['i18n'] = Variable(converter.mapToSql(i18n)); + } + if (!nullToAbsent || extraData != null) { + final converter = $PinnedMessagesTable.$converter6; map['extra_data'] = Variable(converter.mapToSql(extraData)); } return map; @@ -1893,6 +1940,7 @@ class PinnedMessageEntity extends DataClass pinExpires: serializer.fromJson(json['pinExpires']), pinnedByUserId: serializer.fromJson(json['pinnedByUserId']), channelCid: serializer.fromJson(json['channelCid']), + i18n: serializer.fromJson?>(json['i18n']), extraData: serializer.fromJson?>(json['extraData']), ); } @@ -1923,6 +1971,7 @@ class PinnedMessageEntity extends DataClass 'pinExpires': serializer.toJson(pinExpires), 'pinnedByUserId': serializer.toJson(pinnedByUserId), 'channelCid': serializer.toJson(channelCid), + 'i18n': serializer.toJson?>(i18n), 'extraData': serializer.toJson?>(extraData), }; } @@ -1951,6 +2000,7 @@ class PinnedMessageEntity extends DataClass Value pinExpires = const Value.absent(), Value pinnedByUserId = const Value.absent(), Value channelCid = const Value.absent(), + Value?> i18n = const Value.absent(), Value?> extraData = const Value.absent()}) => PinnedMessageEntity( id: id ?? this.id, @@ -1982,6 +2032,7 @@ class PinnedMessageEntity extends DataClass pinnedByUserId: pinnedByUserId.present ? pinnedByUserId.value : this.pinnedByUserId, channelCid: channelCid.present ? channelCid.value : this.channelCid, + i18n: i18n.present ? i18n.value : this.i18n, extraData: extraData.present ? extraData.value : this.extraData, ); @override @@ -2010,6 +2061,7 @@ class PinnedMessageEntity extends DataClass ..write('pinExpires: $pinExpires, ') ..write('pinnedByUserId: $pinnedByUserId, ') ..write('channelCid: $channelCid, ') + ..write('i18n: $i18n, ') ..write('extraData: $extraData') ..write(')')) .toString(); @@ -2058,7 +2110,7 @@ class PinnedMessageEntity extends DataClass .hashCode, $mrjc( pinned.hashCode, - $mrjc(pinnedAt.hashCode, $mrjc(pinExpires.hashCode, $mrjc(pinnedByUserId.hashCode, $mrjc(channelCid.hashCode, extraData.hashCode)))))))))))))))))))))))); + $mrjc(pinnedAt.hashCode, $mrjc(pinExpires.hashCode, $mrjc(pinnedByUserId.hashCode, $mrjc(channelCid.hashCode, $mrjc(i18n.hashCode, extraData.hashCode))))))))))))))))))))))))); @override bool operator ==(Object other) => identical(this, other) || @@ -2086,6 +2138,7 @@ class PinnedMessageEntity extends DataClass other.pinExpires == this.pinExpires && other.pinnedByUserId == this.pinnedByUserId && other.channelCid == this.channelCid && + other.i18n == this.i18n && other.extraData == this.extraData); } @@ -2113,6 +2166,7 @@ class PinnedMessagesCompanion extends UpdateCompanion { final Value pinExpires; final Value pinnedByUserId; final Value channelCid; + final Value?> i18n; final Value?> extraData; const PinnedMessagesCompanion({ this.id = const Value.absent(), @@ -2138,6 +2192,7 @@ class PinnedMessagesCompanion extends UpdateCompanion { this.pinExpires = const Value.absent(), this.pinnedByUserId = const Value.absent(), this.channelCid = const Value.absent(), + this.i18n = const Value.absent(), this.extraData = const Value.absent(), }); PinnedMessagesCompanion.insert({ @@ -2164,6 +2219,7 @@ class PinnedMessagesCompanion extends UpdateCompanion { this.pinExpires = const Value.absent(), this.pinnedByUserId = const Value.absent(), this.channelCid = const Value.absent(), + this.i18n = const Value.absent(), this.extraData = const Value.absent(), }) : id = Value(id), attachments = Value(attachments), @@ -2192,6 +2248,7 @@ class PinnedMessagesCompanion extends UpdateCompanion { Expression? pinExpires, Expression? pinnedByUserId, Expression? channelCid, + Expression?>? i18n, Expression?>? extraData, }) { return RawValuesInsertable({ @@ -2218,6 +2275,7 @@ class PinnedMessagesCompanion extends UpdateCompanion { if (pinExpires != null) 'pin_expires': pinExpires, if (pinnedByUserId != null) 'pinned_by_user_id': pinnedByUserId, if (channelCid != null) 'channel_cid': channelCid, + if (i18n != null) 'i18n': i18n, if (extraData != null) 'extra_data': extraData, }); } @@ -2246,6 +2304,7 @@ class PinnedMessagesCompanion extends UpdateCompanion { Value? pinExpires, Value? pinnedByUserId, Value? channelCid, + Value?>? i18n, Value?>? extraData}) { return PinnedMessagesCompanion( id: id ?? this.id, @@ -2271,6 +2330,7 @@ class PinnedMessagesCompanion extends UpdateCompanion { pinExpires: pinExpires ?? this.pinExpires, pinnedByUserId: pinnedByUserId ?? this.pinnedByUserId, channelCid: channelCid ?? this.channelCid, + i18n: i18n ?? this.i18n, extraData: extraData ?? this.extraData, ); } @@ -2356,8 +2416,12 @@ class PinnedMessagesCompanion extends UpdateCompanion { if (channelCid.present) { map['channel_cid'] = Variable(channelCid.value); } - if (extraData.present) { + if (i18n.present) { final converter = $PinnedMessagesTable.$converter5; + map['i18n'] = Variable(converter.mapToSql(i18n.value)); + } + if (extraData.present) { + final converter = $PinnedMessagesTable.$converter6; map['extra_data'] = Variable(converter.mapToSql(extraData.value)); } @@ -2390,6 +2454,7 @@ class PinnedMessagesCompanion extends UpdateCompanion { ..write('pinExpires: $pinExpires, ') ..write('pinnedByUserId: $pinnedByUserId, ') ..write('channelCid: $channelCid, ') + ..write('i18n: $i18n, ') ..write('extraData: $extraData') ..write(')')) .toString(); @@ -2529,12 +2594,17 @@ class $PinnedMessagesTable extends PinnedMessages requiredDuringInsert: false, $customConstraints: 'NULLABLE REFERENCES channels(cid) ON DELETE CASCADE'); + final VerificationMeta _i18nMeta = const VerificationMeta('i18n'); + late final GeneratedColumnWithTypeConverter, String?> + i18n = GeneratedColumn('i18n', aliasedName, true, + typeName: 'TEXT', requiredDuringInsert: false) + .withConverter>($PinnedMessagesTable.$converter5); final VerificationMeta _extraDataMeta = const VerificationMeta('extraData'); late final GeneratedColumnWithTypeConverter, String?> extraData = GeneratedColumn('extra_data', aliasedName, true, typeName: 'TEXT', requiredDuringInsert: false) .withConverter>( - $PinnedMessagesTable.$converter5); + $PinnedMessagesTable.$converter6); @override List get $columns => [ id, @@ -2560,6 +2630,7 @@ class $PinnedMessagesTable extends PinnedMessages pinExpires, pinnedByUserId, channelCid, + i18n, extraData ]; @override @@ -2664,6 +2735,7 @@ class $PinnedMessagesTable extends PinnedMessages channelCid.isAcceptableOrUnknown( data['channel_cid']!, _channelCidMeta)); } + context.handle(_i18nMeta, const VerificationResult.success()); context.handle(_extraDataMeta, const VerificationResult.success()); return context; } @@ -2691,7 +2763,9 @@ class $PinnedMessagesTable extends PinnedMessages MapConverter(); static TypeConverter, String> $converter4 = MapConverter(); - static TypeConverter, String> $converter5 = + static TypeConverter, String> $converter5 = + MapConverter(); + static TypeConverter, String> $converter6 = MapConverter(); } @@ -3030,6 +3104,9 @@ class UserEntity extends DataClass implements Insertable { /// User role final String? role; + /// The language this user prefers. + final String language; + /// Date of user creation final DateTime createdAt; @@ -3050,6 +3127,7 @@ class UserEntity extends DataClass implements Insertable { UserEntity( {required this.id, this.role, + required this.language, required this.createdAt, required this.updatedAt, this.lastActive, @@ -3064,6 +3142,8 @@ class UserEntity extends DataClass implements Insertable { .mapFromDatabaseResponse(data['${effectivePrefix}id'])!, role: const StringType() .mapFromDatabaseResponse(data['${effectivePrefix}role']), + language: const StringType() + .mapFromDatabaseResponse(data['${effectivePrefix}language'])!, createdAt: const DateTimeType() .mapFromDatabaseResponse(data['${effectivePrefix}created_at'])!, updatedAt: const DateTimeType() @@ -3085,6 +3165,7 @@ class UserEntity extends DataClass implements Insertable { if (!nullToAbsent || role != null) { map['role'] = Variable(role); } + map['language'] = Variable(language); map['created_at'] = Variable(createdAt); map['updated_at'] = Variable(updatedAt); if (!nullToAbsent || lastActive != null) { @@ -3105,6 +3186,7 @@ class UserEntity extends DataClass implements Insertable { return UserEntity( id: serializer.fromJson(json['id']), role: serializer.fromJson(json['role']), + language: serializer.fromJson(json['language']), createdAt: serializer.fromJson(json['createdAt']), updatedAt: serializer.fromJson(json['updatedAt']), lastActive: serializer.fromJson(json['lastActive']), @@ -3119,6 +3201,7 @@ class UserEntity extends DataClass implements Insertable { return { 'id': serializer.toJson(id), 'role': serializer.toJson(role), + 'language': serializer.toJson(language), 'createdAt': serializer.toJson(createdAt), 'updatedAt': serializer.toJson(updatedAt), 'lastActive': serializer.toJson(lastActive), @@ -3131,6 +3214,7 @@ class UserEntity extends DataClass implements Insertable { UserEntity copyWith( {String? id, Value role = const Value.absent(), + String? language, DateTime? createdAt, DateTime? updatedAt, Value lastActive = const Value.absent(), @@ -3140,6 +3224,7 @@ class UserEntity extends DataClass implements Insertable { UserEntity( id: id ?? this.id, role: role.present ? role.value : this.role, + language: language ?? this.language, createdAt: createdAt ?? this.createdAt, updatedAt: updatedAt ?? this.updatedAt, lastActive: lastActive.present ? lastActive.value : this.lastActive, @@ -3152,6 +3237,7 @@ class UserEntity extends DataClass implements Insertable { return (StringBuffer('UserEntity(') ..write('id: $id, ') ..write('role: $role, ') + ..write('language: $language, ') ..write('createdAt: $createdAt, ') ..write('updatedAt: $updatedAt, ') ..write('lastActive: $lastActive, ') @@ -3168,19 +3254,24 @@ class UserEntity extends DataClass implements Insertable { $mrjc( role.hashCode, $mrjc( - createdAt.hashCode, + language.hashCode, $mrjc( - updatedAt.hashCode, + createdAt.hashCode, $mrjc( - lastActive.hashCode, - $mrjc(online.hashCode, - $mrjc(banned.hashCode, extraData.hashCode)))))))); + updatedAt.hashCode, + $mrjc( + lastActive.hashCode, + $mrjc( + online.hashCode, + $mrjc( + banned.hashCode, extraData.hashCode))))))))); @override bool operator ==(Object other) => identical(this, other) || (other is UserEntity && other.id == this.id && other.role == this.role && + other.language == this.language && other.createdAt == this.createdAt && other.updatedAt == this.updatedAt && other.lastActive == this.lastActive && @@ -3192,6 +3283,7 @@ class UserEntity extends DataClass implements Insertable { class UsersCompanion extends UpdateCompanion { final Value id; final Value role; + final Value language; final Value createdAt; final Value updatedAt; final Value lastActive; @@ -3201,6 +3293,7 @@ class UsersCompanion extends UpdateCompanion { const UsersCompanion({ this.id = const Value.absent(), this.role = const Value.absent(), + this.language = const Value.absent(), this.createdAt = const Value.absent(), this.updatedAt = const Value.absent(), this.lastActive = const Value.absent(), @@ -3211,6 +3304,7 @@ class UsersCompanion extends UpdateCompanion { UsersCompanion.insert({ required String id, this.role = const Value.absent(), + this.language = const Value.absent(), this.createdAt = const Value.absent(), this.updatedAt = const Value.absent(), this.lastActive = const Value.absent(), @@ -3222,6 +3316,7 @@ class UsersCompanion extends UpdateCompanion { static Insertable custom({ Expression? id, Expression? role, + Expression? language, Expression? createdAt, Expression? updatedAt, Expression? lastActive, @@ -3232,6 +3327,7 @@ class UsersCompanion extends UpdateCompanion { return RawValuesInsertable({ if (id != null) 'id': id, if (role != null) 'role': role, + if (language != null) 'language': language, if (createdAt != null) 'created_at': createdAt, if (updatedAt != null) 'updated_at': updatedAt, if (lastActive != null) 'last_active': lastActive, @@ -3244,6 +3340,7 @@ class UsersCompanion extends UpdateCompanion { UsersCompanion copyWith( {Value? id, Value? role, + Value? language, Value? createdAt, Value? updatedAt, Value? lastActive, @@ -3253,6 +3350,7 @@ class UsersCompanion extends UpdateCompanion { return UsersCompanion( id: id ?? this.id, role: role ?? this.role, + language: language ?? this.language, createdAt: createdAt ?? this.createdAt, updatedAt: updatedAt ?? this.updatedAt, lastActive: lastActive ?? this.lastActive, @@ -3271,6 +3369,9 @@ class UsersCompanion extends UpdateCompanion { if (role.present) { map['role'] = Variable(role.value); } + if (language.present) { + map['language'] = Variable(language.value); + } if (createdAt.present) { map['created_at'] = Variable(createdAt.value); } @@ -3299,6 +3400,7 @@ class UsersCompanion extends UpdateCompanion { return (StringBuffer('UsersCompanion(') ..write('id: $id, ') ..write('role: $role, ') + ..write('language: $language, ') ..write('createdAt: $createdAt, ') ..write('updatedAt: $updatedAt, ') ..write('lastActive: $lastActive, ') @@ -3322,6 +3424,12 @@ class $UsersTable extends Users with TableInfo<$UsersTable, UserEntity> { late final GeneratedColumn role = GeneratedColumn( 'role', aliasedName, true, typeName: 'TEXT', requiredDuringInsert: false); + final VerificationMeta _languageMeta = const VerificationMeta('language'); + late final GeneratedColumn language = GeneratedColumn( + 'language', aliasedName, false, + typeName: 'TEXT', + requiredDuringInsert: false, + defaultValue: const Constant('en')); final VerificationMeta _createdAtMeta = const VerificationMeta('createdAt'); late final GeneratedColumn createdAt = GeneratedColumn( 'created_at', aliasedName, false, @@ -3358,8 +3466,17 @@ class $UsersTable extends Users with TableInfo<$UsersTable, UserEntity> { typeName: 'TEXT', requiredDuringInsert: true) .withConverter>($UsersTable.$converter0); @override - List get $columns => - [id, role, createdAt, updatedAt, lastActive, online, banned, extraData]; + List get $columns => [ + id, + role, + language, + createdAt, + updatedAt, + lastActive, + online, + banned, + extraData + ]; @override String get aliasedName => _alias ?? 'users'; @override @@ -3378,6 +3495,10 @@ class $UsersTable extends Users with TableInfo<$UsersTable, UserEntity> { context.handle( _roleMeta, role.isAcceptableOrUnknown(data['role']!, _roleMeta)); } + if (data.containsKey('language')) { + context.handle(_languageMeta, + language.isAcceptableOrUnknown(data['language']!, _languageMeta)); + } if (data.containsKey('created_at')) { context.handle(_createdAtMeta, createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); diff --git a/packages/stream_chat_persistence/lib/src/entity/messages.dart b/packages/stream_chat_persistence/lib/src/entity/messages.dart index 4006d6ce..0aef2b84 100644 --- a/packages/stream_chat_persistence/lib/src/entity/messages.dart +++ b/packages/stream_chat_persistence/lib/src/entity/messages.dart @@ -80,6 +80,9 @@ class Messages extends Table { TextColumn get channelCid => text().nullable().customConstraint( 'NULLABLE REFERENCES channels(cid) ON DELETE CASCADE')(); + /// A Map of [messageText] translations. + TextColumn get i18n => text().nullable().map(MapConverter())(); + /// Message custom extraData TextColumn get extraData => text().nullable().map(MapConverter())(); diff --git a/packages/stream_chat_persistence/lib/src/entity/users.dart b/packages/stream_chat_persistence/lib/src/entity/users.dart index 303092c9..49aae500 100644 --- a/packages/stream_chat_persistence/lib/src/entity/users.dart +++ b/packages/stream_chat_persistence/lib/src/entity/users.dart @@ -11,6 +11,9 @@ class Users extends Table { /// User role TextColumn get role => text().nullable()(); + /// The language this user prefers. + TextColumn get language => text().withDefault(const Constant('en'))(); + /// Date of user creation DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); diff --git a/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart index 558c6e45..85692296 100644 --- a/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart +++ b/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart @@ -44,6 +44,7 @@ extension MessageEntityX on MessageEntity { pinnedBy: pinnedBy, mentionedUsers: mentionedUsers.map((e) => User.fromJson(jsonDecode(e))).toList(), + i18n: i18n, ); } @@ -75,5 +76,6 @@ extension MessageX on Message { pinnedAt: pinnedAt, pinExpires: pinExpires, pinnedByUserId: pinnedBy?.id, + i18n: i18n, ); } diff --git a/packages/stream_chat_persistence/lib/src/mapper/pinned_message_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/pinned_message_mapper.dart index 1083e239..b1a7849a 100644 --- a/packages/stream_chat_persistence/lib/src/mapper/pinned_message_mapper.dart +++ b/packages/stream_chat_persistence/lib/src/mapper/pinned_message_mapper.dart @@ -42,6 +42,9 @@ extension PinnedMessageEntityX on PinnedMessageEntity { pinnedAt: pinnedAt, pinExpires: pinExpires, pinnedBy: pinnedBy, + mentionedUsers: + mentionedUsers.map((e) => User.fromJson(jsonDecode(e))).toList(), + i18n: i18n, ); } @@ -73,5 +76,6 @@ extension PMessageX on Message { pinnedAt: pinnedAt, pinExpires: pinExpires, pinnedByUserId: pinnedBy?.id, + i18n: i18n, ); } diff --git a/packages/stream_chat_persistence/lib/src/mapper/user_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/user_mapper.dart index 533a45b1..8b38a74d 100644 --- a/packages/stream_chat_persistence/lib/src/mapper/user_mapper.dart +++ b/packages/stream_chat_persistence/lib/src/mapper/user_mapper.dart @@ -7,6 +7,7 @@ extension UserEntityX on UserEntity { User toUser() => User( id: id, updatedAt: updatedAt, + language: language, role: role, online: online, lastActive: lastActive, @@ -22,6 +23,7 @@ extension UserX on User { UserEntity toEntity() => UserEntity( id: id, role: role, + language: language, createdAt: createdAt, updatedAt: updatedAt, lastActive: lastActive, diff --git a/packages/stream_chat_persistence/test/src/dao/message_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/message_dao_test.dart index 54cc219a..fd0a43ce 100644 --- a/packages/stream_chat_persistence/test/src/dao/message_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/message_dao_test.dart @@ -35,10 +35,15 @@ void main() { replyCount: index, updatedAt: DateTime.now(), extraData: const {'extra_test_field': 'extraTestData'}, - text: 'Dummy text #$index', + text: 'Hello #$index', pinned: math.Random().nextBool(), pinnedAt: DateTime.now(), pinnedBy: User(id: 'testUserId$index'), + i18n: { + 'en_text': 'Hello #$index', + 'hi_text': 'नमस्ते #$index', + 'language': 'en', + }, ), ); final quotedMessages = List.generate( @@ -52,11 +57,16 @@ void main() { replyCount: index, updatedAt: DateTime.now(), extraData: const {'extra_test_field': 'extraTestData'}, - text: 'Dummy text #$index', + text: 'Hello #$index', quotedMessageId: messages[index].id, pinned: math.Random().nextBool(), pinnedAt: DateTime.now(), pinnedBy: User(id: 'testUserId$index'), + i18n: { + 'en_text': 'Hello #$index', + 'hi_text': 'नमस्ते #$index', + 'language': 'en', + }, ), ); final threadMessages = List.generate( @@ -72,10 +82,15 @@ void main() { replyCount: index, updatedAt: DateTime.now(), extraData: const {'extra_test_field': 'extraTestData'}, - text: 'Dummy text #$index', + text: 'Hello #$index', pinned: math.Random().nextBool(), pinnedAt: DateTime.now(), pinnedBy: User(id: 'testUserId$index'), + i18n: { + 'en_text': 'Hello #$index', + 'hi_text': 'नमस्ते #$index', + 'language': 'en', + }, ), ); final allMessages = [ diff --git a/packages/stream_chat_persistence/test/src/dao/user_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/user_dao_test.dart index ffb50edd..5b5d04d4 100644 --- a/packages/stream_chat_persistence/test/src/dao/user_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/user_dao_test.dart @@ -22,6 +22,7 @@ void main() { (index) => User( id: 'testUserId$index', role: 'testRole', + language: 'hi', createdAt: DateTime.now(), updatedAt: DateTime.now(), lastActive: DateTime.now(), diff --git a/packages/stream_chat_persistence/test/src/mapper/message_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/message_mapper_test.dart index 7d867deb..0f9e8cf6 100644 --- a/packages/stream_chat_persistence/test/src/mapper/message_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/message_mapper_test.dart @@ -56,11 +56,16 @@ void main() { extraData: {'extra_test_data': 'extraData'}, userId: user.id, deletedAt: DateTime.now(), - messageText: 'dummy text', + messageText: 'Hello', pinned: true, pinExpires: DateTime.now().toUtc(), pinnedAt: DateTime.now(), pinnedByUserId: user.id, + i18n: const { + 'en_text': 'Hello', + 'hi_text': 'नमस्ते', + 'language': 'en', + }, ); final message = entity.toMessage( user: user, @@ -99,6 +104,7 @@ void main() { expect(message.pinnedBy!.id, entity.pinnedByUserId); expect(message.reactionCounts, entity.reactionCounts); expect(message.reactionScores, entity.reactionScores); + expect(message.i18n, entity.i18n); for (var i = 0; i < message.attachments.length; i++) { final messageAttachment = message.attachments[i]; final entityAttachmentData = jsonDecode(entity.attachments[i]); @@ -156,11 +162,16 @@ void main() { extraData: const {'extra_test_data': 'extraData'}, user: user, deletedAt: DateTime.now(), - text: 'dummy text', + text: 'Hello', pinned: true, pinExpires: DateTime.now(), pinnedAt: DateTime.now(), pinnedBy: user, + i18n: const { + 'en_text': 'Hello', + 'hi_text': 'नमस्ते', + 'language': 'en', + }, ); final entity = message.toEntity(cid: cid); expect(entity, isA()); @@ -193,5 +204,6 @@ void main() { entity.attachments, message.attachments.map((it) => jsonEncode(it.toData())).toList(), ); + expect(entity.i18n, message.i18n); }); } diff --git a/packages/stream_chat_persistence/test/src/mapper/pinned_message_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/pinned_message_mapper_test.dart index 8b5f4db4..5150df62 100644 --- a/packages/stream_chat_persistence/test/src/mapper/pinned_message_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/pinned_message_mapper_test.dart @@ -54,11 +54,16 @@ void main() { extraData: {'extra_test_data': 'extraData'}, userId: user.id, deletedAt: DateTime.now(), - messageText: 'dummy text', + messageText: 'Hello', pinned: true, pinExpires: DateTime.now().toUtc(), pinnedAt: DateTime.now(), pinnedByUserId: user.id, + i18n: const { + 'en_text': 'Hello', + 'hi_text': 'नमस्ते', + 'language': 'en', + }, ); final message = entity.toMessage( user: user, @@ -92,6 +97,7 @@ void main() { expect(message.pinnedBy!.id, entity.pinnedByUserId); expect(message.reactionCounts, entity.reactionCounts); expect(message.reactionScores, entity.reactionScores); + expect(message.i18n, entity.i18n); for (var i = 0; i < message.attachments.length; i++) { final messageAttachment = message.attachments[i]; final entityAttachmentData = jsonDecode(entity.attachments[i]); @@ -146,11 +152,16 @@ void main() { extraData: const {'extra_test_data': 'extraData'}, user: user, deletedAt: DateTime.now(), - text: 'dummy text', + text: 'Hello', pinned: true, pinExpires: DateTime.now(), pinnedAt: DateTime.now(), pinnedBy: user, + i18n: const { + 'en_text': 'Hello', + 'hi_text': 'नमस्ते', + 'language': 'en', + }, ); final entity = message.toPinnedEntity(cid: cid); expect(entity, isA()); @@ -181,5 +192,6 @@ void main() { entity.attachments, message.attachments.map((it) => jsonEncode(it.toData())).toList(), ); + expect(entity.i18n, message.i18n); }); } diff --git a/packages/stream_chat_persistence/test/src/mapper/user_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/user_mapper_test.dart index 4454ad73..eca2a32a 100644 --- a/packages/stream_chat_persistence/test/src/mapper/user_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/user_mapper_test.dart @@ -11,6 +11,7 @@ void main() { final entity = UserEntity( id: 'testUserId', role: 'testType', + language: 'hi', createdAt: DateTime.now(), updatedAt: DateTime.now(), lastActive: DateTime.now(), @@ -22,6 +23,7 @@ void main() { expect(user, isA()); expect(user.id, entity.id); expect(user.role, entity.role); + expect(user.language, entity.language); expect(user.createdAt, isSameDateAs(entity.createdAt)); expect(user.updatedAt, isSameDateAs(entity.updatedAt)); expect(user.lastActive, isSameDateAs(entity.lastActive!)); @@ -34,6 +36,7 @@ void main() { final user = User( id: 'testUserId', role: 'testType', + language: 'hi', createdAt: DateTime.now(), updatedAt: DateTime.now(), lastActive: DateTime.now(), @@ -45,6 +48,7 @@ void main() { expect(entity, isA()); expect(entity.id, user.id); expect(entity.role, user.role); + expect(entity.language, user.language); expect(entity.createdAt, isSameDateAs(user.createdAt)); expect(entity.updatedAt, isSameDateAs(user.updatedAt)); expect(entity.lastActive, isSameDateAs(user.lastActive!)); From 39449e57008899bfe9788816518c04ac6e5af74d Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 27 Jul 2021 20:07:55 +0530 Subject: [PATCH 16/22] chore(persistence): update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat_persistence/CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/stream_chat_persistence/CHANGELOG.md b/packages/stream_chat_persistence/CHANGELOG.md index 3a65db58..466d1e05 100644 --- a/packages/stream_chat_persistence/CHANGELOG.md +++ b/packages/stream_chat_persistence/CHANGELOG.md @@ -1,3 +1,9 @@ +## Upcoming + +✅ Added +- Added support for `Message.i18n` +- Added support for `User.language` + ## 2.0.0 * Migrate this package to null safety * Minor fixes and improvements From f6cf835b4e439945e92fb2d801c798c135cb34ea Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 27 Jul 2021 20:23:28 +0530 Subject: [PATCH 17/22] fix(llc): update currentUser after successful connection Signed-off-by: xsahil03x --- packages/stream_chat/lib/src/client/client.dart | 4 ++-- packages/stream_chat_flutter/example/lib/main.dart | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/stream_chat/lib/src/client/client.dart b/packages/stream_chat/lib/src/client/client.dart index e1212e83..b25ebe88 100644 --- a/packages/stream_chat/lib/src/client/client.dart +++ b/packages/stream_chat/lib/src/client/client.dart @@ -325,8 +325,8 @@ class StreamChatClient { _chatPersistenceClient = _originalChatPersistenceClient; await _chatPersistenceClient!.connect(ownUser.id); } - final res = await openConnection(); - return res; + final connectedUser = await openConnection(); + return state.currentUser = connectedUser; } catch (e, stk) { if (e is StreamWebSocketError && e.isRetriable) { final event = await _chatPersistenceClient?.getConnectionInfo(); diff --git a/packages/stream_chat_flutter/example/lib/main.dart b/packages/stream_chat_flutter/example/lib/main.dart index 42891aa7..76ba60b5 100644 --- a/packages/stream_chat_flutter/example/lib/main.dart +++ b/packages/stream_chat_flutter/example/lib/main.dart @@ -23,16 +23,12 @@ void main() async { /// /// Please see the following for more information: /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/ - await client.connectUser( - User(id: 'super-band-9', language: 'fr'), + final res = await client.connectUser( + User(id: 'super-band-9'), 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.' '0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A', ); - client.on().listen((e) { - print('e.user.language ${e.me?.language}'); - }); - final channel = client.channel('messaging', id: 'godevs'); await channel.watch(); From ba251e4d88a52a18648bb3ce0c3413b4374b9beb Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 27 Jul 2021 20:25:26 +0530 Subject: [PATCH 18/22] chore(ui): fix unused variable warning Signed-off-by: xsahil03x --- packages/stream_chat_flutter/example/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/example/lib/main.dart b/packages/stream_chat_flutter/example/lib/main.dart index 76ba60b5..89362d81 100644 --- a/packages/stream_chat_flutter/example/lib/main.dart +++ b/packages/stream_chat_flutter/example/lib/main.dart @@ -23,7 +23,7 @@ void main() async { /// /// Please see the following for more information: /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/ - final res = await client.connectUser( + await client.connectUser( User(id: 'super-band-9'), 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.' '0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A', From fe7a8a1ed0d3d995dcaa784f64236ec6cde5207b Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 28 Jul 2021 11:20:07 +0200 Subject: [PATCH 19/22] make language nullable --- .../lib/src/core/models/own_user.dart | 2 +- .../lib/src/core/models/own_user.g.dart | 2 +- .../stream_chat/lib/src/core/models/user.dart | 10 +--- .../lib/src/core/models/user.g.dart | 4 +- .../test/utils/golden.dart | 58 +++++++++++++++++++ .../lib/src/db/moor_chat_database.g.dart | 32 +++++----- .../lib/src/entity/users.dart | 2 +- 7 files changed, 82 insertions(+), 28 deletions(-) create mode 100644 packages/stream_chat_flutter/test/utils/golden.dart diff --git a/packages/stream_chat/lib/src/core/models/own_user.dart b/packages/stream_chat/lib/src/core/models/own_user.dart index 116a2f84..fb5b1caf 100644 --- a/packages/stream_chat/lib/src/core/models/own_user.dart +++ b/packages/stream_chat/lib/src/core/models/own_user.dart @@ -27,7 +27,7 @@ class OwnUser extends User { Map extraData = const {}, bool banned = false, List teams = const [], - String language = 'en', + String? language, }) : super( id: id, role: role, diff --git a/packages/stream_chat/lib/src/core/models/own_user.g.dart b/packages/stream_chat/lib/src/core/models/own_user.g.dart index 5d80cc1c..f5ad08a7 100644 --- a/packages/stream_chat/lib/src/core/models/own_user.g.dart +++ b/packages/stream_chat/lib/src/core/models/own_user.g.dart @@ -39,6 +39,6 @@ OwnUser _$OwnUserFromJson(Map json) { teams: (json['teams'] as List?)?.map((e) => e as String).toList() ?? [], - language: json['language'] as String? ?? 'en', + language: json['language'] as String, ); } diff --git a/packages/stream_chat/lib/src/core/models/user.dart b/packages/stream_chat/lib/src/core/models/user.dart index dd4abcc6..a7e7a51e 100644 --- a/packages/stream_chat/lib/src/core/models/user.dart +++ b/packages/stream_chat/lib/src/core/models/user.dart @@ -18,7 +18,7 @@ class User extends Equatable { this.extraData = const {}, this.banned = false, this.teams = const [], - this.language = 'en', + this.language, }) : createdAt = createdAt ?? DateTime.now(), updatedAt = updatedAt ?? DateTime.now(); @@ -87,12 +87,8 @@ class User extends Equatable { /// The language this user prefers. /// /// Defaults to 'en'. - @JsonKey( - includeIfNull: false, - toJson: Serializer.readOnly, - defaultValue: 'en', - ) - final String language; + @JsonKey(includeIfNull: false) + final String? language; /// Shortcut for user name String get name { diff --git a/packages/stream_chat/lib/src/core/models/user.g.dart b/packages/stream_chat/lib/src/core/models/user.g.dart index 7b193f04..dd3183d5 100644 --- a/packages/stream_chat/lib/src/core/models/user.g.dart +++ b/packages/stream_chat/lib/src/core/models/user.g.dart @@ -25,7 +25,7 @@ User _$UserFromJson(Map json) { teams: (json['teams'] as List?)?.map((e) => e as String).toList() ?? [], - language: json['language'] as String? ?? 'en', + language: json['language'] as String?, ); } @@ -48,6 +48,6 @@ Map _$UserToJson(User instance) { writeNotNull('online', readonly(instance.online)); writeNotNull('banned', readonly(instance.banned)); val['extra_data'] = instance.extraData; - writeNotNull('language', readonly(instance.language)); + writeNotNull('language', instance.language); return val; } diff --git a/packages/stream_chat_flutter/test/utils/golden.dart b/packages/stream_chat_flutter/test/utils/golden.dart new file mode 100644 index 00000000..4fc0aff3 --- /dev/null +++ b/packages/stream_chat_flutter/test/utils/golden.dart @@ -0,0 +1,58 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:golden_toolkit/golden_toolkit.dart'; +import 'package:golden_toolkit/src/testing_tools.dart'; +import 'package:path/path.dart' as path; + +const double _kGoldenDiffTolerance = 0.05; + +/// Wrapper function for golden tests. +Future customExpectGoldenMatches( + WidgetTester tester, + String name, { + bool? autoHeight, + Finder? finder, + CustomPump? customPump, + @Deprecated(''' +This method level parameter will be removed in an upcoming release. This can be configured globally. If you have concerns, please file an issue with your use case.''') bool? skip, +}) { + final goldenPath = path.join('test/src/goldens'); + print('goldenPath: $goldenPath'); + goldenFileComparator = CustomGoldenFileComparator(Uri.parse(goldenPath)); + + return compareWithGolden( + tester, + name, + autoHeight: autoHeight, + finder: finder, + customPump: customPump, + skip: skip, + // This value is actually ignored. We are forced to pass it because the + // downstream API is structured poorly. This should be refactored. + device: Device.phone, + fileNameFactory: (String name, Device device) => + GoldenToolkit.configuration.fileNameFactory(name), + ); +} + +class CustomGoldenFileComparator extends LocalFileComparator { + CustomGoldenFileComparator(Uri testFile) : super(testFile); + + @override + Future compare(Uint8List imageBytes, Uri golden) async { + print('golden.toString(): ${golden.toString()}'); + final result = await GoldenFileComparator.compareLists( + imageBytes, + await getGoldenBytes(golden), + ); + + if (!result.passed && result.diffPercent > _kGoldenDiffTolerance) { + final error = await generateFailureOutput(result, golden, basedir); + throw FlutterError(error); + } + return result.passed; + } +} diff --git a/packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart b/packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart index 6274e932..a61d1e6e 100644 --- a/packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart +++ b/packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart @@ -3105,7 +3105,7 @@ class UserEntity extends DataClass implements Insertable { final String? role; /// The language this user prefers. - final String language; + final String? language; /// Date of user creation final DateTime createdAt; @@ -3127,7 +3127,7 @@ class UserEntity extends DataClass implements Insertable { UserEntity( {required this.id, this.role, - required this.language, + this.language, required this.createdAt, required this.updatedAt, this.lastActive, @@ -3143,7 +3143,7 @@ class UserEntity extends DataClass implements Insertable { role: const StringType() .mapFromDatabaseResponse(data['${effectivePrefix}role']), language: const StringType() - .mapFromDatabaseResponse(data['${effectivePrefix}language'])!, + .mapFromDatabaseResponse(data['${effectivePrefix}language']), createdAt: const DateTimeType() .mapFromDatabaseResponse(data['${effectivePrefix}created_at'])!, updatedAt: const DateTimeType() @@ -3165,7 +3165,9 @@ class UserEntity extends DataClass implements Insertable { if (!nullToAbsent || role != null) { map['role'] = Variable(role); } - map['language'] = Variable(language); + if (!nullToAbsent || language != null) { + map['language'] = Variable(language); + } map['created_at'] = Variable(createdAt); map['updated_at'] = Variable(updatedAt); if (!nullToAbsent || lastActive != null) { @@ -3186,7 +3188,7 @@ class UserEntity extends DataClass implements Insertable { return UserEntity( id: serializer.fromJson(json['id']), role: serializer.fromJson(json['role']), - language: serializer.fromJson(json['language']), + language: serializer.fromJson(json['language']), createdAt: serializer.fromJson(json['createdAt']), updatedAt: serializer.fromJson(json['updatedAt']), lastActive: serializer.fromJson(json['lastActive']), @@ -3201,7 +3203,7 @@ class UserEntity extends DataClass implements Insertable { return { 'id': serializer.toJson(id), 'role': serializer.toJson(role), - 'language': serializer.toJson(language), + 'language': serializer.toJson(language), 'createdAt': serializer.toJson(createdAt), 'updatedAt': serializer.toJson(updatedAt), 'lastActive': serializer.toJson(lastActive), @@ -3214,7 +3216,7 @@ class UserEntity extends DataClass implements Insertable { UserEntity copyWith( {String? id, Value role = const Value.absent(), - String? language, + Value language = const Value.absent(), DateTime? createdAt, DateTime? updatedAt, Value lastActive = const Value.absent(), @@ -3224,7 +3226,7 @@ class UserEntity extends DataClass implements Insertable { UserEntity( id: id ?? this.id, role: role.present ? role.value : this.role, - language: language ?? this.language, + language: language.present ? language.value : this.language, createdAt: createdAt ?? this.createdAt, updatedAt: updatedAt ?? this.updatedAt, lastActive: lastActive.present ? lastActive.value : this.lastActive, @@ -3283,7 +3285,7 @@ class UserEntity extends DataClass implements Insertable { class UsersCompanion extends UpdateCompanion { final Value id; final Value role; - final Value language; + final Value language; final Value createdAt; final Value updatedAt; final Value lastActive; @@ -3316,7 +3318,7 @@ class UsersCompanion extends UpdateCompanion { static Insertable custom({ Expression? id, Expression? role, - Expression? language, + Expression? language, Expression? createdAt, Expression? updatedAt, Expression? lastActive, @@ -3340,7 +3342,7 @@ class UsersCompanion extends UpdateCompanion { UsersCompanion copyWith( {Value? id, Value? role, - Value? language, + Value? language, Value? createdAt, Value? updatedAt, Value? lastActive, @@ -3370,7 +3372,7 @@ class UsersCompanion extends UpdateCompanion { map['role'] = Variable(role.value); } if (language.present) { - map['language'] = Variable(language.value); + map['language'] = Variable(language.value); } if (createdAt.present) { map['created_at'] = Variable(createdAt.value); @@ -3426,10 +3428,8 @@ class $UsersTable extends Users with TableInfo<$UsersTable, UserEntity> { typeName: 'TEXT', requiredDuringInsert: false); final VerificationMeta _languageMeta = const VerificationMeta('language'); late final GeneratedColumn language = GeneratedColumn( - 'language', aliasedName, false, - typeName: 'TEXT', - requiredDuringInsert: false, - defaultValue: const Constant('en')); + 'language', aliasedName, true, + typeName: 'TEXT', requiredDuringInsert: false); final VerificationMeta _createdAtMeta = const VerificationMeta('createdAt'); late final GeneratedColumn createdAt = GeneratedColumn( 'created_at', aliasedName, false, diff --git a/packages/stream_chat_persistence/lib/src/entity/users.dart b/packages/stream_chat_persistence/lib/src/entity/users.dart index 49aae500..286bfb40 100644 --- a/packages/stream_chat_persistence/lib/src/entity/users.dart +++ b/packages/stream_chat_persistence/lib/src/entity/users.dart @@ -12,7 +12,7 @@ class Users extends Table { TextColumn get role => text().nullable()(); /// The language this user prefers. - TextColumn get language => text().withDefault(const Constant('en'))(); + TextColumn get language => text().nullable()(); /// Date of user creation DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); From 5954bb2abc968939c64ce7ef5708c4220445a518 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 28 Jul 2021 11:24:10 +0200 Subject: [PATCH 20/22] fix tests --- packages/stream_chat/lib/src/core/models/own_user.g.dart | 2 +- packages/stream_chat/test/fixtures/user.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/stream_chat/lib/src/core/models/own_user.g.dart b/packages/stream_chat/lib/src/core/models/own_user.g.dart index f5ad08a7..ca4acdea 100644 --- a/packages/stream_chat/lib/src/core/models/own_user.g.dart +++ b/packages/stream_chat/lib/src/core/models/own_user.g.dart @@ -39,6 +39,6 @@ OwnUser _$OwnUserFromJson(Map json) { teams: (json['teams'] as List?)?.map((e) => e as String).toList() ?? [], - language: json['language'] as String, + language: json['language'] as String?, ); } diff --git a/packages/stream_chat/test/fixtures/user.json b/packages/stream_chat/test/fixtures/user.json index 49b972a3..b22c8553 100644 --- a/packages/stream_chat/test/fixtures/user.json +++ b/packages/stream_chat/test/fixtures/user.json @@ -1,5 +1,6 @@ { "id": "bbb19d9a-ee50-45bc-84e5-0584e79d0c9e", "role": "test-role", - "name": "John" + "name": "John", + "language": "en" } \ No newline at end of file From 3e88dbedef8a1a0964337e6d6c85a0d5cd73e5a4 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 28 Jul 2021 11:25:55 +0200 Subject: [PATCH 21/22] update stream_chat_flutter changelog --- packages/stream_chat_flutter/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 41e4fa5d..8843205f 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -3,6 +3,7 @@ ✅ Added - Added `MessageListView.paginationLimit` +- `MessageText` renders message translation if available - Allow the various ListView widgets to be themed via ThemeData classes 🔄 Changed From 0eb85684cf3afdf945ad164839d448eda7b5dc8e Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 28 Jul 2021 11:39:26 +0200 Subject: [PATCH 22/22] fix message text with null language --- packages/stream_chat_flutter/lib/src/message_text.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_text.dart b/packages/stream_chat_flutter/lib/src/message_text.dart index a1e3a4f8..721e4670 100644 --- a/packages/stream_chat_flutter/lib/src/message_text.dart +++ b/packages/stream_chat_flutter/lib/src/message_text.dart @@ -31,9 +31,9 @@ class MessageText extends StatelessWidget { Widget build(BuildContext context) { final streamChat = StreamChat.of(context); assert(streamChat.currentUser != null, ''); - return BetterStreamBuilder( - stream: streamChat.currentUserStream.map((it) => it!.language), - initialData: streamChat.currentUser!.language, + return BetterStreamBuilder( + stream: streamChat.currentUserStream.map((it) => it!.language ?? 'en'), + initialData: streamChat.currentUser!.language ?? 'en', builder: (context, language) { final translatedText = message.i18n?['${language}_text'] ?? message.text;