From acd932ccc9dedc63ed0f6d3f7031ad935d6452c7 Mon Sep 17 00:00:00 2001 From: Gordon Hayes Date: Wed, 4 Aug 2021 16:42:01 +0200 Subject: [PATCH] feat: add image property to user --- .../lib/src/core/models/own_user.dart | 38 ++++--- .../lib/src/core/models/own_user.g.dart | 1 + .../stream_chat/lib/src/core/models/user.dart | 104 ++++++++++++++---- .../lib/src/core/models/user.g.dart | 4 +- .../lib/src/user_avatar.dart | 7 +- 5 files changed, 112 insertions(+), 42 deletions(-) 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..5adc753c 100644 --- a/packages/stream_chat/lib/src/core/models/own_user.dart +++ b/packages/stream_chat/lib/src/core/models/own_user.dart @@ -28,6 +28,7 @@ class OwnUser extends User { bool banned = false, List teams = const [], String? language, + String? image, }) : super( id: id, role: role, @@ -39,6 +40,7 @@ class OwnUser extends User { banned: banned, teams: teams, language: language, + image: image, ); /// Create a new instance from a json @@ -57,6 +59,7 @@ class OwnUser extends User { extraData: user.extraData, teams: user.teams, language: user.language, + image: user.image, ); /// Creates a copy of [OwnUser] with specified attributes overridden. @@ -77,24 +80,26 @@ class OwnUser extends User { int? totalUnreadCount, int? unreadChannels, String? language, + String? image, }) => OwnUser( - id: id ?? this.id, - banned: banned ?? this.banned, - role: role ?? this.role, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - lastActive: lastActive ?? this.lastActive, - online: online ?? this.online, - extraData: extraData ?? this.extraData, - teams: teams ?? this.teams, - channelMutes: channelMutes ?? this.channelMutes, - devices: devices ?? this.devices, - mutes: mutes ?? this.mutes, - totalUnreadCount: totalUnreadCount ?? this.totalUnreadCount, - unreadChannels: unreadChannels ?? this.unreadChannels, - language: language ?? this.language, - ); + id: id ?? this.id, + banned: banned ?? this.banned, + role: role ?? this.role, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + lastActive: lastActive ?? this.lastActive, + online: online ?? this.online, + extraData: extraData ?? this.extraData, + teams: teams ?? this.teams, + channelMutes: channelMutes ?? this.channelMutes, + devices: devices ?? this.devices, + mutes: mutes ?? this.mutes, + totalUnreadCount: totalUnreadCount ?? this.totalUnreadCount, + unreadChannels: unreadChannels ?? this.unreadChannels, + language: language ?? this.language, + image: image // if null, it will be retrieved from extraData['image'] + ); /// Returns a new [OwnUser] that is a combination of this ownUser /// and the given [other] ownUser. @@ -116,6 +121,7 @@ class OwnUser extends User { unreadChannels: other.unreadChannels, updatedAt: other.updatedAt, language: other.language, + image: other.image, ); } 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 ca4acdea..cacb99c5 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 @@ -40,5 +40,6 @@ OwnUser _$OwnUserFromJson(Map json) { (json['teams'] as List?)?.map((e) => e as String).toList() ?? [], language: json['language'] as String?, + image: json['image'] 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 a7e7a51e..9baaf049 100644 --- a/packages/stream_chat/lib/src/core/models/user.dart +++ b/packages/stream_chat/lib/src/core/models/user.dart @@ -4,29 +4,45 @@ 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. + /// + /// 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 + /// ``` User({ required this.id, this.role, + 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(); + }) : _image = image, + createdAt = createdAt ?? DateTime.now(), + updatedAt = updatedAt ?? DateTime.now(), - /// Create a new instance from a json + // For backwards compatibalitity, set 'image' on [extraData]. + extraData = + (image != null) ? {...extraData, 'image': image} : extraData; + + /// 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', @@ -38,16 +54,64 @@ class User extends Equatable { 'banned', 'teams', 'language', + 'image', ]; - /// User id + /// User id. final String id; - /// User role + /// User role. @JsonKey(includeIfNull: false, toJson: Serializer.readOnly) final String? role; - /// User role + /// Image for user. This is also set on `extraData['image']`. + /// + /// {@template image} + /// There are a few ways to set an image. + /// + /// Setting an image by passing in an image argument: + /// ```dart + /// final user = User( + /// id: 'id', + /// image: 'https://getstream.io/image', + /// ); + /// ``` + /// + /// Or by directly setting it in [extraData], for example: + /// ```dart + /// final user = User( + /// id: 'id', + /// extraData: const {'image': 'https://getstream.io/image'}, + /// ); + /// + /// ``` + /// Parsing json with an 'image' key will automatically set the `image` + /// property and `extraData['image']` key/value. + /// + /// ```dart + /// final user = User.fromJson({ + /// id: 'id', + /// image: 'https://getstream.io/image', // key: image + /// }); + /// + /// print(user.image == user.extraData['image']); // true + /// ``` + /// {@endtemplate} + final String? _image; + + /// Shortcut for user image. + /// + /// {@macro image} + @JsonKey(includeIfNull: false, toJson: Serializer.readOnly) + String? get image { + if (_image != null) { + return _image; + } else { + return extraData['image'] as String?; + } + } + + /// User teams @JsonKey( includeIfNull: false, toJson: Serializer.readOnly, @@ -55,29 +119,29 @@ class User extends Equatable { ) final List teams; - /// Date of user creation + /// Date of user creation. @JsonKey(includeIfNull: false, toJson: Serializer.readOnly) final DateTime createdAt; - /// Date of last user update + /// Date of last user update. @JsonKey(includeIfNull: false, toJson: Serializer.readOnly) final DateTime updatedAt; - /// Date of last user connection + /// Date of last user connection. @JsonKey(includeIfNull: false, toJson: Serializer.readOnly) final DateTime? lastActive; - /// True if user is online + /// True if user is online. @JsonKey( includeIfNull: false, toJson: Serializer.readOnly, defaultValue: false) final bool online; - /// True if user is banned from the chat + /// True if user is banned from the chat. @JsonKey( includeIfNull: false, toJson: Serializer.readOnly, defaultValue: false) final bool banned; - /// Map of custom user extraData + /// Map of custom user extraData. @JsonKey( includeIfNull: false, defaultValue: {}, @@ -85,12 +149,10 @@ class User extends Equatable { final Map extraData; /// The language this user prefers. - /// - /// Defaults to 'en'. @JsonKey(includeIfNull: false) final String? language; - /// Shortcut for user name + /// Shortcut for user name. String get name { if (extraData.containsKey('name')) { final name = extraData['name']! as String; @@ -99,11 +161,11 @@ class User extends Equatable { return id; } - /// List of users to list of userIds + /// 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), ); @@ -120,6 +182,7 @@ class User extends Equatable { bool? banned, List? teams, String? language, + String? image, }) => User( id: id ?? this.id, @@ -132,6 +195,7 @@ class User extends Equatable { banned: banned ?? this.banned, teams: teams ?? this.teams, language: language ?? this.language, + image: image, // if null, it will be retrieved from extraData['image'] ); @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 dd3183d5..c685cd57 100644 --- a/packages/stream_chat/lib/src/core/models/user.g.dart +++ b/packages/stream_chat/lib/src/core/models/user.g.dart @@ -10,6 +10,7 @@ User _$UserFromJson(Map json) { return User( id: json['id'] as String, role: json['role'] as String?, + image: json['image'] as String?, createdAt: json['created_at'] == null ? null : DateTime.parse(json['created_at'] as String), @@ -19,8 +20,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() ?? @@ -41,6 +42,7 @@ Map _$UserToJson(User instance) { } writeNotNull('role', readonly(instance.role)); + writeNotNull('image', readonly(instance.image)); writeNotNull('teams', readonly(instance.teams)); writeNotNull('created_at', readonly(instance.createdAt)); writeNotNull('updated_at', readonly(instance.updatedAt)); diff --git a/packages/stream_chat_flutter/lib/src/user_avatar.dart b/packages/stream_chat_flutter/lib/src/user_avatar.dart index 31e07f0a..0c897084 100644 --- a/packages/stream_chat_flutter/lib/src/user_avatar.dart +++ b/packages/stream_chat_flutter/lib/src/user_avatar.dart @@ -56,9 +56,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); Widget avatar = FittedBox( @@ -75,8 +73,7 @@ class UserAvatar extends StatelessWidget { child: hasImage ? CachedNetworkImage( filterQuality: FilterQuality.high, - // ignore: cast_nullable_to_non_nullable - imageUrl: user.extraData['image'] as String, + imageUrl: user.image!, errorWidget: (_, __, ___) => streamChatTheme.defaultUserImage(context, user), fit: BoxFit.cover,