diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index d8860425..e3e31db6 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -4,6 +4,11 @@ - Fixed `channel.markAllRead` throwing failed host lookup. +✅ Added + +- `User` and `OwnUser` classes now have an `image` property. Setting an image will also set the 'image' key on `extraData`, so `user.image` and `user.extraData['image']` is the same. +- `User` and `OwnUser` classes now have a `name` property. Setting a name will also set the 'name' key on `extraData`, so `user.name` and `user.extraData['name']` is the same. + ## 2.1.1 🐞 Fixed diff --git a/packages/stream_chat/example/lib/main.dart b/packages/stream_chat/example/lib/main.dart index 46030341..d2c5dcea 100644 --- a/packages/stream_chat/example/lib/main.dart +++ b/packages/stream_chat/example/lib/main.dart @@ -13,10 +13,9 @@ Future main() async { await client.connectUser( User( id: 'cool-shadow-7', - extraData: const { - 'image': - 'https://getstream.io/random_png/?id=cool-shadow-7&name=Cool+shadow', - }, + name: 'Cool Shadow', + image: + 'https://getstream.io/random_png/?id=cool-shadow-7&name=Cool+shadow', ), '''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9.gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo''', ); 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 fb5b1caf..a65052f4 100644 --- a/packages/stream_chat/lib/src/core/models/own_user.dart +++ b/packages/stream_chat/lib/src/core/models/own_user.dart @@ -7,11 +7,12 @@ import 'package:stream_chat/stream_chat.dart'; part 'own_user.g.dart'; -/// The class that defines the own user model -/// This object can be found in [Event] +/// The class that defines the own user model. +/// +/// This object can be found in [Event]. @JsonSerializable(createToJson: false) class OwnUser extends User { - /// Constructor used for json serialization + /// Constructor used for json serialization. OwnUser({ this.devices = const [], this.mutes = const [], @@ -20,6 +21,8 @@ class OwnUser extends User { this.channelMutes = const [], required String id, String? role, + String? name, + String? image, DateTime? createdAt, DateTime? updatedAt, DateTime? lastActive, @@ -31,6 +34,8 @@ class OwnUser extends User { }) : super( id: id, role: role, + name: name, + image: image, createdAt: createdAt, updatedAt: updatedAt, lastActive: lastActive, @@ -41,14 +46,16 @@ class OwnUser extends User { language: language, ); - /// Create a new instance from a json + /// Create a new instance from json. factory OwnUser.fromJson(Map json) => _$OwnUserFromJson( Serializer.moveToExtraDataFromRoot(json, topLevelFields)); - /// Create a new instance from [User] object + /// Create a new instance from [User] object. factory OwnUser.fromUser(User user) => OwnUser( id: user.id, role: user.role, + name: user.name, + image: user.image, createdAt: user.createdAt, updatedAt: user.updatedAt, lastActive: user.lastActive, @@ -64,6 +71,8 @@ class OwnUser extends User { OwnUser copyWith({ String? id, String? role, + String? name, + String? image, DateTime? createdAt, DateTime? updatedAt, DateTime? lastActive, @@ -80,8 +89,12 @@ class OwnUser extends User { }) => OwnUser( id: id ?? this.id, - banned: banned ?? this.banned, role: role ?? this.role, + /* if null, it will be retrieved from extraData['name']*/ + name: name, + /* if null, it will be retrieved from extraData['image']*/ + image: image, + banned: banned ?? this.banned, createdAt: createdAt ?? this.createdAt, updatedAt: updatedAt ?? this.updatedAt, lastActive: lastActive ?? this.lastActive, @@ -101,16 +114,18 @@ class OwnUser extends User { OwnUser merge(OwnUser? other) { if (other == null) return this; return copyWith( + id: other.id, + role: other.role, + name: other.name, + image: other.image, banned: other.banned, channelMutes: other.channelMutes, createdAt: other.createdAt, devices: other.devices, extraData: other.extraData, - id: other.id, lastActive: other.lastActive, mutes: other.mutes, online: other.online, - role: other.role, teams: other.teams, totalUnreadCount: other.totalUnreadCount, unreadChannels: other.unreadChannels, @@ -119,27 +134,28 @@ class OwnUser extends User { ); } - /// List of user devices + /// List of user devices. @JsonKey(includeIfNull: false, defaultValue: []) final List devices; - /// List of users muted by the user + /// List of users muted by the user. @JsonKey(includeIfNull: false, defaultValue: []) final List mutes; - /// List of users muted by the user + /// List of users muted by the user. @JsonKey(includeIfNull: false, defaultValue: []) final List channelMutes; - /// Total unread messages by the user + /// Total unread messages by the user. @JsonKey(includeIfNull: false, defaultValue: 0) final int totalUnreadCount; - /// Total unread channels by the user + /// Total unread channels by the user. @JsonKey(includeIfNull: false) final int? unreadChannels; /// Known top level fields. + /// /// Useful for [Serializer] methods. static final topLevelFields = [ 'devices', diff --git a/packages/stream_chat/lib/src/core/models/user.dart b/packages/stream_chat/lib/src/core/models/user.dart index a7e7a51e..5a5ef8e5 100644 --- a/packages/stream_chat/lib/src/core/models/user.dart +++ b/packages/stream_chat/lib/src/core/models/user.dart @@ -4,29 +4,60 @@ import 'package:stream_chat/src/core/util/serializer.dart'; part 'user.g.dart'; -/// The class that defines the user model +/// Class that defines a Stream Chat User. @JsonSerializable() class User extends Equatable { - /// Constructor used for json serialization + /// Creates a new user. + /// + /// {@template name} + /// If an [name] is provided it will be set on [extraData] with a `key` + /// of 'name'. + /// + /// For example: + /// ```dart + /// final user = User(id: 'id', name: 'Sahil Kumar'); + /// print(user.name == user.extraData['name']); // true + /// ``` + /// {@endtemplate} + /// + /// {@template image} + /// If an [image] is provided it will be set on [extraData] with a `key` + /// of 'image'. + /// + /// For example: + /// ```dart + /// final user = User(id: 'id', image: 'https://getstream.io/image.png'); + /// print(user.image == user.extraData['image']); // true + /// ``` + /// {@endtemplate} User({ required this.id, this.role, + String? name, + String? image, DateTime? createdAt, DateTime? updatedAt, this.lastActive, + Map extraData = const {}, this.online = false, - this.extraData = const {}, this.banned = false, this.teams = const [], this.language, }) : createdAt = createdAt ?? DateTime.now(), - updatedAt = updatedAt ?? DateTime.now(); + updatedAt = updatedAt ?? DateTime.now(), + /*For backwards compatibility, set 'name', 'image' in [extraData].*/ + extraData = { + ...extraData, + if (name != null) 'name': name, + if (image != null) 'image': image, + }; - /// Create a new instance from a json + /// Create a new instance from json. factory User.fromJson(Map json) => _$UserFromJson(Serializer.moveToExtraDataFromRoot(json, topLevelFields)); /// Known top level fields. + /// /// Useful for [Serializer] methods. static const topLevelFields = [ 'id', @@ -40,57 +71,13 @@ class User extends Equatable { 'language', ]; - /// User id + /// User id. final String id; - /// User role - @JsonKey(includeIfNull: false, toJson: Serializer.readOnly) - final String? role; - - /// User role - @JsonKey( - includeIfNull: false, - toJson: Serializer.readOnly, - defaultValue: [], - ) - final List teams; - - /// Date of user creation - @JsonKey(includeIfNull: false, toJson: Serializer.readOnly) - final DateTime createdAt; - - /// Date of last user update - @JsonKey(includeIfNull: false, toJson: Serializer.readOnly) - final DateTime updatedAt; - - /// Date of last user connection - @JsonKey(includeIfNull: false, toJson: Serializer.readOnly) - final DateTime? lastActive; - - /// True if user is online - @JsonKey( - includeIfNull: false, toJson: Serializer.readOnly, defaultValue: false) - final bool online; - - /// True if user is banned from the chat - @JsonKey( - includeIfNull: false, toJson: Serializer.readOnly, defaultValue: false) - final bool banned; - - /// Map of custom user extraData - @JsonKey( - includeIfNull: false, - defaultValue: {}, - ) - final Map extraData; - - /// The language this user prefers. + /// Shortcut for user name. /// - /// Defaults to 'en'. - @JsonKey(includeIfNull: false) - final String? language; - - /// Shortcut for user name + /// {@macro name} + @JsonKey(ignore: true) String get name { if (extraData.containsKey('name')) { final name = extraData['name']! as String; @@ -99,11 +86,62 @@ class User extends Equatable { return id; } - /// List of users to list of userIds + /// Shortcut for user image. + /// + /// {@macro image} + @JsonKey(ignore: true) + String? get image => extraData['image'] as String?; + + /// User role. + @JsonKey(includeIfNull: false, toJson: Serializer.readOnly) + final String? role; + + /// User teams + @JsonKey( + includeIfNull: false, + toJson: Serializer.readOnly, + defaultValue: [], + ) + final List teams; + + /// Date of user creation. + @JsonKey(includeIfNull: false, toJson: Serializer.readOnly) + final DateTime createdAt; + + /// Date of last user update. + @JsonKey(includeIfNull: false, toJson: Serializer.readOnly) + final DateTime updatedAt; + + /// Date of last user connection. + @JsonKey(includeIfNull: false, toJson: Serializer.readOnly) + final DateTime? lastActive; + + /// True if user is online. + @JsonKey( + includeIfNull: false, toJson: Serializer.readOnly, defaultValue: false) + final bool online; + + /// True if user is banned from the chat. + @JsonKey( + includeIfNull: false, toJson: Serializer.readOnly, defaultValue: false) + final bool banned; + + /// Map of custom user extraData. + @JsonKey( + includeIfNull: false, + defaultValue: {}, + ) + final Map extraData; + + /// The language this user prefers. + @JsonKey(includeIfNull: false) + final String? language; + + /// List of users to list of userIds. static List? toIds(List? users) => users?.map((u) => u.id).toList(); - /// Serialize to json + /// Serialize to json. Map toJson() => Serializer.moveFromExtraDataToRoot( _$UserToJson(this), ); @@ -112,6 +150,8 @@ class User extends Equatable { User copyWith({ String? id, String? role, + String? name, + String? image, DateTime? createdAt, DateTime? updatedAt, DateTime? lastActive, @@ -124,6 +164,10 @@ class User extends Equatable { User( id: id ?? this.id, role: role ?? this.role, + /* if null, it will be retrieved from extraData['name']*/ + name: name, + /* if null, it will be retrieved from extraData['image']*/ + image: image, createdAt: createdAt ?? this.createdAt, updatedAt: updatedAt ?? this.updatedAt, lastActive: lastActive ?? this.lastActive, @@ -135,5 +179,5 @@ class User extends Equatable { ); @override - List get props => [id]; + List get props => [id, role]; } 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 dd3183d5..ab2f04d5 100644 --- a/packages/stream_chat/lib/src/core/models/user.g.dart +++ b/packages/stream_chat/lib/src/core/models/user.g.dart @@ -19,8 +19,8 @@ User _$UserFromJson(Map json) { lastActive: json['last_active'] == null ? null : DateTime.parse(json['last_active'] as String), - online: json['online'] as bool? ?? false, extraData: json['extra_data'] as Map? ?? {}, + online: json['online'] as bool? ?? false, banned: json['banned'] as bool? ?? false, teams: (json['teams'] as List?)?.map((e) => e as String).toList() ?? diff --git a/packages/stream_chat/test/fixtures/user.json b/packages/stream_chat/test/fixtures/user.json index b22c8553..146a4242 100644 --- a/packages/stream_chat/test/fixtures/user.json +++ b/packages/stream_chat/test/fixtures/user.json @@ -2,5 +2,16 @@ "id": "bbb19d9a-ee50-45bc-84e5-0584e79d0c9e", "role": "test-role", "name": "John", + "image": "https://getstream.io/random_png/?id=cool-shadow-7&name=Cool+shadow", + "extraDataStringTest": "Extra data test", + "extraDataIntTest": 1, + "extraDataDoubleTest": 1.1, + "extraDataBoolTest": true, + "banned": true, + "online": true, + "teams": ["team-1", "team-2"], + "created_at": "2021-08-03 12:39:21.817646", + "updated_at": "2021-08-04 12:39:21.817646", + "last_active" : "2021-08-05 12:39:21.817646", "language": "en" } \ No newline at end of file diff --git a/packages/stream_chat/test/src/core/models/own_user_test.dart b/packages/stream_chat/test/src/core/models/own_user_test.dart index 7aef859d..e1d12ada 100644 --- a/packages/stream_chat/test/src/core/models/own_user_test.dart +++ b/packages/stream_chat/test/src/core/models/own_user_test.dart @@ -1,10 +1,23 @@ +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat/src/core/models/own_user.dart'; import 'package:stream_chat/src/core/models/user.dart'; +import 'package:stream_chat/stream_chat.dart'; import 'package:test/test.dart'; import '../../utils.dart'; +class MockMute extends Mock implements Mute {} + +class MockDevice extends Mock implements Device {} + void main() { + final devices = [MockDevice(), MockDevice()]; + final mutes = [MockMute(), MockMute()]; + final channelMutes = [MockMute()]; + final createdAt = DateTime.parse('2021-05-03 12:39:21.817646'); + final updatedAt = DateTime.parse('2021-04-03 12:39:21.817646'); + final lastActive = DateTime.parse('2021-03-03 12:39:21.817646'); + group('src/models/own_user', () { test('should parse json correctly', () { final ownUser = OwnUser.fromJson(jsonFixture('own_user.json')); @@ -22,6 +35,7 @@ void main() { expect(ownUser.channelMutes.length, 1); expect(ownUser.totalUnreadCount, 0); expect(ownUser.unreadChannels, 0); + expect(ownUser.image, 'https://placehold.jp/150x150.png'); expect(ownUser.extraData['image'], 'https://placehold.jp/150x150.png'); expect(ownUser.extraData['name'], 'Proud darkness'); expect(ownUser.extraData['username'], 'Rioland'); @@ -40,6 +54,7 @@ void main() { expect(ownUser.online, user.online); expect(ownUser.banned, user.banned); expect(ownUser.extraData, user.extraData); + expect(ownUser.image, user.image); }); test('copyWith', () { @@ -49,35 +64,116 @@ void main() { expect(newUser.id, user.id); expect(newUser.role, user.role); expect(newUser.name, user.name); + expect(newUser.devices, user.devices); + expect(newUser.mutes, user.mutes); + expect(newUser.totalUnreadCount, user.totalUnreadCount); + expect(newUser.channelMutes, user.channelMutes); + expect(newUser.createdAt, user.createdAt); + expect(newUser.updatedAt, user.updatedAt); + expect(newUser.lastActive, user.lastActive); + expect(newUser.online, user.online); + expect(newUser.extraData, user.extraData); + expect(newUser.banned, user.banned); + expect(newUser.teams, user.teams); + expect(newUser.language, user.language); + expect(newUser.image, user.image); newUser = user.copyWith( id: 'test', role: 'test', + image: 'https://getstream.io/image-new', extraData: { 'name': 'test', }, + devices: devices, + mutes: mutes, + totalUnreadCount: 10, + unreadChannels: 5, + channelMutes: channelMutes, + createdAt: createdAt, + updatedAt: updatedAt, + lastActive: lastActive, + online: true, + banned: true, + teams: ['team1', 'team2'], + language: 'fr', ); expect(newUser.id, 'test'); expect(newUser.role, 'test'); expect(newUser.name, 'test'); + expect(newUser.image, 'https://getstream.io/image-new'); + expect( + newUser.extraData, + { + 'name': 'test', + 'image': 'https://getstream.io/image-new', + }, + reason: 'Should get image from user.image', + ); + expect(newUser.devices, devices); + expect(newUser.mutes, mutes); + expect(newUser.totalUnreadCount, 10); + expect(newUser.unreadChannels, 5); + expect(newUser.channelMutes, channelMutes); + expect(newUser.createdAt, createdAt); + expect(newUser.updatedAt, updatedAt); + expect(newUser.createdAt, createdAt); + expect(newUser.online, true); + expect(newUser.banned, true); + expect(newUser.teams, ['team1', 'team2']); + expect(newUser.language, 'fr'); }); test('merge', () { final user = OwnUser.fromJson(jsonFixture('own_user.json')); - final newUser = user.merge(OwnUser( - id: 'test', - role: 'test', - extraData: const { - 'name': 'test', - }, - banned: true, - )); + final newUser = user.merge( + OwnUser( + id: 'test', + role: 'test', + extraData: const { + 'name': 'test', + }, + image: 'https://getstream.io/image-new', + devices: devices, + mutes: mutes, + totalUnreadCount: 10, + unreadChannels: 5, + channelMutes: channelMutes, + createdAt: createdAt, + updatedAt: updatedAt, + lastActive: lastActive, + online: true, + banned: true, + teams: const ['team1', 'team2'], + language: 'fr', + ), + ); expect(newUser.id, 'test'); expect(newUser.role, 'test'); expect(newUser.name, 'test'); + expect(newUser.image, 'https://getstream.io/image-new'); + expect( + newUser.extraData, + { + 'name': 'test', + 'image': 'https://getstream.io/image-new', + }, + reason: 'Should get image from user.image', + ); + expect(newUser.devices, devices); + expect(newUser.mutes, mutes); + expect(newUser.totalUnreadCount, 10); + expect(newUser.unreadChannels, 5); + expect(newUser.channelMutes, channelMutes); + expect(newUser.createdAt, createdAt); + expect(newUser.updatedAt, updatedAt); + expect(newUser.createdAt, createdAt); + expect(newUser.online, true); expect(newUser.banned, true); + expect(newUser.teams, ['team1', 'team2']); + expect(newUser.language, 'fr'); }); }); } 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..c8e04ede 100644 --- a/packages/stream_chat/test/src/core/models/user_test.dart +++ b/packages/stream_chat/test/src/core/models/user_test.dart @@ -4,21 +4,73 @@ import 'package:test/test.dart'; import '../../utils.dart'; void main() { + const id = 'bbb19d9a-ee50-45bc-84e5-0584e79d0c9e'; + const role = 'test-role'; + const name = 'John'; + const image = + 'https://getstream.io/random_png/?id=cool-shadow-7&name=Cool+shadow'; + const extraDataStringTest = 'Extra data test'; + const extraDataIntTest = 1; + const extraDataDoubleTest = 1.1; + const extraDataBoolTest = true; + const online = true; + const banned = true; + const teams = ['team-1', 'team-2']; + const createdAtString = '2021-08-03 12:39:21.817646'; + const updatedAtString = '2021-08-04 12:39:21.817646'; + const lastActiveString = '2021-08-05 12:39:21.817646'; + group('src/models/user', () { test('should parse json correctly', () { final user = User.fromJson(jsonFixture('user.json')); - expect(user.id, 'bbb19d9a-ee50-45bc-84e5-0584e79d0c9e'); - expect(user.name, 'John'); + expect(user.id, id); + expect(user.role, role); + expect(user.name, name); + expect(user.image, image); + expect(user.extraData['image'], image); + expect(user.extraData['extraDataStringTest'], extraDataStringTest); + expect(user.extraData['extraDataIntTest'], extraDataIntTest); + expect(user.extraData['extraDataDoubleTest'], extraDataDoubleTest); + expect(user.extraData['extraDataBoolTest'], extraDataBoolTest); + expect(user.online, online); + expect(user.banned, banned); + expect(user.teams, teams); + expect(user.createdAt, DateTime.parse(createdAtString)); + expect(user.updatedAt, DateTime.parse(updatedAtString)); + expect(user.lastActive, DateTime.parse(lastActiveString)); + expect(user.language, 'en'); }); test('should serialize to json correctly', () { final user = User( - id: 'bbb19d9a-ee50-45bc-84e5-0584e79d0c9e', - role: 'abc', + id: id, + role: role, + name: name, + image: image, + extraData: const { + 'extraDataStringTest': extraDataStringTest, + 'extraDataIntTest': extraDataIntTest, + 'extraDataDoubleTest': extraDataDoubleTest, + 'extraDataBoolTest': extraDataBoolTest, + }, + createdAt: DateTime.parse(createdAtString), + updatedAt: DateTime.parse(updatedAtString), + lastActive: DateTime.parse(lastActiveString), + banned: online, + online: banned, + teams: const ['team-1', 'team-2'], + language: 'fr', ); expect(user.toJson(), { - 'id': 'bbb19d9a-ee50-45bc-84e5-0584e79d0c9e', + 'id': id, + 'name': name, + 'image': image, + 'extraDataStringTest': extraDataStringTest, + 'extraDataIntTest': extraDataIntTest, + 'extraDataDoubleTest': extraDataDoubleTest, + 'extraDataBoolTest': extraDataBoolTest, + 'language': 'fr', }); }); @@ -29,18 +81,141 @@ void main() { expect(newUser.id, user.id); expect(newUser.role, user.role); expect(newUser.name, user.name); + expect(newUser.image, user.image); + expect(newUser.online, user.online); + expect(newUser.banned, user.banned); + expect(newUser.teams, user.teams); + expect(newUser.createdAt, user.createdAt); + expect(newUser.updatedAt, user.updatedAt); + expect(newUser.lastActive, user.lastActive); + expect(newUser.language, user.language); newUser = user.copyWith( id: 'test', role: 'test', - extraData: { - 'name': 'test', - }, + name: 'test', + image: 'https://stream.io/new-image', + online: false, + banned: false, + teams: ['new-team1', 'new-team2'], + createdAt: DateTime.parse('2021-05-03 12:39:21.817646'), + updatedAt: DateTime.parse('2021-05-04 12:39:21.817646'), + lastActive: DateTime.parse('2021-05-06 12:39:21.817646'), + language: 'it', ); expect(newUser.id, 'test'); expect(newUser.role, 'test'); expect(newUser.name, 'test'); + expect(newUser.image, 'https://stream.io/new-image'); + expect(newUser.extraData['image'], 'https://stream.io/new-image'); + expect(newUser.online, false); + expect(newUser.banned, false); + expect(newUser.teams, ['new-team1', 'new-team2']); + expect(newUser.createdAt, DateTime.parse('2021-05-03 12:39:21.817646')); + expect(newUser.updatedAt, DateTime.parse('2021-05-04 12:39:21.817646')); + expect(newUser.lastActive, DateTime.parse('2021-05-06 12:39:21.817646')); + expect(newUser.language, 'it'); + }); + + test('name property and extraData manipulation', () { + final user = User(id: id, name: name); + + expect(user.name, name); + expect(user.extraData['name'], name); + expect(user.toJson(), {'id': id, 'name': name}); + expect(User.fromJson(user.toJson()).toJson(), {'id': id, 'name': name}); + + const nameOne = 'Name One'; + var newUser = user.copyWith( + extraData: {'name': nameOne}, + ); + + expect(newUser.extraData['name'], nameOne); + expect(newUser.name, nameOne); + + const nameTwo = 'Name Two'; + newUser = user.copyWith( + name: nameTwo, + ); + + expect(newUser.extraData['name'], nameTwo); + expect(newUser.name, nameTwo); + + const nameThree = 'Name Three'; + newUser = user.copyWith( + name: nameThree, + extraData: {'name': nameThree}, + ); + + expect(newUser.extraData['name'], nameThree); + expect(newUser.name, nameThree); + }); + + test('image property and extraData manipulation', () { + final user = User(id: id, image: image); + + expect(user.image, image); + expect(user.extraData['image'], image); + expect(user.toJson(), {'id': id, 'image': image}); + expect(User.fromJson(user.toJson()).toJson(), {'id': id, 'image': image}); + + const imageURLOne = 'https://stream.io/image-one'; + var newUser = user.copyWith( + extraData: {'image': imageURLOne}, + ); + + expect(newUser.extraData['image'], imageURLOne); + expect(newUser.image, imageURLOne); + + const imageURLTwo = 'https://stream.io/image-two'; + newUser = user.copyWith( + image: imageURLTwo, + ); + + expect(newUser.extraData['image'], imageURLTwo); + expect(newUser.image, imageURLTwo); + + const imageURLThree = 'https://stream.io/image-three'; + newUser = user.copyWith( + image: imageURLThree, + extraData: {'image': imageURLThree}, + ); + + expect(newUser.extraData['image'], imageURLThree); + expect(newUser.image, imageURLThree); + }); + + test('default values, constructor', () { + final user = User(id: id); + + expect(user.id, id); + expect(user.role, null); + expect(user.name, id, reason: 'if a name is not supplied, default to id'); + expect(user.image, null); + expect(user.extraData, const {}); + expect(user.online, false); + expect(user.banned, false); + expect(user.teams, []); + expect(user.lastActive, null); + expect(user.createdAt, isNotNull); + expect(user.updatedAt, isNotNull); + }); + + test('default values, parse json', () { + final user = User.fromJson(const {'id': id}); + + expect(user.id, id); + expect(user.role, null); + expect(user.name, id, reason: 'if a name is not supplied, default to id'); + expect(user.image, null); + expect(user.extraData, const {}); + expect(user.online, false); + expect(user.banned, false); + expect(user.teams, []); + expect(user.lastActive, null); + expect(user.createdAt, isNotNull); + expect(user.updatedAt, isNotNull); }); }); } diff --git a/packages/stream_chat_flutter/example/lib/main.dart b/packages/stream_chat_flutter/example/lib/main.dart index c2663a6a..a9e1366c 100644 --- a/packages/stream_chat_flutter/example/lib/main.dart +++ b/packages/stream_chat_flutter/example/lib/main.dart @@ -22,8 +22,7 @@ void main() async { /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/ await client.connectUser( User(id: 'super-band-9'), - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.' - '0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A', + '''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''', ); final channel = client.channel('messaging', id: 'godevs'); diff --git a/packages/stream_chat_flutter/lib/src/user_avatar.dart b/packages/stream_chat_flutter/lib/src/user_avatar.dart index 61627a81..6a5ef0de 100644 --- a/packages/stream_chat_flutter/lib/src/user_avatar.dart +++ b/packages/stream_chat_flutter/lib/src/user_avatar.dart @@ -60,9 +60,7 @@ class UserAvatar extends StatelessWidget { @override Widget build(BuildContext context) { - final hasImage = user.extraData.containsKey('image') && - user.extraData['image'] != null && - user.extraData['image'] != ''; + final hasImage = user.image != null && user.image!.isNotEmpty; final streamChatTheme = StreamChatTheme.of(context); final placeholder = @@ -80,8 +78,7 @@ class UserAvatar extends StatelessWidget { ? CachedNetworkImage( fit: BoxFit.cover, filterQuality: FilterQuality.high, - // ignore: cast_nullable_to_non_nullable - imageUrl: user.extraData['image'] as String, + imageUrl: user.image!, errorWidget: (context, __, ___) => streamChatTheme.defaultUserImage(context, user), placeholder: placeholder != null diff --git a/packages/stream_chat_flutter_core/example/lib/main.dart b/packages/stream_chat_flutter_core/example/lib/main.dart index 0a7b0bce..03e791de 100644 --- a/packages/stream_chat_flutter_core/example/lib/main.dart +++ b/packages/stream_chat_flutter_core/example/lib/main.dart @@ -13,13 +13,10 @@ Future main() async { await client.connectUser( User( id: 'cool-shadow-7', - extraData: const { - 'image': - 'https://getstream.io/random_png/?id=cool-shadow-7&name=Cool+shadow', - }, + image: + 'https://getstream.io/random_png/?id=cool-shadow-7&name=Cool+shadow', ), - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9' - '.gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo', + '''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9.gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo''', ); runApp( diff --git a/packages/stream_chat_localizations/example/lib/add_new_lang.dart b/packages/stream_chat_localizations/example/lib/add_new_lang.dart index 1412f4e0..8a1d196e 100644 --- a/packages/stream_chat_localizations/example/lib/add_new_lang.dart +++ b/packages/stream_chat_localizations/example/lib/add_new_lang.dart @@ -401,8 +401,7 @@ void main() async { /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/ await client.connectUser( User(id: 'super-band-9'), - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.' - '0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A', + '''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''', ); final channel = client.channel('messaging', id: 'godevs'); diff --git a/packages/stream_chat_localizations/example/lib/main.dart b/packages/stream_chat_localizations/example/lib/main.dart index 818086df..c99a4c6c 100644 --- a/packages/stream_chat_localizations/example/lib/main.dart +++ b/packages/stream_chat_localizations/example/lib/main.dart @@ -20,8 +20,7 @@ void main() async { /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/ await client.connectUser( User(id: 'super-band-9'), - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.' - '0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A', + '''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''', ); final channel = client.channel('messaging', id: 'godevs'); diff --git a/packages/stream_chat_localizations/example/lib/override_lang.dart b/packages/stream_chat_localizations/example/lib/override_lang.dart index 0e45019a..5be8f9a6 100644 --- a/packages/stream_chat_localizations/example/lib/override_lang.dart +++ b/packages/stream_chat_localizations/example/lib/override_lang.dart @@ -45,8 +45,7 @@ void main() async { /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/ await client.connectUser( User(id: 'super-band-9'), - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.' - '0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A', + '''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''', ); final channel = client.channel('messaging', id: 'godevs'); diff --git a/packages/stream_chat_persistence/example/lib/main.dart b/packages/stream_chat_persistence/example/lib/main.dart index 5c83ed78..e187da56 100644 --- a/packages/stream_chat_persistence/example/lib/main.dart +++ b/packages/stream_chat_persistence/example/lib/main.dart @@ -22,10 +22,8 @@ Future main() async { await client.connectUser( User( id: 'cool-shadow-7', - extraData: const { - 'image': - 'https://getstream.io/random_png/?id=cool-shadow-7&name=Cool+shadow', - }, + image: + 'https://getstream.io/random_png/?id=cool-shadow-7&name=Cool+shadow', ), 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9.' 'gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo',