diff --git a/.github/workflows/dart_code_metrics.yaml b/.github/workflows/dart_code_metrics.yaml index 12c3a3d4..a4296e8b 100644 --- a/.github/workflows/dart_code_metrics.yaml +++ b/.github/workflows/dart_code_metrics.yaml @@ -1,7 +1,7 @@ name: Dart Code Metrics env: - flutter_version: "2.10.5" + flutter_version: "3.0.0" folders: "lib, test" melos_version: "2.1.0" diff --git a/.github/workflows/stream_flutter_workflow.yml b/.github/workflows/stream_flutter_workflow.yml index 1b2263ce..81039fd1 100644 --- a/.github/workflows/stream_flutter_workflow.yml +++ b/.github/workflows/stream_flutter_workflow.yml @@ -2,7 +2,7 @@ name: stream_flutter_workflow env: ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' - flutter_version: "2.10.5" + flutter_version: "3.0.0" melos_version: "2.1.0" on: diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index 3edd8c88..7609e0c3 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -1,10 +1,22 @@ +## 4.1.0 + +✅ Added + +- Added support for extra data in attachment file uploader. Thanks, [@rlee1990](https://github.com/rlee1990). + +🔄 Changed + +- Deprecated `role` in `Member` in favor of `channelRole` +- Deprecated `currentUserRole` getter in `Channel` in favor of `currentUserChannelRole` + ## 4.0.1 - Minor fixes ## 4.0.0 -For upgrading to V4, please refer to the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migration_guide_4_0/) +For upgrading to V4, please refer to +the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migration_guide_4_0/) ✅ Added @@ -19,8 +31,10 @@ For upgrading to V4, please refer to the [V4 Migration Guide](https://getstream. any channel. - [[#1047]](https://github.com/GetStream/stream-chat-flutter/issues/1047) `own_capabilities` extraData missing after channel update. -- [[#1054]](https://github.com/GetStream/stream-chat-flutter/issues/1054) Fix `Unsupported operation: Cannot remove from an unmodifiable list`. -- [[#1033]](https://github.com/GetStream/stream-chat-flutter/issues/1033) Hard delete from dashboard does not delete message from client. +- [[#1054]](https://github.com/GetStream/stream-chat-flutter/issues/1054) + Fix `Unsupported operation: Cannot remove from an unmodifiable list`. +- [[#1033]](https://github.com/GetStream/stream-chat-flutter/issues/1033) Hard delete from dashboard does not delete + message from client. - Send only `user_id` while reconnecting. ✅ Added @@ -53,8 +67,10 @@ For upgrading to V4, please refer to the [V4 Migration Guide](https://getstream. any channel. - [[#1047]](https://github.com/GetStream/stream-chat-flutter/issues/1047) `own_capabilities` extraData missing after channel update. -- [[#1054]](https://github.com/GetStream/stream-chat-flutter/issues/1054) Fix `Unsupported operation: Cannot remove from an unmodifiable list`. -- [[#1033]](https://github.com/GetStream/stream-chat-flutter/issues/1033) Hard delete from dashboard does not delete message from client. +- [[#1054]](https://github.com/GetStream/stream-chat-flutter/issues/1054) + Fix `Unsupported operation: Cannot remove from an unmodifiable list`. +- [[#1033]](https://github.com/GetStream/stream-chat-flutter/issues/1033) Hard delete from dashboard does not delete + message from client. - Send only `user_id` while reconnecting. ✅ Added diff --git a/packages/stream_chat/lib/src/client/channel.dart b/packages/stream_chat/lib/src/client/channel.dart index 29a2d5ad..a27b672f 100644 --- a/packages/stream_chat/lib/src/client/channel.dart +++ b/packages/stream_chat/lib/src/client/channel.dart @@ -448,12 +448,14 @@ class Channel { it.file!, onSendProgress: onSendProgress, cancelToken: cancelToken, + extraData: it.extraData, ).then((it) => it.file); } else { future = sendFile( it.file!, onSendProgress: onSendProgress, cancelToken: cancelToken, + extraData: it.extraData, ).then((it) => it.file); } _cancelableAttachmentUploadRequest[it.id] = cancelToken; @@ -757,6 +759,7 @@ class Channel { AttachmentFile file, { ProgressCallback? onSendProgress, CancelToken? cancelToken, + Map? extraData, }) { _checkInitialized(); return _client.sendFile( @@ -765,6 +768,7 @@ class Channel { type, onSendProgress: onSendProgress, cancelToken: cancelToken, + extraData: extraData, ); } @@ -773,6 +777,7 @@ class Channel { AttachmentFile file, { ProgressCallback? onSendProgress, CancelToken? cancelToken, + Map? extraData, }) { _checkInitialized(); return _client.sendImage( @@ -781,6 +786,7 @@ class Channel { type, onSendProgress: onSendProgress, cancelToken: cancelToken, + extraData: extraData, ); } @@ -805,6 +811,7 @@ class Channel { Future deleteFile( String url, { CancelToken? cancelToken, + Map? extraData, }) { _checkInitialized(); return _client.deleteFile( @@ -812,6 +819,7 @@ class Channel { id!, type, cancelToken: cancelToken, + extraData: extraData, ); } @@ -819,6 +827,7 @@ class Channel { Future deleteImage( String url, { CancelToken? cancelToken, + Map? extraData, }) { _checkInitialized(); return _client.deleteImage( @@ -826,6 +835,7 @@ class Channel { id!, type, cancelToken: cancelToken, + extraData: extraData, ); } @@ -2007,8 +2017,12 @@ class ChannelClientState { ); /// User role for the current user. + @Deprecated('Please use currentUserChannelRole') String? get currentUserRole => currentUserMember?.role; + /// Channel role for the current user + String? get currentUserChannelRole => currentUserMember?.channelRole; + /// Channel read list. List get read => _channelState.read ?? []; diff --git a/packages/stream_chat/lib/src/client/client.dart b/packages/stream_chat/lib/src/client/client.dart index d16f56c5..0f2781c1 100644 --- a/packages/stream_chat/lib/src/client/client.dart +++ b/packages/stream_chat/lib/src/client/client.dart @@ -728,6 +728,7 @@ class StreamChatClient { String channelType, { ProgressCallback? onSendProgress, CancelToken? cancelToken, + Map? extraData, }) => _chatApi.fileUploader.sendFile( file, @@ -735,6 +736,7 @@ class StreamChatClient { channelType, onSendProgress: onSendProgress, cancelToken: cancelToken, + extraData: extraData, ); /// Send a [image] to the [channelId] of type [channelType] @@ -744,6 +746,7 @@ class StreamChatClient { String channelType, { ProgressCallback? onSendProgress, CancelToken? cancelToken, + Map? extraData, }) => _chatApi.fileUploader.sendImage( image, @@ -751,6 +754,7 @@ class StreamChatClient { channelType, onSendProgress: onSendProgress, cancelToken: cancelToken, + extraData: extraData, ); /// Delete a file from this channel @@ -759,12 +763,14 @@ class StreamChatClient { String channelId, String channelType, { CancelToken? cancelToken, + Map? extraData, }) => _chatApi.fileUploader.deleteFile( url, channelId, channelType, cancelToken: cancelToken, + extraData: extraData, ); /// Delete an image from this channel @@ -773,12 +779,14 @@ class StreamChatClient { String channelId, String channelType, { CancelToken? cancelToken, + Map? extraData, }) => _chatApi.fileUploader.deleteImage( url, channelId, channelType, cancelToken: cancelToken, + extraData: extraData, ); /// Replaces the [channelId] of type [ChannelType] data with [data]. diff --git a/packages/stream_chat/lib/src/core/api/attachment_file_uploader.dart b/packages/stream_chat/lib/src/core/api/attachment_file_uploader.dart index d6dc249f..096b2c2d 100644 --- a/packages/stream_chat/lib/src/core/api/attachment_file_uploader.dart +++ b/packages/stream_chat/lib/src/core/api/attachment_file_uploader.dart @@ -16,6 +16,7 @@ abstract class AttachmentFileUploader { String channelType, { ProgressCallback? onSendProgress, CancelToken? cancelToken, + Map? extraData, }); /// Uploads a [file] to the given channel. @@ -29,6 +30,7 @@ abstract class AttachmentFileUploader { String channelType, { ProgressCallback? onSendProgress, CancelToken? cancelToken, + Map? extraData, }); /// Deletes a image using its [url] from the given channel. @@ -40,6 +42,7 @@ abstract class AttachmentFileUploader { String channelId, String channelType, { CancelToken? cancelToken, + Map? extraData, }); /// Deletes a file using its [url] from the given channel. @@ -51,6 +54,7 @@ abstract class AttachmentFileUploader { String channelId, String channelType, { CancelToken? cancelToken, + Map? extraData, }); } @@ -68,6 +72,7 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader { String channelType, { ProgressCallback? onSendProgress, CancelToken? cancelToken, + Map? extraData, }) async { final multiPartFile = await file.toMultipartFile(); final response = await _client.postFile( @@ -86,6 +91,7 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader { String channelType, { ProgressCallback? onSendProgress, CancelToken? cancelToken, + Map? extraData, }) async { final multiPartFile = await file.toMultipartFile(); final response = await _client.postFile( @@ -103,6 +109,7 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader { String channelId, String channelType, { CancelToken? cancelToken, + Map? extraData, }) async { final response = await _client.delete( '/channels/$channelType/$channelId/image', @@ -118,6 +125,7 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader { String channelId, String channelType, { CancelToken? cancelToken, + Map? extraData, }) async { final response = await _client.delete( '/channels/$channelType/$channelId/file', diff --git a/packages/stream_chat/lib/src/core/models/member.dart b/packages/stream_chat/lib/src/core/models/member.dart index e8b0ecff..c2dc9eb1 100644 --- a/packages/stream_chat/lib/src/core/models/member.dart +++ b/packages/stream_chat/lib/src/core/models/member.dart @@ -1,3 +1,5 @@ +// ignore_for_file: deprecated_member_use_from_same_package + import 'package:equatable/equatable.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:stream_chat/src/core/models/user.dart'; @@ -15,6 +17,7 @@ class Member extends Equatable { this.inviteRejectedAt, this.invited = false, this.role, + this.channelRole, this.userId, this.isModerator = false, DateTime? createdAt, @@ -46,8 +49,12 @@ class Member extends Equatable { final bool invited; /// The role of the user in the channel + @Deprecated('Please use channelRole') final String? role; + /// The role of this member in the channel + final String? channelRole; + /// The id of the interested user final String? userId; @@ -76,6 +83,7 @@ class Member extends Equatable { DateTime? inviteRejectedAt, bool? invited, String? role, + String? channelRole, String? userId, bool? isModerator, DateTime? createdAt, @@ -93,6 +101,7 @@ class Member extends Equatable { banExpires: banExpires ?? this.banExpires, shadowBanned: shadowBanned ?? this.shadowBanned, role: role ?? this.role, + channelRole: channelRole ?? this.channelRole, userId: userId ?? this.userId, isModerator: isModerator ?? this.isModerator, createdAt: createdAt ?? this.createdAt, @@ -109,6 +118,7 @@ class Member extends Equatable { inviteRejectedAt, invited, role, + channelRole, userId, isModerator, banned, diff --git a/packages/stream_chat/lib/src/core/models/member.g.dart b/packages/stream_chat/lib/src/core/models/member.g.dart index 4da3cfd2..1bcde74d 100644 --- a/packages/stream_chat/lib/src/core/models/member.g.dart +++ b/packages/stream_chat/lib/src/core/models/member.g.dart @@ -18,6 +18,7 @@ Member _$MemberFromJson(Map json) => Member( : DateTime.parse(json['invite_rejected_at'] as String), invited: json['invited'] as bool? ?? false, role: json['role'] as String?, + channelRole: json['channel_role'] as String?, userId: json['user_id'] as String?, isModerator: json['is_moderator'] as bool? ?? false, createdAt: json['created_at'] == null @@ -39,6 +40,7 @@ Map _$MemberToJson(Member instance) => { 'invite_rejected_at': instance.inviteRejectedAt?.toIso8601String(), 'invited': instance.invited, 'role': instance.role, + 'channel_role': instance.channelRole, 'user_id': instance.userId, 'is_moderator': instance.isModerator, 'banned': instance.banned, diff --git a/packages/stream_chat/lib/version.dart b/packages/stream_chat/lib/version.dart index 5ea92b4d..360c57e9 100644 --- a/packages/stream_chat/lib/version.dart +++ b/packages/stream_chat/lib/version.dart @@ -3,4 +3,4 @@ import 'package:stream_chat/src/client/client.dart'; /// Current package version /// Used in [StreamChatClient] to build the `x-stream-client` header // ignore: constant_identifier_names -const PACKAGE_VERSION = '4.0.1'; +const PACKAGE_VERSION = '4.1.0'; diff --git a/packages/stream_chat/pubspec.yaml b/packages/stream_chat/pubspec.yaml index acce67b7..1689550a 100644 --- a/packages/stream_chat/pubspec.yaml +++ b/packages/stream_chat/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat homepage: https://getstream.io/ description: The official Dart client for Stream Chat, a service for building chat applications. -version: 4.0.1 +version: 4.1.0 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues diff --git a/packages/stream_chat/test/fixtures/member.json b/packages/stream_chat/test/fixtures/member.json index a33acf13..163878a3 100644 --- a/packages/stream_chat/test/fixtures/member.json +++ b/packages/stream_chat/test/fixtures/member.json @@ -9,7 +9,7 @@ "name": "Robin Papa", "image": "https://pbs.twimg.com/profile_images/669512187778498560/L7wQctBt.jpg" }, - "role": "member", + "channel_role": "channel_member", "created_at": "2020-01-28T22:17:30.95443Z", "updated_at": "2020-01-28T22:17:30.95443Z" } \ No newline at end of file diff --git a/packages/stream_chat/test/src/client/channel_test.dart b/packages/stream_chat/test/src/client/channel_test.dart index 9452a433..f90430f2 100644 --- a/packages/stream_chat/test/src/client/channel_test.dart +++ b/packages/stream_chat/test/src/client/channel_test.dart @@ -313,6 +313,7 @@ void main() { channelType, onSendProgress: any(named: 'onSendProgress'), cancelToken: any(named: 'cancelToken'), + extraData: any(named: 'extraData'), )).thenAnswer((_) async => sendImageResponse); when(() => client.sendFile( @@ -321,6 +322,7 @@ void main() { channelType, onSendProgress: any(named: 'onSendProgress'), cancelToken: any(named: 'cancelToken'), + extraData: any(named: 'extraData'), )).thenAnswer((_) async => sendFileResponse); when(() => client.sendMessage( @@ -440,6 +442,7 @@ void main() { channelType, onSendProgress: any(named: 'onSendProgress'), cancelToken: any(named: 'cancelToken'), + extraData: any(named: 'extraData'), )).called(2); verify(() => client.sendFile( @@ -448,6 +451,7 @@ void main() { channelType, onSendProgress: any(named: 'onSendProgress'), cancelToken: any(named: 'cancelToken'), + extraData: any(named: 'extraData'), )).called(1); verify(() => client.sendMessage( @@ -524,6 +528,7 @@ void main() { channelType, onSendProgress: any(named: 'onSendProgress'), cancelToken: any(named: 'cancelToken'), + extraData: any(named: 'extraData'), )).thenAnswer((_) async => sendImageResponse); when(() => client.sendFile( @@ -532,6 +537,7 @@ void main() { channelType, onSendProgress: any(named: 'onSendProgress'), cancelToken: any(named: 'cancelToken'), + extraData: any(named: 'extraData'), )).thenAnswer((_) async => sendFileResponse); when(() => client.updateMessage( @@ -649,6 +655,7 @@ void main() { channelType, onSendProgress: any(named: 'onSendProgress'), cancelToken: any(named: 'cancelToken'), + extraData: any(named: 'extraData'), )).called(2); verify(() => client.sendFile( @@ -657,6 +664,7 @@ void main() { channelType, onSendProgress: any(named: 'onSendProgress'), cancelToken: any(named: 'cancelToken'), + extraData: any(named: 'extraData'), )).called(1); verify(() => client.updateMessage( diff --git a/packages/stream_chat/test/src/core/models/member_test.dart b/packages/stream_chat/test/src/core/models/member_test.dart index 4cd8efda..98d42743 100644 --- a/packages/stream_chat/test/src/core/models/member_test.dart +++ b/packages/stream_chat/test/src/core/models/member_test.dart @@ -9,7 +9,7 @@ void main() { test('should parse json correctly', () { final member = Member.fromJson(jsonFixture('member.json')); expect(member.user, isA()); - expect(member.role, 'member'); + expect(member.channelRole, 'channel_member'); expect(member.createdAt, DateTime.parse('2020-01-28T22:17:30.95443Z')); expect(member.updatedAt, DateTime.parse('2020-01-28T22:17:30.95443Z')); }); diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 5adf5c65..6ad163e3 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,9 +1,20 @@ -## Upcoming +## 4.1.0 + +✅ Added + +- [[#1119]](https://github.com/GetStream/stream-chat-flutter/issues/1119) Added an option to disable mentions overlay in `StreamMessageInput` +- Deprecated `disableEmojiSuggestionsOverlay` in favor of `enableEmojiSuggestionsOverlay` in `StreamMessageInput` 🐞 Fixed - Fixed attachment picker ui. +- Fixed StreamChannelHeader and StreamThreadHeader subtitle alignment. - Fixed message widget thread indicator in reverse mode. +- [[#1044]](https://github.com/GetStream/stream-chat-flutter/issues/1044): Refactor StreamMessageWidget bottom row to use Text.rich. + +🔄 Changed + +- Removed `isOwner` condition from `ChannelBottomSheet` and `StreamChannelInfoBottomSheet` for delete option tile. ## 4.0.1 diff --git a/packages/stream_chat_flutter/lib/src/channel_bottom_sheet.dart b/packages/stream_chat_flutter/lib/src/channel_bottom_sheet.dart index 66438479..8b273fe2 100644 --- a/packages/stream_chat_flutter/lib/src/channel_bottom_sheet.dart +++ b/packages/stream_chat_flutter/lib/src/channel_bottom_sheet.dart @@ -33,7 +33,6 @@ class _ChannelBottomSheetState extends State { final userAsMember = members .firstWhere((e) => e.user?.id == _streamChatState.currentUser?.id); - final isOwner = userAsMember.role == 'owner'; return Material( color: _streamChatThemeData.colorTheme.barsBg, @@ -179,9 +178,8 @@ class _ChannelBottomSheetState extends State { }); }, ), - if (isOwner && - channel.ownCapabilities - .contains(PermissionType.deleteChannel)) + if (channel.ownCapabilities + .contains(PermissionType.deleteChannel)) StreamOptionListTile( leading: Padding( padding: const EdgeInsets.symmetric(horizontal: 16), diff --git a/packages/stream_chat_flutter/lib/src/channel_info.dart b/packages/stream_chat_flutter/lib/src/channel_info.dart index b30058b9..9dcfb9b4 100644 --- a/packages/stream_chat_flutter/lib/src/channel_info.dart +++ b/packages/stream_chat_flutter/lib/src/channel_info.dart @@ -100,12 +100,10 @@ class StreamChannelInfo extends StatelessWidget { return alternativeWidget ?? const Offstage(); } - return Align( - child: StreamTypingIndicator( - parentId: parentId, - style: textStyle, - alternativeWidget: alternativeWidget, - ), + return StreamTypingIndicator( + parentId: parentId, + style: textStyle, + alternativeWidget: alternativeWidget, ); } diff --git a/packages/stream_chat_flutter/lib/src/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget.dart index 050d13b7..77cfa8b2 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget.dart @@ -878,7 +878,7 @@ class _StreamMessageWidgetState extends State const Offstage(); } - final children = []; + final children = []; final threadParticipants = widget.message.threadParticipants?.take(2); final showThreadParticipants = threadParticipants?.isNotEmpty == true; @@ -909,68 +909,72 @@ class _StreamMessageWidgetState extends State const usernameKey = Key('username'); children.addAll([ - if (showUsername) _buildUsername(usernameKey), + if (showUsername) WidgetSpan(child: _buildUsername(usernameKey)), if (showTimeStamp) - Text( - Jiffy(widget.message.createdAt.toLocal()).jm, - style: widget.messageTheme.createdAtStyle, + WidgetSpan( + child: Text( + Jiffy(widget.message.createdAt.toLocal()).jm, + style: widget.messageTheme.createdAtStyle, + ), + ), + if (showSendingIndicator) + WidgetSpan( + child: _buildSendingIndicator(), ), - if (showSendingIndicator) _buildSendingIndicator(), ]); final showThreadTail = !(hasUrlAttachments || isGiphy || isOnlyEmoji) && (showThreadReplyIndicator || showInChannel); - final threadIndicatorWidgets = [ + final threadIndicatorWidgets = [ if (showThreadTail) - Container( - margin: EdgeInsets.only( - bottom: context.textScaleFactor * - ((widget.messageTheme.repliesStyle?.fontSize ?? 1) / 2), - ), - child: CustomPaint( - size: const Size(16, 32) * context.textScaleFactor, - painter: _ThreadReplyPainter( - context: context, - color: widget.messageTheme.messageBorderColor, - reverse: widget.reverse, + WidgetSpan( + child: Container( + margin: EdgeInsets.only( + bottom: context.textScaleFactor * + ((widget.messageTheme.repliesStyle?.fontSize ?? 1) / 2), + ), + child: CustomPaint( + size: const Size(16, 32) * context.textScaleFactor, + painter: _ThreadReplyPainter( + context: context, + color: widget.messageTheme.messageBorderColor, + reverse: widget.reverse, + ), ), ), ), if (showInChannel || showThreadReplyIndicator) ...[ if (showThreadParticipants) - SizedBox.fromSize( - size: Size((threadParticipants!.length * 8.0) + 8, 16), - child: _buildThreadParticipantsIndicator(threadParticipants), + WidgetSpan( + child: SizedBox.fromSize( + size: Size((threadParticipants!.length * 8.0) + 8, 16), + child: _buildThreadParticipantsIndicator(threadParticipants), + ), + ), + WidgetSpan( + child: InkWell( + onTap: widget.onThreadTap != null ? onThreadTap : null, + child: Text(msg, style: widget.messageTheme.repliesStyle), ), - InkWell( - onTap: widget.onThreadTap != null ? onThreadTap : null, - child: Text(msg, style: widget.messageTheme.repliesStyle), ), ], ]; - return Row( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisAlignment: - widget.reverse ? MainAxisAlignment.end : MainAxisAlignment.start, - children: [ - if (showThreadTail && !widget.reverse) ...threadIndicatorWidgets, - ...children.map( - (child) { - Widget mappedChild = SizedBox( - height: context.textScaleFactor * 14, - child: child, - ); - if (child.key == usernameKey) { - mappedChild = Flexible(child: mappedChild); - } - return mappedChild; - }, - ), - if (showThreadTail && widget.reverse) - ...threadIndicatorWidgets.reversed, - ].insertBetween(const SizedBox(width: 8)), + if (widget.reverse) { + children.addAll(threadIndicatorWidgets.reversed); + } else { + children.insertAll(0, threadIndicatorWidgets); + } + + return Text.rich( + TextSpan( + children: [ + ...children, + ].insertBetween(const WidgetSpan(child: SizedBox(width: 8))), + ), + maxLines: 1, + textAlign: widget.reverse ? TextAlign.right : TextAlign.left, ); } @@ -1263,6 +1267,7 @@ class _StreamMessageWidgetState extends State ); if (isMessageRead) { child = Row( + mainAxisSize: MainAxisSize.min, children: [ if (memberCount > 2) Text( diff --git a/packages/stream_chat_flutter/lib/src/thread_header.dart b/packages/stream_chat_flutter/lib/src/thread_header.dart index f6f77a32..2767b76b 100644 --- a/packages/stream_chat_flutter/lib/src/thread_header.dart +++ b/packages/stream_chat_flutter/lib/src/thread_header.dart @@ -186,13 +186,11 @@ class StreamThreadHeader extends StatelessWidget ), const SizedBox(height: 2), if (showTypingIndicator) - Align( - child: StreamTypingIndicator( - channel: StreamChannel.of(context).channel, - style: channelHeaderTheme.subtitleStyle, - parentId: parent.id, - alternativeWidget: defaultSubtitle, - ), + StreamTypingIndicator( + channel: StreamChannel.of(context).channel, + style: channelHeaderTheme.subtitleStyle, + parentId: parent.id, + alternativeWidget: defaultSubtitle, ) else defaultSubtitle, diff --git a/packages/stream_chat_flutter/lib/src/v4/message_input/stream_message_input.dart b/packages/stream_chat_flutter/lib/src/v4/message_input/stream_message_input.dart index 786ba22e..808760c7 100644 --- a/packages/stream_chat_flutter/lib/src/v4/message_input/stream_message_input.dart +++ b/packages/stream_chat_flutter/lib/src/v4/message_input/stream_message_input.dart @@ -207,8 +207,10 @@ class StreamMessageInput extends StatefulWidget { this.enableSafeArea, this.elevation, this.shadow, - this.autoCorrect, - this.disableEmojiSuggestionsOverlay, + this.autoCorrect = true, + this.disableEmojiSuggestionsOverlay = false, + this.enableEmojiSuggestionsOverlay = true, + this.enableMentionsOverlay = true, }) : super(key: key); /// List of options for showing overlays. @@ -327,11 +329,20 @@ class StreamMessageInput extends StatefulWidget { /// Disable autoCorrect by passing false /// autoCorrect is enabled by default - final bool? autoCorrect; + final bool autoCorrect; /// Disable the default emoji suggestions /// Enabled by default - final bool? disableEmojiSuggestionsOverlay; + @Deprecated('Please use enableEmojiSuggestionsOverlay') + final bool disableEmojiSuggestionsOverlay; + + /// Disable the default emoji suggestions by passing `false` + /// Enabled by default + final bool enableEmojiSuggestionsOverlay; + + /// Disable the mentions overlay by passing false + /// Enabled by default + final bool enableMentionsOverlay; static bool _defaultValidator(Message message) => message.text?.isNotEmpty == true || message.attachments.isNotEmpty; @@ -364,11 +375,6 @@ class StreamMessageInputState extends State bool get _isEditing => _effectiveController.value.status != MessageSendingStatus.sending; - bool get _autoCorrect => widget.autoCorrect ?? true; - - bool get _disableEmojiSuggestionsOverlay => - widget.disableEmojiSuggestionsOverlay ?? false; - StreamRestorableMessageInputController? _controller; StreamMessageInputController get _effectiveController => @@ -598,7 +604,8 @@ class StreamMessageInputState extends State visible: _showCommandsOverlay, widget: _buildCommandsOverlayEntry(), ), - if (!_disableEmojiSuggestionsOverlay) + if (widget.enableEmojiSuggestionsOverlay && + !widget.disableEmojiSuggestionsOverlay) OverlayOptions( visible: _focusNode.hasFocus && _effectiveController.text.isNotEmpty && @@ -611,10 +618,11 @@ class StreamMessageInputState extends State .contains(':'), widget: _buildEmojiOverlay(), ), - OverlayOptions( - visible: _showMentionsOverlay, - widget: _buildMentionsOverlayEntry(), - ), + if (widget.enableMentionsOverlay) + OverlayOptions( + visible: _showMentionsOverlay, + widget: _buildMentionsOverlayEntry(), + ), ...widget.customOverlays, ], child: child, @@ -813,7 +821,7 @@ class StreamMessageInputState extends State textAlignVertical: TextAlignVertical.center, decoration: _getInputDecoration(context), textCapitalization: TextCapitalization.sentences, - autocorrect: _autoCorrect, + autocorrect: widget.autoCorrect, ), ), ], diff --git a/packages/stream_chat_flutter/lib/src/v4/stream_channel_info_bottom_sheet.dart b/packages/stream_chat_flutter/lib/src/v4/stream_channel_info_bottom_sheet.dart index 986a9cb3..19c2349b 100644 --- a/packages/stream_chat_flutter/lib/src/v4/stream_channel_info_bottom_sheet.dart +++ b/packages/stream_chat_flutter/lib/src/v4/stream_channel_info_bottom_sheet.dart @@ -59,10 +59,6 @@ class StreamChannelInfoBottomSheet extends StatelessWidget { final members = channel.state?.members ?? []; - final isOwner = members.any( - (it) => it.user?.id == currentUser?.id && it.role == 'owner', - ); - // remove current user in case it's 1-1 conversation if (isOneToOneChannel) { members.removeWhere((it) => it.user?.id == currentUser?.id); @@ -153,7 +149,7 @@ class StreamChannelInfoBottomSheet extends StatelessWidget { ), onTap: onLeaveChannelTap, ), - if (isOwner) + if (channel.ownCapabilities.contains(PermissionType.deleteChannel)) StreamOptionListTile( leading: Padding( padding: const EdgeInsets.symmetric(horizontal: 16), diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index 69e7c42c..7df5cc83 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat_flutter homepage: https://github.com/GetStream/stream-chat-flutter description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter. -version: 4.0.1 +version: 4.1.0 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues @@ -36,7 +36,7 @@ dependencies: rxdart: ^0.27.0 share_plus: ^4.0.1 shimmer: ^2.0.0 - stream_chat_flutter_core: ^4.0.1 + stream_chat_flutter_core: ^4.1.0 substring_highlight: ^1.0.26 url_launcher: ^6.1.0 video_player: ^2.1.0 diff --git a/packages/stream_chat_flutter_core/CHANGELOG.md b/packages/stream_chat_flutter_core/CHANGELOG.md index 9927e734..7eb8845a 100644 --- a/packages/stream_chat_flutter_core/CHANGELOG.md +++ b/packages/stream_chat_flutter_core/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.1.0 + +- Updated `stream_chat` dependency to [`4.1.0`](https://pub.dev/packages/stream_chat/changelog). + ## 4.0.1 - Minor fixes diff --git a/packages/stream_chat_flutter_core/pubspec.yaml b/packages/stream_chat_flutter_core/pubspec.yaml index d7b1cfc2..07d5f842 100644 --- a/packages/stream_chat_flutter_core/pubspec.yaml +++ b/packages/stream_chat_flutter_core/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat_flutter_core homepage: https://github.com/GetStream/stream-chat-flutter description: Stream Chat official Flutter SDK Core. Build your own chat experience using Dart and Flutter. -version: 4.0.1 +version: 4.1.0 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues @@ -17,7 +17,7 @@ dependencies: freezed_annotation: ^1.0.0 meta: ^1.3.0 rxdart: ^0.27.0 - stream_chat: ^4.0.1 + stream_chat: ^4.1.0 dev_dependencies: build_runner: ^2.0.1 diff --git a/packages/stream_chat_localizations/CHANGELOG.md b/packages/stream_chat_localizations/CHANGELOG.md index 4257cc88..0d3f0a7a 100644 --- a/packages/stream_chat_localizations/CHANGELOG.md +++ b/packages/stream_chat_localizations/CHANGELOG.md @@ -1,4 +1,4 @@ -## Upcoming +## 3.1.0 * Added support for [German](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_de.dart) locale. diff --git a/packages/stream_chat_localizations/pubspec.yaml b/packages/stream_chat_localizations/pubspec.yaml index d3007663..e9ffb1c8 100644 --- a/packages/stream_chat_localizations/pubspec.yaml +++ b/packages/stream_chat_localizations/pubspec.yaml @@ -1,6 +1,6 @@ name: stream_chat_localizations description: The Official localizations for Stream Chat Flutter, a service for building chat applications -version: 3.0.0 +version: 3.1.0 homepage: https://github.com/GetStream/stream-chat-flutter repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues @@ -14,7 +14,7 @@ dependencies: sdk: flutter flutter_localizations: sdk: flutter - stream_chat_flutter: ^4.0.0 + stream_chat_flutter: ^4.1.0 dev_dependencies: dart_code_metrics: ^4.4.0 diff --git a/packages/stream_chat_persistence/CHANGELOG.md b/packages/stream_chat_persistence/CHANGELOG.md index 6dd74f8b..fc409305 100644 --- a/packages/stream_chat_persistence/CHANGELOG.md +++ b/packages/stream_chat_persistence/CHANGELOG.md @@ -1,3 +1,9 @@ +## 4.1.0 + +🔄 Changed + +- Deprecated `role` field in `Member` table in favor of `channelRole` + ## 4.0.1 - Updated `stream_chat` dependency to [`4.0.1`](https://pub.dev/packages/stream_chat/changelog). diff --git a/packages/stream_chat_persistence/lib/src/db/drift_chat_database.dart b/packages/stream_chat_persistence/lib/src/db/drift_chat_database.dart index 9b73312d..1de78245 100644 --- a/packages/stream_chat_persistence/lib/src/db/drift_chat_database.dart +++ b/packages/stream_chat_persistence/lib/src/db/drift_chat_database.dart @@ -56,7 +56,7 @@ class DriftChatDatabase extends _$DriftChatDatabase { // you should bump this number whenever you change or add a table definition. @override - int get schemaVersion => 7; + int get schemaVersion => 8; @override MigrationStrategy get migration => MigrationStrategy( diff --git a/packages/stream_chat_persistence/lib/src/db/drift_chat_database.g.dart b/packages/stream_chat_persistence/lib/src/db/drift_chat_database.g.dart index e5382861..9c44cb2f 100644 --- a/packages/stream_chat_persistence/lib/src/db/drift_chat_database.g.dart +++ b/packages/stream_chat_persistence/lib/src/db/drift_chat_database.g.dart @@ -3888,6 +3888,9 @@ class MemberEntity extends DataClass implements Insertable { /// The role of the user in the channel final String? role; + /// The role of the user in the channel + final String? channelRole; + /// The date on which the user accepted the invite to the channel final DateTime? inviteAcceptedAt; @@ -3915,6 +3918,7 @@ class MemberEntity extends DataClass implements Insertable { {required this.userId, required this.channelCid, this.role, + this.channelRole, this.inviteAcceptedAt, this.inviteRejectedAt, required this.invited, @@ -3932,6 +3936,8 @@ class MemberEntity extends DataClass implements Insertable { .mapFromDatabaseResponse(data['${effectivePrefix}channel_cid'])!, role: const StringType() .mapFromDatabaseResponse(data['${effectivePrefix}role']), + channelRole: const StringType() + .mapFromDatabaseResponse(data['${effectivePrefix}channel_role']), inviteAcceptedAt: const DateTimeType().mapFromDatabaseResponse( data['${effectivePrefix}invite_accepted_at']), inviteRejectedAt: const DateTimeType().mapFromDatabaseResponse( @@ -3958,6 +3964,9 @@ class MemberEntity extends DataClass implements Insertable { if (!nullToAbsent || role != null) { map['role'] = Variable(role); } + if (!nullToAbsent || channelRole != null) { + map['channel_role'] = Variable(channelRole); + } if (!nullToAbsent || inviteAcceptedAt != null) { map['invite_accepted_at'] = Variable(inviteAcceptedAt); } @@ -3980,6 +3989,7 @@ class MemberEntity extends DataClass implements Insertable { userId: serializer.fromJson(json['userId']), channelCid: serializer.fromJson(json['channelCid']), role: serializer.fromJson(json['role']), + channelRole: serializer.fromJson(json['channelRole']), inviteAcceptedAt: serializer.fromJson(json['inviteAcceptedAt']), inviteRejectedAt: @@ -3999,6 +4009,7 @@ class MemberEntity extends DataClass implements Insertable { 'userId': serializer.toJson(userId), 'channelCid': serializer.toJson(channelCid), 'role': serializer.toJson(role), + 'channelRole': serializer.toJson(channelRole), 'inviteAcceptedAt': serializer.toJson(inviteAcceptedAt), 'inviteRejectedAt': serializer.toJson(inviteRejectedAt), 'invited': serializer.toJson(invited), @@ -4014,6 +4025,7 @@ class MemberEntity extends DataClass implements Insertable { {String? userId, String? channelCid, Value role = const Value.absent(), + Value channelRole = const Value.absent(), Value inviteAcceptedAt = const Value.absent(), Value inviteRejectedAt = const Value.absent(), bool? invited, @@ -4026,6 +4038,7 @@ class MemberEntity extends DataClass implements Insertable { userId: userId ?? this.userId, channelCid: channelCid ?? this.channelCid, role: role.present ? role.value : this.role, + channelRole: channelRole.present ? channelRole.value : this.channelRole, inviteAcceptedAt: inviteAcceptedAt.present ? inviteAcceptedAt.value : this.inviteAcceptedAt, @@ -4045,6 +4058,7 @@ class MemberEntity extends DataClass implements Insertable { ..write('userId: $userId, ') ..write('channelCid: $channelCid, ') ..write('role: $role, ') + ..write('channelRole: $channelRole, ') ..write('inviteAcceptedAt: $inviteAcceptedAt, ') ..write('inviteRejectedAt: $inviteRejectedAt, ') ..write('invited: $invited, ') @@ -4062,6 +4076,7 @@ class MemberEntity extends DataClass implements Insertable { userId, channelCid, role, + channelRole, inviteAcceptedAt, inviteRejectedAt, invited, @@ -4077,6 +4092,7 @@ class MemberEntity extends DataClass implements Insertable { other.userId == this.userId && other.channelCid == this.channelCid && other.role == this.role && + other.channelRole == this.channelRole && other.inviteAcceptedAt == this.inviteAcceptedAt && other.inviteRejectedAt == this.inviteRejectedAt && other.invited == this.invited && @@ -4091,6 +4107,7 @@ class MembersCompanion extends UpdateCompanion { final Value userId; final Value channelCid; final Value role; + final Value channelRole; final Value inviteAcceptedAt; final Value inviteRejectedAt; final Value invited; @@ -4103,6 +4120,7 @@ class MembersCompanion extends UpdateCompanion { this.userId = const Value.absent(), this.channelCid = const Value.absent(), this.role = const Value.absent(), + this.channelRole = const Value.absent(), this.inviteAcceptedAt = const Value.absent(), this.inviteRejectedAt = const Value.absent(), this.invited = const Value.absent(), @@ -4116,6 +4134,7 @@ class MembersCompanion extends UpdateCompanion { required String userId, required String channelCid, this.role = const Value.absent(), + this.channelRole = const Value.absent(), this.inviteAcceptedAt = const Value.absent(), this.inviteRejectedAt = const Value.absent(), this.invited = const Value.absent(), @@ -4130,6 +4149,7 @@ class MembersCompanion extends UpdateCompanion { Expression? userId, Expression? channelCid, Expression? role, + Expression? channelRole, Expression? inviteAcceptedAt, Expression? inviteRejectedAt, Expression? invited, @@ -4143,6 +4163,7 @@ class MembersCompanion extends UpdateCompanion { if (userId != null) 'user_id': userId, if (channelCid != null) 'channel_cid': channelCid, if (role != null) 'role': role, + if (channelRole != null) 'channel_role': channelRole, if (inviteAcceptedAt != null) 'invite_accepted_at': inviteAcceptedAt, if (inviteRejectedAt != null) 'invite_rejected_at': inviteRejectedAt, if (invited != null) 'invited': invited, @@ -4158,6 +4179,7 @@ class MembersCompanion extends UpdateCompanion { {Value? userId, Value? channelCid, Value? role, + Value? channelRole, Value? inviteAcceptedAt, Value? inviteRejectedAt, Value? invited, @@ -4170,6 +4192,7 @@ class MembersCompanion extends UpdateCompanion { userId: userId ?? this.userId, channelCid: channelCid ?? this.channelCid, role: role ?? this.role, + channelRole: channelRole ?? this.channelRole, inviteAcceptedAt: inviteAcceptedAt ?? this.inviteAcceptedAt, inviteRejectedAt: inviteRejectedAt ?? this.inviteRejectedAt, invited: invited ?? this.invited, @@ -4193,6 +4216,9 @@ class MembersCompanion extends UpdateCompanion { if (role.present) { map['role'] = Variable(role.value); } + if (channelRole.present) { + map['channel_role'] = Variable(channelRole.value); + } if (inviteAcceptedAt.present) { map['invite_accepted_at'] = Variable(inviteAcceptedAt.value); } @@ -4226,6 +4252,7 @@ class MembersCompanion extends UpdateCompanion { ..write('userId: $userId, ') ..write('channelCid: $channelCid, ') ..write('role: $role, ') + ..write('channelRole: $channelRole, ') ..write('inviteAcceptedAt: $inviteAcceptedAt, ') ..write('inviteRejectedAt: $inviteRejectedAt, ') ..write('invited: $invited, ') @@ -4262,6 +4289,12 @@ class $MembersTable extends Members late final GeneratedColumn role = GeneratedColumn( 'role', aliasedName, true, type: const StringType(), requiredDuringInsert: false); + final VerificationMeta _channelRoleMeta = + const VerificationMeta('channelRole'); + @override + late final GeneratedColumn channelRole = GeneratedColumn( + 'channel_role', aliasedName, true, + type: const StringType(), requiredDuringInsert: false); final VerificationMeta _inviteAcceptedAtMeta = const VerificationMeta('inviteAcceptedAt'); @override @@ -4327,6 +4360,7 @@ class $MembersTable extends Members userId, channelCid, role, + channelRole, inviteAcceptedAt, inviteRejectedAt, invited, @@ -4363,6 +4397,12 @@ class $MembersTable extends Members context.handle( _roleMeta, role.isAcceptableOrUnknown(data['role']!, _roleMeta)); } + if (data.containsKey('channel_role')) { + context.handle( + _channelRoleMeta, + channelRole.isAcceptableOrUnknown( + data['channel_role']!, _channelRoleMeta)); + } if (data.containsKey('invite_accepted_at')) { context.handle( _inviteAcceptedAtMeta, diff --git a/packages/stream_chat_persistence/lib/src/entity/members.dart b/packages/stream_chat_persistence/lib/src/entity/members.dart index c1709981..371cb2be 100644 --- a/packages/stream_chat_persistence/lib/src/entity/members.dart +++ b/packages/stream_chat_persistence/lib/src/entity/members.dart @@ -12,8 +12,12 @@ class Members extends Table { text().customConstraint('REFERENCES channels(cid) ON DELETE CASCADE')(); /// The role of the user in the channel + @Deprecated('Please use channelRole') TextColumn get role => text().nullable()(); + /// The role of the user in the channel + TextColumn get channelRole => text().nullable()(); + /// The date on which the user accepted the invite to the channel DateTimeColumn get inviteAcceptedAt => dateTime().nullable()(); diff --git a/packages/stream_chat_persistence/lib/src/mapper/member_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/member_mapper.dart index 925abe16..05975ea4 100644 --- a/packages/stream_chat_persistence/lib/src/mapper/member_mapper.dart +++ b/packages/stream_chat_persistence/lib/src/mapper/member_mapper.dart @@ -12,6 +12,7 @@ extension MemberEntityX on MemberEntity { updatedAt: updatedAt, createdAt: createdAt, role: role, + channelRole: channelRole, inviteAcceptedAt: inviteAcceptedAt, invited: invited, inviteRejectedAt: inviteRejectedAt, @@ -32,7 +33,9 @@ extension MemberX on Member { inviteRejectedAt: inviteRejectedAt, invited: invited, inviteAcceptedAt: inviteAcceptedAt, + // ignore: deprecated_member_use role: role, + channelRole: channelRole, updatedAt: updatedAt, ); } diff --git a/packages/stream_chat_persistence/pubspec.yaml b/packages/stream_chat_persistence/pubspec.yaml index de73c91c..4d3ed908 100644 --- a/packages/stream_chat_persistence/pubspec.yaml +++ b/packages/stream_chat_persistence/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat_persistence homepage: https://github.com/GetStream/stream-chat-flutter description: Official Stream Chat Persistence library. Build your own chat experience using Dart and Flutter. -version: 4.0.1 +version: 4.1.0 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues @@ -19,7 +19,7 @@ dependencies: path: ^1.8.0 path_provider: ^2.0.1 sqlite3_flutter_libs: ^0.5.0 - stream_chat: ^4.0.1 + stream_chat: ^4.1.0 dev_dependencies: build_runner: ^2.0.1 diff --git a/packages/stream_chat_persistence/test/src/dao/member_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/member_dao_test.dart index 4a3a31da..1082967e 100644 --- a/packages/stream_chat_persistence/test/src/dao/member_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/member_dao_test.dart @@ -62,7 +62,7 @@ void main() { expect(fetchedMember.createdAt, isSameDateAs(member.createdAt)); expect(fetchedMember.isModerator, member.isModerator); expect(fetchedMember.invited, member.invited); - expect(fetchedMember.role, member.role); + expect(fetchedMember.channelRole, member.channelRole); expect(fetchedMember.updatedAt, isSameDateAs(member.updatedAt)); expect( fetchedMember.inviteAcceptedAt, @@ -89,7 +89,7 @@ void main() { expect(fetchedMember.createdAt, isSameDateAs(member.createdAt)); expect(fetchedMember.isModerator, member.isModerator); expect(fetchedMember.invited, member.invited); - expect(fetchedMember.role, member.role); + expect(fetchedMember.channelRole, member.channelRole); expect(fetchedMember.updatedAt, isSameDateAs(member.updatedAt)); expect( fetchedMember.inviteAcceptedAt, diff --git a/packages/stream_chat_persistence/test/src/mapper/member_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/member_mapper_test.dart index a3878409..e327e752 100644 --- a/packages/stream_chat_persistence/test/src/mapper/member_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/member_mapper_test.dart @@ -28,7 +28,7 @@ void main() { expect(member.user!.id, entity.userId); expect(member.createdAt, isSameDateAs(entity.createdAt)); expect(member.updatedAt, isSameDateAs(entity.updatedAt)); - expect(member.role, entity.role); + expect(member.channelRole, entity.channelRole); expect(member.inviteAcceptedAt, isSameDateAs(entity.inviteAcceptedAt!)); expect(member.inviteRejectedAt, isSameDateAs(entity.inviteRejectedAt!)); expect(member.invited, entity.invited); @@ -58,7 +58,7 @@ void main() { expect(entity.userId, member.user!.id); expect(entity.createdAt, isSameDateAs(member.createdAt)); expect(entity.updatedAt, isSameDateAs(member.updatedAt)); - expect(entity.role, member.role); + expect(entity.channelRole, member.channelRole); expect(entity.inviteAcceptedAt, isSameDateAs(member.inviteAcceptedAt!)); expect(entity.inviteRejectedAt, isSameDateAs(member.inviteRejectedAt!)); expect(entity.invited, member.invited);