diff --git a/packages/stream_chat/lib/src/api/channel.dart b/packages/stream_chat/lib/src/api/channel.dart index 5f33f966..cb3cfe37 100644 --- a/packages/stream_chat/lib/src/api/channel.dart +++ b/packages/stream_chat/lib/src/api/channel.dart @@ -22,7 +22,7 @@ class Channel { this._client, this._type, this._id, { - Map extraData = const {}, + Map extraData = const {}, }) : _cid = _id != null ? '$_type:$_id' : null, _extraData = extraData { _client.logger.info('New Channel instance not initialized created'); @@ -630,7 +630,7 @@ class Channel { Future sendReaction( Message message, String type, { - Map extraData = const {}, + Map extraData = const {}, bool enforceUnique = false, }) async { _checkInitialized(); @@ -1099,7 +1099,7 @@ class Channel { }) async { final payload = { 'sort': sort, - 'filter_conditions': filter, + 'filter_conditions': filter ?? {}, 'type': type, }; diff --git a/packages/stream_chat/lib/src/client.dart b/packages/stream_chat/lib/src/client.dart index c7d29737..3cabe274 100644 --- a/packages/stream_chat/lib/src/client.dart +++ b/packages/stream_chat/lib/src/client.dart @@ -1194,7 +1194,7 @@ class StreamChatClient { Channel channel( String type, { String? id, - Map extraData = const {}, + Map extraData = const {}, }) { if (id != null && state.channels.containsKey('$type:$id')) { return state.channels['$type:$id']!; diff --git a/packages/stream_chat/lib/src/models/attachment.dart b/packages/stream_chat/lib/src/models/attachment.dart index 09ea4f58..b68a1bae 100644 --- a/packages/stream_chat/lib/src/models/attachment.dart +++ b/packages/stream_chat/lib/src/models/attachment.dart @@ -114,7 +114,7 @@ class Attachment extends Equatable { includeIfNull: false, defaultValue: {}, ) - final Map extraData; + final Map extraData; /// The attachment ID. /// @@ -183,7 +183,7 @@ class Attachment extends Equatable { List? actions, AttachmentFile? file, UploadState? uploadState, - Map? extraData, + Map? extraData, }) => Attachment( id: id ?? this.id, diff --git a/packages/stream_chat/lib/src/models/attachment.g.dart b/packages/stream_chat/lib/src/models/attachment.g.dart index c42aaf5f..de129ab6 100644 --- a/packages/stream_chat/lib/src/models/attachment.g.dart +++ b/packages/stream_chat/lib/src/models/attachment.g.dart @@ -30,10 +30,7 @@ Attachment _$AttachmentFromJson(Map json) { ?.map((e) => Action.fromJson(e as Map)) .toList() ?? [], - extraData: (json['extra_data'] as Map?)?.map( - (k, e) => MapEntry(k, e as Object), - ) ?? - {}, + extraData: json['extra_data'] as Map? ?? {}, file: json['file'] == null ? null : AttachmentFile.fromJson(json['file'] as Map), diff --git a/packages/stream_chat/lib/src/models/attachment_file.freezed.dart b/packages/stream_chat/lib/src/models/attachment_file.freezed.dart index a6c5e0c6..5d7075c3 100644 --- a/packages/stream_chat/lib/src/models/attachment_file.freezed.dart +++ b/packages/stream_chat/lib/src/models/attachment_file.freezed.dart @@ -132,9 +132,8 @@ class _$PreparingCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res> Preparing get _value => super._value as Preparing; } -@JsonSerializable() - /// @nodoc +@JsonSerializable() class _$Preparing implements Preparing { const _$Preparing(); @@ -253,9 +252,8 @@ class _$InProgressCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res> } } -@JsonSerializable() - /// @nodoc +@JsonSerializable() class _$InProgress implements InProgress { const _$InProgress({required this.uploaded, required this.total}); @@ -382,9 +380,8 @@ class _$SuccessCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res> Success get _value => super._value as Success; } -@JsonSerializable() - /// @nodoc +@JsonSerializable() class _$Success implements Success { const _$Success(); @@ -497,9 +494,8 @@ class _$FailedCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res> } } -@JsonSerializable() - /// @nodoc +@JsonSerializable() class _$Failed implements Failed { const _$Failed({required this.error}); diff --git a/packages/stream_chat/lib/src/models/attachment_file.g.dart b/packages/stream_chat/lib/src/models/attachment_file.g.dart index 55fcea6c..5844ae16 100644 --- a/packages/stream_chat/lib/src/models/attachment_file.g.dart +++ b/packages/stream_chat/lib/src/models/attachment_file.g.dart @@ -8,10 +8,10 @@ part of 'attachment_file.dart'; AttachmentFile _$AttachmentFileFromJson(Map json) { return AttachmentFile( + size: json['size'] as int?, path: json['path'] as String?, name: json['name'] as String?, bytes: _fromString(json['bytes'] as String?), - size: json['size'] as int?, ); } diff --git a/packages/stream_chat/lib/src/models/channel_model.dart b/packages/stream_chat/lib/src/models/channel_model.dart index 14380592..41230bf7 100644 --- a/packages/stream_chat/lib/src/models/channel_model.dart +++ b/packages/stream_chat/lib/src/models/channel_model.dart @@ -87,7 +87,7 @@ class ChannelModel { includeIfNull: false, defaultValue: {}, ) - final Map extraData; + final Map extraData; /// The team the channel belongs to @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) @@ -132,7 +132,7 @@ class ChannelModel { DateTime? updatedAt, DateTime? deletedAt, int? memberCount, - Map? extraData, + Map? extraData, String? team, }) => ChannelModel( diff --git a/packages/stream_chat/lib/src/models/channel_model.g.dart b/packages/stream_chat/lib/src/models/channel_model.g.dart index 78e93910..4bde3d4a 100644 --- a/packages/stream_chat/lib/src/models/channel_model.g.dart +++ b/packages/stream_chat/lib/src/models/channel_model.g.dart @@ -31,10 +31,7 @@ ChannelModel _$ChannelModelFromJson(Map json) { ? null : DateTime.parse(json['deleted_at'] as String), memberCount: json['member_count'] as int? ?? 0, - extraData: (json['extra_data'] as Map?)?.map( - (k, e) => MapEntry(k, e as Object), - ) ?? - {}, + extraData: json['extra_data'] as Map? ?? {}, team: json['team'] as String?, ); } diff --git a/packages/stream_chat/lib/src/models/event.dart b/packages/stream_chat/lib/src/models/event.dart index 1675197d..b4ee9c1d 100644 --- a/packages/stream_chat/lib/src/models/event.dart +++ b/packages/stream_chat/lib/src/models/event.dart @@ -93,7 +93,7 @@ class Event { /// Map of custom channel extraData @JsonKey(defaultValue: {}) - final Map extraData; + final Map extraData; /// Known top level fields. /// Useful for [Serialization] methods. @@ -140,7 +140,7 @@ class Event { int? unreadChannels, bool? online, String? parentId, - Map? extraData, + Map? extraData, }) => Event( type: type ?? this.type, @@ -180,7 +180,7 @@ class EventChannel extends ChannelModel { required DateTime updatedAt, DateTime? deletedAt, required int memberCount, - Map? extraData, + Map? extraData, }) : super( id: id, type: type, diff --git a/packages/stream_chat/lib/src/models/event.g.dart b/packages/stream_chat/lib/src/models/event.g.dart index b76c675c..aba142f8 100644 --- a/packages/stream_chat/lib/src/models/event.g.dart +++ b/packages/stream_chat/lib/src/models/event.g.dart @@ -38,10 +38,7 @@ Event _$EventFromJson(Map json) { channelId: json['channel_id'] as String?, channelType: json['channel_type'] as String?, parentId: json['parent_id'] as String?, - extraData: (json['extra_data'] as Map?)?.map( - (k, e) => MapEntry(k, e as Object), - ) ?? - {}, + extraData: json['extra_data'] as Map? ?? {}, isLocal: json['is_local'] as bool? ?? false, ); } @@ -89,10 +86,7 @@ EventChannel _$EventChannelFromJson(Map json) { ? null : DateTime.parse(json['deleted_at'] as String), memberCount: json['member_count'] as int? ?? 0, - extraData: (json['extra_data'] as Map?)?.map( - (k, e) => MapEntry(k, e as Object), - ) ?? - {}, + extraData: json['extra_data'] as Map? ?? {}, ); } diff --git a/packages/stream_chat/lib/src/models/filter.dart b/packages/stream_chat/lib/src/models/filter.dart index 2356c57b..eb3932ff 100644 --- a/packages/stream_chat/lib/src/models/filter.dart +++ b/packages/stream_chat/lib/src/models/filter.dart @@ -87,8 +87,8 @@ extension FilterOperatorX on FilterOperator { /// See Query Channels Documentation class Filter extends Equatable { const Filter.__({ - required this.operator, required this.value, + this.operator, this.key, }); @@ -159,13 +159,26 @@ class Filter extends Equatable { /// Creates a custom [Filter] if there isn't one already available. const factory Filter.custom({ - required String operator, required Object value, + String? operator, String? key, }) = Filter.__; + /// Creates a custom [Filter] from a raw map value + /// + /// ```dart + /// final filter = Filter.raw( + /// { + /// 'members': [user1.id, user2.id], + /// } + /// ) + /// ``` + const factory Filter.raw({ + required Map value, + }) = Filter.__; + /// An operator used for the filter. The operator string must start with `$` - final String operator; + final String? operator; /// The "left-hand" side of the filter. /// Specifies the name of the field the filter should match. @@ -183,24 +196,22 @@ class Filter extends Equatable { List get props => [operator, key, value]; /// Serializes to json object - Map toJson() { - final json = {}; + Map toJson() { + final json = {}; final groupOperators = _groupOperators.map((it) => it.rawValue); - assert( - groupOperators.contains(operator) || key != null, - 'Filter must contain the `key` when the operator is not a ' - 'group operator.', - ); - if (groupOperators.contains(operator)) { // Filters with group operators are encoded in the following form: // { $: [ , ] } - json[operator] = value; - } else { + json[operator!] = value; + } else if (operator != null) { // Normal filters are encoded in the following form: // { key: { $: } } json[key!] = {operator: value}; + } else if (key != null) { + json[key!] = value; + } else { + return value as Map; } return json; diff --git a/packages/stream_chat/lib/src/models/message.dart b/packages/stream_chat/lib/src/models/message.dart index cec591c0..f928ce3a 100644 --- a/packages/stream_chat/lib/src/models/message.dart +++ b/packages/stream_chat/lib/src/models/message.dart @@ -208,7 +208,7 @@ class Message extends Equatable { includeIfNull: false, defaultValue: {}, ) - final Map extraData; + final Map extraData; /// True if the message is a system info bool get isSystem => type == 'system'; @@ -289,7 +289,7 @@ class Message extends Equatable { DateTime? pinnedAt, Object? pinExpires = _pinExpires, User? pinnedBy, - Map? extraData, + Map? extraData, MessageSendingStatus? status, bool? skipPush, }) { diff --git a/packages/stream_chat/lib/src/models/message.g.dart b/packages/stream_chat/lib/src/models/message.g.dart index 4168777a..ca094a83 100644 --- a/packages/stream_chat/lib/src/models/message.g.dart +++ b/packages/stream_chat/lib/src/models/message.g.dart @@ -63,10 +63,7 @@ Message _$MessageFromJson(Map json) { pinnedBy: json['pinned_by'] == null ? null : User.fromJson(json['pinned_by'] as Map), - extraData: (json['extra_data'] as Map?)?.map( - (k, e) => MapEntry(k, e as Object), - ) ?? - {}, + extraData: json['extra_data'] as Map? ?? {}, deletedAt: json['deleted_at'] == null ? null : DateTime.parse(json['deleted_at'] as String), diff --git a/packages/stream_chat/lib/src/models/own_user.dart b/packages/stream_chat/lib/src/models/own_user.dart index 292e0f4a..a93af19a 100644 --- a/packages/stream_chat/lib/src/models/own_user.dart +++ b/packages/stream_chat/lib/src/models/own_user.dart @@ -23,7 +23,7 @@ class OwnUser extends User { DateTime? updatedAt, DateTime? lastActive, bool online = false, - Map extraData = const {}, + Map extraData = const {}, bool banned = false, }) : super( id: id, diff --git a/packages/stream_chat/lib/src/models/own_user.g.dart b/packages/stream_chat/lib/src/models/own_user.g.dart index a5c27402..760edc01 100644 --- a/packages/stream_chat/lib/src/models/own_user.g.dart +++ b/packages/stream_chat/lib/src/models/own_user.g.dart @@ -34,9 +34,7 @@ OwnUser _$OwnUserFromJson(Map json) { ? null : DateTime.parse(json['last_active'] as String), online: json['online'] as bool? ?? false, - extraData: (json['extra_data'] as Map).map( - (k, e) => MapEntry(k, e as Object), - ), + extraData: json['extra_data'] as Map? ?? {}, banned: json['banned'] as bool? ?? false, ); } diff --git a/packages/stream_chat/lib/src/models/reaction.dart b/packages/stream_chat/lib/src/models/reaction.dart index 0e60e281..85697204 100644 --- a/packages/stream_chat/lib/src/models/reaction.dart +++ b/packages/stream_chat/lib/src/models/reaction.dart @@ -15,7 +15,7 @@ class Reaction { this.user, String? userId, this.score = 0, - this.extraData, + this.extraData = const {}, }) : userId = userId ?? user?.id, createdAt = createdAt ?? DateTime.now(); @@ -49,8 +49,11 @@ class Reaction { final String? userId; /// Reaction custom extraData - @JsonKey(includeIfNull: false) - final Map? extraData; + @JsonKey( + includeIfNull: false, + defaultValue: {}, + ) + final Map extraData; /// Map of custom user extraData static const topLevelFields = [ @@ -75,7 +78,7 @@ class Reaction { User? user, String? userId, int? score, - Map? extraData, + Map? extraData, }) => Reaction( messageId: messageId ?? this.messageId, diff --git a/packages/stream_chat/lib/src/models/reaction.g.dart b/packages/stream_chat/lib/src/models/reaction.g.dart index 24b60830..3e92150d 100644 --- a/packages/stream_chat/lib/src/models/reaction.g.dart +++ b/packages/stream_chat/lib/src/models/reaction.g.dart @@ -18,9 +18,7 @@ Reaction _$ReactionFromJson(Map json) { : User.fromJson(json['user'] as Map), userId: json['user_id'] as String?, score: json['score'] as int? ?? 0, - extraData: (json['extra_data'] as Map?)?.map( - (k, e) => MapEntry(k, e as Object), - ), + extraData: json['extra_data'] as Map? ?? {}, ); } @@ -40,6 +38,6 @@ Map _$ReactionToJson(Reaction instance) { writeNotNull('user', readonly(instance.user)); val['score'] = instance.score; writeNotNull('user_id', readonly(instance.userId)); - writeNotNull('extra_data', instance.extraData); + val['extra_data'] = instance.extraData; return val; } diff --git a/packages/stream_chat/lib/src/models/user.dart b/packages/stream_chat/lib/src/models/user.dart index 2cb8f8ed..9335f8c8 100644 --- a/packages/stream_chat/lib/src/models/user.dart +++ b/packages/stream_chat/lib/src/models/user.dart @@ -74,8 +74,11 @@ class User { final bool banned; /// Map of custom user extraData - @JsonKey(includeIfNull: false) - final Map extraData; + @JsonKey( + includeIfNull: false, + defaultValue: {}, + ) + final Map extraData; @override int get hashCode => id.hashCode; @@ -107,7 +110,7 @@ class User { DateTime? updatedAt, DateTime? lastActive, bool? online, - Map? extraData, + Map? extraData, bool? banned, List? teams, }) => diff --git a/packages/stream_chat/lib/src/models/user.g.dart b/packages/stream_chat/lib/src/models/user.g.dart index 11538395..befcac03 100644 --- a/packages/stream_chat/lib/src/models/user.g.dart +++ b/packages/stream_chat/lib/src/models/user.g.dart @@ -20,9 +20,7 @@ User _$UserFromJson(Map json) { ? null : DateTime.parse(json['last_active'] as String), online: json['online'] as bool? ?? false, - extraData: (json['extra_data'] as Map).map( - (k, e) => MapEntry(k, e as Object), - ), + 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/models/filter_test.dart b/packages/stream_chat/test/src/models/filter_test.dart index c73c0d0b..ac026c6c 100644 --- a/packages/stream_chat/test/src/models/filter_test.dart +++ b/packages/stream_chat/test/src/models/filter_test.dart @@ -130,6 +130,14 @@ void main() { expect(filter.operator, operator); }); + test('raw', () { + const value = { + 'test': ['a', 'b'], + }; + const filter = Filter.raw(value: value); + expect(filter.value, value); + }); + group('groupedOperator', () { final filter1 = Filter.equal('testKey', 'testValue'); final filter2 = Filter.in_('testKey', const ['testValue']); @@ -180,6 +188,30 @@ void main() { '{"$key":{"${FilterOperator.in_.rawValue}":${json.encode(values)}}}', ); }); + + test('custom with no operator', () { + const key = 'testKey'; + const values = ['testValue']; + final filter = Filter.custom(key: key, value: values); + final encoded = json.encode(filter); + expect( + encoded, + '{"$key":${json.encode(values)}}', + ); + }); + + test('raw', () { + const value = { + 'test': ['a', 'b'], + }; + const filter = Filter.raw(value: value); + + final encoded = json.encode(filter); + expect( + encoded, + json.encode(value), + ); + }); }); test('groupedFilter', () { diff --git a/packages/stream_chat_flutter/lib/src/image_group.dart b/packages/stream_chat_flutter/lib/src/image_group.dart index 51c686c3..4647fe57 100644 --- a/packages/stream_chat_flutter/lib/src/image_group.dart +++ b/packages/stream_chat_flutter/lib/src/image_group.dart @@ -12,12 +12,16 @@ class ImageGroup extends StatelessWidget { required this.message, required this.messageTheme, required this.size, + this.onReturnAction, this.onShowMessage, }) : super(key: key); /// List of attachments to show final List images; + /// Callback when attachment is returned to from other screens + final ValueChanged? onReturnAction; + /// Message which images are attached to final Message message; @@ -111,10 +115,10 @@ class ImageGroup extends StatelessWidget { void _onTap( BuildContext context, int index, - ) { + ) async { final channel = StreamChannel.of(context).channel; - Navigator.push( + final res = await Navigator.push( context, MaterialPageRoute( builder: (context) => StreamChannel( @@ -129,6 +133,7 @@ class ImageGroup extends StatelessWidget { ), ), ); + if (res != null) onReturnAction?.call(res); } Widget _buildImage(BuildContext context, int index) => ImageAttachment( diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index a5ccdb6f..ed098903 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -463,58 +463,57 @@ class MessageInputState extends State { ); } - Widget _buildExpandActionsButton() => Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: AnimatedCrossFade( - crossFadeState: _actionsShrunk - ? CrossFadeState.showFirst - : CrossFadeState.showSecond, - firstChild: IconButton( - onPressed: () => setState(() => _actionsShrunk = false), - icon: Transform.rotate( - angle: (widget.actionsLocation == ActionsLocation.right || - widget.actionsLocation == ActionsLocation.rightInside) - ? pi - : 0, - child: StreamSvgIcon.emptyCircleLeft( - color: StreamChatTheme.of(context) - .messageInputTheme - .expandButtonColor, - ), + Widget _buildExpandActionsButton() { + final channel = StreamChannel.of(context).channel; + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: AnimatedCrossFade( + crossFadeState: _actionsShrunk + ? CrossFadeState.showFirst + : CrossFadeState.showSecond, + firstChild: IconButton( + onPressed: () => setState(() => _actionsShrunk = false), + icon: Transform.rotate( + angle: (widget.actionsLocation == ActionsLocation.right || + widget.actionsLocation == ActionsLocation.rightInside) + ? pi + : 0, + child: StreamSvgIcon.emptyCircleLeft( + color: StreamChatTheme.of(context) + .messageInputTheme + .expandButtonColor, ), - padding: const EdgeInsets.all(0), - constraints: const BoxConstraints.tightFor( - height: 24, - width: 24, - ), - splashRadius: 24, ), - secondChild: widget.disableAttachments && - !widget.showCommandsButton && - widget.actions?.isNotEmpty != true - ? const Offstage() - : FittedBox( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - if (!widget.disableAttachments) _buildAttachmentButton(), - if (widget.showCommandsButton && - widget.editMessage == null && - StreamChannel.of(context) - .channel - .config - ?.commands - .isNotEmpty == - true) - _buildCommandButton(), - ...widget.actions ?? [], - ].insertBetween(const SizedBox(width: 8)), - ), - ), - duration: const Duration(milliseconds: 300), - alignment: Alignment.center, + padding: const EdgeInsets.all(0), + constraints: const BoxConstraints.tightFor( + height: 24, + width: 24, + ), + splashRadius: 24, ), - ); + secondChild: widget.disableAttachments && + !widget.showCommandsButton && + widget.actions?.isNotEmpty != true + ? const Offstage() + : FittedBox( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + if (!widget.disableAttachments) _buildAttachmentButton(), + if (widget.showCommandsButton && + widget.editMessage == null && + channel.state != null && + channel.config?.commands.isNotEmpty == true) + _buildCommandButton(), + ...widget.actions ?? [], + ].insertBetween(const SizedBox(width: 8)), + ), + ), + duration: const Duration(milliseconds: 300), + alignment: Alignment.center, + ), + ); + } Expanded _buildTextInput(BuildContext context) { final theme = StreamChatTheme.of(context); diff --git a/packages/stream_chat_flutter/lib/src/message_list_view.dart b/packages/stream_chat_flutter/lib/src/message_list_view.dart index dc80a7d9..c0b8ac0d 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -270,7 +270,7 @@ class MessageListView extends StatefulWidget { class _MessageListViewState extends State { ItemScrollController? _scrollController; - Function? _onThreadTap; + void Function(Message)? _onThreadTap; bool _showScrollToBottom = false; late final ItemPositionsListener _itemPositionListener; int? _messageListLength; @@ -814,7 +814,7 @@ class _MessageListViewState extends State { showUsername: !isMyMessage, padding: const EdgeInsets.all(8), showSendingIndicator: false, - onThreadTap: _onThreadTap as void Function(Message)?, + onThreadTap: _onThreadTap, borderRadiusGeometry: const BorderRadius.only( topLeft: Radius.circular(16), bottomLeft: Radius.circular(2), @@ -976,7 +976,7 @@ class _MessageListViewState extends State { showThreadReplyMessage: !isThreadMessage, showFlagButton: !isMyMessage, borderSide: borderSide, - onThreadTap: _onThreadTap as void Function(Message)?, + onThreadTap: _onThreadTap, onReplyTap: widget.onReplyTap, attachmentBorderRadiusGeometry: BorderRadius.only( topLeft: Radius.circular(attachmentBorderRadius), diff --git a/packages/stream_chat_flutter/lib/src/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget.dart index 781f9187..42ce4947 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget.dart @@ -125,6 +125,7 @@ class MessageWidget extends StatefulWidget { message: message, messageTheme: messageTheme, onShowMessage: onShowMessage, + onReturnAction: onReturnAction, ), ), border, diff --git a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart b/packages/stream_chat_flutter_core/lib/src/stream_channel.dart index 2760d9cc..20304cdd 100644 --- a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart +++ b/packages/stream_chat_flutter_core/lib/src/stream_channel.dart @@ -159,11 +159,11 @@ class StreamChannelState extends State { bool preferOffline = false, }) async { if (_topPaginationEnded || - _queryTopMessagesController.value! || + _queryTopMessagesController.value == true || channel.state == null) return; _queryTopMessagesController.add(true); - late Message message; + Message? message; if (channel.state!.threads.containsKey(parentId)) { final thread = channel.state!.threads[parentId]!; if (thread.isNotEmpty) { @@ -175,7 +175,7 @@ class StreamChannelState extends State { final response = await channel.getReplies( parentId, PaginationParams( - lessThan: message.id, + lessThan: message?.id, limit: limit, ), preferOffline: preferOffline, diff --git a/packages/stream_chat_persistence/lib/src/db/moor_chat_database.dart b/packages/stream_chat_persistence/lib/src/db/moor_chat_database.dart index 622e0a61..fbb53fd3 100644 --- a/packages/stream_chat_persistence/lib/src/db/moor_chat_database.dart +++ b/packages/stream_chat_persistence/lib/src/db/moor_chat_database.dart @@ -57,7 +57,7 @@ class MoorChatDatabase extends _$MoorChatDatabase { // you should bump this number whenever you change or add a table definition. @override - int get schemaVersion => 2; + int get schemaVersion => 3; @override MigrationStrategy get migration => MigrationStrategy( 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 8220ee4e..b8d404b2 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 @@ -42,22 +42,20 @@ class ChannelEntity extends DataClass implements Insertable { final String? createdById; /// Map of custom channel extraData - final Map? extraData; - - ChannelEntity({ - required this.id, - required this.type, - required this.cid, - required this.config, - required this.frozen, - this.lastMessageAt, - required this.createdAt, - required this.updatedAt, - this.deletedAt, - required this.memberCount, - this.createdById, - this.extraData, - }); + final Map? extraData; + ChannelEntity( + {required this.id, + required this.type, + required this.cid, + required this.config, + required this.frozen, + this.lastMessageAt, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.memberCount, + this.createdById, + this.extraData}); factory ChannelEntity.fromData( Map data, GeneratedDatabase db, {String? prefix}) { @@ -135,7 +133,7 @@ class ChannelEntity extends DataClass implements Insertable { deletedAt: serializer.fromJson(json['deletedAt']), memberCount: serializer.fromJson(json['memberCount']), createdById: serializer.fromJson(json['createdById']), - extraData: serializer.fromJson?>(json['extraData']), + extraData: serializer.fromJson?>(json['extraData']), ); } @override @@ -153,7 +151,7 @@ class ChannelEntity extends DataClass implements Insertable { 'deletedAt': serializer.toJson(deletedAt), 'memberCount': serializer.toJson(memberCount), 'createdById': serializer.toJson(createdById), - 'extraData': serializer.toJson?>(extraData), + 'extraData': serializer.toJson?>(extraData), }; } @@ -169,7 +167,7 @@ class ChannelEntity extends DataClass implements Insertable { Value deletedAt = const Value.absent(), int? memberCount, Value createdById = const Value.absent(), - Value?> extraData = const Value.absent()}) => + Value?> extraData = const Value.absent()}) => ChannelEntity( id: id ?? this.id, type: type ?? this.type, @@ -257,7 +255,7 @@ class ChannelsCompanion extends UpdateCompanion { final Value deletedAt; final Value memberCount; final Value createdById; - final Value?> extraData; + final Value?> extraData; const ChannelsCompanion({ this.id = const Value.absent(), this.type = const Value.absent(), @@ -301,7 +299,7 @@ class ChannelsCompanion extends UpdateCompanion { Expression? deletedAt, Expression? memberCount, Expression? createdById, - Expression?>? extraData, + Expression?>? extraData, }) { return RawValuesInsertable({ if (id != null) 'id': id, @@ -331,7 +329,7 @@ class ChannelsCompanion extends UpdateCompanion { Value? deletedAt, Value? memberCount, Value? createdById, - Value?>? extraData}) { + Value?>? extraData}) { return ChannelsCompanion( id: id ?? this.id, type: type ?? this.type, @@ -638,8 +636,8 @@ class $ChannelsTable extends Channels static TypeConverter, String> $converter0 = MapConverter(); - static TypeConverter, String> $converter1 = - MapConverter(); + static TypeConverter, String> $converter1 = + MapConverter(); } class MessageEntity extends DataClass implements Insertable { @@ -714,7 +712,7 @@ class MessageEntity extends DataClass implements Insertable { final String? channelCid; /// Message custom extraData - final Map? extraData; + final Map? extraData; MessageEntity( {required this.id, this.messageText, @@ -901,7 +899,7 @@ class MessageEntity extends DataClass implements Insertable { pinExpires: serializer.fromJson(json['pinExpires']), pinnedByUserId: serializer.fromJson(json['pinnedByUserId']), channelCid: serializer.fromJson(json['channelCid']), - extraData: serializer.fromJson?>(json['extraData']), + extraData: serializer.fromJson?>(json['extraData']), ); } @override @@ -931,7 +929,7 @@ class MessageEntity extends DataClass implements Insertable { 'pinExpires': serializer.toJson(pinExpires), 'pinnedByUserId': serializer.toJson(pinnedByUserId), 'channelCid': serializer.toJson(channelCid), - 'extraData': serializer.toJson?>(extraData), + 'extraData': serializer.toJson?>(extraData), }; } @@ -959,7 +957,7 @@ class MessageEntity extends DataClass implements Insertable { Value pinExpires = const Value.absent(), Value pinnedByUserId = const Value.absent(), Value channelCid = const Value.absent(), - Value?> extraData = const Value.absent()}) => + Value?> extraData = const Value.absent()}) => MessageEntity( id: id ?? this.id, messageText: messageText.present ? messageText.value : this.messageText, @@ -1121,7 +1119,7 @@ class MessagesCompanion extends UpdateCompanion { final Value pinExpires; final Value pinnedByUserId; final Value channelCid; - final Value?> extraData; + final Value?> extraData; const MessagesCompanion({ this.id = const Value.absent(), this.messageText = const Value.absent(), @@ -1200,7 +1198,7 @@ class MessagesCompanion extends UpdateCompanion { Expression? pinExpires, Expression? pinnedByUserId, Expression? channelCid, - Expression?>? extraData, + Expression?>? extraData, }) { return RawValuesInsertable({ if (id != null) 'id': id, @@ -1254,7 +1252,7 @@ class MessagesCompanion extends UpdateCompanion { Value? pinExpires, Value? pinnedByUserId, Value? channelCid, - Value?>? extraData}) { + Value?>? extraData}) { return MessagesCompanion( id: id ?? this.id, messageText: messageText ?? this.messageText, @@ -1818,8 +1816,8 @@ class $MessagesTable extends Messages MapConverter(); static TypeConverter, String> $converter4 = MapConverter(); - static TypeConverter, String> $converter5 = - MapConverter(); + static TypeConverter, String> $converter5 = + MapConverter(); } class PinnedMessageEntity extends DataClass @@ -1895,7 +1893,7 @@ class PinnedMessageEntity extends DataClass final String? channelCid; /// Message custom extraData - final Map? extraData; + final Map? extraData; PinnedMessageEntity( {required this.id, this.messageText, @@ -2082,7 +2080,7 @@ class PinnedMessageEntity extends DataClass pinExpires: serializer.fromJson(json['pinExpires']), pinnedByUserId: serializer.fromJson(json['pinnedByUserId']), channelCid: serializer.fromJson(json['channelCid']), - extraData: serializer.fromJson?>(json['extraData']), + extraData: serializer.fromJson?>(json['extraData']), ); } @override @@ -2112,7 +2110,7 @@ class PinnedMessageEntity extends DataClass 'pinExpires': serializer.toJson(pinExpires), 'pinnedByUserId': serializer.toJson(pinnedByUserId), 'channelCid': serializer.toJson(channelCid), - 'extraData': serializer.toJson?>(extraData), + 'extraData': serializer.toJson?>(extraData), }; } @@ -2140,7 +2138,7 @@ class PinnedMessageEntity extends DataClass Value pinExpires = const Value.absent(), Value pinnedByUserId = const Value.absent(), Value channelCid = const Value.absent(), - Value?> extraData = const Value.absent()}) => + Value?> extraData = const Value.absent()}) => PinnedMessageEntity( id: id ?? this.id, messageText: messageText.present ? messageText.value : this.messageText, @@ -2302,7 +2300,7 @@ class PinnedMessagesCompanion extends UpdateCompanion { final Value pinExpires; final Value pinnedByUserId; final Value channelCid; - final Value?> extraData; + final Value?> extraData; const PinnedMessagesCompanion({ this.id = const Value.absent(), this.messageText = const Value.absent(), @@ -2381,7 +2379,7 @@ class PinnedMessagesCompanion extends UpdateCompanion { Expression? pinExpires, Expression? pinnedByUserId, Expression? channelCid, - Expression?>? extraData, + Expression?>? extraData, }) { return RawValuesInsertable({ if (id != null) 'id': id, @@ -2435,7 +2433,7 @@ class PinnedMessagesCompanion extends UpdateCompanion { Value? pinExpires, Value? pinnedByUserId, Value? channelCid, - Value?>? extraData}) { + Value?>? extraData}) { return PinnedMessagesCompanion( id: id ?? this.id, messageText: messageText ?? this.messageText, @@ -3000,8 +2998,8 @@ class $PinnedMessagesTable extends PinnedMessages MapConverter(); static TypeConverter, String> $converter4 = MapConverter(); - static TypeConverter, String> $converter5 = - MapConverter(); + static TypeConverter, String> $converter5 = + MapConverter(); } class ReactionEntity extends DataClass implements Insertable { @@ -3021,7 +3019,7 @@ class ReactionEntity extends DataClass implements Insertable { final int score; /// Reaction custom extraData - final Map? extraData; + final Map? extraData; ReactionEntity( {required this.userId, required this.messageId, @@ -3073,7 +3071,7 @@ class ReactionEntity extends DataClass implements Insertable { type: serializer.fromJson(json['type']), createdAt: serializer.fromJson(json['createdAt']), score: serializer.fromJson(json['score']), - extraData: serializer.fromJson?>(json['extraData']), + extraData: serializer.fromJson?>(json['extraData']), ); } @override @@ -3085,7 +3083,7 @@ class ReactionEntity extends DataClass implements Insertable { 'type': serializer.toJson(type), 'createdAt': serializer.toJson(createdAt), 'score': serializer.toJson(score), - 'extraData': serializer.toJson?>(extraData), + 'extraData': serializer.toJson?>(extraData), }; } @@ -3095,7 +3093,7 @@ class ReactionEntity extends DataClass implements Insertable { String? type, DateTime? createdAt, int? score, - Value?> extraData = const Value.absent()}) => + Value?> extraData = const Value.absent()}) => ReactionEntity( userId: userId ?? this.userId, messageId: messageId ?? this.messageId, @@ -3144,7 +3142,7 @@ class ReactionsCompanion extends UpdateCompanion { final Value type; final Value createdAt; final Value score; - final Value?> extraData; + final Value?> extraData; const ReactionsCompanion({ this.userId = const Value.absent(), this.messageId = const Value.absent(), @@ -3169,7 +3167,7 @@ class ReactionsCompanion extends UpdateCompanion { Expression? type, Expression? createdAt, Expression? score, - Expression?>? extraData, + Expression?>? extraData, }) { return RawValuesInsertable({ if (userId != null) 'user_id': userId, @@ -3187,7 +3185,7 @@ class ReactionsCompanion extends UpdateCompanion { Value? type, Value? createdAt, Value? score, - Value?>? extraData}) { + Value?>? extraData}) { return ReactionsCompanion( userId: userId ?? this.userId, messageId: messageId ?? this.messageId, @@ -3357,8 +3355,8 @@ class $ReactionsTable extends Reactions return $ReactionsTable(_db, alias); } - static TypeConverter, String> $converter0 = - MapConverter(); + static TypeConverter, String> $converter0 = + MapConverter(); } class UserEntity extends DataClass implements Insertable { @@ -3384,7 +3382,7 @@ class UserEntity extends DataClass implements Insertable { final bool banned; /// Map of custom user extraData - final Map extraData; + final Map extraData; UserEntity( {required this.id, this.role, @@ -3449,7 +3447,7 @@ class UserEntity extends DataClass implements Insertable { lastActive: serializer.fromJson(json['lastActive']), online: serializer.fromJson(json['online']), banned: serializer.fromJson(json['banned']), - extraData: serializer.fromJson>(json['extraData']), + extraData: serializer.fromJson>(json['extraData']), ); } @override @@ -3463,7 +3461,7 @@ class UserEntity extends DataClass implements Insertable { 'lastActive': serializer.toJson(lastActive), 'online': serializer.toJson(online), 'banned': serializer.toJson(banned), - 'extraData': serializer.toJson>(extraData), + 'extraData': serializer.toJson>(extraData), }; } @@ -3475,7 +3473,7 @@ class UserEntity extends DataClass implements Insertable { Value lastActive = const Value.absent(), bool? online, bool? banned, - Map? extraData}) => + Map? extraData}) => UserEntity( id: id ?? this.id, role: role.present ? role.value : this.role, @@ -3536,7 +3534,7 @@ class UsersCompanion extends UpdateCompanion { final Value lastActive; final Value online; final Value banned; - final Value> extraData; + final Value> extraData; const UsersCompanion({ this.id = const Value.absent(), this.role = const Value.absent(), @@ -3555,7 +3553,7 @@ class UsersCompanion extends UpdateCompanion { this.lastActive = const Value.absent(), this.online = const Value.absent(), this.banned = const Value.absent(), - required Map extraData, + required Map extraData, }) : id = Value(id), extraData = Value(extraData); static Insertable custom({ @@ -3566,7 +3564,7 @@ class UsersCompanion extends UpdateCompanion { Expression? lastActive, Expression? online, Expression? banned, - Expression>? extraData, + Expression>? extraData, }) { return RawValuesInsertable({ if (id != null) 'id': id, @@ -3588,7 +3586,7 @@ class UsersCompanion extends UpdateCompanion { Value? lastActive, Value? online, Value? banned, - Value>? extraData}) { + Value>? extraData}) { return UsersCompanion( id: id ?? this.id, role: role ?? this.role, @@ -3791,8 +3789,8 @@ class $UsersTable extends Users with TableInfo<$UsersTable, UserEntity> { return $UsersTable(_db, alias); } - static TypeConverter, String> $converter0 = - MapConverter(); + static TypeConverter, String> $converter0 = + MapConverter(); } class MemberEntity extends DataClass implements Insertable { diff --git a/packages/stream_chat_persistence/lib/src/entity/channels.dart b/packages/stream_chat_persistence/lib/src/entity/channels.dart index e6c280ed..2c65719b 100644 --- a/packages/stream_chat_persistence/lib/src/entity/channels.dart +++ b/packages/stream_chat_persistence/lib/src/entity/channels.dart @@ -39,7 +39,7 @@ class Channels extends Table { TextColumn get createdById => text().nullable()(); /// Map of custom channel extraData - TextColumn get extraData => text().nullable().map(MapConverter())(); + TextColumn get extraData => text().nullable().map(MapConverter())(); @override Set get primaryKey => {cid}; diff --git a/packages/stream_chat_persistence/lib/src/entity/messages.dart b/packages/stream_chat_persistence/lib/src/entity/messages.dart index 7cb1326d..4006d6ce 100644 --- a/packages/stream_chat_persistence/lib/src/entity/messages.dart +++ b/packages/stream_chat_persistence/lib/src/entity/messages.dart @@ -81,7 +81,7 @@ class Messages extends Table { 'NULLABLE REFERENCES channels(cid) ON DELETE CASCADE')(); /// Message custom extraData - TextColumn get extraData => text().nullable().map(MapConverter())(); + TextColumn get extraData => text().nullable().map(MapConverter())(); @override Set get primaryKey => {id}; diff --git a/packages/stream_chat_persistence/lib/src/entity/reactions.dart b/packages/stream_chat_persistence/lib/src/entity/reactions.dart index 2b7d12b5..62380408 100644 --- a/packages/stream_chat_persistence/lib/src/entity/reactions.dart +++ b/packages/stream_chat_persistence/lib/src/entity/reactions.dart @@ -22,7 +22,7 @@ class Reactions extends Table { IntColumn get score => integer().withDefault(const Constant(0))(); /// Reaction custom extraData - TextColumn get extraData => text().nullable().map(MapConverter())(); + TextColumn get extraData => text().nullable().map(MapConverter())(); @override Set get primaryKey => { diff --git a/packages/stream_chat_persistence/lib/src/entity/users.dart b/packages/stream_chat_persistence/lib/src/entity/users.dart index 49fb4687..303092c9 100644 --- a/packages/stream_chat_persistence/lib/src/entity/users.dart +++ b/packages/stream_chat_persistence/lib/src/entity/users.dart @@ -27,7 +27,7 @@ class Users extends Table { BoolColumn get banned => boolean().withDefault(const Constant(false))(); /// Map of custom user extraData - TextColumn get extraData => text().map(MapConverter())(); + TextColumn get extraData => text().map(MapConverter())(); @override Set get primaryKey => {id}; 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 c97cc92d..558c6e45 100644 --- a/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart +++ b/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart @@ -42,6 +42,8 @@ extension MessageEntityX on MessageEntity { pinnedAt: pinnedAt, pinExpires: pinExpires, pinnedBy: pinnedBy, + mentionedUsers: + mentionedUsers.map((e) => User.fromJson(jsonDecode(e))).toList(), ); } diff --git a/packages/stream_chat_persistence/lib/src/mapper/reaction_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/reaction_mapper.dart index 003802b9..62a0c427 100644 --- a/packages/stream_chat_persistence/lib/src/mapper/reaction_mapper.dart +++ b/packages/stream_chat_persistence/lib/src/mapper/reaction_mapper.dart @@ -5,7 +5,7 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; extension ReactionEntityX on ReactionEntity { /// Maps a [ReactionEntity] into [Reaction] Reaction toReaction({User? user}) => Reaction( - extraData: extraData, + extraData: extraData ?? {}, type: type, createdAt: createdAt, userId: userId, 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 f73102f8..2c852ff2 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 @@ -48,7 +48,9 @@ void main() { (prev, curr) => prev?..update(curr.type, (value) => value + 1, ifAbsent: () => 1), ), - mentionedUsers: const [], + mentionedUsers: [ + jsonEncode(User(id: 'testuser')), + ], status: MessageSendingStatus.sent, updatedAt: DateTime.now(), extraData: {'extra_test_data': 'extraData'}, @@ -77,6 +79,11 @@ void main() { expect(message.createdAt, isSameDateAs(entity.createdAt)); expect(message.shadowed, entity.shadowed); expect(message.showInChannel, entity.showInChannel); + for (var i = 0; i < message.mentionedUsers.length; i++) { + final entityMentionedUser = + User.fromJson(jsonDecode(entity.mentionedUsers[i])); + expect(message.mentionedUsers[i].id, entityMentionedUser.id); + } expect(message.replyCount, entity.replyCount); expect(message.reactionScores, entity.reactionScores); expect(message.reactionCounts, entity.reactionCounts); @@ -135,6 +142,9 @@ void main() { shadowed: math.Random().nextBool(), showInChannel: math.Random().nextBool(), replyCount: 33, + mentionedUsers: [ + User(id: 'testuser'), + ], reactionScores: {for (final r in reactions) r.type: r.score}, reactionCounts: reactions.fold( {}, @@ -163,6 +173,8 @@ void main() { expect(entity.shadowed, message.shadowed); expect(entity.showInChannel, message.showInChannel); expect(entity.replyCount, message.replyCount); + expect(entity.mentionedUsers, + message.mentionedUsers.map((e) => jsonEncode(e)).toList()); expect(entity.reactionScores, message.reactionScores); expect(entity.reactionCounts, message.reactionCounts); expect(entity.status, message.status);