diff --git a/.github/workflows/stream_flutter_workflow.yml b/.github/workflows/stream_flutter_workflow.yml index d1c46db91..27de7c07d 100644 --- a/.github/workflows/stream_flutter_workflow.yml +++ b/.github/workflows/stream_flutter_workflow.yml @@ -111,22 +111,27 @@ jobs: uses: VeryGoodOpenSource/very_good_coverage@v1.1.1 with: path: packages/stream_chat/coverage/lcov.info - min_coverage: 80 + min_coverage: 79 + - name: "Stream Chat Persistence Coverage Check" + uses: VeryGoodOpenSource/very_good_coverage@v1.1.1 + with: + path: packages/stream_chat_localizations/coverage/lcov.info + min_coverage: 88 - name: "Stream Chat Persistence Coverage Check" uses: VeryGoodOpenSource/very_good_coverage@v1.1.1 with: path: packages/stream_chat_persistence/coverage/lcov.info - min_coverage: 95 + min_coverage: 97 - name: "Stream Chat Flutter Core Coverage Check" uses: VeryGoodOpenSource/very_good_coverage@v1.1.1 with: path: packages/stream_chat_flutter_core/coverage/lcov.info - min_coverage: 90 + min_coverage: 30 - name: "Stream Chat Flutter Coverage Check" uses: VeryGoodOpenSource/very_good_coverage@v1.1.1 with: path: packages/stream_chat_flutter/coverage/lcov.info - min_coverage: 67 + min_coverage: 68 draft-build: runs-on: ubuntu-latest diff --git a/packages/stream_chat/lib/src/client/channel.dart b/packages/stream_chat/lib/src/client/channel.dart index b4e1379d3..b6a062d37 100644 --- a/packages/stream_chat/lib/src/client/channel.dart +++ b/packages/stream_chat/lib/src/client/channel.dart @@ -2078,10 +2078,6 @@ class ChannelClientState { (m) => m.user?.id == _channel.client.state.currentUser?.id, ); - /// 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; diff --git a/packages/stream_chat/lib/src/core/api/requests.dart b/packages/stream_chat/lib/src/core/api/requests.dart index fa83ff735..719b2b9b4 100644 --- a/packages/stream_chat/lib/src/core/api/requests.dart +++ b/packages/stream_chat/lib/src/core/api/requests.dart @@ -62,8 +62,6 @@ class PaginationParams extends Equatable { /// ``` const PaginationParams({ this.limit = 10, - this.before = 10, - this.after = 10, this.offset, this.next, this.idAround, @@ -88,14 +86,6 @@ class PaginationParams extends Equatable { /// The amount of items requested from the APIs. final int limit; - /// The amount of items requested before message ID from the APIs. - @Deprecated('before is deprecated, use limit instead') - final int before; - - /// The amount of items requested after message ID from the APIs. - @Deprecated('after is deprecated, use limit instead') - final int after; - /// The offset of requesting items. final int? offset; @@ -165,8 +155,6 @@ class PaginationParams extends Equatable { }) => PaginationParams( limit: limit ?? this.limit, - before: before ?? this.before, - after: limit ?? this.after, offset: offset ?? this.offset, idAround: idAround ?? this.idAround, next: next ?? this.next, @@ -186,8 +174,6 @@ class PaginationParams extends Equatable { @override List get props => [ limit, - before, - after, offset, next, idAround, diff --git a/packages/stream_chat/lib/src/core/api/requests.g.dart b/packages/stream_chat/lib/src/core/api/requests.g.dart index 90bd789d1..b70b87676 100644 --- a/packages/stream_chat/lib/src/core/api/requests.g.dart +++ b/packages/stream_chat/lib/src/core/api/requests.g.dart @@ -21,8 +21,6 @@ Map _$SortOptionToJson(SortOption instance) => PaginationParams _$PaginationParamsFromJson(Map json) => PaginationParams( limit: json['limit'] as int? ?? 10, - before: json['before'] as int? ?? 10, - after: json['after'] as int? ?? 10, offset: json['offset'] as int?, next: json['next'] as String?, idAround: json['id_around'] as String?, @@ -50,8 +48,6 @@ PaginationParams _$PaginationParamsFromJson(Map json) => Map _$PaginationParamsToJson(PaginationParams instance) { final val = { 'limit': instance.limit, - 'before': instance.before, - 'after': instance.after, }; void writeNotNull(String key, dynamic value) { diff --git a/packages/stream_chat/lib/src/core/models/member.dart b/packages/stream_chat/lib/src/core/models/member.dart index c2dc9eb18..8fe65b446 100644 --- a/packages/stream_chat/lib/src/core/models/member.dart +++ b/packages/stream_chat/lib/src/core/models/member.dart @@ -16,7 +16,6 @@ class Member extends Equatable { this.inviteAcceptedAt, this.inviteRejectedAt, this.invited = false, - this.role, this.channelRole, this.userId, this.isModerator = false, @@ -48,10 +47,6 @@ class Member extends Equatable { /// True if the user has been invited to the channel 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; @@ -100,7 +95,6 @@ class Member extends Equatable { banned: banned ?? this.banned, banExpires: banExpires ?? this.banExpires, shadowBanned: shadowBanned ?? this.shadowBanned, - role: role ?? this.role, channelRole: channelRole ?? this.channelRole, userId: userId ?? this.userId, isModerator: isModerator ?? this.isModerator, @@ -117,7 +111,6 @@ class Member extends Equatable { inviteAcceptedAt, inviteRejectedAt, invited, - role, channelRole, userId, isModerator, 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 1bcde74db..2cda32a41 100644 --- a/packages/stream_chat/lib/src/core/models/member.g.dart +++ b/packages/stream_chat/lib/src/core/models/member.g.dart @@ -17,7 +17,6 @@ Member _$MemberFromJson(Map json) => Member( ? null : 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, @@ -39,7 +38,6 @@ Map _$MemberToJson(Member instance) => { 'invite_accepted_at': instance.inviteAcceptedAt?.toIso8601String(), 'invite_rejected_at': instance.inviteRejectedAt?.toIso8601String(), 'invited': instance.invited, - 'role': instance.role, 'channel_role': instance.channelRole, 'user_id': instance.userId, 'is_moderator': instance.isModerator, diff --git a/packages/stream_chat_flutter/lib/src/attachment/attachment_title.dart b/packages/stream_chat_flutter/lib/src/attachment/attachment_title.dart index 8c2badb5b..dc9c34c1d 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/attachment_title.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/attachment_title.dart @@ -1,10 +1,6 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro attachmentTitle} -@Deprecated("Use 'StreamAttachmentTitle' instead") -typedef AttachmentTitle = StreamAttachmentTitle; - /// {@template attachmentTitle} /// Title for attachments /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/attachment/attachment_upload_state_builder.dart b/packages/stream_chat_flutter/lib/src/attachment/attachment_upload_state_builder.dart index df14b5f2c..3a0d4ca76 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/attachment_upload_state_builder.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/attachment_upload_state_builder.dart @@ -2,10 +2,6 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/utils/utils.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamAttachmentUploadStateBuilder} -@Deprecated("Use 'StreamAttachmentsUploadStateBuilder' instead") -typedef AttachmentUploadStateBuilder = StreamAttachmentUploadStateBuilder; - /// {@template streamAttachmentUploadStateBuilder} /// Widget to display attachment upload state /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/attachment/attachment_widget.dart b/packages/stream_chat_flutter/lib/src/attachment/attachment_widget.dart index dd51c9473..182d5aea6 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/attachment_widget.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/attachment_widget.dart @@ -24,10 +24,6 @@ enum AttachmentSource { } } -/// {@macro streamAttachmentWidget} -@Deprecated("Use 'StreamAttachmentWidget' instead") -typedef AttachmentWidget = StreamAttachmentWidget; - /// {@template streamAttachmentWidget} /// Abstract class for deriving attachment types /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart index 63775f5f9..6798a15c3 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart @@ -10,10 +10,6 @@ import 'package:stream_chat_flutter/src/utils/utils.dart'; import 'package:stream_chat_flutter/src/video/video_thumbnail_image.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; -/// {@macro streamFileAttachment} -@Deprecated("Use 'StreamFileAttachment' instead") -typedef FileAttachment = StreamFileAttachment; - /// {@template streamFileAttachment} /// Displays file attachments that have been sent in a chat. /// diff --git a/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart index b66431668..da78bd9b7 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart @@ -5,10 +5,6 @@ import 'package:stream_chat_flutter/src/attachment/attachment_widget.dart'; import 'package:stream_chat_flutter/src/utils/utils.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamGiphyAttachment} -@Deprecated("Use 'StreamGiphyAttachment' instead") -typedef GiphyAttachment = StreamGiphyAttachment; - /// {@template streamGiphyAttachment} /// Shows a GIF attachment in a [StreamMessageWidget]. /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/attachment/image_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/image_attachment.dart index c49a69b34..9ae5bc285 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/image_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/image_attachment.dart @@ -4,10 +4,6 @@ import 'package:shimmer/shimmer.dart'; import 'package:stream_chat_flutter/src/attachment/attachment_widget.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamImageAttachment} -@Deprecated("use 'StreamImageAttachment' instead") -typedef ImageAttachment = StreamImageAttachment; - /// {@template streamImageAttachment} /// Shows an image attachment in a [StreamMessageWidget]. /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/attachment/url_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/url_attachment.dart index 150cebf53..ce166195d 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/url_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/url_attachment.dart @@ -2,10 +2,6 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamUrlAttachment} -@Deprecated("Use 'StreamUrlAttachment' instead") -typedef UrlAttachment = StreamUrlAttachment; - /// {@template streamUrlAttachment} /// Displays a URL attachment in a [StreamMessageWidget]. /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/attachment/video_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/video_attachment.dart index fc97ef1b3..18da43125 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/video_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/video_attachment.dart @@ -3,10 +3,6 @@ import 'package:stream_chat_flutter/src/attachment/attachment_widget.dart'; import 'package:stream_chat_flutter/src/video/video_thumbnail_image.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamVideoAttachment} -@Deprecated("Use 'StreamVideoAttachment' instead") -typedef VideoAttachment = StreamVideoAttachment; - /// {@template streamVideoAttachment} /// Shows a video attachment in a [StreamMessageWidget]. /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart b/packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart index a6d371f74..4c7f23d25 100644 --- a/packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart +++ b/packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart @@ -3,10 +3,6 @@ import 'dart:ui' as ui; import 'package:flutter/material.dart'; -/// {@macro streamGradientAvatar} -@Deprecated("Use 'StreamGradientAvatar' instead") -typedef GradientAvatar = StreamGradientAvatar; - /// {@template streamGradientAvatar} /// Fallback user avatar with a polygon gradient overlaid with text /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/avatars/group_avatar.dart b/packages/stream_chat_flutter/lib/src/avatars/group_avatar.dart index 29cb3d11d..0571c7fd5 100644 --- a/packages/stream_chat_flutter/lib/src/avatars/group_avatar.dart +++ b/packages/stream_chat_flutter/lib/src/avatars/group_avatar.dart @@ -1,10 +1,6 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamGroupAvatar} -@Deprecated("Use 'StreamGroupAvatar' instead") -typedef GroupAvatar = StreamGroupAvatar; - /// {@template streamGroupAvatar} /// Widget for constructing a group of images /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/avatars/user_avatar.dart b/packages/stream_chat_flutter/lib/src/avatars/user_avatar.dart index 19c3a1025..8c3eaba92 100644 --- a/packages/stream_chat_flutter/lib/src/avatars/user_avatar.dart +++ b/packages/stream_chat_flutter/lib/src/avatars/user_avatar.dart @@ -2,10 +2,6 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamUserAvatar} -@Deprecated("Use 'StreamUserAvatar' instead") -typedef UserAvatar = StreamUserAvatar; - /// {@template streamUserAvatar} /// Displays a user's avatar. /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/v4/stream_channel_info_bottom_sheet.dart b/packages/stream_chat_flutter/lib/src/bottom_sheets/stream_channel_info_bottom_sheet.dart similarity index 100% rename from packages/stream_chat_flutter/lib/src/v4/stream_channel_info_bottom_sheet.dart rename to packages/stream_chat_flutter/lib/src/bottom_sheets/stream_channel_info_bottom_sheet.dart diff --git a/packages/stream_chat_flutter/lib/src/channel/channel_avatar.dart b/packages/stream_chat_flutter/lib/src/channel/channel_avatar.dart deleted file mode 100644 index 30a7d859e..000000000 --- a/packages/stream_chat_flutter/lib/src/channel/channel_avatar.dart +++ /dev/null @@ -1,204 +0,0 @@ -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template channelAvatar} -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_image.png) -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_image_paint.png) -/// -/// The image that represents the current [Channel]. -/// -/// ```dart -/// class MyApp extends StatelessWidget { -/// final StreamChatClient client; -/// final Channel channel; -/// -/// MyApp(this.client, this.channel); -/// -/// @override -/// Widget build(BuildContext context) { -/// return MaterialApp( -/// debugShowCheckedModeBanner: false, -/// home: StreamChat( -/// client: client, -/// child: StreamChannel( -/// channel: channel, -/// child: Center( -/// child: ChannelAvatar( -/// channel: channel, -/// ), -/// ), -/// ), -/// ), -/// ); -/// } -/// } -/// ``` -/// -/// Uses a [StreamBuilder] to render the channel information image as soon as -/// it updates. -/// -/// By default, the widget radius size is 40x40 pixels. Set the [constraints] -/// property to set a custom dimension. -/// -/// The UI is rendered based on the first ancestor of type [StreamChatTheme]. -/// Modify it to change the widget's appearance. -/// {@endtemplate} - -@Deprecated( - "'ChannelAvatar' is deprecated and shouldn't be used. " - "Please use 'StreamChannelAvatar' instead.", -) -class ChannelAvatar extends StatelessWidget { - /// {@macro channelAvatar} - const ChannelAvatar({ - super.key, - this.channel, - this.constraints, - this.onTap, - this.borderRadius, - this.selected = false, - this.selectionColor, - this.selectionThickness = 4, - }); - - /// The [BorderRadius] for this [ChannelAvatar] - final BorderRadius? borderRadius; - - /// The channel to show the image of - final Channel? channel; - - /// The sizing constraints of the image - final BoxConstraints? constraints; - - /// The action to perform when the image is tapped or clicked - final VoidCallback? onTap; - - /// Whether the image is currently selected or not - final bool selected; - - /// The color to use when the image is selected - final Color? selectionColor; - - /// The value to use for the border thickness and padding of the - /// selected image - final double selectionThickness; - - @override - Widget build(BuildContext context) { - final streamChat = StreamChat.of(context); - final channel = this.channel ?? StreamChannel.of(context).channel; - - assert(channel.state != null, 'Channel ${channel.id} is not initialized'); - - final chatThemeData = StreamChatTheme.of(context); - final colorTheme = chatThemeData.colorTheme; - final previewTheme = chatThemeData.channelPreviewTheme.avatarTheme; - - return BetterStreamBuilder( - stream: channel.imageStream, - initialData: channel.image, - builder: (context, channelImage) { - Widget child = ClipRRect( - borderRadius: borderRadius ?? previewTheme?.borderRadius, - child: Container( - constraints: constraints ?? previewTheme?.constraints, - decoration: BoxDecoration(color: colorTheme.accentPrimary), - child: InkWell( - onTap: onTap, - child: CachedNetworkImage( - imageUrl: channelImage, - errorWidget: (_, __, ___) => Center( - child: Text( - channel.name?[0] ?? '', - style: TextStyle( - color: colorTheme.barsBg, - fontWeight: FontWeight.bold, - ), - ), - ), - fit: BoxFit.cover, - ), - ), - ), - ); - - if (selected) { - child = ClipRRect( - key: const Key('selectedImage'), - borderRadius: BorderRadius.circular(selectionThickness) + - (borderRadius ?? - previewTheme?.borderRadius ?? - BorderRadius.zero), - child: Container( - constraints: constraints ?? previewTheme?.constraints, - color: selectionColor ?? colorTheme.accentPrimary, - child: Padding( - padding: EdgeInsets.all(selectionThickness), - child: child, - ), - ), - ); - } - return child; - }, - noDataBuilder: (context) { - final currentUser = streamChat.currentUser!; - final otherMembers = channel.state!.members - .where((it) => it.userId != currentUser.id) - .toList(growable: false); - - // our own space, no other members - if (otherMembers.isEmpty) { - return BetterStreamBuilder( - stream: streamChat.client.state.currentUserStream.map((it) => it!), - initialData: currentUser, - builder: (context, user) => StreamUserAvatar( - borderRadius: borderRadius ?? previewTheme?.borderRadius, - user: user, - constraints: constraints ?? previewTheme?.constraints, - onTap: onTap != null ? (_) => onTap!() : null, - selected: selected, - selectionColor: selectionColor ?? colorTheme.accentPrimary, - selectionThickness: selectionThickness, - ), - ); - } - - // 1-1 Conversation - if (otherMembers.length == 1) { - final member = otherMembers.first; - return BetterStreamBuilder( - stream: channel.state!.membersStream.map( - (members) => members.firstWhere( - (it) => it.userId == member.userId, - orElse: () => member, - ), - ), - initialData: member, - builder: (context, member) => StreamUserAvatar( - borderRadius: borderRadius ?? previewTheme?.borderRadius, - user: member.user!, - constraints: constraints ?? previewTheme?.constraints, - onTap: onTap != null ? (_) => onTap!() : null, - selected: selected, - selectionColor: selectionColor ?? colorTheme.accentPrimary, - selectionThickness: selectionThickness, - ), - ); - } - - // Group conversation - return StreamGroupAvatar( - members: otherMembers, - borderRadius: borderRadius ?? previewTheme?.borderRadius, - constraints: constraints ?? previewTheme?.constraints, - onTap: onTap, - selected: selected, - selectionColor: selectionColor ?? colorTheme.accentPrimary, - selectionThickness: selectionThickness, - ); - }, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/channel/channel_bottom_sheet.dart b/packages/stream_chat_flutter/lib/src/channel/channel_bottom_sheet.dart deleted file mode 100644 index 62b17ce01..000000000 --- a/packages/stream_chat_flutter/lib/src/channel/channel_bottom_sheet.dart +++ /dev/null @@ -1,257 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/utils/extensions.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template channelBottomSheet} -/// Bottom Sheet with options -/// {@endtemplate} -@Deprecated("Use 'StreamChannelInfoBottomSheet' instead") -class ChannelBottomSheet extends StatefulWidget { - /// {@macro channelBottomSheet} - const ChannelBottomSheet({super.key, this.onViewInfoTap}); - - /// The action to perform when 'View Info' is tapped or clicked. - final VoidCallback? onViewInfoTap; - - @override - _ChannelBottomSheetState createState() => _ChannelBottomSheetState(); -} - -// ignore: deprecated_member_use_from_same_package -class _ChannelBottomSheetState extends State { - bool _showActions = true; - - late StreamChannelState _streamChannelState; - late StreamChannelPreviewThemeData _channelPreviewThemeData; - late StreamChatThemeData _streamChatThemeData; - late StreamChatState _streamChatState; - - @override - void didChangeDependencies() { - _streamChannelState = StreamChannel.of(context); - _streamChatThemeData = StreamChatTheme.of(context); - _channelPreviewThemeData = StreamChannelPreviewTheme.of(context); - _streamChatState = StreamChat.of(context); - super.didChangeDependencies(); - } - - @override - Widget build(BuildContext context) { - final channel = _streamChannelState.channel; - - final members = channel.state?.members ?? []; - - final userAsMember = members - .firstWhere((e) => e.user?.id == _streamChatState.currentUser?.id); - - return Material( - color: _streamChatThemeData.colorTheme.barsBg, - clipBehavior: Clip.antiAlias, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), - ), - ), - child: !_showActions - ? const SizedBox() - : ListView( - shrinkWrap: true, - children: [ - const SizedBox( - height: 24, - ), - Center( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: StreamChannelName( - channel: channel, - textStyle: _streamChatThemeData.textTheme.headlineBold, - ), - ), - ), - const SizedBox( - height: 5, - ), - Center( - child: StreamChannelInfo( - showTypingIndicator: false, - channel: _streamChannelState.channel, - textStyle: _channelPreviewThemeData.subtitleStyle, - ), - ), - const SizedBox( - height: 17, - ), - if (channel.isDistinct && channel.memberCount == 2) - Column( - children: [ - StreamUserAvatar( - user: members - .firstWhere( - (e) => e.user?.id != userAsMember.user?.id, - ) - .user!, - constraints: const BoxConstraints( - maxHeight: 64, - maxWidth: 64, - ), - borderRadius: BorderRadius.circular(32), - onlineIndicatorConstraints: - BoxConstraints.tight(const Size(12, 12)), - ), - const SizedBox( - height: 6, - ), - Text( - members - .firstWhere( - (e) => e.user?.id != userAsMember.user?.id, - ) - .user - ?.name ?? - '', - style: _streamChatThemeData.textTheme.footnoteBold, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ], - ), - if (!(channel.isDistinct && channel.memberCount == 2)) - Container( - height: 94, - alignment: Alignment.center, - child: ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: members.length, - shrinkWrap: true, - itemBuilder: (context, index) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Column( - children: [ - StreamUserAvatar( - user: members[index].user!, - constraints: const BoxConstraints.tightFor( - height: 64, - width: 64, - ), - borderRadius: BorderRadius.circular(32), - onlineIndicatorConstraints: - BoxConstraints.tight(const Size(12, 12)), - ), - const SizedBox( - height: 6, - ), - Text( - members[index].user?.name ?? '', - style: - _streamChatThemeData.textTheme.footnoteBold, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ], - ), - ), - ), - ), - const SizedBox( - height: 24, - ), - StreamOptionListTile( - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: StreamSvgIcon.user( - color: _streamChatThemeData.colorTheme.textLowEmphasis, - ), - ), - title: context.translations.viewInfoLabel, - onTap: widget.onViewInfoTap, - ), - if (!channel.isDistinct && - channel.ownCapabilities - .contains(PermissionType.leaveChannel)) - StreamOptionListTile( - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: StreamSvgIcon.userRemove( - color: _streamChatThemeData.colorTheme.textLowEmphasis, - ), - ), - title: context.translations.leaveGroupLabel, - onTap: () async { - setState(() => _showActions = false); - await _showLeaveBottomSheet(); - setState(() => _showActions = true); - }, - ), - if (channel.ownCapabilities - .contains(PermissionType.deleteChannel)) - StreamOptionListTile( - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: StreamSvgIcon.delete( - color: _streamChatThemeData.colorTheme.accentError, - ), - ), - title: context.translations.deleteConversationLabel, - titleColor: _streamChatThemeData.colorTheme.accentError, - onTap: () async { - setState(() => _showActions = false); - await _showDeleteBottomSheet(); - setState(() => _showActions = true); - }, - ), - StreamOptionListTile( - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: StreamSvgIcon.closeSmall( - color: _streamChatThemeData.colorTheme.textLowEmphasis, - ), - ), - title: context.translations.cancelLabel, - onTap: () => Navigator.of(context).pop(), - ), - ], - ), - ); - } - - Future _showDeleteBottomSheet() async { - final res = await showConfirmationBottomSheet( - context, - title: context.translations.deleteConversationLabel, - okText: context.translations.deleteLabel, - question: context.translations.deleteConversationQuestion, - cancelText: context.translations.cancelLabel, - icon: StreamSvgIcon.delete( - color: _streamChatThemeData.colorTheme.accentError, - ), - ); - final channel = _streamChannelState.channel; - if (res == true) { - await channel.delete(); - Navigator.of(context).pop(); - } - } - - Future _showLeaveBottomSheet() async { - final res = await showConfirmationBottomSheet( - context, - title: context.translations.leaveConversationLabel, - okText: context.translations.leaveLabel, - question: context.translations.leaveConversationQuestion, - cancelText: context.translations.cancelLabel, - icon: StreamSvgIcon.userRemove( - color: _streamChatThemeData.colorTheme.accentError, - ), - ); - if (res == true) { - final channel = _streamChannelState.channel; - final user = _streamChatState.currentUser; - if (user != null) { - await channel.removeMembers([user.id]); - } - Navigator.of(context).pop(); - } - } -} diff --git a/packages/stream_chat_flutter/lib/src/channel/channel_header.dart b/packages/stream_chat_flutter/lib/src/channel/channel_header.dart index 2d6b13b58..36fe125f0 100644 --- a/packages/stream_chat_flutter/lib/src/channel/channel_header.dart +++ b/packages/stream_chat_flutter/lib/src/channel/channel_header.dart @@ -3,10 +3,6 @@ import 'package:flutter/services.dart'; import 'package:stream_chat_flutter/src/utils/extensions.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -///{@macro streamChannelHeader} -@Deprecated("Use 'StreamChannelHeader' instead") -typedef ChannelHeader = StreamChannelHeader; - /// {@template streamChannelHeader} /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_header.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_header_paint.png) diff --git a/packages/stream_chat_flutter/lib/src/channel/channel_list_header.dart b/packages/stream_chat_flutter/lib/src/channel/channel_list_header.dart index 627ae5baf..85f07eded 100644 --- a/packages/stream_chat_flutter/lib/src/channel/channel_list_header.dart +++ b/packages/stream_chat_flutter/lib/src/channel/channel_list_header.dart @@ -4,10 +4,6 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:stream_chat_flutter/src/utils/utils.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamChannelListHeader} -@Deprecated("Use 'StreamChannelListHeader' instead") -typedef ChannelListHeader = StreamChannelListHeader; - /// {@template streamChannelListHeader} /// Shows the current [StreamChatClient] status. /// diff --git a/packages/stream_chat_flutter/lib/src/channel/channel_list_view.dart b/packages/stream_chat_flutter/lib/src/channel/channel_list_view.dart deleted file mode 100644 index f7ca2e5b5..000000000 --- a/packages/stream_chat_flutter/lib/src/channel/channel_list_view.dart +++ /dev/null @@ -1,789 +0,0 @@ -// ignore_for_file: deprecated_member_use, deprecated_member_use_from_same_package, lines_longer_than_80_chars - -import 'package:flutter/material.dart'; -import 'package:flutter_slidable/flutter_slidable.dart'; -import 'package:shimmer/shimmer.dart'; -import 'package:stream_chat_flutter/src/utils/utils.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template channelListView} -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_list_view.png) -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_list_view_paint.png) -/// -/// Shows the list of active channels. -/// -/// ```dart -/// class ChannelListPage extends StatelessWidget { -/// @override -/// Widget build(BuildContext context) { -/// return Scaffold( -/// body: ChannelListView( -/// filter: { -/// 'members': { -/// '\$in': [StreamChat.of(context).user.id], -/// } -/// }, -/// sort: [SortOption('last_message_at')], -/// pagination: PaginationParams( -/// limit: 20, -/// ), -/// channelWidget: ChannelPage(), -/// ), -/// ); -/// } -/// } -/// ``` -/// -/// A [StreamChat] ancestor is required in order to provide the information -/// about the channels. -/// -/// A [ListView.custom] is used to render the list of channels. -/// -/// The UI is rendered based on the first ancestor of type [StreamChatTheme]. -/// Modify it to change the widget's appearance. -/// {@endtemplate} -@Deprecated("Use 'StreamChannelListView' instead") -class ChannelListView extends StatefulWidget { - /// {@macro channelListView} - @Deprecated("Use 'StreamChannelListView' instead") - ChannelListView({ - super.key, - this.filter, - this.sort, - this.showChannelState = true, - this.watchChannel = true, - this.presence = false, - this.memberLimit, - this.messageLimit, - @Deprecated( - "'pagination' is deprecated and shouldn't be used. " - "This property is no longer used, Please use 'limit' instead", - ) - this.pagination, - int? limit, - this.onChannelTap, - this.onChannelLongPress, - this.channelWidget, - this.channelPreviewBuilder, - this.separatorBuilder, - this.onImageTap, - this.onStartChatPressed, - this.swipeToAction = false, - this.pullToRefresh = true, - this.crossAxisCount = 1, - this.padding, - this.selectedChannels = const [], - this.onViewInfoTap, - this.errorBuilder, - this.emptyBuilder, - this.loadingBuilder, - this.listBuilder, - this.onMoreDetailsPressed, - this.onDeletePressed, - this.swipeActions, - this.channelListController, - }) : limit = limit ?? pagination?.limit ?? 25; - - /// Whether to add a default swipe action behavior to this widget. - /// - /// Defaults to `false`. - final bool swipeToAction; - - /// The query filters to use for filtering the list of channels. - /// - /// You can query on any of the custom fields you've defined on the [Channel]. - /// You can also filter other built-in channel fields. - final Filter? filter; - - /// The sorting used for the channels matching the filters. - /// - /// Sorting is based on field and direction. Multiple sorting options - /// can be provided. - /// - /// You can sort based on `last_updated`, `last_message_at`, `updated_at`, - /// `created_at` or `member_count`. - /// - /// Direction can be ascending or descending. - final List>? sort; - - /// Whether to return the [Channel] state. - /// - /// Defaults to `true`. - final bool showChannelState; - - /// Whether to listen to changes to this [Channel] in real time. - /// - /// Defaults to `true`. - final bool watchChannel; - - /// Whether to receive user presence updates via the websocket events - final bool presence; - - /// The maximum number of members to fetch in each channel - final int? memberLimit; - - /// The maximum number of messages to fetch in each channel - final int? messageLimit; - - /// Pagination parameters - /// - /// limit: the number of channels to return (max is 30) - /// offset: the offset (max is 1000) - /// message_limit: how many messages should be included to each channel - @Deprecated( - "'pagination' is deprecated and shouldn't be used. " - "This property is no longer used, Please use 'limit' instead", - ) - final PaginationParams? pagination; - - /// The maximum amount of channels to request per API call. - final int limit; - - /// The action to perform when tapping on a channel. - /// - /// By default it calls [Navigator.push] building a [MaterialPageRoute], - /// with the [channelWidget] as its child. - final ChannelTapCallback? onChannelTap; - - /// The action to perform when long pressing on a channel - final Function(Channel)? onChannelLongPress; - - /// The widget used when opening a channel - final Widget? channelWidget; - - /// {@macro channelPreviewBuilder} - final ChannelPreviewBuilder? channelPreviewBuilder; - - /// Builder used to create a custom item separator - final Function(BuildContext, int)? separatorBuilder; - - /// The action to perform when the image is tapped - final Function(Channel)? onImageTap; - - /// Whether to disable the pull-to-refresh widget. - /// - /// Defaults to `true`. - final bool pullToRefresh; - - /// Callback used in the default empty list widget - final VoidCallback? onStartChatPressed; - - /// The number of children in the cross axis. - final int crossAxisCount; - - /// The amount of space by which to inset the children. - final EdgeInsetsGeometry? padding; - - /// List of selected channels which are displayed differently - /// FIXME: What the heck does this mean??? - final List selectedChannels; - - /// {@macro viewInfoCallback} - final ViewInfoCallback? onViewInfoTap; - - /// The builder that will be used in case of error - final ErrorBuilder? errorBuilder; - - /// The builder that will be used in case of loading - final WidgetBuilder? loadingBuilder; - - /// The builder for creating custom channel widgets - final Function(BuildContext, List)? listBuilder; - - /// The builder used when the channel list is empty. - final WidgetBuilder? emptyBuilder; - - /// This function is executed when the 'more details' slidable option is - /// tapped or clicked. - final ChannelInfoCallback? onMoreDetailsPressed; - - /// This function is executed when the 'delete' slidable option is tapped - /// or clicked. - final ChannelInfoCallback? onDeletePressed; - - /// List of actions that can be selected for slidable channel widgets - final List? swipeActions; - - /// Enables channel list reloading and pagination. - /// - /// Use [ChannelListController.loadData] and - /// [ChannelListController.paginateData] respectively for reloading and - /// pagination. - final ChannelListController? channelListController; - - @override - _ChannelListViewState createState() => _ChannelListViewState(); -} - -class _ChannelListViewState extends State { - late final _defaultController = ChannelListController(); - - ChannelListController get _channelListController => - widget.channelListController ?? _defaultController; - - @override - Widget build(BuildContext context) { - Widget child = ChannelListCore( - filter: widget.filter, - sort: widget.sort, - state: widget.showChannelState, - watch: widget.watchChannel, - presence: widget.presence, - memberLimit: widget.memberLimit, - messageLimit: widget.messageLimit, - limit: widget.limit, - channelListController: _channelListController, - listBuilder: widget.listBuilder ?? _buildListView, - emptyBuilder: widget.emptyBuilder ?? _buildEmptyWidget, - errorBuilder: widget.errorBuilder ?? _buildErrorWidget, - loadingBuilder: widget.loadingBuilder ?? _buildLoadingWidget, - ); - - if (widget.pullToRefresh) { - child = RefreshIndicator( - onRefresh: () => _channelListController.loadData!(), - child: child, - ); - } - - child = LazyLoadScrollView( - onEndOfPage: () => _channelListController.paginateData!(), - child: child, - ); - - final backgroundColor = - StreamChannelListViewTheme.of(context).backgroundColor; - - if (backgroundColor != null) { - return ColoredBox( - color: backgroundColor, - child: child, - ); - } - - return child; - } - - Widget _buildListView(BuildContext context, List channels) { - if (widget.crossAxisCount > 1) { - return GridView.builder( - padding: widget.padding, - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: widget.crossAxisCount, - ), - itemCount: channels.length, - physics: const AlwaysScrollableScrollPhysics(), - itemBuilder: (context, index) => _GridItemBuilder( - index: index, - channels: channels, - selectedChannels: widget.selectedChannels, - channelTapCallback: (channel, widget) => _getChannelTap(context), - ), - ); - } - return SlidableAutoCloseBehavior( - child: ListView.separated( - padding: widget.padding, - physics: const AlwaysScrollableScrollPhysics(), - // all channels + progress loader - itemCount: channels.length + 1, - separatorBuilder: (_, index) { - if (widget.separatorBuilder != null) { - return widget.separatorBuilder!(context, index); - } - return _Separator(index: index); - }, - itemBuilder: (context, index) => - _listItemBuilder(context, index, channels), - ), - ); - } - - Widget _buildEmptyWidget(BuildContext context) { - return LayoutBuilder( - builder: (context, viewportConstraints) { - final chatThemeData = StreamChatTheme.of(context); - return SingleChildScrollView( - physics: const AlwaysScrollableScrollPhysics(), - child: Stack( - children: [ - ConstrainedBox( - constraints: BoxConstraints( - minHeight: viewportConstraints.maxHeight, - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.all(8), - child: StreamSvgIcon.message( - size: 136, - color: chatThemeData.colorTheme.disabled, - ), - ), - Padding( - padding: const EdgeInsets.all(8), - child: Text( - context.translations.letsStartChattingLabel, - style: chatThemeData.textTheme.headline, - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 52, - ), - child: Text( - context.translations.sendingFirstMessageLabel, - textAlign: TextAlign.center, - style: chatThemeData.textTheme.body.copyWith( - color: chatThemeData.colorTheme.textLowEmphasis, - ), - ), - ), - ], - ), - ), - if (widget.onStartChatPressed != null) - Positioned( - right: 0, - left: 0, - bottom: 32, - child: Center( - child: TextButton( - onPressed: widget.onStartChatPressed, - child: Text( - context.translations.startAChatLabel, - style: chatThemeData.textTheme.bodyBold.copyWith( - color: chatThemeData.colorTheme.accentPrimary, - ), - ), - ), - ), - ), - ], - ), - ); - }, - ); - } - - Widget _buildLoadingWidget(BuildContext context) { - return ListView( - padding: widget.padding, - physics: const AlwaysScrollableScrollPhysics(), - children: List.generate( - 25, - (i) { - if (widget.crossAxisCount == 1) { - if (i % 2 != 0) { - if (widget.separatorBuilder != null) { - return widget.separatorBuilder!(context, i); - } - return _Separator(index: i); - } - } - return _buildLoadingItem(context); - }, - ), - ); - } - - Shimmer _buildLoadingItem(BuildContext context) { - final chatThemeData = StreamChatTheme.of(context); - if (widget.crossAxisCount > 1) { - return Shimmer.fromColors( - baseColor: chatThemeData.colorTheme.disabled, - highlightColor: chatThemeData.colorTheme.inputBg, - child: Column( - children: [ - const SizedBox(height: 4), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - for (int i = 0; i < widget.crossAxisCount; i++) - Container( - decoration: const BoxDecoration( - color: Colors.white, - shape: BoxShape.circle, - ), - constraints: const BoxConstraints.tightFor( - height: 70, - width: 70, - ), - ), - ], - ), - const SizedBox( - height: 16, - ), - ], - ), - ); - } else { - return Shimmer.fromColors( - baseColor: chatThemeData.colorTheme.disabled, - highlightColor: chatThemeData.colorTheme.inputBg, - child: ListTile( - leading: Container( - decoration: BoxDecoration( - color: chatThemeData.colorTheme.barsBg, - shape: BoxShape.circle, - ), - constraints: const BoxConstraints.tightFor( - height: 40, - width: 40, - ), - ), - contentPadding: const EdgeInsets.only( - left: 8, - right: 8, - ), - title: Align( - alignment: Alignment.centerLeft, - child: Container( - decoration: BoxDecoration( - color: chatThemeData.colorTheme.barsBg, - borderRadius: BorderRadius.circular(11), - ), - constraints: const BoxConstraints.tightFor( - height: 16, - width: 82, - ), - ), - ), - subtitle: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Align( - alignment: Alignment.centerLeft, - child: Container( - decoration: BoxDecoration( - color: chatThemeData.colorTheme.barsBg, - borderRadius: BorderRadius.circular(11), - ), - constraints: const BoxConstraints.expand( - height: 16, - ), - ), - ), - ), - Container( - margin: const EdgeInsets.only(left: 16), - decoration: BoxDecoration( - color: chatThemeData.colorTheme.barsBg, - borderRadius: BorderRadius.circular(11), - ), - constraints: const BoxConstraints.tightFor( - height: 16, - width: 42, - ), - ), - ], - ), - ), - ); - } - } - - Widget _buildErrorWidget(BuildContext context, Object error) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text.rich( - TextSpan( - children: [ - const WidgetSpan( - child: Padding( - padding: EdgeInsets.only( - right: 2, - ), - child: Icon(Icons.error_outline), - ), - ), - TextSpan(text: context.translations.loadingChannelsError), - ], - ), - style: Theme.of(context).textTheme.headline6, - ), - TextButton( - onPressed: () => _channelListController.loadData!(), - child: Text(context.translations.retryLabel), - ), - ], - ), - ); - } - - Widget _listItemBuilder(BuildContext context, int i, List channels) { - final channelsBloc = ChannelsBloc.of(context); - - if (i == channels.length) { - return _QueryProgressIndicator(channelsProvider: channelsBloc); - } - - final onTap = _getChannelTap(context); - final chatThemeData = StreamChatTheme.of(context); - final backgroundColor = chatThemeData.colorTheme.inputBg; - final channel = channels[i]; - - final canDeleteChannel = - channel.ownCapabilities.contains(PermissionType.deleteChannel); - - final actionPaneChildren = - widget.swipeActions?.length ?? (canDeleteChannel ? 2 : 1); - final actionPaneExtentRatio = actionPaneChildren > 5 - ? 1 / actionPaneChildren - : actionPaneChildren * 0.2; - - return StreamChannel( - key: ValueKey('CHANNEL-${channel.cid}'), - channel: channel, - child: Slidable( - enabled: widget.swipeToAction, - endActionPane: ActionPane( - extentRatio: actionPaneExtentRatio, - motion: const BehindMotion(), - children: widget.swipeActions - ?.map((e) => CustomSlidableAction( - backgroundColor: e.color ?? Colors.white, - child: e.iconWidget, - onPressed: (_) { - e.onTap?.call(channel); - }, - )) - .toList() ?? - [ - CustomSlidableAction( - backgroundColor: backgroundColor, - onPressed: widget.onMoreDetailsPressed != null - ? (_) { - widget.onMoreDetailsPressed!(channel); - } - : (_) { - showModalBottomSheet( - clipBehavior: Clip.hardEdge, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(32), - topRight: Radius.circular(32), - ), - ), - context: context, - builder: (context) => StreamChannel( - channel: channel, - child: StreamChannelInfoBottomSheet( - channel: channel, - onViewInfoTap: () { - widget.onViewInfoTap?.call(channel); - }, - ), - ), - ); - }, - child: const Icon(Icons.more_horiz), - ), - if (canDeleteChannel) - CustomSlidableAction( - backgroundColor: backgroundColor, - onPressed: widget.onDeletePressed != null - ? (_) { - widget.onDeletePressed?.call(channel); - } - : (_) async { - final res = await showConfirmationBottomSheet( - context, - title: - context.translations.deleteConversationLabel, - question: context - .translations.deleteConversationQuestion, - okText: context.translations.deleteLabel, - cancelText: context.translations.cancelLabel, - icon: StreamSvgIcon.delete( - color: chatThemeData.colorTheme.accentError, - ), - ); - if (res == true) { - await channel.delete(); - } - }, - child: StreamSvgIcon.delete( - color: chatThemeData.colorTheme.accentError, - ), - ), - ], - ), - child: widget.channelPreviewBuilder?.call(context, channel) ?? - DecoratedBox( - decoration: BoxDecoration( - color: chatThemeData.channelListViewTheme.backgroundColor, - ), - child: ChannelPreview( - onLongPress: widget.onChannelLongPress, - channel: channel, - onImageTap: widget.onImageTap != null - ? () => widget.onImageTap!(channel) - : null, - onTap: (channel) => onTap(channel, widget.channelWidget), - onViewInfoTap: widget.onViewInfoTap, - ), - ), - ), - ); - } - - ChannelTapCallback _getChannelTap(BuildContext context) { - ChannelTapCallback onTap; - if (widget.onChannelTap != null) { - onTap = widget.onChannelTap!; - } else { - onTap = (channel, _) { - if (widget.channelWidget == null) { - return; - } - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => StreamChannel( - channel: channel, - child: widget.channelWidget!, - ), - ), - ); - }; - } - return onTap; - } -} - -class _GridItemBuilder extends StatelessWidget { - const _GridItemBuilder({ - required this.index, - required this.channels, - required this.selectedChannels, - required this.channelTapCallback, - }); - - final int index; - final List channels; - final List selectedChannels; - final ChannelTapCallback channelTapCallback; - - @override - Widget build(BuildContext context) { - final channel = channels[index]; - - final selected = selectedChannels.contains(channel); - - return Container( - key: ValueKey('CHANNEL-${channel.id}'), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - StreamChannelAvatar( - channel: channel, - borderRadius: BorderRadius.circular(32), - selected: selected, - constraints: const BoxConstraints.tightFor( - width: 64, - height: 64, - ), - onTap: () => channelTapCallback, - ), - const SizedBox(height: 7), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: StreamChannel( - channel: channel, - child: StreamChannelName( - channel: channel, - textStyle: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - ), - ), - ), - ), - ], - ), - ); - } -} - -class _QueryProgressIndicator extends StatelessWidget { - const _QueryProgressIndicator({ - required this.channelsProvider, - }); - - final ChannelsBlocState channelsProvider; - - @override - Widget build(BuildContext context) { - return BetterStreamBuilder( - stream: channelsProvider.queryChannelsLoading, - initialData: false, - errorBuilder: (context, err) { - final theme = StreamChatTheme.of(context); - return ColoredBox( - color: theme.colorTheme.textLowEmphasis.withOpacity(0.9), - child: Padding( - padding: const EdgeInsets.all(16), - child: Text( - context.translations.loadingChannelsError, - style: theme.textTheme.body.copyWith( - color: Colors.white, - ), - ), - ), - ); - }, - builder: (context, showLoading) { - if (!showLoading) return const Offstage(); - return const Center( - child: Padding( - padding: EdgeInsets.all(16), - child: CircularProgressIndicator(), - ), - ); - }, - ); - } -} - -class _Separator extends StatelessWidget { - const _Separator({ - required this.index, - }); - - final int index; - - @override - Widget build(BuildContext context) { - final effect = StreamChatTheme.of(context).colorTheme.borderBottom; - - return Container( - height: 1, - color: effect.color!.withOpacity(effect.alpha ?? 1.0), - ); - } -} - -/// Class for slidable action -class SwipeAction { - /// Constructor for creating [SwipeAction] - SwipeAction({ - this.color, - required this.iconWidget, - this.onTap, - }); - - /// Background color of action - Color? color; - - /// Widget to display as icon - Widget iconWidget; - - /// Callback when icon is tapped - ChannelInfoCallback? onTap; -} diff --git a/packages/stream_chat_flutter/lib/src/v4/stream_channel_avatar.dart b/packages/stream_chat_flutter/lib/src/channel/stream_channel_avatar.dart similarity index 100% rename from packages/stream_chat_flutter/lib/src/v4/stream_channel_avatar.dart rename to packages/stream_chat_flutter/lib/src/channel/stream_channel_avatar.dart diff --git a/packages/stream_chat_flutter/lib/src/v4/stream_channel_name.dart b/packages/stream_chat_flutter/lib/src/channel/stream_channel_name.dart similarity index 100% rename from packages/stream_chat_flutter/lib/src/v4/stream_channel_name.dart rename to packages/stream_chat_flutter/lib/src/channel/stream_channel_name.dart diff --git a/packages/stream_chat_flutter/lib/src/v4/stream_message_preview_text.dart b/packages/stream_chat_flutter/lib/src/channel/stream_message_preview_text.dart similarity index 100% rename from packages/stream_chat_flutter/lib/src/v4/stream_message_preview_text.dart rename to packages/stream_chat_flutter/lib/src/channel/stream_message_preview_text.dart diff --git a/packages/stream_chat_flutter/lib/src/gallery/gallery_footer.dart b/packages/stream_chat_flutter/lib/src/gallery/gallery_footer.dart index 5966791a1..610864654 100644 --- a/packages/stream_chat_flutter/lib/src/gallery/gallery_footer.dart +++ b/packages/stream_chat_flutter/lib/src/gallery/gallery_footer.dart @@ -9,10 +9,6 @@ import 'package:stream_chat_flutter/src/utils/extensions.dart'; import 'package:stream_chat_flutter/src/video/video_thumbnail_image.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamGalleryFooter} -@Deprecated("Use 'StreamGalleryFooter' instead") -typedef GalleryFooter = StreamGalleryFooter; - /// {@template streamGalleryFooter} /// Footer widget for media display /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/gallery/gallery_header.dart b/packages/stream_chat_flutter/lib/src/gallery/gallery_header.dart index 84d9dc51e..3c6c487e7 100644 --- a/packages/stream_chat_flutter/lib/src/gallery/gallery_header.dart +++ b/packages/stream_chat_flutter/lib/src/gallery/gallery_header.dart @@ -7,10 +7,6 @@ import 'package:stream_chat_flutter/src/theme/themes.dart'; import 'package:stream_chat_flutter/src/utils/utils.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; -/// {@macro streamGalleryHeader} -@Deprecated("Use 'StreamGalleryHeader' instead") -typedef GalleryHeader = StreamGalleryHeader; - /// {@macro streamGalleryHeader} /// Header/AppBar widget for media display screen /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/indicators/sending_indicator.dart b/packages/stream_chat_flutter/lib/src/indicators/sending_indicator.dart index c1a7a4265..f4f1d4c87 100644 --- a/packages/stream_chat_flutter/lib/src/indicators/sending_indicator.dart +++ b/packages/stream_chat_flutter/lib/src/indicators/sending_indicator.dart @@ -1,10 +1,6 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamSendingIndicator} -@Deprecated("Use 'StreamSendingIndicator' instead") -typedef SendingIndicator = StreamSendingIndicator; - /// {@template streamSendingIndicator} /// Shows the sending status of a message. /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/indicators/typing_indicator.dart b/packages/stream_chat_flutter/lib/src/indicators/typing_indicator.dart index 9ccbc3d00..5ad1b5ef9 100644 --- a/packages/stream_chat_flutter/lib/src/indicators/typing_indicator.dart +++ b/packages/stream_chat_flutter/lib/src/indicators/typing_indicator.dart @@ -3,10 +3,6 @@ import 'package:lottie/lottie.dart'; import 'package:stream_chat_flutter/src/utils/extensions.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; -/// {@macro streamTypingIndicator} -@Deprecated("Use 'StreamTypingIndicator' instead") -typedef TypingIndicator = StreamTypingIndicator; - /// {@template streamTypingIndicator} /// Shows the list of user who are actively typing. /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/indicators/unread_indicator.dart b/packages/stream_chat_flutter/lib/src/indicators/unread_indicator.dart index b8573ef67..f13533b44 100644 --- a/packages/stream_chat_flutter/lib/src/indicators/unread_indicator.dart +++ b/packages/stream_chat_flutter/lib/src/indicators/unread_indicator.dart @@ -1,10 +1,6 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamUnreadIndicator} -@Deprecated("Use 'StreamUnreadIndicator' instead") -typedef UnreadIndicator = StreamUnreadIndicator; - /// {@template streamUnreadIndicator} /// Shows an unread indicator for a message. /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/indicators/upload_progress_indicator.dart b/packages/stream_chat_flutter/lib/src/indicators/upload_progress_indicator.dart index bb73a5b18..2260cf9e0 100644 --- a/packages/stream_chat_flutter/lib/src/indicators/upload_progress_indicator.dart +++ b/packages/stream_chat_flutter/lib/src/indicators/upload_progress_indicator.dart @@ -1,10 +1,6 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamUploadProgressIndicator} -@Deprecated("Use 'StreamUploadProgressIndicator' instead") -typedef UploadProgressIndicator = StreamUploadProgressIndicator; - /// {@template streamUploadProgressIndicator} /// Shows the upload progress of an attachment. /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/localization/translations.dart b/packages/stream_chat_flutter/lib/src/localization/translations.dart index 706031d17..b92c1900d 100644 --- a/packages/stream_chat_flutter/lib/src/localization/translations.dart +++ b/packages/stream_chat_flutter/lib/src/localization/translations.dart @@ -1,7 +1,6 @@ import 'package:jiffy/jiffy.dart'; import 'package:stream_chat_flutter/src/message_list_view/message_list_view.dart'; import 'package:stream_chat_flutter/src/misc/connection_status_builder.dart'; -import 'package:stream_chat_flutter/src/v4/message_input/stream_message_input.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart' show User; diff --git a/packages/stream_chat_flutter/lib/src/message_actions_modal/message_action.dart b/packages/stream_chat_flutter/lib/src/message_actions_modal/message_action.dart index 53090442f..f3acaac96 100644 --- a/packages/stream_chat_flutter/lib/src/message_actions_modal/message_action.dart +++ b/packages/stream_chat_flutter/lib/src/message_actions_modal/message_action.dart @@ -1,10 +1,6 @@ import 'package:flutter/widgets.dart'; import 'package:stream_chat_flutter/src/utils/typedefs.dart'; -/// {@macro message_action} -@Deprecated("Use 'streamMessageAction' instead") -typedef MessageAction = StreamMessageAction; - /// {@template streamMessageAction} /// Class describing a message action /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/message_input/animated_send_button.dart b/packages/stream_chat_flutter/lib/src/message_input/animated_send_button.dart index ef564580b..d28898a54 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/animated_send_button.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/animated_send_button.dart @@ -1,7 +1,6 @@ // ignore_for_file: deprecated_member_use_from_same_package import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/message_input/countdown_button.dart'; import 'package:stream_chat_flutter/src/message_input/send_button.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; @@ -60,7 +59,7 @@ class AnimatedSendButton extends StatelessWidget { Widget build(BuildContext context) { late Widget sendButton; if (cooldown > 0) { - sendButton = CountdownButton(count: cooldown); + sendButton = StreamCountdownButton(count: cooldown); } else if (!messageIsPresent && attachmentsIsEmpty) { sendButton = idleSendButton ?? Padding( diff --git a/packages/stream_chat_flutter/lib/src/message_input/countdown_button.dart b/packages/stream_chat_flutter/lib/src/message_input/countdown_button.dart index 07d4d442f..2b832a0d4 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/countdown_button.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/countdown_button.dart @@ -1,17 +1,16 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@template countdownButton} -/// Shows the countdown to when the user can send another message. -/// {@endtemplate} -class CountdownButton extends StatelessWidget { - /// {@macro countdownButton} - const CountdownButton({ +/// Button for showing visual component of slow mode. +class StreamCountdownButton extends StatelessWidget { + /// Constructor for creating [StreamCountdownButton]. + const StreamCountdownButton({ super.key, required this.count, }); - /// The countdown, in seconds. + /// The amount of time remaining until the user can send a message again. + /// Measured in seconds. final int count; @override diff --git a/packages/stream_chat_flutter/lib/src/message_input/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input/message_input.dart deleted file mode 100644 index 8bdaa49dd..000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/message_input.dart +++ /dev/null @@ -1,1500 +0,0 @@ -// ignore_for_file: deprecated_member_use_from_same_package - -import 'dart:async'; -import 'dart:io'; -import 'dart:math'; - -import 'package:collection/collection.dart'; -import 'package:desktop_drop/desktop_drop.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:image_picker/image_picker.dart'; -import 'package:photo_manager/photo_manager.dart'; -import 'package:stream_chat_flutter/platform_widget_builder/platform_widget_builder.dart'; -import 'package:stream_chat_flutter/src/emoji/emoji.dart'; -import 'package:stream_chat_flutter/src/media_list_view_controller.dart'; -import 'package:stream_chat_flutter/src/message_input/animated_send_button.dart'; -import 'package:stream_chat_flutter/src/message_input/attachment_button.dart'; -import 'package:stream_chat_flutter/src/message_input/command_button.dart'; -import 'package:stream_chat_flutter/src/message_input/dm_checkbox.dart'; -import 'package:stream_chat_flutter/src/message_input/file_upload_error_handler.dart'; -import 'package:stream_chat_flutter/src/message_input/input_attachments.dart'; -import 'package:stream_chat_flutter/src/message_input/picker_widget.dart'; -import 'package:stream_chat_flutter/src/message_input/quoted_message_widget.dart'; -import 'package:stream_chat_flutter/src/message_input/quoting_message_top_area.dart'; -import 'package:stream_chat_flutter/src/message_input/user_mentions_overlay.dart'; -import 'package:stream_chat_flutter/src/overlays/commands_overlay.dart'; -import 'package:stream_chat_flutter/src/overlays/emoji_overlay.dart'; -import 'package:stream_chat_flutter/src/utils/utils.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -const _kMinMediaPickerSize = 360.0; - -const _kDefaultMaxAttachmentSize = 20971520; // 20MB in Bytes - -/// Inactive state -/// -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/message_input.png) -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/message_input_paint.png) -/// -/// Focused state -/// -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/message_input2.png) -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/message_input2_paint.png) -/// -/// Widget used to enter the message and add attachments -/// -/// ```dart -/// class ChannelPage extends StatelessWidget { -/// const ChannelPage({ -/// Key key, -/// }) : super(key: key); -/// -/// @override -/// Widget build(BuildContext context) { -/// return Scaffold( -/// appBar: ChannelHeader(), -/// body: Column( -/// children: [ -/// Expanded( -/// child: MessageListView( -/// threadBuilder: (_, parentMessage) { -/// return ThreadPage( -/// parent: parentMessage, -/// ); -/// }, -/// ), -/// ), -/// MessageInput(), -/// ], -/// ), -/// ); -/// } -/// } -/// ``` -/// -/// You usually put this widget in the same page of a [StreamMessageListView] -/// as the bottom widget. -/// -/// The widget renders the ui based on the first ancestor of -/// type [StreamChatTheme]. -/// Modify it to change the widget appearance. -@Deprecated("Use 'StreamMessageInput' instead") -class MessageInput extends StatefulWidget { - /// Instantiate a new MessageInput - const MessageInput({ - super.key, - this.onMessageSent, - this.preMessageSending, - this.parentMessage, - this.editMessage, - this.maxHeight = 150, - this.keyboardType = TextInputType.multiline, - this.disableAttachments = false, - this.initialMessage, - this.textEditingController, - this.actions = const [], - this.actionsLocation = ActionsLocation.left, - this.attachmentThumbnailBuilders, - this.focusNode, - this.quotedMessage, - this.onQuotedMessageCleared, - this.sendButtonLocation = SendButtonLocation.outside, - this.autofocus = false, - this.hideSendAsDm = false, - this.idleSendButton, - this.activeSendButton, - this.showCommandsButton = true, - @Deprecated('''Use `userMentionsTileBuilder` instead. Will be removed in future release''') - this.mentionsTileBuilder, - this.userMentionsTileBuilder, - this.maxAttachmentSize = _kDefaultMaxAttachmentSize, - this.onError, - this.attachmentLimit = 10, - this.onAttachmentLimitExceed, - this.attachmentButtonBuilder, - this.commandButtonBuilder, - this.customOverlays = const [], - this.mentionAllAppUsers = false, - this.shouldKeepFocusAfterMessage, - }) : assert( - initialMessage == null || editMessage == null, - "Can't provide both `initialMessage` and `editMessage`", - ); - - /// List of options for showing overlays - final List customOverlays; - - /// Message to edit - final Message? editMessage; - - /// Max attachment size in bytes - /// Defaults to 20 MB - /// do not set it if you're using our default CDN - final int maxAttachmentSize; - - /// Message to start with - final Message? initialMessage; - - /// Function called after sending the message - final void Function(Message)? onMessageSent; - - /// Function called right before sending the message - /// Use this to transform the message - final FutureOr Function(Message)? preMessageSending; - - /// Parent message in case of a thread - final Message? parentMessage; - - /// Maximum Height for the TextField to grow before it starts scrolling - final double maxHeight; - - /// The keyboard type assigned to the TextField - final TextInputType keyboardType; - - /// If true the attachments button will not be displayed - final bool disableAttachments; - - /// Use this property to hide/show the commands button - final bool showCommandsButton; - - /// Hide send as dm checkbox - final bool hideSendAsDm; - - /// The text controller of the TextField - final TextEditingController? textEditingController; - - /// List of action widgets - final List actions; - - /// The location of the custom actions - final ActionsLocation actionsLocation; - - /// Map that defines a thumbnail builder for an attachment type - final Map? attachmentThumbnailBuilders; - - /// The focus node associated to the TextField - final FocusNode? focusNode; - - /// TODO: document me! - final Message? quotedMessage; - - /// TODO: document me! - final VoidCallback? onQuotedMessageCleared; - - /// The location of the send button - final SendButtonLocation sendButtonLocation; - - /// Autofocus property passed to the TextField - final bool autofocus; - - /// Send button widget in an idle state - final Widget? idleSendButton; - - /// Send button widget in an active state - final Widget? activeSendButton; - - /// {@macro mentionTileBuilder} - final MentionTileBuilder? mentionsTileBuilder; - - /// {@macro userMentionTileBuilder} - final UserMentionTileBuilder? userMentionsTileBuilder; - - /// {@macro errorListener} - final ErrorListener? onError; - - /// A limit for the no. of attachments that can be sent with a single message. - final int attachmentLimit; - - /// {@macro attachmentLimitExceededListener} - /// - /// This will override the default error alert behaviour. - final AttachmentLimitExceedListener? onAttachmentLimitExceed; - - /// {@macro actionButtonBuilder} - /// - /// The builder contains the default [IconButton] that can be customized by - /// calling `.copyWith`. - final ActionButtonBuilder? attachmentButtonBuilder; - - /// Builder for customizing the command button. - /// - /// The builder contains the default [IconButton] that can be customized by - /// calling `.copyWith`. - final ActionButtonBuilder? commandButtonBuilder; - - /// When enabled mentions search users across the entire app. - /// - /// Defaults to false. - final bool mentionAllAppUsers; - - /// Defines if the [MessageInput] loses focuses after a message is sent. - /// The default behaviour keeps focus until a command is enabled. - final bool? shouldKeepFocusAfterMessage; - - @override - MessageInputState createState() => MessageInputState(); - - /// Use this method to get the current [StreamChatState] instance - static MessageInputState of(BuildContext context) { - MessageInputState? messageInputState; - messageInputState = context.findAncestorStateOfType(); - assert( - messageInputState != null, - 'You must have a MessageInput widget as ancestor of your widget tree', - ); - return messageInputState!; - } -} - -/// State of [MessageInput] -@Deprecated("Use 'StreamMessageInput' instead") -class MessageInputState extends State { - final _attachments = {}; - final List _mentionedUsers = []; - - final _imagePicker = ImagePicker(); - final _mediaListViewController = MediaListViewController(); - late final _focusNode = widget.focusNode ?? FocusNode(); - late final _isInternalFocusNode = widget.focusNode == null; - bool _inputEnabled = true; - bool _commandEnabled = false; - bool _showCommandsOverlay = false; - bool _showMentionsOverlay = false; - - Command? _chosenCommand; - bool _actionsShrunk = false; - bool _sendAsDm = false; - bool _openFilePickerSection = false; - int _filePickerIndex = 0; - - /// The editing controller passed to the input TextField - late final TextEditingController textEditingController = - widget.textEditingController ?? TextEditingController(); - - late StreamChatThemeData _streamChatTheme; - late StreamMessageInputThemeData _messageInputTheme; - - bool get _hasQuotedMessage => widget.quotedMessage != null; - - bool get _messageIsPresent => textEditingController.text.trim().isNotEmpty; - - BoxBorder? _draggingBorder; - - @override - void initState() { - super.initState(); - if (widget.editMessage != null || widget.initialMessage != null) { - _parseExistingMessage(widget.editMessage ?? widget.initialMessage!); - } - textEditingController.addListener(_onChangedDebounced); - _focusNode.addListener(_focusNodeListener); - } - - void _focusNodeListener() { - if (_focusNode.hasFocus) { - _openFilePickerSection = false; - } - } - - int _timeOut = 0; - Timer? _slowModeTimer; - - void _startSlowMode() { - if (!mounted) { - return; - } - final channel = StreamChannel.of(context).channel; - final cooldownStartedAt = channel.cooldownStartedAt; - if (cooldownStartedAt != null) { - final diff = DateTime.now().difference(cooldownStartedAt).inSeconds; - if (diff < channel.cooldown) { - _timeOut = channel.cooldown - diff; - if (_timeOut > 0) { - _slowModeTimer = Timer.periodic(const Duration(seconds: 1), (timer) { - if (_timeOut == 0) { - timer.cancel(); - } else { - if (mounted) { - setState(() => _timeOut -= 1); - } - } - }); - } - } - } - } - - void _stopSlowMode() => _slowModeTimer?.cancel(); - - @override - Widget build(BuildContext context) { - Widget child = DecoratedBox( - decoration: BoxDecoration( - color: _messageInputTheme.inputBackgroundColor, - ), - child: SafeArea( - child: GestureDetector( - onPanUpdate: (details) { - if (details.delta.dy > 0) { - _focusNode.unfocus(); - if (_openFilePickerSection) { - setState(() => _openFilePickerSection = false); - } - } - }, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // Ensure this doesn't show on web & desktop - PlatformWidgetBuilder( - mobile: (context, child) => child, - child: QuotingMessageTopArea( - hasQuotedMessage: _hasQuotedMessage, - onQuotedMessageCleared: widget.onQuotedMessageCleared, - ), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: _buildTextField(context), - ), - if (widget.parentMessage != null && !widget.hideSendAsDm) - Padding( - padding: const EdgeInsets.only( - right: 12, - left: 12, - bottom: 12, - ), - child: DmCheckbox( - foregroundDecoration: BoxDecoration( - border: _sendAsDm - ? null - : Border.all( - color: _streamChatTheme - .colorTheme.textHighEmphasis - .withOpacity(0.5), - width: 2, - ), - borderRadius: BorderRadius.circular(3), - ), - color: _sendAsDm - ? _streamChatTheme.colorTheme.accentPrimary - : _streamChatTheme.colorTheme.barsBg, - onTap: () { - setState(() => _sendAsDm = !_sendAsDm); - }, - crossFadeState: _sendAsDm - ? CrossFadeState.showFirst - : CrossFadeState.showSecond, - ), - ), - PlatformWidgetBuilder( - mobile: (context, child) => _buildFilePickerSection(), - ), - ], - ), - ), - ), - ); - if (widget.editMessage == null) { - child = Material( - elevation: 8, - color: _messageInputTheme.inputBackgroundColor, - child: child, - ); - } - - return StreamMultiOverlay( - childAnchor: Alignment.topCenter, - overlayAnchor: Alignment.bottomCenter, - overlayOptions: [ - OverlayOptions( - visible: _showCommandsOverlay, - widget: _buildCommandsOverlayEntry(), - ), - OverlayOptions( - visible: _focusNode.hasFocus && - textEditingController.text.isNotEmpty && - textEditingController.selection.baseOffset > 0 && - textEditingController.text - .substring( - 0, - textEditingController.selection.baseOffset, - ) - .contains(':'), - widget: _buildEmojiOverlay(), - ), - OverlayOptions( - visible: _showMentionsOverlay, - widget: _buildMentionsOverlayEntry(), - ), - ...widget.customOverlays, - ], - child: child, - ); - } - - Flex _buildTextField(BuildContext context) { - return Flex( - direction: Axis.horizontal, - children: [ - if (!_commandEnabled && widget.actionsLocation == ActionsLocation.left) - _buildExpandActionsButton(context), - _buildTextInput(context), - if (!_commandEnabled && widget.actionsLocation == ActionsLocation.right) - _buildExpandActionsButton(context), - if (widget.sendButtonLocation == SendButtonLocation.outside) - AnimatedSendButton( - cooldown: _timeOut, - messageIsPresent: _messageIsPresent, - attachmentsIsEmpty: _attachments.isEmpty, - messageInputTheme: _messageInputTheme, - onTap: sendMessage, - commandEnabled: _commandEnabled, - activeSendButton: widget.activeSendButton, - idleSendButton: widget.idleSendButton, - editMessage: widget.editMessage, - ), - ], - ); - } - - Widget _buildExpandActionsButton(BuildContext context) { - final channel = StreamChannel.of(context).channel; - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: AnimatedCrossFade( - crossFadeState: _actionsShrunk - ? CrossFadeState.showFirst - : CrossFadeState.showSecond, - firstCurve: Curves.easeOut, - secondCurve: Curves.easeIn, - firstChild: IconButton( - onPressed: () { - if (_actionsShrunk) { - setState(() => _actionsShrunk = false); - } - }, - icon: Transform.rotate( - angle: (widget.actionsLocation == ActionsLocation.right || - widget.actionsLocation == ActionsLocation.rightInside) - ? pi - : 0, - child: StreamSvgIcon.emptyCircleLeft( - color: _messageInputTheme.expandButtonColor, - ), - ), - padding: EdgeInsets.zero, - constraints: const BoxConstraints.tightFor( - height: 24, - width: 24, - ), - splashRadius: 24, - ), - secondChild: widget.disableAttachments && - !widget.showCommandsButton && - !widget.actions.isNotEmpty - ? const Offstage() - : Wrap( - children: [ - if (!widget.disableAttachments) - AttachmentButton( - color: _openFilePickerSection - ? _messageInputTheme.actionButtonColor! - : _messageInputTheme.actionButtonIdleColor!, - onPressed: _handleFileSelect, - ), - if (widget.showCommandsButton && - widget.editMessage == null && - channel.state != null && - channel.config?.commands.isNotEmpty == true) - _buildCommandButton(context), - ...widget.actions, - ].insertBetween(const SizedBox(width: 8)), - ), - duration: const Duration(milliseconds: 300), - alignment: Alignment.center, - ), - ); - } - - /// Handle the platform-specific logic for selecting files. - /// - /// On mobile, this will open the file selection bottom sheet. On desktop, - /// this will open the native file system and allow the user to select one - /// or more files. - Future _handleFileSelect() async { - if (isDesktopDeviceOrWeb) { - final desktopAttachmentHandler = DesktopAttachmentHandler( - maxAttachmentSize: widget.maxAttachmentSize, - ); - desktopAttachmentHandler.upload().then((attachments) { - // If attachments is empty, it means the user closed the file system - // without selecting any files. - if (attachments.isNotEmpty) { - setState(() => _addAttachments(attachments)); - } - }).catchError((error) { - handleFileUploadError(context, error, widget.maxAttachmentSize); - }); - } else { - _showCommandsOverlay = false; - _showMentionsOverlay = false; - - if (_openFilePickerSection) { - setState(() => _openFilePickerSection = false); - } else { - showAttachmentModal(); - } - } - } - - Expanded _buildTextInput(BuildContext context) { - final margin = (widget.sendButtonLocation == SendButtonLocation.inside - ? const EdgeInsets.only(right: 8) - : EdgeInsets.zero) + - (widget.actionsLocation != ActionsLocation.left || _commandEnabled - ? const EdgeInsets.only(left: 8) - : EdgeInsets.zero); - final _desktopAttachmentHandler = DesktopAttachmentHandler( - maxAttachmentSize: widget.maxAttachmentSize, - ); - - return Expanded( - child: DropTarget( - onDragDone: (details) { - _desktopAttachmentHandler - .uploadViaDragNDrop(details.files) - .then((attachments) { - if (attachments.isNotEmpty) { - setState(() => _addAttachments(attachments)); - } - }).catchError((error) { - handleFileUploadError(context, error, widget.maxAttachmentSize); - }); - }, - onDragEntered: (details) { - setState(() { - _draggingBorder = Border.all( - color: _streamChatTheme.colorTheme.accentPrimary, - ); - }); - }, - onDragExited: (details) { - setState(() => _draggingBorder = null); - }, - child: Container( - clipBehavior: Clip.hardEdge, - margin: margin, - decoration: BoxDecoration( - borderRadius: _messageInputTheme.borderRadius, - gradient: _focusNode.hasFocus - ? _messageInputTheme.activeBorderGradient - : _messageInputTheme.idleBorderGradient, - border: _draggingBorder, - ), - child: Padding( - padding: const EdgeInsets.all(1.5), - child: DecoratedBox( - decoration: BoxDecoration( - borderRadius: _messageInputTheme.borderRadius, - color: _messageInputTheme.inputBackgroundColor, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildReplyToMessage(), - InputAttachments( - attachments: _attachments, - attachmentThumbnailBuilders: - widget.attachmentThumbnailBuilders, - ), - LimitedBox( - maxHeight: widget.maxHeight, - child: KeyboardShortcutRunner( - onEnterKeypress: sendMessage, - onEscapeKeypress: () { - if (_hasQuotedMessage && - textEditingController.text.isEmpty) { - widget.onQuotedMessageCleared?.call(); - } - }, - child: TextField( - key: const Key('messageInputText'), - enabled: _inputEnabled, - maxLines: null, - onSubmitted: (_) => sendMessage(), - keyboardType: widget.keyboardType, - controller: textEditingController, - focusNode: _focusNode, - style: _messageInputTheme.inputTextStyle, - autofocus: widget.autofocus, - textAlignVertical: TextAlignVertical.center, - decoration: _getInputDecoration(context), - textCapitalization: TextCapitalization.sentences, - ), - ), - ), - ], - ), - ), - ), - ), - ), - ); - } - - InputDecoration _getInputDecoration(BuildContext context) { - final passedDecoration = _messageInputTheme.inputDecoration; - return InputDecoration( - isDense: true, - hintText: _getHint(context), - hintStyle: _messageInputTheme.inputTextStyle!.copyWith( - color: _streamChatTheme.colorTheme.textLowEmphasis, - ), - border: const OutlineInputBorder( - borderSide: BorderSide( - color: Colors.transparent, - ), - ), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide( - color: Colors.transparent, - ), - ), - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide( - color: Colors.transparent, - ), - ), - errorBorder: const OutlineInputBorder( - borderSide: BorderSide( - color: Colors.transparent, - ), - ), - disabledBorder: const OutlineInputBorder( - borderSide: BorderSide( - color: Colors.transparent, - ), - ), - contentPadding: const EdgeInsets.fromLTRB(16, 12, 13, 11), - prefixIcon: _commandEnabled - ? Row( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.all(8), - child: Container( - constraints: BoxConstraints.tight(const Size(64, 24)), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: _streamChatTheme.colorTheme.accentPrimary, - ), - alignment: Alignment.center, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - StreamSvgIcon.lightning( - color: Colors.white, - size: 16, - ), - Text( - _chosenCommand?.name.toUpperCase() ?? '', - style: - _streamChatTheme.textTheme.footnoteBold.copyWith( - color: Colors.white, - ), - ), - ], - ), - ), - ), - ], - ) - : (widget.actionsLocation == ActionsLocation.leftInside - ? Row( - mainAxisSize: MainAxisSize.min, - children: [_buildExpandActionsButton(context)], - ) - : null), - suffixIconConstraints: const BoxConstraints.tightFor(height: 40), - prefixIconConstraints: const BoxConstraints.tightFor(height: 40), - suffixIcon: Row( - mainAxisSize: MainAxisSize.min, - children: [ - if (_commandEnabled) - Padding( - padding: const EdgeInsets.only(right: 8), - child: IconButton( - icon: StreamSvgIcon.closeSmall(), - splashRadius: 24, - padding: EdgeInsets.zero, - constraints: const BoxConstraints.tightFor( - height: 24, - width: 24, - ), - onPressed: () { - setState(() => _commandEnabled = false); - }, - ), - ), - if (!_commandEnabled && - widget.actionsLocation == ActionsLocation.rightInside) - _buildExpandActionsButton(context), - if (widget.sendButtonLocation == SendButtonLocation.inside) - AnimatedSendButton( - cooldown: _timeOut, - messageIsPresent: _messageIsPresent, - attachmentsIsEmpty: _attachments.isEmpty, - messageInputTheme: _messageInputTheme, - onTap: sendMessage, - commandEnabled: _commandEnabled, - activeSendButton: widget.activeSendButton, - idleSendButton: widget.idleSendButton, - editMessage: widget.editMessage, - ), - ], - ), - ).merge(passedDecoration); - } - - late final _onChangedDebounced = debounce( - () { - var value = textEditingController.text; - if (!mounted) return; - value = value.trim(); - - final channel = StreamChannel.of(context).channel; - if (value.isNotEmpty) { - // ignore: no-empty-block - channel.keyStroke(widget.parentMessage?.id).catchError((e) {}); - } - - var actionsLength = widget.actions.length; - if (widget.showCommandsButton) actionsLength += 1; - if (!widget.disableAttachments) actionsLength += 1; - - setState(() => _actionsShrunk = value.isNotEmpty && actionsLength > 1); - - _checkCommands(value, context); - _checkMentions(value, context); - _checkEmoji(value, context); - }, - const Duration(milliseconds: 350), - leading: true, - ); - - String _getHint(BuildContext context) { - if (_commandEnabled && _chosenCommand!.name == 'giphy') { - return context.translations.searchGifLabel; - } - if (_attachments.isNotEmpty) { - return context.translations.addACommentOrSendLabel; - } - if (_timeOut != 0) { - return context.translations.slowModeOnLabel; - } - - return context.translations.writeAMessageLabel; - } - - void _checkEmoji(String s, BuildContext context) { - if (s.isNotEmpty && - textEditingController.selection.baseOffset > 0 && - textEditingController.text - .substring(0, textEditingController.selection.baseOffset) - .contains(':')) { - final textToSelection = textEditingController.text - .substring(0, textEditingController.value.selection.start); - final splits = textToSelection.split(':'); - final query = splits[splits.length - 2].toLowerCase(); - final emoji = Emoji.byName(query); - - if (textToSelection.endsWith(':') && emoji != null) { - _chooseEmoji(splits.sublist(0, splits.length - 1), emoji); - } - } - } - - void _checkMentions(String s, BuildContext context) { - if (s.isNotEmpty && - textEditingController.selection.baseOffset > 0 && - textEditingController.text - .substring(0, textEditingController.selection.baseOffset) - .split(' ') - .last - .contains('@')) { - if (!_showMentionsOverlay) { - setState(() => _showMentionsOverlay = true); - } - } else if (_showMentionsOverlay) { - setState(() => _showMentionsOverlay = false); - } - } - - void _checkCommands(String s, BuildContext context) { - if (s.startsWith('/')) { - final allCommands = StreamChannel.of(context).channel.config?.commands; - final command = - allCommands?.firstWhereOrNull((it) => it.name == s.substring(1)); - if (command != null) { - return _setCommand(command); - } else if (!_showCommandsOverlay) { - setState(() => _showCommandsOverlay = true); - } - } else if (_showCommandsOverlay) { - setState(() => _showCommandsOverlay = false); - } - } - - Widget _buildCommandsOverlayEntry() { - final text = textEditingController.text.trimLeft(); - - final renderObject = context.findRenderObject() as RenderBox?; - if (renderObject == null) { - return const Offstage(); - } - return StreamCommandsOverlay( - channel: StreamChannel.of(context).channel, - size: Size(renderObject.size.width - 16, 400), - text: text, - onCommandResult: _setCommand, - ); - } - - Widget _buildFilePickerSection() { - final _attachmentContainsFile = - _attachments.values.any((it) => it.type == 'file'); - - final attachmentLimitCrossed = - _attachments.length >= widget.attachmentLimit; - - Color _getIconColor(int index) { - final streamChatThemeData = _streamChatTheme; - switch (index) { - case 0: - return _attachments.isEmpty - ? streamChatThemeData.colorTheme.accentPrimary - : (!_attachmentContainsFile - ? streamChatThemeData.colorTheme.accentPrimary - : streamChatThemeData.colorTheme.textHighEmphasis - .withOpacity(0.2)); - case 1: - return _attachmentContainsFile - ? streamChatThemeData.colorTheme.accentPrimary - : (_attachments.isEmpty - ? streamChatThemeData.colorTheme.textHighEmphasis - .withOpacity(0.5) - : streamChatThemeData.colorTheme.textHighEmphasis - .withOpacity(0.2)); - case 2: - return attachmentLimitCrossed - ? streamChatThemeData.colorTheme.textHighEmphasis.withOpacity(0.2) - : _attachmentContainsFile && _attachments.isNotEmpty - ? streamChatThemeData.colorTheme.textHighEmphasis - .withOpacity(0.2) - : streamChatThemeData.colorTheme.textHighEmphasis - .withOpacity(0.5); - case 3: - return attachmentLimitCrossed - ? streamChatThemeData.colorTheme.textHighEmphasis.withOpacity(0.2) - : _attachmentContainsFile && _attachments.isNotEmpty - ? streamChatThemeData.colorTheme.textHighEmphasis - .withOpacity(0.2) - : streamChatThemeData.colorTheme.textHighEmphasis - .withOpacity(0.5); - default: - return Colors.black; - } - } - - return AnimatedContainer( - duration: _openFilePickerSection - ? const Duration(milliseconds: 300) - : Duration.zero, - curve: Curves.easeOut, - height: _openFilePickerSection ? _kMinMediaPickerSize : 0, - child: SingleChildScrollView( - child: SizedBox( - height: _kMinMediaPickerSize, - child: Material( - color: _streamChatTheme.colorTheme.inputBg, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - children: [ - IconButton( - icon: StreamSvgIcon.pictures( - color: _getIconColor(0), - ), - onPressed: - _attachmentContainsFile && _attachments.isNotEmpty - ? null - : () { - setState(() { - _filePickerIndex = 0; - }); - }, - ), - IconButton( - iconSize: 32, - icon: StreamSvgIcon.files( - color: _getIconColor(1), - ), - onPressed: - !_attachmentContainsFile && _attachments.isNotEmpty - ? null - : () { - pickFile(DefaultAttachmentTypes.file); - }, - ), - IconButton( - icon: StreamSvgIcon.camera( - color: _getIconColor(2), - ), - onPressed: attachmentLimitCrossed || - (_attachmentContainsFile && - _attachments.isNotEmpty) - ? null - : () { - pickFile( - DefaultAttachmentTypes.image, - camera: true, - ); - }, - ), - IconButton( - padding: EdgeInsets.zero, - icon: StreamSvgIcon.record( - color: _getIconColor(3), - ), - onPressed: attachmentLimitCrossed || - (_attachmentContainsFile && - _attachments.isNotEmpty) - ? null - : () { - pickFile( - DefaultAttachmentTypes.video, - camera: true, - ); - }, - ), - const Spacer(), - FutureBuilder( - future: PhotoManager.requestPermissionExtend(), - builder: (context, snapshot) { - if (snapshot.hasData && - snapshot.data == PermissionState.limited) { - return TextButton( - child: Text(context.translations.viewLibrary), - onPressed: () async { - await PhotoManager.presentLimited(); - _mediaListViewController.updateMedia( - newValue: true, - ); - }, - ); - } - - return const SizedBox.shrink(); - }, - ), - ], - ), - DecoratedBox( - decoration: BoxDecoration( - color: _streamChatTheme.colorTheme.barsBg, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), - ), - ), - child: Center( - child: Padding( - padding: const EdgeInsets.all(8), - child: Container( - width: 40, - height: 4, - decoration: BoxDecoration( - color: _streamChatTheme.colorTheme.inputBg, - borderRadius: BorderRadius.circular(4), - ), - ), - ), - ), - ), - if (_openFilePickerSection) - Expanded( - child: DecoratedBox( - decoration: BoxDecoration( - color: _streamChatTheme.colorTheme.barsBg, - borderRadius: BorderRadius.circular(8), - ), - child: PickerWidget( - mediaListViewController: _mediaListViewController, - filePickerIndex: _filePickerIndex, - streamChatTheme: _streamChatTheme, - containsFile: _attachmentContainsFile, - selectedMedias: _attachments.keys.toList(), - onAddMoreFilesClick: pickFile, - onMediaSelected: (media) { - if (_attachments.containsKey(media.id)) { - setState(() => _attachments.remove(media.id)); - } else { - _addAssetAttachment(media); - } - }, - ), - ), - ), - ], - ), - ), - ), - ), - ); - } - - Future _addAssetAttachment(AssetEntity medium) async { - final mediaFile = await medium.originFile.timeout( - const Duration(seconds: 5), - onTimeout: () => medium.originFile, - ); - - if (mediaFile == null) return; - - final file = AttachmentFile( - path: mediaFile.path, - size: await mediaFile.length(), - bytes: mediaFile.readAsBytesSync(), - ); - - if (file.size! > widget.maxAttachmentSize) { - return _showErrorAlert( - context.translations.fileTooLargeError( - widget.maxAttachmentSize / (1024 * 1024), - ), - ); - } - - setState(() { - final attachment = Attachment( - id: medium.id, - file: file, - type: medium.type == AssetType.image ? 'image' : 'video', - ); - _addAttachments([attachment]); - }); - } - - Widget _buildMentionsOverlayEntry() { - final channel = StreamChannel.of(context).channel; - if (textEditingController.value.selection.start < 0 || - channel.state == null) { - return const Offstage(); - } - - final splits = textEditingController.text - .substring(0, textEditingController.value.selection.start) - .split('@'); - final query = splits.last.toLowerCase(); - - // ignore: cast_nullable_to_non_nullable - final renderObject = context.findRenderObject() as RenderBox; - - var tileBuilder = widget.userMentionsTileBuilder; - if (tileBuilder == null && widget.mentionsTileBuilder != null) { - tileBuilder = (context, user) { - final member = Member( - user: user, - userId: user.id, - createdAt: user.createdAt, - updatedAt: user.updatedAt, - ); - return widget.mentionsTileBuilder!(context, member); - }; - } - - return LayoutBuilder( - builder: (context, snapshot) => StreamUserMentionsOverlay( - query: query, - mentionAllAppUsers: widget.mentionAllAppUsers, - client: StreamChat.of(context).client, - channel: channel, - size: Size( - renderObject.size.width - 16, - min(400, (snapshot.maxHeight - renderObject.size.height - 16).abs()), - ), - mentionsTileBuilder: tileBuilder, - onMentionUserTap: (user) { - _mentionedUsers.add(user); - splits[splits.length - 1] = user.name; - final rejoin = splits.join('@'); - - textEditingController.value = TextEditingValue( - text: rejoin + - textEditingController.text.substring( - textEditingController.selection.start, - ), - selection: TextSelection.collapsed( - offset: rejoin.length, - ), - ); - _onChangedDebounced.cancel(); - - setState(() => _showMentionsOverlay = false); - }, - ), - ); - } - - Widget _buildEmojiOverlay() { - if (textEditingController.value.selection.baseOffset < 0) { - return const Offstage(); - } - - final splits = textEditingController.text - .substring(0, textEditingController.value.selection.baseOffset) - .split(':'); - - final query = splits.last.toLowerCase(); - // ignore: cast_nullable_to_non_nullable - final renderObject = context.findRenderObject() as RenderBox; - - return StreamEmojiOverlay( - size: Size(renderObject.size.width - 16, 200), - query: query, - onEmojiResult: (emoji) { - _chooseEmoji(splits, emoji); - }, - ); - } - - void _chooseEmoji(List splits, Emoji emoji) { - final rejoin = splits.sublist(0, splits.length - 1).join(':') + emoji.char!; - - textEditingController.value = TextEditingValue( - text: rejoin + - textEditingController.text - .substring(textEditingController.selection.start), - selection: TextSelection.collapsed( - offset: rejoin.length, - ), - ); - } - - void _setCommand(Command c) { - textEditingController.clear(); - setState(() { - _chosenCommand = c; - _commandEnabled = true; - _showCommandsOverlay = false; - }); - } - - Widget _buildReplyToMessage() { - if (!_hasQuotedMessage) return const Offstage(); - final containsUrl = widget.quotedMessage!.attachments - .any((element) => element.ogScrapeUrl != null); - return StreamQuotedMessageWidget( - reverse: true, - showBorder: !containsUrl, - message: widget.quotedMessage!, - messageTheme: _streamChatTheme.otherMessageTheme, - padding: const EdgeInsets.fromLTRB(8, 8, 8, 0), - onQuotedMessageClear: widget.onQuotedMessageCleared, - ); - } - - Widget _buildCommandButton(BuildContext context) { - final s = textEditingController.text.trim(); - final defaultButton = CommandButton( - icon: StreamSvgIcon.lightning( - color: s.isNotEmpty - ? _streamChatTheme.colorTheme.disabled - : (_showCommandsOverlay - ? _messageInputTheme.actionButtonColor - : _messageInputTheme.actionButtonIdleColor), - ), - onPressed: () async { - if (_openFilePickerSection) { - setState(() => _openFilePickerSection = false); - await Future.delayed(const Duration(milliseconds: 300)); - } - - setState(() => _showCommandsOverlay = !_showCommandsOverlay); - }, - ); - - return widget.commandButtonBuilder?.call(context, defaultButton) ?? - defaultButton; - } - - /// Show the attachment modal, making the user choose where to - /// pick a media from - void showAttachmentModal() { - if (_focusNode.hasFocus) { - _focusNode.unfocus(); - } - - // TODO(Groovin): review this to make sure it is correct - if (!kIsWeb) { - setState(() => _openFilePickerSection = true); - } else { - showModalBottomSheet( - clipBehavior: Clip.hardEdge, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(32), - topRight: Radius.circular(32), - ), - ), - context: context, - isScrollControlled: true, - builder: (_) => AttachmentModalSheet( - onFileTap: () => pickFile(DefaultAttachmentTypes.file), - onPhotoTap: () => pickFile(DefaultAttachmentTypes.image), - onVideoTap: () => pickFile(DefaultAttachmentTypes.video), - ), - ); - } - } - - /// Add an attachment to the sending message - /// Use this to add custom type attachments - /// - /// Note: Only meant to be used from outside the state. - void addAttachment(Attachment attachment) { - setState(() => _addAttachments([attachment])); - } - - /// Adds an attachment to the [_attachments] map - void _addAttachments(Iterable attachments) { - final limit = widget.attachmentLimit; - final length = _attachments.length + attachments.length; - if (length > limit) { - final onAttachmentLimitExceed = widget.onAttachmentLimitExceed; - if (onAttachmentLimitExceed != null) { - return onAttachmentLimitExceed( - widget.attachmentLimit, - context.translations.attachmentLimitExceedError(limit), - ); - } - return _showErrorAlert( - context.translations.attachmentLimitExceedError(limit), - ); - } - for (final attachment in attachments) { - _attachments[attachment.id] = attachment; - } - } - - /// Pick a file from the device - /// If [camera] is true then the camera will open - /// - /// Used only for mobile devices. - Future pickFile( - DefaultAttachmentTypes fileType, { - bool camera = false, - }) async { - setState(() => _inputEnabled = false); - final attachmentHandler = MobileAttachmentHandler( - maxAttachmentSize: widget.maxAttachmentSize, - imagePicker: _imagePicker, - ); - - attachmentHandler - .upload(fileType: fileType, camera: camera) - .then((attachments) { - setState(() => _inputEnabled = true); - - if (attachments.isNotEmpty) { - setState(() => _addAttachments(attachments)); - } - }).catchError((error) { - if (error.runtimeType == FileSystemException) { - switch (error.message) { - case 'File size too large after compression and exceeds maximum ' - 'attachment size': - _showErrorAlert( - context.translations.fileTooLargeAfterCompressionError( - widget.maxAttachmentSize / (1024 * 1024), - ), - ); - break; - case 'File size exceeds maximum attachment size': - _showErrorAlert( - context.translations.fileTooLargeError( - widget.maxAttachmentSize / (1024 * 1024), - ), - ); - break; - default: - _showErrorAlert(context.translations.somethingWentWrongError); - break; - } - } - }); - } - - /// Sends the current message - Future sendMessage() async { - var text = textEditingController.text.trim(); - if (text.isEmpty && _attachments.isEmpty) { - return; - } - - var shouldKeepFocus = widget.shouldKeepFocusAfterMessage; - - shouldKeepFocus ??= !_commandEnabled; - - if (_commandEnabled) { - text = '${'/${_chosenCommand!.name} '}$text'; - } - - final attachments = [..._attachments.values]; - - textEditingController.clear(); - _attachments.clear(); - widget.onQuotedMessageCleared?.call(); - - setState(() => _commandEnabled = false); - - Message message; - if (widget.editMessage != null) { - message = widget.editMessage!.copyWith( - text: text, - attachments: attachments, - mentionedUsers: - _mentionedUsers.where((u) => text.contains('@${u.name}')).toList(), - ); - } else { - message = (widget.initialMessage ?? Message()).copyWith( - parentId: widget.parentMessage?.id, - text: text, - attachments: attachments, - mentionedUsers: - _mentionedUsers.where((u) => text.contains('@${u.name}')).toList(), - showInChannel: widget.parentMessage != null ? _sendAsDm : null, - ); - } - - if (widget.quotedMessage != null) { - message = message.copyWith( - quotedMessageId: widget.quotedMessage!.id, - ); - } - - if (widget.preMessageSending != null) { - message = await widget.preMessageSending!(message); - } - - final streamChannel = StreamChannel.of(context); - final channel = streamChannel.channel; - if (!channel.state!.isUpToDate) { - await streamChannel.reloadChannel(); - } - - _mentionedUsers.clear(); - - message = _replaceUserNameWithId(message); - - try { - Future sendingFuture; - if (widget.editMessage == null || - widget.editMessage!.status == MessageSendingStatus.failed || - widget.editMessage!.status == MessageSendingStatus.sending) { - sendingFuture = channel.sendMessage(message); - } else { - sendingFuture = channel.updateMessage(message); - } - - if (shouldKeepFocus) { - FocusScope.of(context).requestFocus(_focusNode); - } else { - FocusScope.of(context).unfocus(); - } - - final resp = await sendingFuture; - if (resp.message?.type == 'error') { - _parseExistingMessage(message); - } - _startSlowMode(); - widget.onMessageSent?.call(resp.message); - } catch (e, stk) { - if (widget.onError != null) { - widget.onError?.call(e, stk); - } else { - rethrow; - } - } - } - - void _showErrorAlert(String description) { - showModalBottomSheet( - backgroundColor: _streamChatTheme.colorTheme.barsBg, - context: context, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), - ), - ), - builder: (context) => ErrorAlertSheet( - errorDescription: context.translations.somethingWentWrongError, - ), - ); - } - - void _parseExistingMessage(Message message) { - final messageText = message.text; - if (messageText != null) textEditingController.text = messageText; - _addAttachments(message.attachments); - } - - @override - void dispose() { - textEditingController.dispose(); - _focusNode.removeListener(_focusNodeListener); - if (_isInternalFocusNode) _focusNode.dispose(); - _stopSlowMode(); - _onChangedDebounced.cancel(); - super.dispose(); - } - - bool _initialized = false; - - @override - void didChangeDependencies() { - _streamChatTheme = StreamChatTheme.of(context); - _messageInputTheme = StreamMessageInputTheme.of(context); - if (widget.editMessage == null) _startSlowMode(); - - if ((widget.editMessage != null || widget.initialMessage != null) && - !_initialized) { - FocusScope.of(context).requestFocus(_focusNode); - _initialized = true; - } - super.didChangeDependencies(); - } -} - -Message _replaceUserNameWithId(Message message) { - final mentionedUsers = message.mentionedUsers; - if (mentionedUsers.isEmpty) return message; - - var messageTextToSend = message.text; - if (messageTextToSend == null) return message; - - for (final user in mentionedUsers.toSet()) { - final userName = user.name; - messageTextToSend = messageTextToSend!.replaceAll( - '@$userName', - '@${user.id}', - ); - } - - return message.copyWith(text: messageTextToSend); -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/quoted_message_widget.dart b/packages/stream_chat_flutter/lib/src/message_input/quoted_message_widget.dart index 1b7124524..3ec6e8f72 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/quoted_message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/quoted_message_widget.dart @@ -7,10 +7,6 @@ import 'package:stream_chat_flutter/src/video/video_thumbnail_image.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:video_player/video_player.dart'; -/// {@macro streamQuotedMessage} -@Deprecated("Use 'StreamQuotedMessageWidget' instead") -typedef QuotedMessageWidget = StreamQuotedMessageWidget; - /// {@template streamQuotedMessage} /// Widget for the quoted message. /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/v4/message_input/simple_safe_area.dart b/packages/stream_chat_flutter/lib/src/message_input/simple_safe_area.dart similarity index 100% rename from packages/stream_chat_flutter/lib/src/v4/message_input/simple_safe_area.dart rename to packages/stream_chat_flutter/lib/src/message_input/simple_safe_area.dart diff --git a/packages/stream_chat_flutter/lib/src/v4/message_input/stream_attachment_picker.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_attachment_picker.dart similarity index 100% rename from packages/stream_chat_flutter/lib/src/v4/message_input/stream_attachment_picker.dart rename to packages/stream_chat_flutter/lib/src/message_input/stream_attachment_picker.dart diff --git a/packages/stream_chat_flutter/lib/src/v4/message_input/stream_message_input.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart similarity index 99% rename from packages/stream_chat_flutter/lib/src/v4/message_input/stream_message_input.dart rename to packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart index 862ab6a55..d9817b3c1 100644 --- a/packages/stream_chat_flutter/lib/src/v4/message_input/stream_message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart @@ -20,12 +20,12 @@ import 'package:stream_chat_flutter/src/message_input/dm_checkbox.dart'; import 'package:stream_chat_flutter/src/message_input/file_upload_error_handler.dart'; import 'package:stream_chat_flutter/src/message_input/quoted_message_widget.dart'; import 'package:stream_chat_flutter/src/message_input/quoting_message_top_area.dart'; +import 'package:stream_chat_flutter/src/message_input/simple_safe_area.dart'; +import 'package:stream_chat_flutter/src/message_input/tld.dart'; import 'package:stream_chat_flutter/src/message_input/user_mentions_overlay.dart'; import 'package:stream_chat_flutter/src/overlays/commands_overlay.dart'; import 'package:stream_chat_flutter/src/overlays/emoji_overlay.dart'; import 'package:stream_chat_flutter/src/utils/utils.dart'; -import 'package:stream_chat_flutter/src/v4/message_input/simple_safe_area.dart'; -import 'package:stream_chat_flutter/src/v4/message_input/tld.dart'; import 'package:stream_chat_flutter/src/video/video_thumbnail_image.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; @@ -113,8 +113,6 @@ class StreamMessageInput extends StatefulWidget { this.elevation, this.shadow, this.autoCorrect = true, - @Deprecated('Please use enableEmojiSuggestionsOverlay') - this.disableEmojiSuggestionsOverlay = false, this.enableEmojiSuggestionsOverlay = true, this.enableMentionsOverlay = true, this.onQuotedMessageCleared, @@ -238,11 +236,6 @@ class StreamMessageInput extends StatefulWidget { /// autoCorrect is enabled by default final bool autoCorrect; - /// Disable the default emoji suggestions - /// Enabled by default - @Deprecated('Please use enableEmojiSuggestionsOverlay') - final bool disableEmojiSuggestionsOverlay; - /// Disable the default emoji suggestions by passing `false` /// Enabled by default final bool enableEmojiSuggestionsOverlay; @@ -528,8 +521,7 @@ class StreamMessageInputState extends State visible: _showCommandsOverlay, widget: _buildCommandsOverlayEntry(), ), - if (widget.enableEmojiSuggestionsOverlay && - !widget.disableEmojiSuggestionsOverlay) + if (widget.enableEmojiSuggestionsOverlay) OverlayOptions( visible: _focusNode.hasFocus && _effectiveController.text.isNotEmpty && diff --git a/packages/stream_chat_flutter/lib/src/v4/message_input/stream_message_send_button.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_send_button.dart similarity index 100% rename from packages/stream_chat_flutter/lib/src/v4/message_input/stream_message_send_button.dart rename to packages/stream_chat_flutter/lib/src/message_input/stream_message_send_button.dart diff --git a/packages/stream_chat_flutter/lib/src/v4/message_input/stream_message_text_field.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_text_field.dart similarity index 100% rename from packages/stream_chat_flutter/lib/src/v4/message_input/stream_message_text_field.dart rename to packages/stream_chat_flutter/lib/src/message_input/stream_message_text_field.dart diff --git a/packages/stream_chat_flutter/lib/src/v4/message_input/tld.dart b/packages/stream_chat_flutter/lib/src/message_input/tld.dart similarity index 100% rename from packages/stream_chat_flutter/lib/src/v4/message_input/tld.dart rename to packages/stream_chat_flutter/lib/src/message_input/tld.dart diff --git a/packages/stream_chat_flutter/lib/src/message_input/user_mentions_overlay.dart b/packages/stream_chat_flutter/lib/src/message_input/user_mentions_overlay.dart index c818088aa..b84f699df 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/user_mentions_overlay.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/user_mentions_overlay.dart @@ -4,10 +4,6 @@ import 'package:stream_chat_flutter/src/user/user_mention_tile.dart'; import 'package:stream_chat_flutter/src/utils/utils.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; -/// {@macro streamUserMentionsOverlay} -@Deprecated("Use 'StreamUserMentionsOverlay' instead") -typedef UserMentionsOverlay = StreamUserMentionsOverlay; - /// {@template streamUserMentionsOverlay} /// Overlay for displaying users that can be mentioned. /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart b/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart index 48dc79b8c..f74dfea91 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart @@ -961,18 +961,27 @@ class _StreamMessageListViewState extends State { ), if (showUnreadCount) Positioned( - width: 20, - height: 20, - left: 10, + left: 0, + right: 0, top: -10, - child: CircleAvatar( - child: Padding( - padding: const EdgeInsets.all(3), - child: Text( - '$unreadCount', - style: const TextStyle( - fontSize: 11, - fontWeight: FontWeight.bold, + child: Center( + child: Material( + borderRadius: BorderRadius.circular(8), + color: + StreamChatTheme.of(context).colorTheme.accentPrimary, + child: Padding( + padding: const EdgeInsets.only( + left: 5, + right: 5, + top: 2, + bottom: 2, + ), + child: Text( + '${unreadCount > 99 ? '99+' : unreadCount}', + style: const TextStyle( + fontSize: 11, + color: Colors.white, + ), ), ), ), diff --git a/packages/stream_chat_flutter/lib/src/message_search/message_search_item.dart b/packages/stream_chat_flutter/lib/src/message_search/message_search_item.dart deleted file mode 100644 index d48a92c4a..000000000 --- a/packages/stream_chat_flutter/lib/src/message_search/message_search_item.dart +++ /dev/null @@ -1,194 +0,0 @@ -// ignore_for_file: deprecated_member_use_from_same_package - -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/utils/extensions.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@macro streamMessageSearchItem} -@Deprecated("Use 'StreamMessageSearchItem' instead") -class MessageSearchItem extends StatelessWidget { - /// Instantiate a new MessageSearchItem - const MessageSearchItem({ - super.key, - required this.getMessageResponse, - this.onTap, - this.showOnlineStatus = true, - }); - - /// [Message] displayed - final GetMessageResponse getMessageResponse; - - /// Function called when tapping this widget - final VoidCallback? onTap; - - /// If true the [StreamMessageSearchItem] will show the current online Status - final bool showOnlineStatus; - - @override - Widget build(BuildContext context) { - final message = getMessageResponse.message; - final channel = getMessageResponse.channel; - final channelName = channel?.extraData['name']; - final user = message.user!; - final channelPreviewTheme = StreamChannelPreviewTheme.of(context); - return ListTile( - onTap: onTap, - leading: StreamUserAvatar( - user: user, - showOnlineStatus: showOnlineStatus, - constraints: const BoxConstraints.tightFor( - height: 40, - width: 40, - ), - ), - title: Row( - children: [ - Text( - user.id == StreamChat.of(context).currentUser?.id - ? context.translations.youText - : user.name, - style: channelPreviewTheme.titleStyle, - ), - if (channelName != null) ...[ - Text( - ' ${context.translations.inText} ', - style: channelPreviewTheme.titleStyle?.copyWith( - fontWeight: FontWeight.normal, - ), - ), - Text( - channelName as String, - style: channelPreviewTheme.titleStyle, - ), - ], - ], - ), - subtitle: Row( - children: [ - Expanded(child: _SearchItemSubtitle(message: message)), - const SizedBox(width: 16), - _Date(message: message), - ], - ), - ); - } -} - -class _Date extends StatelessWidget { - const _Date({ - required this.message, - }); - - final Message message; - - @override - Widget build(BuildContext context) { - final createdAt = message.createdAt; - String stringDate; - final now = DateTime.now(); - - if (now.year != createdAt.year || - now.month != createdAt.month || - now.day != createdAt.day) { - stringDate = Jiffy(createdAt.toLocal()).yMd; - } else { - stringDate = Jiffy(createdAt.toLocal()).jm; - } - - return Text( - stringDate, - style: StreamChatTheme.of(context).channelPreviewTheme.lastMessageAtStyle, - ); - } -} - -class _SearchItemSubtitle extends StatelessWidget { - const _SearchItemSubtitle({ - required this.message, - }); - - final Message message; - - @override - Widget build(BuildContext context) { - var text = message.text; - if (message.isDeleted) { - text = context.translations.messageDeletedText; - } else if (message.attachments.isNotEmpty) { - final parts = [ - ...message.attachments.map((e) { - if (e.type == 'image') { - return '📷'; - } else if (e.type == 'video') { - return '🎬'; - } else if (e.type == 'giphy') { - return '[GIF]'; - } - return e == message.attachments.last - ? (e.title ?? 'File') - : '${e.title ?? 'File'} , '; - }), - message.text ?? '', - ]; - - text = parts.join(' '); - } - - final channelPreviewTheme = StreamChannelPreviewTheme.of(context); - return Text.rich( - _getDisplayText( - text!, - message.mentionedUsers, - message.attachments, - channelPreviewTheme.subtitleStyle?.copyWith( - fontStyle: (message.isSystem || message.isDeleted) - ? FontStyle.italic - : FontStyle.normal, - ), - channelPreviewTheme.subtitleStyle?.copyWith( - fontStyle: (message.isSystem || message.isDeleted) - ? FontStyle.italic - : FontStyle.normal, - fontWeight: FontWeight.bold, - ), - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ); - } - - TextSpan _getDisplayText( - String text, - List mentions, - List attachments, - TextStyle? normalTextStyle, - TextStyle? mentionsTextStyle, - ) { - final textList = text.split(' '); - final resList = []; - for (final e in textList) { - if (mentions.isNotEmpty && - mentions.any((element) => '@${element.name}' == e)) { - resList.add(TextSpan( - text: '$e ', - style: mentionsTextStyle, - )); - } else if (attachments.isNotEmpty && - attachments - .where((e) => e.title != null) - .any((element) => element.title == e)) { - resList.add(TextSpan( - text: '$e ', - style: normalTextStyle?.copyWith(fontStyle: FontStyle.italic), - )); - } else { - resList.add(TextSpan( - text: e == textList.last ? e : '$e ', - style: normalTextStyle, - )); - } - } - - return TextSpan(children: resList); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_search/message_search_list_view.dart b/packages/stream_chat_flutter/lib/src/message_search/message_search_list_view.dart deleted file mode 100644 index 7fe2c3f1f..000000000 --- a/packages/stream_chat_flutter/lib/src/message_search/message_search_list_view.dart +++ /dev/null @@ -1,272 +0,0 @@ -// ignore: lines_longer_than_80_chars -// ignore_for_file: deprecated_member_use_from_same_package, deprecated_member_use - -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/message_search/query_progress_indicator.dart'; -import 'package:stream_chat_flutter/src/utils/utils.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template messageSearchListView} -/// Shows the list of searched messages. -/// -/// ```dart -/// class MessageSearchPage extends StatelessWidget { -/// @override -/// Widget build(BuildContext context) { -/// return Scaffold( -/// body: MessageSearchListView( -/// messageQuery: _channelQuery, -/// filters: { -/// 'members': { -/// r'$in': [user.id] -/// } -/// }, -/// limit: 20, -/// ), -/// ); -/// } -/// } -/// ``` -/// -/// -/// A [MessageSearchBloc] ancestor widget is required in order to provide the -/// information about the messages. -/// -/// This widget uses a [ListView.separated] to render the list of messages. -/// -/// The UI is rendered based on the first ancestor of type [StreamChatTheme]. -/// Modify it to change the widget's appearance. -/// {@endtemplate} -@Deprecated('Use StreamMessageSearchListView instead') -class MessageSearchListView extends StatefulWidget { - /// {@macro messageSearchListView} - @Deprecated("Use 'StreamMessageSearchListView' instead") - const MessageSearchListView({ - super.key, - required this.filters, - this.messageQuery, - this.sortOptions, - this.limit = 30, - this.messageFilters, - this.separatorBuilder, - this.itemBuilder, - this.onItemTap, - this.showResultCount = true, - this.pullToRefresh = true, - this.showErrorTile = false, - this.emptyBuilder, - this.errorBuilder, - this.loadingBuilder, - this.childBuilder, - this.messageSearchListController, - }); - - /// Message String to search on - final String? messageQuery; - - /// The query filters to use. - /// You can query on any of the custom fields you've defined on the [Channel]. - /// You can also filter other built-in channel fields. - final Filter filters; - - /// The sorting used for the channels matching the filters. - /// Sorting is based on field and direction, multiple sorting options - /// can be provided. - /// You can sort based on last_updated, last_message_at, updated_at, - /// created_at or member_count. - /// Direction can be ascending or descending. - final List? sortOptions; - - /// The amount of messages requested per API call. - final int limit; - - /// The message query filters to use. - /// You can query on any of the custom fields you've defined on the [Channel]. - /// You can also filter other built-in channel fields. - final Filter? messageFilters; - - /// {@macro messageSearchItemBuilder} - final MessageSearchItemBuilder? itemBuilder; - - /// {@macro messageSearchItemTapCallback} - final MessageSearchItemTapCallback? onItemTap; - - /// Builder used to create a custom item separator - final IndexedWidgetBuilder? separatorBuilder; - - /// Set it to false to hide total results text - final bool showResultCount; - - /// Set it to false to disable the pull-to-refresh widget - final bool pullToRefresh; - - /// Show error tile on top - final bool showErrorTile; - - /// The builder that is used when the search messages are fetched - final Widget Function(List)? childBuilder; - - /// The builder used when the channel list is empty. - final WidgetBuilder? emptyBuilder; - - /// The builder that will be used in case of error - final ErrorBuilder? errorBuilder; - - /// The builder that will be used in case of loading - final WidgetBuilder? loadingBuilder; - - /// A [MessageSearchListController] allows reloading and pagination. - /// Use [MessageSearchListController.loadData] and - /// [MessageSearchListController.paginateData] respectively for reloading and - /// pagination. - final MessageSearchListController? messageSearchListController; - - @override - _MessageSearchListViewState createState() => _MessageSearchListViewState(); -} - -class _MessageSearchListViewState extends State { - late final _defaultController = MessageSearchListController(); - - MessageSearchListController get _messageSearchListController => - widget.messageSearchListController ?? _defaultController; - - @override - Widget build(BuildContext context) { - final messageSearchListCore = MessageSearchListCore( - filters: widget.filters, - sortOptions: widget.sortOptions, - messageQuery: widget.messageQuery, - limit: widget.limit, - messageFilters: widget.messageFilters, - messageSearchListController: _messageSearchListController, - emptyBuilder: widget.emptyBuilder ?? - (context) { - return LayoutBuilder( - builder: (context, viewportConstraints) => SingleChildScrollView( - physics: const AlwaysScrollableScrollPhysics(), - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: viewportConstraints.maxHeight, - ), - child: Center( - child: Text(context.translations.emptyMessagesText), - ), - ), - ), - ); - }, - errorBuilder: widget.errorBuilder ?? - (BuildContext context, dynamic error) { - if (error is Error) { - print(error.stackTrace); - } - return StreamInfoTile( - showMessage: widget.showErrorTile, - tileAnchor: Alignment.topCenter, - childAnchor: Alignment.topCenter, - message: context.translations.genericErrorText, - child: const SizedBox.shrink(), - ); - }, - loadingBuilder: widget.loadingBuilder ?? - (context) { - return LayoutBuilder( - builder: (context, viewportConstraints) => SingleChildScrollView( - physics: const AlwaysScrollableScrollPhysics(), - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: viewportConstraints.maxHeight, - ), - child: const Center( - child: CircularProgressIndicator(), - ), - ), - ), - ); - }, - childBuilder: widget.childBuilder ?? _buildListView, - ); - - final backgroundColor = - StreamMessageSearchListViewTheme.of(context).backgroundColor; - - if (backgroundColor != null) { - return ColoredBox( - color: backgroundColor, - child: messageSearchListCore, - ); - } - - return messageSearchListCore; - } - - Widget _buildListView(List data) { - final items = data; - - Widget child = ListView.separated( - physics: const AlwaysScrollableScrollPhysics(), - itemCount: items.isNotEmpty ? items.length + 1 : items.length, - separatorBuilder: (_, index) { - if (widget.separatorBuilder != null) { - return widget.separatorBuilder!(context, index); - } - return Container( - height: 1, - color: StreamChatTheme.of(context).colorTheme.borders, - ); - }, - itemBuilder: (context, index) { - if (index < items.length) { - if (widget.itemBuilder != null) { - return widget.itemBuilder!(context, items[index]); - } - return MessageSearchItem( - getMessageResponse: items[index], - onTap: () => widget.onItemTap!(items[index]), - ); - } - return const QueryProgressIndicator(); - }, - ); - if (widget.pullToRefresh) { - child = RefreshIndicator( - onRefresh: () => _messageSearchListController.loadData!(), - child: child, - ); - } - - child = LazyLoadScrollView( - onEndOfPage: () => _messageSearchListController.paginateData!(), - child: child, - ); - - if (widget.showResultCount) { - final chatThemeData = StreamChatTheme.of(context); - child = Column( - children: [ - Container( - width: double.maxFinite, - decoration: BoxDecoration( - gradient: chatThemeData.colorTheme.bgGradient, - ), - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 8, - ), - child: Text( - context.translations.resultCountText(items.length), - style: TextStyle( - color: chatThemeData.colorTheme.textLowEmphasis, - ), - ), - ), - ), - Expanded(child: child), - ], - ); - } - return child; - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_search/query_progress_indicator.dart b/packages/stream_chat_flutter/lib/src/message_search/query_progress_indicator.dart deleted file mode 100644 index f86e4e78a..000000000 --- a/packages/stream_chat_flutter/lib/src/message_search/query_progress_indicator.dart +++ /dev/null @@ -1,53 +0,0 @@ -// ignore: lines_longer_than_80_chars -// ignore_for_file: deprecated_member_use_from_same_package, deprecated_member_use - -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/utils/extensions.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template queryProgressIndicator} -/// Shows the progress of a search query performed via [MessageSearchListView]. -/// -/// Not recommended for use outside [MessageSearchListView]. -/// {@endtemplate} -class QueryProgressIndicator extends StatelessWidget { - /// {@macro queryProgressIndicator} - const QueryProgressIndicator({ - super.key, - }); - - @override - Widget build(BuildContext context) { - final messageSearchBloc = MessageSearchBloc.of(context); - - return StreamBuilder( - stream: messageSearchBloc.queryMessagesLoading, - initialData: false, - builder: (context, snapshot) { - if (snapshot.hasError) { - return ColoredBox( - color: StreamChatTheme.of(context) - .colorTheme - .accentError - .withOpacity(0.2), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 16), - child: Center( - child: Text(context.translations.loadingMessagesError), - ), - ), - ); - } - return Container( - height: 100, - padding: const EdgeInsets.all(32), - child: Center( - child: snapshot.data! - ? const CircularProgressIndicator() - : const SizedBox.shrink(), - ), - ); - }, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/deleted_message.dart b/packages/stream_chat_flutter/lib/src/message_widget/deleted_message.dart index ef1dbed39..3db36ef32 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget/deleted_message.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget/deleted_message.dart @@ -3,10 +3,6 @@ import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/theme/themes.dart'; import 'package:stream_chat_flutter/src/utils/extensions.dart'; -/// {@macro streamDeletedMessage} -@Deprecated("Use 'StreamDeletedMessage' instead") -typedef DeletedMessage = StreamDeletedMessage; - /// {@template streamDeletedMessage} /// Displays that a message was deleted at this position in the message list. /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/message_card.dart b/packages/stream_chat_flutter/lib/src/message_widget/message_card.dart index e5732ed2a..69aae89a8 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget/message_card.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget/message_card.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/utils/extensions.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; @@ -99,18 +101,27 @@ class MessageCard extends StatefulWidget { class _MessageCardState extends State { final GlobalKey attachmentsKey = GlobalKey(); - double? attachmentsWidth; + final GlobalKey linksKey = GlobalKey(); + double? widthLimit; @override void initState() { WidgetsBinding.instance.addPostFrameCallback((_) { - final renderBox = + final attachmentsRenderBox = attachmentsKey.currentContext?.findRenderObject() as RenderBox?; - final size = renderBox?.size; + final attachmentsWidth = attachmentsRenderBox?.size.width; + + final linkRenderBox = + linksKey.currentContext?.findRenderObject() as RenderBox?; + final linkWidth = linkRenderBox?.size.width; - if (mounted && attachmentsWidth != size?.width) { + if (mounted) { setState(() { - attachmentsWidth = size?.width; + if (attachmentsWidth != null && linkWidth != null) { + widthLimit = max(attachmentsWidth, linkWidth); + } else { + widthLimit = attachmentsWidth ?? linkWidth; + } }); } }); @@ -137,7 +148,7 @@ class _MessageCardState extends State { color: _getBackgroundColor(), child: ConstrainedBox( constraints: BoxConstraints( - maxWidth: attachmentsWidth ?? double.infinity, + maxWidth: widthLimit ?? double.infinity, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -192,7 +203,7 @@ class _MessageCardState extends State { hostName.capitalize(); return StreamUrlAttachment( - key: attachmentsKey, + key: linksKey, urlAttachment: urlAttachment, hostDisplayName: hostDisplayName, textPadding: widget.textPadding, diff --git a/packages/stream_chat_flutter/lib/src/message_widget/message_text.dart b/packages/stream_chat_flutter/lib/src/message_widget/message_text.dart index ff5d7b553..93dc09363 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget/message_text.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget/message_text.dart @@ -4,10 +4,6 @@ import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:stream_chat_flutter/src/utils/extensions.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamMessageText} -@Deprecated("Use 'StreamMessageText' instead") -typedef MessageText = StreamMessageText; - /// {@template streamMessageText} /// The text content of a message. /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart index cf4405c14..bfef72da2 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart @@ -90,14 +90,6 @@ class StreamMessageWidget extends StatefulWidget { vertical: 8, ), this.attachmentPadding = EdgeInsets.zero, - @Deprecated(''' - allRead is now deprecated and it will be removed in future releases. - The MessageWidget now listens for read events on its own. - ''') this.allRead = false, - @Deprecated(''' - readList is now deprecated and it will be removed in future releases. - The MessageWidget now listens for read events on its own. - ''') this.readList, this.onQuotedMessageTap, this.customActions = const [], this.onAttachmentTap, @@ -385,9 +377,6 @@ class StreamMessageWidget extends StatefulWidget { /// {@endtemplate} final bool showReactions; - /// - final bool allRead; - /// {@template showThreadReplyIndicator} /// If true the widget will show the thread reply indicator /// {@endtemplate} @@ -413,11 +402,6 @@ class StreamMessageWidget extends StatefulWidget { /// {@endtemplate} final bool showReactionPickerIndicator; - /// {@template readList} - /// List of users who have read the [message]. - /// {@endtemplate} - final List? readList; - /// {@template onShowMessage} /// Callback when show message is tapped /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/reactions/message_reactions_modal.dart b/packages/stream_chat_flutter/lib/src/message_widget/reactions/message_reactions_modal.dart index 29973904e..5c55e1475 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget/reactions/message_reactions_modal.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget/reactions/message_reactions_modal.dart @@ -5,10 +5,6 @@ import 'package:stream_chat_flutter/src/message_widget/reactions/reaction_bubble import 'package:stream_chat_flutter/src/utils/extensions.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamMessageReactionsModal} -@Deprecated("Use 'StreamMessageReactionsModal' instead") -typedef MessageReactionsModal = StreamMessageReactionsModal; - /// {@template streamMessageReactionsModal} /// Modal widget for displaying message reactions /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_bubble.dart b/packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_bubble.dart index 826ff699b..1d9ca78fc 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_bubble.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_bubble.dart @@ -4,10 +4,6 @@ import 'package:collection/collection.dart' show IterableExtension; import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamReactionBubble} -@Deprecated("Use 'StreamReactionBubble' instead") -typedef ReactionBubble = StreamReactionBubble; - /// {@template streamReactionBubble} /// Creates a reaction bubble that displays over messages. /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_picker.dart b/packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_picker.dart index 03abfa67d..3dbcd4ced 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_picker.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_picker.dart @@ -3,10 +3,6 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/utils/extensions.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamReactionPicker} -@Deprecated("Use 'StreamReactionPicker' instead") -typedef ReactionPicker = StreamReactionPicker; - /// {@template streamReactionPicker} /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/reaction_picker.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/reaction_picker_paint.png) diff --git a/packages/stream_chat_flutter/lib/src/misc/connection_status_builder.dart b/packages/stream_chat_flutter/lib/src/misc/connection_status_builder.dart index 4256dd5cb..161112ecb 100644 --- a/packages/stream_chat_flutter/lib/src/misc/connection_status_builder.dart +++ b/packages/stream_chat_flutter/lib/src/misc/connection_status_builder.dart @@ -1,10 +1,6 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamConnectionStatusBuilder} -@Deprecated("Use 'StreamConnectionStatusBuilder' instead") -typedef ConnectionStatusBuilder = StreamConnectionStatusBuilder; - /// {@template streamConnectionStatusBuilder} /// A widget that builds itself based on the latest snapshot of interaction with /// a [Stream] of type [ConnectionStatus]. diff --git a/packages/stream_chat_flutter/lib/src/misc/date_divider.dart b/packages/stream_chat_flutter/lib/src/misc/date_divider.dart index a2b8ff1ab..a1fbc0022 100644 --- a/packages/stream_chat_flutter/lib/src/misc/date_divider.dart +++ b/packages/stream_chat_flutter/lib/src/misc/date_divider.dart @@ -3,10 +3,6 @@ import 'package:jiffy/jiffy.dart'; import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/utils/extensions.dart'; -/// {@macro streamDateDivider} -@Deprecated("Use 'StreamDateDivider' instead") -typedef DateDivider = StreamDateDivider; - /// {@template streamDateDivider} /// Shows a date divider depending on the date difference /// FIXME: write better description! diff --git a/packages/stream_chat_flutter/lib/src/misc/info_tile.dart b/packages/stream_chat_flutter/lib/src/misc/info_tile.dart index f1a36c6ee..75bc6d4fc 100644 --- a/packages/stream_chat_flutter/lib/src/misc/info_tile.dart +++ b/packages/stream_chat_flutter/lib/src/misc/info_tile.dart @@ -2,10 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_portal/flutter_portal.dart'; import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -/// {@macro streamInfoTile} -@Deprecated("Use 'StreamInfoTile' instead") -typedef InfoTile = StreamInfoTile; - /// {@template streamInfoTile} /// Displays a message. Often used to display connection status. /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/misc/option_list_tile.dart b/packages/stream_chat_flutter/lib/src/misc/option_list_tile.dart index 2878b5a6f..ddb1db768 100644 --- a/packages/stream_chat_flutter/lib/src/misc/option_list_tile.dart +++ b/packages/stream_chat_flutter/lib/src/misc/option_list_tile.dart @@ -1,10 +1,6 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -/// {@macro streamOptionListTile} -@Deprecated("Use 'StreamOptionListTile' instead") -typedef OptionListTile = StreamOptionListTile; - /// {@template streamOptionListTile} /// List tile for [ChannelBottomSheet] /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/misc/reaction_icon.dart b/packages/stream_chat_flutter/lib/src/misc/reaction_icon.dart index 9245b80dc..890db8448 100644 --- a/packages/stream_chat_flutter/lib/src/misc/reaction_icon.dart +++ b/packages/stream_chat_flutter/lib/src/misc/reaction_icon.dart @@ -1,9 +1,5 @@ import 'package:flutter/material.dart'; -/// {@macro streamReactionIcon} -@Deprecated("Use 'StreamReactionIcon' instead") -typedef ReactionIcon = StreamReactionIcon; - /// {@template streamReactionIcon} /// Reaction icon data /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/misc/system_message.dart b/packages/stream_chat_flutter/lib/src/misc/system_message.dart index d33dbe3cc..b69d14133 100644 --- a/packages/stream_chat_flutter/lib/src/misc/system_message.dart +++ b/packages/stream_chat_flutter/lib/src/misc/system_message.dart @@ -1,10 +1,6 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamSystemMessage} -@Deprecated("Use 'StreamSystemMessage' instead") -typedef SystemMessage = StreamSystemMessage; - /// {@template streamSystemMessage} /// Shows a date divider depending on the date difference /// FIXME: This is NOT a good description! diff --git a/packages/stream_chat_flutter/lib/src/misc/thread_header.dart b/packages/stream_chat_flutter/lib/src/misc/thread_header.dart index 12b4a0a58..82caa9b2f 100644 --- a/packages/stream_chat_flutter/lib/src/misc/thread_header.dart +++ b/packages/stream_chat_flutter/lib/src/misc/thread_header.dart @@ -3,10 +3,6 @@ import 'package:flutter/services.dart'; import 'package:stream_chat_flutter/src/utils/extensions.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamThreadHeader} -@Deprecated("Use 'StreamThreadHeader' instead") -typedef ThreadHeader = StreamThreadHeader; - /// {@template streamThreadHeader} /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/thread_header.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/thread_header_paint.png) diff --git a/packages/stream_chat_flutter/lib/src/misc/visible_footnote.dart b/packages/stream_chat_flutter/lib/src/misc/visible_footnote.dart index 9fbe405cd..998a8cb53 100644 --- a/packages/stream_chat_flutter/lib/src/misc/visible_footnote.dart +++ b/packages/stream_chat_flutter/lib/src/misc/visible_footnote.dart @@ -2,10 +2,6 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/utils/extensions.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamVisibleFootnote} -@Deprecated("Use 'StreamVisibleFootnote' instead") -typedef VisibleFootnote = StreamVisibleFootnote; - /// {@template streamVisibleFootnote} /// Informs the user about a [StreamMessageWidget]'s visibility to the current /// user. diff --git a/packages/stream_chat_flutter/lib/src/overlays/commands_overlay.dart b/packages/stream_chat_flutter/lib/src/overlays/commands_overlay.dart index 08da6d276..976acab21 100644 --- a/packages/stream_chat_flutter/lib/src/overlays/commands_overlay.dart +++ b/packages/stream_chat_flutter/lib/src/overlays/commands_overlay.dart @@ -2,10 +2,6 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/utils/extensions.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro commands_overlay} -@Deprecated("Use 'StreamCommandsOverlay' instead") -typedef CommandsOverlay = StreamCommandsOverlay; - /// {@template streamCommandsOverlay} /// Overlay for displaying commands that can be used /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/overlays/emoji_overlay.dart b/packages/stream_chat_flutter/lib/src/overlays/emoji_overlay.dart index 162706763..bcf6feee8 100644 --- a/packages/stream_chat_flutter/lib/src/overlays/emoji_overlay.dart +++ b/packages/stream_chat_flutter/lib/src/overlays/emoji_overlay.dart @@ -4,10 +4,6 @@ import 'package:stream_chat_flutter/src/utils/extensions.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:substring_highlight/substring_highlight.dart'; -/// {@macro streamEmojiOverlay} -@Deprecated("Use 'StreamEmojiOverlay' instead") -typedef EmojiOverlay = StreamEmojiOverlay; - /// {@template streamEmojiOverlay} /// Overlay for displaying emoji that can be used /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/overlays/multi_overlay.dart b/packages/stream_chat_flutter/lib/src/overlays/multi_overlay.dart index 5f85bd4b1..0246f34de 100644 --- a/packages/stream_chat_flutter/lib/src/overlays/multi_overlay.dart +++ b/packages/stream_chat_flutter/lib/src/overlays/multi_overlay.dart @@ -2,10 +2,6 @@ import 'package:collection/collection.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_portal/flutter_portal.dart'; -/// {@macro streamMultiOverlay} -@Deprecated("Use 'StreamMultiOverlay' instead") -typedef MultiOverlay = StreamMultiOverlay; - /// {@template streamMultiOverlay} /// Renders a single overlay widget from a list of [overlayOptions]. /// diff --git a/packages/stream_chat_flutter/lib/src/v4/scroll_view/channel_scroll_view/stream_channel_grid_tile.dart b/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_grid_tile.dart similarity index 100% rename from packages/stream_chat_flutter/lib/src/v4/scroll_view/channel_scroll_view/stream_channel_grid_tile.dart rename to packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_grid_tile.dart diff --git a/packages/stream_chat_flutter/lib/src/v4/scroll_view/channel_scroll_view/stream_channel_grid_view.dart b/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_grid_view.dart similarity index 97% rename from packages/stream_chat_flutter/lib/src/v4/scroll_view/channel_scroll_view/stream_channel_grid_view.dart rename to packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_grid_view.dart index 1a96a6455..fe40a02ae 100644 --- a/packages/stream_chat_flutter/lib/src/v4/scroll_view/channel_scroll_view/stream_channel_grid_view.dart +++ b/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_grid_view.dart @@ -1,10 +1,10 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_error_widget.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_error.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_indicator.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_loading_widget.dart'; import 'package:stream_chat_flutter/src/utils/extensions.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_error_widget.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_load_more_error.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_load_more_indicator.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_loading_widget.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// Default grid delegate for [StreamChannelGridView]. diff --git a/packages/stream_chat_flutter/lib/src/v4/scroll_view/channel_scroll_view/stream_channel_list_tile.dart b/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_tile.dart similarity index 100% rename from packages/stream_chat_flutter/lib/src/v4/scroll_view/channel_scroll_view/stream_channel_list_tile.dart rename to packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_tile.dart diff --git a/packages/stream_chat_flutter/lib/src/v4/scroll_view/channel_scroll_view/stream_channel_list_view.dart b/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_view.dart similarity index 97% rename from packages/stream_chat_flutter/lib/src/v4/scroll_view/channel_scroll_view/stream_channel_list_view.dart rename to packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_view.dart index 9ac010c8a..c1eae970d 100644 --- a/packages/stream_chat_flutter/lib/src/v4/scroll_view/channel_scroll_view/stream_channel_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_view.dart @@ -1,10 +1,10 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_error_widget.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_error.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_indicator.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_loading_widget.dart'; import 'package:stream_chat_flutter/src/utils/extensions.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_error_widget.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_load_more_error.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_load_more_indicator.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_loading_widget.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// Default separator builder for [StreamChannelListView]. diff --git a/packages/stream_chat_flutter/lib/src/v4/scroll_view/message_search_scroll_view/stream_message_search_grid_view.dart b/packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_grid_view.dart similarity index 97% rename from packages/stream_chat_flutter/lib/src/v4/scroll_view/message_search_scroll_view/stream_message_search_grid_view.dart rename to packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_grid_view.dart index 7ab00d3cd..db147f392 100644 --- a/packages/stream_chat_flutter/lib/src/v4/scroll_view/message_search_scroll_view/stream_message_search_grid_view.dart +++ b/packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_grid_view.dart @@ -1,10 +1,10 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_error_widget.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_error.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_indicator.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_loading_widget.dart'; import 'package:stream_chat_flutter/src/utils/extensions.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_error_widget.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_load_more_error.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_load_more_indicator.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_loading_widget.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// Default grid delegate for [StreamMessageSearchGridView]. diff --git a/packages/stream_chat_flutter/lib/src/v4/scroll_view/message_search_scroll_view/stream_message_search_list_tile.dart b/packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_list_tile.dart similarity index 100% rename from packages/stream_chat_flutter/lib/src/v4/scroll_view/message_search_scroll_view/stream_message_search_list_tile.dart rename to packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_list_tile.dart diff --git a/packages/stream_chat_flutter/lib/src/v4/scroll_view/message_search_scroll_view/stream_message_search_list_view.dart b/packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_list_view.dart similarity index 97% rename from packages/stream_chat_flutter/lib/src/v4/scroll_view/message_search_scroll_view/stream_message_search_list_view.dart rename to packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_list_view.dart index b1823331b..3e2b0ba3e 100644 --- a/packages/stream_chat_flutter/lib/src/v4/scroll_view/message_search_scroll_view/stream_message_search_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_list_view.dart @@ -2,11 +2,11 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_error_widget.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_error.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_indicator.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_loading_widget.dart'; import 'package:stream_chat_flutter/src/utils/utils.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_error_widget.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_load_more_error.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_load_more_indicator.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_loading_widget.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// Default separator builder for [StreamMessageSearchListView]. diff --git a/packages/stream_chat_flutter/lib/src/v4/scroll_view/stream_scroll_view_empty_widget.dart b/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_empty_widget.dart similarity index 100% rename from packages/stream_chat_flutter/lib/src/v4/scroll_view/stream_scroll_view_empty_widget.dart rename to packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_empty_widget.dart diff --git a/packages/stream_chat_flutter/lib/src/v4/scroll_view/stream_scroll_view_error_widget.dart b/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_error_widget.dart similarity index 100% rename from packages/stream_chat_flutter/lib/src/v4/scroll_view/stream_scroll_view_error_widget.dart rename to packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_error_widget.dart diff --git a/packages/stream_chat_flutter/lib/src/v4/scroll_view/stream_scroll_view_indexed_widget_builder.dart b/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_indexed_widget_builder.dart similarity index 100% rename from packages/stream_chat_flutter/lib/src/v4/scroll_view/stream_scroll_view_indexed_widget_builder.dart rename to packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_indexed_widget_builder.dart diff --git a/packages/stream_chat_flutter/lib/src/v4/scroll_view/stream_scroll_view_load_more_error.dart b/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_load_more_error.dart similarity index 100% rename from packages/stream_chat_flutter/lib/src/v4/scroll_view/stream_scroll_view_load_more_error.dart rename to packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_load_more_error.dart diff --git a/packages/stream_chat_flutter/lib/src/v4/scroll_view/stream_scroll_view_load_more_indicator.dart b/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_load_more_indicator.dart similarity index 100% rename from packages/stream_chat_flutter/lib/src/v4/scroll_view/stream_scroll_view_load_more_indicator.dart rename to packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_load_more_indicator.dart diff --git a/packages/stream_chat_flutter/lib/src/v4/scroll_view/stream_scroll_view_loading_widget.dart b/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_loading_widget.dart similarity index 100% rename from packages/stream_chat_flutter/lib/src/v4/scroll_view/stream_scroll_view_loading_widget.dart rename to packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_loading_widget.dart diff --git a/packages/stream_chat_flutter/lib/src/v4/scroll_view/user_scroll_view/stream_user_grid_tile.dart b/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_grid_tile.dart similarity index 100% rename from packages/stream_chat_flutter/lib/src/v4/scroll_view/user_scroll_view/stream_user_grid_tile.dart rename to packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_grid_tile.dart diff --git a/packages/stream_chat_flutter/lib/src/v4/scroll_view/user_scroll_view/stream_user_grid_view.dart b/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_grid_view.dart similarity index 97% rename from packages/stream_chat_flutter/lib/src/v4/scroll_view/user_scroll_view/stream_user_grid_view.dart rename to packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_grid_view.dart index 7d02ba5f3..647061ddb 100644 --- a/packages/stream_chat_flutter/lib/src/v4/scroll_view/user_scroll_view/stream_user_grid_view.dart +++ b/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_grid_view.dart @@ -1,10 +1,10 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_error_widget.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_error.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_indicator.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_loading_widget.dart'; import 'package:stream_chat_flutter/src/utils/extensions.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_error_widget.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_load_more_error.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_load_more_indicator.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_loading_widget.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// Default grid delegate for [StreamUserGridView]. diff --git a/packages/stream_chat_flutter/lib/src/v4/scroll_view/user_scroll_view/stream_user_list_tile.dart b/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_list_tile.dart similarity index 100% rename from packages/stream_chat_flutter/lib/src/v4/scroll_view/user_scroll_view/stream_user_list_tile.dart rename to packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_list_tile.dart diff --git a/packages/stream_chat_flutter/lib/src/v4/scroll_view/user_scroll_view/stream_user_list_view.dart b/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_list_view.dart similarity index 97% rename from packages/stream_chat_flutter/lib/src/v4/scroll_view/user_scroll_view/stream_user_list_view.dart rename to packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_list_view.dart index 144ca411a..a0add720d 100644 --- a/packages/stream_chat_flutter/lib/src/v4/scroll_view/user_scroll_view/stream_user_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_list_view.dart @@ -2,11 +2,11 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_error_widget.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_error.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_indicator.dart'; +import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_loading_widget.dart'; import 'package:stream_chat_flutter/src/utils/utils.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_error_widget.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_load_more_error.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_load_more_indicator.dart'; -import 'package:stream_chat_flutter/src/v4/scroll_view/stream_scroll_view_loading_widget.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// Default separator builder for [StreamUserListView]. diff --git a/packages/stream_chat_flutter/lib/src/theme/avatar_theme.dart b/packages/stream_chat_flutter/lib/src/theme/avatar_theme.dart index f14cc7ee1..dbc36da93 100644 --- a/packages/stream_chat_flutter/lib/src/theme/avatar_theme.dart +++ b/packages/stream_chat_flutter/lib/src/theme/avatar_theme.dart @@ -1,10 +1,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -/// {@macro avatarThemeData} -@Deprecated("Use 'StreamAvatarThemeData' instead") -typedef AvatarThemeData = StreamAvatarThemeData; - /// {@template avatarThemeData} /// A style that overrides the default appearance of various avatar widgets. /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/theme/channel_header_theme.dart b/packages/stream_chat_flutter/lib/src/theme/channel_header_theme.dart index cbb5e2191..2df24f685 100644 --- a/packages/stream_chat_flutter/lib/src/theme/channel_header_theme.dart +++ b/packages/stream_chat_flutter/lib/src/theme/channel_header_theme.dart @@ -4,10 +4,6 @@ import 'package:stream_chat_flutter/src/theme/avatar_theme.dart'; import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/theme/themes.dart'; -/// {@macro channelHeaderTheme} -@Deprecated("Use 'StreamChannelHeaderTheme' instead") -typedef ChannelHeaderTheme = StreamChannelHeaderTheme; - /// {@template channel_header_theme} /// Overrides the default style of [ChannelHeader] descendants. /// @@ -54,10 +50,6 @@ class StreamChannelHeaderTheme extends InheritedTheme { data != oldWidget.data; } -/// {@macro channel_header_theme_data} -@Deprecated("Use 'StreamChannelHeaderThemeData' instead") -typedef ChannelHeaderThemeData = StreamChannelHeaderThemeData; - /// {@template channel_header_theme_data} /// A style that overrides the default appearance of [ChannelHeader]s when used /// with [StreamChannelHeaderTheme] or with the overall [StreamChatTheme]'s diff --git a/packages/stream_chat_flutter/lib/src/theme/channel_list_header_theme.dart b/packages/stream_chat_flutter/lib/src/theme/channel_list_header_theme.dart index 90c83fb00..7452065df 100644 --- a/packages/stream_chat_flutter/lib/src/theme/channel_list_header_theme.dart +++ b/packages/stream_chat_flutter/lib/src/theme/channel_list_header_theme.dart @@ -3,10 +3,6 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/theme/avatar_theme.dart'; import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -/// {@macro channelListHeaderTheme} -@Deprecated("Use 'StreamChannelListHeaderTheme' instead") -typedef ChannelListHeaderTheme = StreamChannelListHeaderTheme; - /// {@template channelListHeaderTheme} /// Overrides the default style of [ChannelListHeader] descendants. /// @@ -54,10 +50,6 @@ class StreamChannelListHeaderTheme extends InheritedTheme { data != oldWidget.data; } -/// {@macro channel_list_header_theme_data} -@Deprecated("Use ''StreamChannelListHeaderThemeData' instead") -typedef ChannelListHeaderThemeData = StreamChannelListHeaderThemeData; - /// {@template channel_list_header_theme_data} /// Theme dedicated to the [ChannelListHeader] /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/theme/channel_list_view_theme.dart b/packages/stream_chat_flutter/lib/src/theme/channel_list_view_theme.dart index 5e9d8c496..a6f79adc0 100644 --- a/packages/stream_chat_flutter/lib/src/theme/channel_list_view_theme.dart +++ b/packages/stream_chat_flutter/lib/src/theme/channel_list_view_theme.dart @@ -2,10 +2,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -/// {@macro channelListViewTheme} -@Deprecated("Use 'StreamChannelListViewTheme' instead") -typedef ChannelListViewTheme = StreamChannelListViewTheme; - /// {@template channelListViewTheme} /// Overrides the default style of [ChannelListView] descendants. /// @@ -52,10 +48,6 @@ class StreamChannelListViewTheme extends InheritedTheme { data != oldWidget.data; } -/// {@macro channelListViewThemeData} -@Deprecated("Use 'StreamChannelListViewThemeData' instead") -typedef ChannelListViewThemeData = StreamChannelListViewThemeData; - /// {@template channelListViewThemeData} /// A style that overrides the default appearance of [ChannelListView]s when /// used with [StreamChannelListViewTheme] diff --git a/packages/stream_chat_flutter/lib/src/theme/channel_preview_theme.dart b/packages/stream_chat_flutter/lib/src/theme/channel_preview_theme.dart index b58a194d9..ff7e5deac 100644 --- a/packages/stream_chat_flutter/lib/src/theme/channel_preview_theme.dart +++ b/packages/stream_chat_flutter/lib/src/theme/channel_preview_theme.dart @@ -3,10 +3,6 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/theme/avatar_theme.dart'; import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -/// {@macro channelPreviewTheme} -@Deprecated("Use 'StreamChannelPreviewTheme' instead") -typedef ChannelPreviewTheme = StreamChannelPreviewTheme; - /// {@template channelPreviewTheme} /// Overrides the default style of [ChannelPreview] descendants. /// @@ -53,10 +49,6 @@ class StreamChannelPreviewTheme extends InheritedTheme { data != oldWidget.data; } -/// {@macro channelPreviewThemeData} -@Deprecated("Use 'StreamChannelPreviewThemeData' instead") -typedef ChannelPreviewThemeData = StreamChannelPreviewThemeData; - /// {@template channelPreviewThemeData} /// A style that overrides the default appearance of [ChannelPreview]s when used /// with [StreamChannelPreviewTheme] or with the overall [StreamChatTheme]'s diff --git a/packages/stream_chat_flutter/lib/src/theme/color_theme.dart b/packages/stream_chat_flutter/lib/src/theme/color_theme.dart index 2e5d8f1e1..0babd24c4 100644 --- a/packages/stream_chat_flutter/lib/src/theme/color_theme.dart +++ b/packages/stream_chat_flutter/lib/src/theme/color_theme.dart @@ -1,9 +1,5 @@ import 'package:flutter/material.dart'; -/// {@macro color_theme} -@Deprecated("Use 'StreamColorTheme' instead") -typedef ColorTheme = StreamColorTheme; - /// {@template color_theme} /// Theme that holds colors /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/theme/gallery_footer_theme.dart b/packages/stream_chat_flutter/lib/src/theme/gallery_footer_theme.dart index 388529b0c..84e1688a6 100644 --- a/packages/stream_chat_flutter/lib/src/theme/gallery_footer_theme.dart +++ b/packages/stream_chat_flutter/lib/src/theme/gallery_footer_theme.dart @@ -2,10 +2,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -/// {@macro galleryFooterTheme} -@Deprecated("Use 'StreamGalleryFooterTheme' instead") -typedef GalleryFooterTheme = StreamGalleryFooterTheme; - /// {@template galleryFooterTheme} /// Overrides the default style of [GalleryFooter] descendants. /// @@ -52,10 +48,6 @@ class StreamGalleryFooterTheme extends InheritedTheme { data != oldWidget.data; } -/// {@macro galleryFooterThemeData} -@Deprecated("Use 'StreamGalleryFooterThemeData' instead") -typedef GalleryFooterThemeData = StreamGalleryFooterThemeData; - /// {@template galleryFooterThemeData} /// A style that overrides the default appearance of [GalleryFooter]s when used /// with [StreamGalleryFooterTheme] or with the overall [StreamChatTheme]'s diff --git a/packages/stream_chat_flutter/lib/src/theme/gallery_header_theme.dart b/packages/stream_chat_flutter/lib/src/theme/gallery_header_theme.dart index 75d02a6fb..90977d4c9 100644 --- a/packages/stream_chat_flutter/lib/src/theme/gallery_header_theme.dart +++ b/packages/stream_chat_flutter/lib/src/theme/gallery_header_theme.dart @@ -2,10 +2,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -/// {@macro galleryHeaderTheme} -@Deprecated("Use 'StreamGalleryHeaderTheme' instead") -typedef GalleryHeaderTheme = StreamGalleryHeaderTheme; - /// {@template galleryHeaderTheme} /// Overrides the default style of [GalleryHeader] descendants. /// @@ -52,10 +48,6 @@ class StreamGalleryHeaderTheme extends InheritedTheme { data != oldWidget.data; } -/// {@macro galleryHeaderThemeData} -@Deprecated("Use 'StreamGalleryHeaderThemeData' instead") -typedef GalleryHeaderThemeData = StreamGalleryHeaderThemeData; - /// {@template galleryHeaderThemeData} /// A style that overrides the default appearance of [GalleryHeader]s when used /// with [StreamGalleryHeaderTheme] or with the overall [StreamChatTheme]'s diff --git a/packages/stream_chat_flutter/lib/src/theme/message_input_theme.dart b/packages/stream_chat_flutter/lib/src/theme/message_input_theme.dart index 844b3f712..fe77d9c26 100644 --- a/packages/stream_chat_flutter/lib/src/theme/message_input_theme.dart +++ b/packages/stream_chat_flutter/lib/src/theme/message_input_theme.dart @@ -5,10 +5,6 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/utils/extensions.dart'; -/// {@macro messageInputTheme} -@Deprecated("Use 'StreamMessageInputTheme' instead") -typedef MessageInputTheme = StreamMessageInputTheme; - /// {@template messageInputTheme} /// Overrides the default style of [MessageInput] descendants. /// @@ -55,10 +51,6 @@ class StreamMessageInputTheme extends InheritedTheme { data != oldWidget.data; } -/// {@macro messageInputThemeData} -@Deprecated("Use 'StreamMessageInputThemeData' instead") -typedef MessageInputThemeData = StreamMessageInputThemeData; - /// {@template messageInputThemeData} /// A style that overrides the default appearance of [MessageInput] widgets /// when used with [StreamMessageInputTheme] diff --git a/packages/stream_chat_flutter/lib/src/theme/message_list_view_theme.dart b/packages/stream_chat_flutter/lib/src/theme/message_list_view_theme.dart index f62395eb0..d21b3725e 100644 --- a/packages/stream_chat_flutter/lib/src/theme/message_list_view_theme.dart +++ b/packages/stream_chat_flutter/lib/src/theme/message_list_view_theme.dart @@ -2,10 +2,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -/// {@macro messageListViewTheme} -@Deprecated("Use 'StreamMessageListViewTheme' instead") -typedef MessageListViewTheme = StreamMessageListViewTheme; - /// {@template messageListViewTheme} /// Overrides the default style of [MessageListView] descendants. /// @@ -52,10 +48,6 @@ class StreamMessageListViewTheme extends InheritedTheme { data != oldWidget.data; } -/// {@macro messageListViewThemeData} -@Deprecated("Use 'StreamMessageListViewThemeData' instead") -typedef MessageListViewThemeData = StreamMessageListViewThemeData; - /// {@template messageListViewThemeData} /// A style that overrides the default appearance of [MessageListView]s when /// used with [StreamMessageListViewTheme] or with diff --git a/packages/stream_chat_flutter/lib/src/theme/message_search_list_view_theme.dart b/packages/stream_chat_flutter/lib/src/theme/message_search_list_view_theme.dart index 85eec9937..91f21a55a 100644 --- a/packages/stream_chat_flutter/lib/src/theme/message_search_list_view_theme.dart +++ b/packages/stream_chat_flutter/lib/src/theme/message_search_list_view_theme.dart @@ -2,10 +2,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -/// {@macro messageSearchListViewTheme} -@Deprecated("Use 'StreamMessageSearchListViewTheme' instead") -typedef MessageSearchListViewTheme = StreamMessageSearchListViewTheme; - /// {@template messageSearchListViewTheme} /// Overrides the default style of [MessageSearchListView] descendants. /// @@ -52,10 +48,6 @@ class StreamMessageSearchListViewTheme extends InheritedTheme { data != oldWidget.data; } -/// {@macro messageSearchListViewThemeData} -@Deprecated("Use 'StreamMessageSearchListViewThemeData' instead") -typedef MessageSearchListViewThemeData = StreamMessageSearchListViewThemeData; - /// {@macro messageSearchListViewThemeData} /// A style that overrides the default appearance of [MessageSearchListView]s /// when used with [MessageSearchListView] or with the overall diff --git a/packages/stream_chat_flutter/lib/src/theme/message_theme.dart b/packages/stream_chat_flutter/lib/src/theme/message_theme.dart index 9790da3b9..f83971f23 100644 --- a/packages/stream_chat_flutter/lib/src/theme/message_theme.dart +++ b/packages/stream_chat_flutter/lib/src/theme/message_theme.dart @@ -2,10 +2,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/theme/avatar_theme.dart'; -/// {@macro message_theme_data} -@Deprecated("Use 'StreamMessageThemeData' instead") -typedef MessageThemeData = StreamMessageThemeData; - /// {@template message_theme_data} /// Class for getting message theme /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/theme/text_theme.dart b/packages/stream_chat_flutter/lib/src/theme/text_theme.dart index 06ddf2d95..e44dcc20f 100644 --- a/packages/stream_chat_flutter/lib/src/theme/text_theme.dart +++ b/packages/stream_chat_flutter/lib/src/theme/text_theme.dart @@ -1,9 +1,5 @@ import 'package:flutter/material.dart'; -/// {@macro text_theme} -@Deprecated("Use 'StreamTextTheme' instead") -typedef TextTheme = StreamTextTheme; - /// {@template text_theme} /// Class for holding text theme /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/src/theme/user_list_view_theme.dart b/packages/stream_chat_flutter/lib/src/theme/user_list_view_theme.dart index 32ee835b5..e1e4ba6bf 100644 --- a/packages/stream_chat_flutter/lib/src/theme/user_list_view_theme.dart +++ b/packages/stream_chat_flutter/lib/src/theme/user_list_view_theme.dart @@ -2,10 +2,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -/// {@macro userListViewTheme} -@Deprecated("Use 'StreamUserListViewTheme' instead") -typedef UserListViewTheme = StreamUserListViewTheme; - /// {@template userListViewTheme} /// Overrides the default style of [UserListView] descendants. /// @@ -52,10 +48,6 @@ class StreamUserListViewTheme extends InheritedTheme { data != oldWidget.data; } -/// {@macro userListViewThemeData} -@Deprecated("Use 'StreamUserListViewThemeData' instead") -typedef UserListViewThemeData = StreamUserListViewThemeData; - /// {@template userListViewThemeData} /// A style that overrides the default appearance of [UserListView]s when /// used with [StreamUserListViewTheme] or with the overall [StreamChatTheme]'s diff --git a/packages/stream_chat_flutter/lib/src/user/user_item.dart b/packages/stream_chat_flutter/lib/src/user/user_item.dart index e6740dd44..348440a7b 100644 --- a/packages/stream_chat_flutter/lib/src/user/user_item.dart +++ b/packages/stream_chat_flutter/lib/src/user/user_item.dart @@ -2,10 +2,6 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/utils/extensions.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamUserItem} -@Deprecated("Use 'StreamUserItem' instead") -typedef UserItem = StreamUserItem; - /// {@template streamUserItem} /// Shows a preview of the current [User]. /// diff --git a/packages/stream_chat_flutter/lib/src/user/user_list_view.dart b/packages/stream_chat_flutter/lib/src/user/user_list_view.dart deleted file mode 100644 index 76d700911..000000000 --- a/packages/stream_chat_flutter/lib/src/user/user_list_view.dart +++ /dev/null @@ -1,441 +0,0 @@ -// ignore: lines_longer_than_80_chars -// ignore_for_file: deprecated_member_use_from_same_package, deprecated_member_use - -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/utils/utils.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template userListView} -/// Shows the list of current users. -/// -/// ```dart -/// class UsersListPage extends StatelessWidget { -/// @override -/// Widget build(BuildContext context) { -/// return Scaffold( -/// body: UsersListView( -/// filter: { -/// 'members': { -/// '\$in': [StreamChat.of(context).user.id], -/// } -/// }, -/// sort: [SortOption('last_message_at')], -/// pagination: PaginationParams( -/// limit: 20, -/// ), -/// channelWidget: ChannelPage(), -/// ), -/// ); -/// } -/// } -/// ``` -/// -/// -/// A [UsersBloc] ancestor widget is required in order to provide the -/// information about the users. -/// -/// This widget uses [ListView.separated] or [GridView.builder] to render the -/// list, or grid, of channels. -/// -/// The widget components render the ui based on the first ancestor of -/// type [StreamChatTheme]. Modify it to change the widget's appearance. -/// {@endtemplate} -@Deprecated('User StreamUserListView instead') -class UserListView extends StatefulWidget { - /// {@macro userListView} - @Deprecated("Use 'StreamUserListView' instead") - UserListView({ - super.key, - this.filter = const Filter.empty(), - this.sort, - this.presence, - @Deprecated( - "'pagination' is deprecated and shouldn't be used. " - "This property is no longer used, Please use 'limit' instead", - ) - this.pagination, - int? limit, - this.onUserTap, - this.onUserLongPress, - this.userWidget, - this.userItemBuilder, - this.separatorBuilder, - this.onImageTap, - this.selectedUsers, - this.pullToRefresh = true, - this.groupAlphabetically = false, - this.crossAxisCount = 1, - this.errorBuilder, - this.emptyBuilder, - this.loadingBuilder, - this.listBuilder, - this.userListController, - }) : assert( - crossAxisCount == 1 || !groupAlphabetically, - 'Cannot group alphabetically when crossAxisCount > 1', - ), - limit = limit ?? pagination?.limit ?? 30; - - /// The query filters to use. - /// You can query on any of the custom fields you've defined on the [Channel]. - /// You can also filter other built-in channel fields. - final Filter filter; - - /// The sorting used for the channels matching the filters. - /// Sorting is based on field and direction, multiple sorting options can - /// be provided. - /// You can sort based on last_updated, last_message_at, updated_at, created_ - /// at or member_count. - /// Direction can be ascending or descending. - final List? sort; - - /// If true you’ll receive user presence updates via the websocket events - final bool? presence; - - /// Pagination parameters - /// limit: the number of users to return (max is 30) - /// offset: the offset (max is 1000) - /// message_limit: how many messages should be included to each channel - @Deprecated( - "'pagination' is deprecated and shouldn't be used. " - "This property is no longer used, Please use 'limit' instead", - ) - final PaginationParams? pagination; - - /// The amount of users requested per API call. - final int limit; - - /// {@macro userTapCallback} - /// - /// By default it calls [Navigator.push] building a [MaterialPageRoute] - /// with the widget [userWidget] as child. - final UserTapCallback? onUserTap; - - /// Function called when long pressing on a channel - final Function(User)? onUserLongPress; - - /// Widget used when opening a channel - final Widget? userWidget; - - /// {@macro userItemBuilder} - final UserItemBuilder? userItemBuilder; - - /// Builder used to create a custom item separator - final Function(BuildContext, int)? separatorBuilder; - - /// The function called when the image is tapped - final Function(User)? onImageTap; - - /// Set it to false to disable the pull-to-refresh widget - final bool pullToRefresh; - - /// Sets a blue trailing checkMark in [ListUserItem] for all the - /// [selectedUsers] - final Set? selectedUsers; - - /// Set it to true to group users by their first character - /// - /// defaults to false - final bool groupAlphabetically; - - /// The number of children in the cross axis. - final int crossAxisCount; - - /// The builder that will be used in case of error - final ErrorBuilder? errorBuilder; - - /// The builder that will be used to build the list - final Widget Function(BuildContext context, List users)? - listBuilder; - - /// The builder that will be used for loading - final WidgetBuilder? loadingBuilder; - - /// The builder used when the channel list is empty. - final WidgetBuilder? emptyBuilder; - - /// A [UserListController] allows reloading and pagination. - /// Use [UserListController.loadData] and [UserListController.paginateData] - /// respectively for reloading and pagination. - final UserListController? userListController; - - @override - _UserListViewState createState() => _UserListViewState(); -} - -class _UserListViewState extends State - with WidgetsBindingObserver { - bool get _isListView => widget.crossAxisCount == 1; - - late final _defaultController = UserListController(); - - UserListController get _userListController => - widget.userListController ?? _defaultController; - - @override - Widget build(BuildContext context) { - final userListCore = UserListCore( - errorBuilder: widget.errorBuilder ?? - (BuildContext context, Object err) => _buildError(err), - emptyBuilder: widget.emptyBuilder ?? (context) => _buildEmpty(), - loadingBuilder: widget.loadingBuilder ?? - (context) => LayoutBuilder( - builder: (context, viewportConstraints) => - SingleChildScrollView( - physics: const AlwaysScrollableScrollPhysics(), - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: viewportConstraints.maxHeight, - ), - child: const Center( - child: CircularProgressIndicator(), - ), - ), - ), - ), - listBuilder: - widget.listBuilder ?? (context, list) => _buildListView(list), - limit: widget.limit, - sort: widget.sort, - filter: widget.filter, - presence: widget.presence, - groupAlphabetically: widget.groupAlphabetically, - userListController: _userListController, - ); - - final backgroundColor = StreamUserListViewTheme.of(context).backgroundColor; - - Widget child; - - if (backgroundColor != null) { - child = ColoredBox( - color: backgroundColor, - child: userListCore, - ); - } else { - child = userListCore; - } - - if (!widget.pullToRefresh) { - return child; - } else { - return RefreshIndicator( - onRefresh: () => _userListController.loadData!(), - child: child, - ); - } - } - - bool get isListAlreadySorted => - widget.sort?.any((e) => e.field == 'name' && e.direction == 1) ?? false; - - Widget _buildError(Object error) => Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text.rich( - TextSpan( - children: [ - const WidgetSpan( - child: Padding( - padding: EdgeInsets.only( - right: 2, - ), - child: Icon(Icons.error_outline), - ), - ), - TextSpan(text: context.translations.loadingUsersError), - ], - ), - style: Theme.of(context).textTheme.headline6, - ), - TextButton( - onPressed: () => _userListController.loadData!(), - child: Text(context.translations.retryLabel), - ), - ], - ), - ); - - Widget _buildEmpty() => LayoutBuilder( - builder: (context, viewportConstraints) => SingleChildScrollView( - physics: const AlwaysScrollableScrollPhysics(), - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: viewportConstraints.maxHeight, - ), - child: Center( - child: Text(context.translations.noUsersLabel), - ), - ), - ), - ); - - Widget _buildListView( - List items, - ) { - final child = _isListView - ? ListView.separated( - physics: const AlwaysScrollableScrollPhysics(), - itemCount: items.isNotEmpty ? items.length + 1 : items.length, - separatorBuilder: (_, index) { - if (widget.separatorBuilder != null) { - return widget.separatorBuilder!(context, index); - } - return _separatorBuilder(context, index); - }, - itemBuilder: (context, index) => - _listItemBuilder(context, index, items), - ) - : GridView.builder( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: widget.crossAxisCount, - ), - itemCount: items.isNotEmpty ? items.length + 1 : items.length, - physics: const AlwaysScrollableScrollPhysics(), - itemBuilder: (context, index) => - _gridItemBuilder(context, index, items), - ); - - return LazyLoadScrollView( - onEndOfPage: () => _userListController.paginateData!(), - child: child, - ); - } - - Widget _listItemBuilder(BuildContext context, int i, List items) { - final usersProvider = UsersBloc.of(context); - if (i < items.length) { - final item = items[i]; - return item.when( - headerItem: (header) { - final chatThemeData = StreamChatTheme.of(context); - return ColoredBox( - key: ValueKey('HEADER-$header'), - color: chatThemeData.colorTheme.textHighEmphasis.withOpacity(0.05), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6), - child: Text( - header, - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 14.5, - color: chatThemeData.colorTheme.textLowEmphasis, - ), - ), - ), - ); - }, - userItem: (user) { - final selected = widget.selectedUsers?.contains(user) ?? false; - return Container( - key: ValueKey('USER-${user.id}'), - child: widget.userItemBuilder != null - ? widget.userItemBuilder!(context, user, selected) - : StreamUserItem( - user: user, - onTap: (user) => widget.onUserTap!(user, widget.userWidget), - onLongPress: widget.onUserLongPress, - onImageTap: widget.onImageTap, - selected: selected, - ), - ); - }, - ); - } else { - return _buildQueryProgressIndicator(context, usersProvider); - } - } - - Widget _gridItemBuilder(BuildContext context, int i, List items) { - final usersProvider = UsersBloc.of(context); - if (i < items.length) { - final item = items[i]; - return item.when( - headerItem: (_) => const Offstage(), - userItem: (user) { - final selected = widget.selectedUsers?.contains(user) ?? false; - return Container( - key: ValueKey('USER-${user.id}'), - child: widget.userItemBuilder != null - ? widget.userItemBuilder!(context, user, selected) - : Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - StreamUserAvatar( - user: user, - borderRadius: BorderRadius.circular(32), - selected: selected, - constraints: const BoxConstraints.tightFor( - height: 64, - width: 64, - ), - onlineIndicatorConstraints: - const BoxConstraints.tightFor( - height: 12, - width: 12, - ), - onTap: (user) => - widget.onUserTap!(user, widget.userWidget), - onLongPress: widget.onUserLongPress, - ), - const SizedBox(height: 4), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Text( - user.name, - textAlign: TextAlign.center, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 12, - ), - ), - ), - ], - ), - ); - }, - ); - } else { - return _buildQueryProgressIndicator(context, usersProvider); - } - } - - Widget _buildQueryProgressIndicator(context, UsersBlocState usersProvider) => - StreamBuilder( - stream: usersProvider.queryUsersLoading, - initialData: false, - builder: (context, snapshot) { - if (snapshot.hasError) { - return ColoredBox( - color: StreamChatTheme.of(context) - .colorTheme - .accentError - .withOpacity(0.2), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 16), - child: Center( - child: Text(context.translations.loadingUsersError), - ), - ), - ); - } - return Container( - height: 100, - padding: const EdgeInsets.all(32), - child: Center( - child: snapshot.data! - ? const CircularProgressIndicator() - : Container(), - ), - ); - }, - ); - - Widget _separatorBuilder(context, i) => Container( - height: 1, - color: StreamChatTheme.of(context).colorTheme.borders, - ); -} diff --git a/packages/stream_chat_flutter/lib/src/user/user_mention_tile.dart b/packages/stream_chat_flutter/lib/src/user/user_mention_tile.dart index 15506e9de..8e9d948d4 100644 --- a/packages/stream_chat_flutter/lib/src/user/user_mention_tile.dart +++ b/packages/stream_chat_flutter/lib/src/user/user_mention_tile.dart @@ -1,10 +1,6 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@macro streamUserMentionTile} -@Deprecated("Use 'StreamUserMentionTile' instead") -typedef UserMentionTile = StreamUserMentionTile; - /// {@template streamUserMentionTile} /// Shows user tiles for mentions. /// diff --git a/packages/stream_chat_flutter/lib/src/v4/message_input/countdown_button.dart b/packages/stream_chat_flutter/lib/src/v4/message_input/countdown_button.dart deleted file mode 100644 index 754167900..000000000 --- a/packages/stream_chat_flutter/lib/src/v4/message_input/countdown_button.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Button for showing visual component of slow mode. -class StreamCountdownButton extends StatelessWidget { - /// Constructor for creating [StreamCountdownButton]. - const StreamCountdownButton({ - super.key, - required this.count, - }); - - /// Count of time remaining to show to the user. - final int count; - - @override - Widget build(BuildContext context) => Padding( - padding: const EdgeInsets.all(8), - child: DecoratedBox( - decoration: BoxDecoration( - color: StreamChatTheme.of(context).colorTheme.disabled, - shape: BoxShape.circle, - ), - child: SizedBox( - height: 24, - width: 24, - child: Center( - child: Text('$count'), - ), - ), - ), - ); -} diff --git a/packages/stream_chat_flutter/lib/src/video/video_service.dart b/packages/stream_chat_flutter/lib/src/video/video_service.dart index 5fd026c0d..6538bcc4c 100644 --- a/packages/stream_chat_flutter/lib/src/video/video_service.dart +++ b/packages/stream_chat_flutter/lib/src/video/video_service.dart @@ -74,11 +74,6 @@ class _IVideoService { } } -/// Get instance of [_IVideoService] -@Deprecated("Use 'StreamVideoService' instead") -// ignore: non_constant_identifier_names -_IVideoService get VideoService => _IVideoService.instance; - /// Get instance of [_IVideoService] // ignore: non_constant_identifier_names _IVideoService get StreamVideoService => _IVideoService.instance; diff --git a/packages/stream_chat_flutter/lib/src/video/video_thumbnail_image.dart b/packages/stream_chat_flutter/lib/src/video/video_thumbnail_image.dart index a3ffd7129..f6a6ed560 100644 --- a/packages/stream_chat_flutter/lib/src/video/video_thumbnail_image.dart +++ b/packages/stream_chat_flutter/lib/src/video/video_thumbnail_image.dart @@ -6,10 +6,6 @@ import 'package:stream_chat_flutter/src/video/video_service.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:video_thumbnail/video_thumbnail.dart'; -/// {@macro streamVideoThumbnailImage} -@Deprecated("Use 'StreamVideoThumbnailImage' instead") -typedef VideoThumbnailImage = StreamVideoThumbnailImage; - /// {@template streamVideoThumbnailImage} /// Displays a video thumbnail for video attachments in a message. /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart index 92b06dcde..9ac412136 100644 --- a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart +++ b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart @@ -20,13 +20,15 @@ export 'src/avatars/user_avatar.dart'; export 'src/bottom_sheets/attachment_modal_sheet.dart'; export 'src/bottom_sheets/edit_message_sheet.dart'; export 'src/bottom_sheets/error_alert_sheet.dart'; -export 'src/channel/channel_avatar.dart'; +export 'src/bottom_sheets/stream_channel_info_bottom_sheet.dart'; export 'src/channel/channel_header.dart'; export 'src/channel/channel_info.dart'; export 'src/channel/channel_list_header.dart'; -export 'src/channel/channel_list_view.dart'; export 'src/channel/channel_name.dart'; export 'src/channel/channel_preview.dart'; +export 'src/channel/stream_channel_avatar.dart'; +export 'src/channel/stream_channel_name.dart'; +export 'src/channel/stream_message_preview_text.dart'; export 'src/fullscreen_media/fsm_enums.dart'; export 'src/fullscreen_media/full_screen_media.dart'; export 'src/fullscreen_media/full_screen_media_builder.dart'; @@ -41,13 +43,14 @@ export 'src/localization/stream_chat_localizations.dart'; export 'src/localization/translations.dart' show DefaultTranslations; export 'src/media_list_view/media_list_view.dart'; export 'src/message_actions_modal/message_action.dart'; -// ignore: deprecated_member_use_from_same_package +export 'src/message_input/countdown_button.dart'; export 'src/message_input/enums.dart'; -export 'src/message_input/message_input.dart'; +export 'src/message_input/stream_attachment_picker.dart'; +export 'src/message_input/stream_message_input.dart'; +export 'src/message_input/stream_message_send_button.dart'; +export 'src/message_input/stream_message_text_field.dart'; export 'src/message_list_view/message_details.dart'; export 'src/message_list_view/message_list_view.dart'; -export 'src/message_search/message_search_item.dart'; -export 'src/message_search/message_search_list_view.dart'; export 'src/message_widget/deleted_message.dart'; export 'src/message_widget/message_text.dart'; export 'src/message_widget/message_widget.dart'; @@ -65,38 +68,27 @@ export 'src/misc/system_message.dart'; export 'src/misc/thread_header.dart'; export 'src/misc/visible_footnote.dart'; export 'src/overlays/multi_overlay.dart'; +export 'src/scroll_view/channel_scroll_view/stream_channel_grid_tile.dart'; +export 'src/scroll_view/channel_scroll_view/stream_channel_grid_view.dart'; +export 'src/scroll_view/channel_scroll_view/stream_channel_list_tile.dart'; +export 'src/scroll_view/channel_scroll_view/stream_channel_list_view.dart'; +export 'src/scroll_view/message_search_scroll_view/stream_message_search_grid_view.dart'; +export 'src/scroll_view/message_search_scroll_view/stream_message_search_list_tile.dart'; +export 'src/scroll_view/message_search_scroll_view/stream_message_search_list_view.dart'; +export 'src/scroll_view/stream_scroll_view_empty_widget.dart'; +export 'src/scroll_view/stream_scroll_view_indexed_widget_builder.dart'; +export 'src/scroll_view/user_scroll_view/stream_user_grid_tile.dart'; +export 'src/scroll_view/user_scroll_view/stream_user_grid_tile.dart'; +export 'src/scroll_view/user_scroll_view/stream_user_grid_view.dart'; +export 'src/scroll_view/user_scroll_view/stream_user_grid_view.dart'; +export 'src/scroll_view/user_scroll_view/stream_user_list_tile.dart'; +export 'src/scroll_view/user_scroll_view/stream_user_list_view.dart'; export 'src/stream_chat.dart'; export 'src/theme/stream_chat_theme.dart'; export 'src/theme/themes.dart'; export 'src/user/user_item.dart'; -export 'src/user/user_list_view.dart'; export 'src/user/user_mention_tile.dart'; export 'src/utils/device_segmentation.dart'; export 'src/utils/extensions.dart' show IconButtonX; export 'src/utils/helpers.dart'; export 'src/utils/typedefs.dart'; -// v4 -export 'src/v4/message_input/countdown_button.dart'; -export 'src/v4/message_input/stream_attachment_picker.dart'; -export 'src/v4/message_input/stream_message_input.dart'; -export 'src/v4/message_input/stream_message_send_button.dart'; -export 'src/v4/message_input/stream_message_text_field.dart'; -export 'src/v4/scroll_view/channel_scroll_view/stream_channel_grid_tile.dart'; -export 'src/v4/scroll_view/channel_scroll_view/stream_channel_grid_view.dart'; -export 'src/v4/scroll_view/channel_scroll_view/stream_channel_list_tile.dart'; -export 'src/v4/scroll_view/channel_scroll_view/stream_channel_list_view.dart'; -export 'src/v4/scroll_view/message_search_scroll_view/stream_message_search_grid_view.dart'; -export 'src/v4/scroll_view/message_search_scroll_view/stream_message_search_list_tile.dart'; -export 'src/v4/scroll_view/message_search_scroll_view/stream_message_search_list_view.dart'; -export 'src/v4/scroll_view/stream_scroll_view_empty_widget.dart'; -export 'src/v4/scroll_view/stream_scroll_view_indexed_widget_builder.dart'; -export 'src/v4/scroll_view/user_scroll_view/stream_user_grid_tile.dart'; -export 'src/v4/scroll_view/user_scroll_view/stream_user_grid_tile.dart'; -export 'src/v4/scroll_view/user_scroll_view/stream_user_grid_view.dart'; -export 'src/v4/scroll_view/user_scroll_view/stream_user_grid_view.dart'; -export 'src/v4/scroll_view/user_scroll_view/stream_user_list_tile.dart'; -export 'src/v4/scroll_view/user_scroll_view/stream_user_list_view.dart'; -export 'src/v4/stream_channel_avatar.dart'; -export 'src/v4/stream_channel_info_bottom_sheet.dart'; -export 'src/v4/stream_channel_name.dart'; -export 'src/v4/stream_message_preview_text.dart'; diff --git a/packages/stream_chat_flutter/test/src/message_input/countdown_button_test.dart b/packages/stream_chat_flutter/test/src/message_input/countdown_button_test.dart index b2c35f8d2..a81092603 100644 --- a/packages/stream_chat_flutter/test/src/message_input/countdown_button_test.dart +++ b/packages/stream_chat_flutter/test/src/message_input/countdown_button_test.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:golden_toolkit/golden_toolkit.dart'; -import 'package:stream_chat_flutter/src/message_input/countdown_button.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; void main() { @@ -12,7 +11,7 @@ void main() { data: StreamChatThemeData.light(), child: const Scaffold( body: Center( - child: CountdownButton(count: 5), + child: StreamCountdownButton(count: 5), ), ), ), @@ -29,7 +28,7 @@ void main() { data: StreamChatThemeData.light(), child: const Scaffold( body: Center( - child: CountdownButton(count: 5), + child: StreamCountdownButton(count: 5), ), ), ), diff --git a/packages/stream_chat_flutter/test/test_utils/golden.dart b/packages/stream_chat_flutter/test/test_utils/golden.dart index 436f10f2c..bb6508b94 100644 --- a/packages/stream_chat_flutter/test/test_utils/golden.dart +++ b/packages/stream_chat_flutter/test/test_utils/golden.dart @@ -15,8 +15,7 @@ Future customExpectGoldenMatches( bool? autoHeight, Finder? finder, CustomPump? customPump, - @Deprecated(''' -This method level parameter will be removed in an upcoming release. This can be configured globally. If you have concerns, please file an issue with your use case.''') bool? skip, + bool? skip, }) { final goldenPath = path.join('test/src/goldens'); print('goldenPath: $goldenPath'); diff --git a/packages/stream_chat_flutter_core/example/windows/flutter/generated_plugins.cmake b/packages/stream_chat_flutter_core/example/windows/flutter/generated_plugins.cmake index ba4a21757..8cf5d4267 100644 --- a/packages/stream_chat_flutter_core/example/windows/flutter/generated_plugins.cmake +++ b/packages/stream_chat_flutter_core/example/windows/flutter/generated_plugins.cmake @@ -6,6 +6,9 @@ list(APPEND FLUTTER_PLUGIN_LIST connectivity_plus_windows ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -14,3 +17,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart deleted file mode 100644 index 556b44cc6..000000000 --- a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart +++ /dev/null @@ -1,261 +0,0 @@ -// ignore_for_file: deprecated_member_use_from_same_package - -import 'dart:async'; -import 'dart:convert'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_flutter_core/src/better_stream_builder.dart'; -import 'package:stream_chat_flutter_core/src/channels_bloc.dart'; -import 'package:stream_chat_flutter_core/src/stream_chat_core.dart'; -import 'package:stream_chat_flutter_core/src/typedef.dart'; - -/// [ChannelListCore] is a simplified class that allows fetching a list of -/// channels while exposing UI builders. -/// A [ChannelListController] is used to reload and paginate data. -/// -/// -/// ```dart -/// class ChannelListPage extends StatelessWidget { -/// @override -/// Widget build(BuildContext context) { -/// return Scaffold( -/// body: ChannelListCore( -/// filter: Filter.in_( -/// 'members', -/// [StreamChat.of(context).user!.id], -/// ), -/// sort: [SortOption('last_message_at')], -/// pagination: PaginationParams( -/// limit: 20, -/// ), -/// errorBuilder: (context, err) { -/// return Center( -/// child: Text('An error has occured'), -/// ); -/// }, -/// emptyBuilder: (context) { -/// return Center( -/// child: Text('Nothing here...'), -/// ); -/// }, -/// loadingBuilder: (context) { -/// return Center( -/// child: CircularProgressIndicator(), -/// ); -/// }, -/// listBuilder: (context, list) { -/// return ChannelPage(list); -/// } -/// ), -/// ); -/// } -/// } -/// ``` -/// -/// Make sure to have a [StreamChatCore] ancestor in order to provide the -/// information about the channels. -@Deprecated(''' -ChannelListCore is deprecated and will be removed in the next -major version. Use StreamChannelListController instead to create your custom list. -More details here https://getstream.io/chat/docs/sdk/flutter/stream_chat_flutter_core/stream_channel_list_controller -''') -class ChannelListCore extends StatefulWidget { - /// Instantiate a new ChannelListView - const ChannelListCore({ - super.key, - required this.errorBuilder, - required this.emptyBuilder, - required this.loadingBuilder, - required this.listBuilder, - this.filter, - this.state = true, - this.watch = true, - this.presence = false, - this.memberLimit, - this.messageLimit, - this.sort, - this.channelListController, - this.limit = 25, - }); - - /// A [ChannelListController] allows reloading and pagination. - /// Use [ChannelListController.loadData] and - /// [ChannelListController.paginateData] respectively for reloading and - /// pagination. - final ChannelListController? channelListController; - - /// The builder that will be used in case of error - final ErrorBuilder errorBuilder; - - /// The builder that will be used in case of loading - final WidgetBuilder loadingBuilder; - - /// The builder which is used when list of channels loads - final Function(BuildContext, List) listBuilder; - - /// The builder used when the channel list is empty. - final WidgetBuilder emptyBuilder; - - /// The query filters to use. - /// You can query on any of the custom fields you've defined on the [Channel]. - /// You can also filter other built-in channel fields. - final Filter? filter; - - /// The sorting used for the channels matching the filters. - /// Sorting is based on field and direction, multiple sorting options can be - /// provided. - /// You can sort based on last_updated, last_message_at, updated_at, created - /// _at or member_count. Direction can be ascending or descending. - final List>? sort; - - /// If true returns the Channel state - final bool state; - - /// If true listen to changes to this Channel in real time. - final bool watch; - - /// If true you’ll receive user presence updates via the websocket events - final bool presence; - - /// Number of members to fetch in each channel - final int? memberLimit; - - /// Number of messages to fetch in each channel - final int? messageLimit; - - /// The amount of channels requested per API call. - final int limit; - - @override - ChannelListCoreState createState() => ChannelListCoreState(); -} - -/// The current state of the [ChannelListCore]. -class ChannelListCoreState extends State { - late ChannelsBlocState _channelsBloc; - StreamChatCoreState? _streamChatCoreState; - - @override - Widget build(BuildContext context) => _buildListView(_channelsBloc); - - BetterStreamBuilder> _buildListView( - ChannelsBlocState channelsBlocState, - ) => - BetterStreamBuilder>( - stream: channelsBlocState.channelsStream, - errorBuilder: widget.errorBuilder, - noDataBuilder: widget.loadingBuilder, - builder: (context, channels) { - if (channels.isEmpty) { - return widget.emptyBuilder(context); - } - return widget.listBuilder(context, channels); - }, - ); - - /// Fetches initial channels and updates the widget - Future loadData() => _channelsBloc.queryChannels( - filter: widget.filter, - sortOptions: widget.sort, - state: widget.state, - watch: widget.watch, - presence: widget.presence, - memberLimit: widget.memberLimit, - messageLimit: widget.messageLimit, - paginationParams: PaginationParams(limit: widget.limit, offset: 0), - ); - - /// Fetches more channels with updated pagination and updates the widget - Future paginateData() => _channelsBloc.queryChannels( - filter: widget.filter, - sortOptions: widget.sort, - state: widget.state, - watch: widget.watch, - presence: widget.presence, - memberLimit: widget.memberLimit, - messageLimit: widget.messageLimit, - paginationParams: PaginationParams( - limit: widget.limit, - offset: _channelsBloc.channels?.length ?? 0, - ), - ); - - StreamSubscription? _subscription; - - @override - void initState() { - super.initState(); - _setupController(); - } - - @override - void didChangeDependencies() { - _channelsBloc = ChannelsBloc.of(context); - final newStreamChatCoreState = StreamChatCore.of(context); - - if (newStreamChatCoreState != _streamChatCoreState) { - _streamChatCoreState = newStreamChatCoreState; - loadData(); - final client = _streamChatCoreState!.client; - _subscription?.cancel(); - _subscription = client - .on( - EventType.connectionRecovered, - EventType.notificationAddedToChannel, - EventType.notificationMessageNew, - EventType.channelVisible, - ) - .listen((event) => loadData()); - } - - super.didChangeDependencies(); - } - - @override - void didUpdateWidget(ChannelListCore oldWidget) { - super.didUpdateWidget(oldWidget); - - if (jsonEncode(widget.filter) != jsonEncode(oldWidget.filter) || - jsonEncode(widget.sort) != jsonEncode(oldWidget.sort) || - widget.state != oldWidget.state || - widget.watch != oldWidget.watch || - widget.presence != oldWidget.presence || - widget.messageLimit != oldWidget.messageLimit || - widget.memberLimit != oldWidget.memberLimit || - widget.limit != oldWidget.limit) { - loadData(); - } - - if (widget.channelListController != oldWidget.channelListController) { - _setupController(); - } - } - - void _setupController() { - if (widget.channelListController != null) { - widget.channelListController!.loadData = loadData; - widget.channelListController!.paginateData = paginateData; - } - } - - @override - void dispose() { - _subscription?.cancel(); - super.dispose(); - } -} - -/// Controller used for loading more data and controlling pagination in -/// [ChannelListCore]. -class ChannelListController { - /// This function calls Stream's servers to load a list of channels. - /// If there is existing data, calling this function causes a reload. - AsyncCallback? loadData; - - /// This function is used to load another page of data. Note, [loadData] - /// should be used to populate the initial page of data. Calling - /// [paginateData] performs a query to load subsequent pages. - AsyncCallback? paginateData; -} diff --git a/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart b/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart deleted file mode 100644 index 561ae6f07..000000000 --- a/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart +++ /dev/null @@ -1,253 +0,0 @@ -// ignore_for_file: deprecated_member_use_from_same_package - -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:rxdart/rxdart.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_flutter_core/src/channel_list_core.dart'; -import 'package:stream_chat_flutter_core/src/stream_chat_core.dart'; -import 'package:stream_chat_flutter_core/src/stream_controller_extension.dart'; - -/// Widget dedicated to the management of a channel list with pagination -/// [ChannelsBloc] is used together with [ChannelListCore] to manage a list of -/// [Channel]s with pagination, re-ordering, querying and other operations -/// associated with [Channel]s. -/// -/// [ChannelsBloc] can be access at anytime by using the static [of] method -/// using Flutter's [BuildContext]. -/// -/// API docs: https://getstream.io/chat/docs/flutter-dart/query_channels/ -@Deprecated("Use 'StreamChannelListController' instead") -class ChannelsBloc extends StatefulWidget { - /// Creates a new [ChannelsBloc]. The parameter [child] must be supplied and - /// not null. - const ChannelsBloc({ - super.key, - required this.child, - this.lockChannelsOrder = false, - this.channelsComparator, - this.shouldAddChannel, - }); - - /// The widget child - final Widget child; - - /// Set this to true to prevent channels to be brought to the top of the list - /// when a new message arrives - final bool lockChannelsOrder; - - /// Comparator used to sort the channels when a message.new event is received - final Comparator? channelsComparator; - - /// Function used to evaluate if a channel should be added to the list when a - /// message.new event is received - final bool Function(Event)? shouldAddChannel; - - @override - ChannelsBlocState createState() => ChannelsBlocState(); - - /// Use this method to get the current [ChannelsBlocState] instance - static ChannelsBlocState of(BuildContext context) { - ChannelsBlocState? streamChatState; - - streamChatState = context.findAncestorStateOfType(); - - assert( - streamChatState != null, - 'You must have a ChannelsBloc widget as ancestor', - ); - - return streamChatState!; - } -} - -/// The current state of the [ChannelsBloc]. -class ChannelsBlocState extends State - with AutomaticKeepAliveClientMixin { - StreamChatCoreState? _streamChatCoreState; - - @override - Widget build(BuildContext context) { - super.build(context); - return widget.child; - } - - /// The current channel list - List? get channels => _channelsController.valueOrNull; - - /// The current channel list as a stream - Stream> get channelsStream => _channelsController.stream; - - final _queryChannelsLoadingController = BehaviorSubject.seeded(false); - - final BehaviorSubject> _channelsController = - BehaviorSubject>(); - - /// The stream notifying the state of queryChannel call - Stream get queryChannelsLoading => - _queryChannelsLoadingController.stream; - - final List _hiddenChannels = []; - - bool _paginationEnded = false; - - final List _subscriptions = []; - - /// Calls [client.queryChannels] updating [queryChannelsLoading] stream - Future queryChannels({ - Filter? filter, - List>? sortOptions, - bool state = true, - bool watch = true, - bool presence = false, - int? memberLimit, - int? messageLimit, - bool waitForConnect = true, - PaginationParams paginationParams = const PaginationParams(limit: 30), - }) async { - final client = _streamChatCoreState!.client; - - final offset = paginationParams.offset; - final clear = offset == null || offset == 0; - if (clear && _paginationEnded) { - _paginationEnded = false; - } - - if ((!clear && _paginationEnded) || _queryChannelsLoadingController.value) { - return; - } - - if (_channelsController.hasValue) { - _queryChannelsLoadingController.safeAdd(true); - } - - try { - final oldChannels = List.from(channels ?? []); - var newChannels = []; - await for (final channels in client.queryChannels( - filter: filter, - sort: sortOptions, - state: state, - watch: watch, - presence: presence, - memberLimit: memberLimit, - messageLimit: messageLimit, - waitForConnect: waitForConnect, - paginationParams: paginationParams, - )) { - newChannels = channels; - if (clear) { - _channelsController.safeAdd(channels); - } else { - final temp = oldChannels + channels; - _channelsController.safeAdd(temp); - } - if (_channelsController.hasValue && - _queryChannelsLoadingController.value) { - _queryChannelsLoadingController.safeAdd(false); - } - } - if (newChannels.isEmpty || newChannels.length < paginationParams.limit) { - _paginationEnded = true; - } - } catch (e, stk) { - // reset loading controller - _queryChannelsLoadingController.safeAdd(false); - if (_channelsController.hasValue) { - _queryChannelsLoadingController.safeAddError(e, stk); - } else { - _channelsController.safeAddError(e, stk); - } - } - } - - @override - void didChangeDependencies() { - final newStreamChatCoreState = StreamChatCore.of(context); - - if (newStreamChatCoreState != _streamChatCoreState) { - _streamChatCoreState = newStreamChatCoreState; - final client = _streamChatCoreState!.client; - - _cancelSubscriptions(); - if (!widget.lockChannelsOrder) { - _subscriptions.add(client - .on( - EventType.messageNew, - ) - .listen((e) { - if (e.message?.parentId != null && e.message?.showInChannel != true) { - return; - } - final newChannels = List.from(channels ?? []); - final index = newChannels.indexWhere((c) => c.cid == e.cid); - if (index != -1) { - if (index > 0) { - final channel = newChannels.removeAt(index); - newChannels.insert(0, channel); - } - } else if (widget.shouldAddChannel?.call(e) == true) { - final hiddenIndex = - _hiddenChannels.indexWhere((c) => c.cid == e.cid); - if (hiddenIndex != -1) { - newChannels.insert(0, _hiddenChannels[hiddenIndex]); - _hiddenChannels.removeAt(hiddenIndex); - } else { - if (client.state.channels[e.cid] != null) { - newChannels.insert(0, client.state.channels[e.cid]!); - } - } - } - - if (widget.channelsComparator != null) { - newChannels.sort(widget.channelsComparator); - } - _channelsController.safeAdd(newChannels); - })); - } - - _subscriptions - ..add(client.on(EventType.channelHidden).listen((event) async { - final newChannels = List.from(channels ?? []); - final channelIndex = - newChannels.indexWhere((c) => c.cid == event.cid); - if (channelIndex > -1) { - final channel = newChannels.removeAt(channelIndex); - _hiddenChannels.add(channel); - _channelsController.safeAdd(newChannels); - } - })) - ..add(client - .on( - EventType.channelDeleted, - EventType.notificationRemovedFromChannel, - ) - .listen((e) { - final channel = e.channel; - _channelsController.safeAdd(List.from( - (channels ?? [])..removeWhere((c) => c.id == channel?.cid), - )); - })); - } - - super.didChangeDependencies(); - } - - @override - void dispose() { - _channelsController.close(); - _queryChannelsLoadingController.close(); - _cancelSubscriptions(); - super.dispose(); - } - - void _cancelSubscriptions() { - _subscriptions - ..forEach((s) => s.cancel()) - ..clear(); - } - - @override - bool get wantKeepAlive => true; -} diff --git a/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart b/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart deleted file mode 100644 index 0ba47fd2e..000000000 --- a/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart +++ /dev/null @@ -1,170 +0,0 @@ -// ignore_for_file: deprecated_member_use_from_same_package - -import 'package:flutter/material.dart'; -import 'package:rxdart/rxdart.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_flutter_core/src/stream_chat_core.dart'; -import 'package:stream_chat_flutter_core/src/stream_controller_extension.dart'; - -/// [MessageSearchBloc] is used to manage a list of messages with pagination. -/// This class can be used to load messages, perform queries, etc. -/// -/// [MessageSearchBloc] can be access at anytime by using the static [of] method -/// using Flutter's [BuildContext]. -/// -/// API docs: https://getstream.io/chat/docs/flutter-dart/send_message/ -@Deprecated("Use 'StreamMessageSearchListController' instead") -class MessageSearchBloc extends StatefulWidget { - /// Instantiate a new MessageSearchBloc - const MessageSearchBloc({ - super.key, - required this.child, - }); - - /// The widget child - final Widget child; - - @override - MessageSearchBlocState createState() => MessageSearchBlocState(); - - /// Use this method to get the current [MessageSearchBlocState] instance - static MessageSearchBlocState of(BuildContext context) { - MessageSearchBlocState? state; - - state = context.findAncestorStateOfType(); - - assert( - state != null, - 'You must have a MessageSearchBloc widget as ancestor', - ); - - return state!; - } -} - -/// The current state of the [MessageSearchBloc] -class MessageSearchBlocState extends State - with AutomaticKeepAliveClientMixin { - late StreamChatCoreState _streamChatCoreState; - - /// The key used to paginate next items. - String? nextId; - - /// The key used to paginate previous items. - String? previousId; - - /// The current messages list - List? get messageResponses => - _messageResponses.valueOrNull; - - /// The current messages list as a stream - Stream> get messagesStream => - _messageResponses.stream; - - final _messageResponses = BehaviorSubject>(); - - final _queryMessagesLoadingController = BehaviorSubject.seeded(false); - - /// The stream notifying the state of queryUsers call - Stream get queryMessagesLoading => - _queryMessagesLoadingController.stream; - - bool _paginationEnded = false; - - /// Calls [StreamChatClient.search] updating - /// [messagesStream] and [queryMessagesLoading] stream - Future search({ - required Filter filter, - Filter? messageFilter, - List? sort, - String? query, - PaginationParams pagination = const PaginationParams(limit: 30), - }) async { - final client = _streamChatCoreState.client; - - var clear = false; - if (sort != null) { - clear |= pagination.next == null; - } else { - final offset = pagination.offset; - clear |= offset == null || offset == 0; - } - - if (clear && _paginationEnded) { - _paginationEnded = false; - } - - if ((!clear && _paginationEnded) || _queryMessagesLoadingController.value) { - return; - } - - if (_messageResponses.hasValue) { - _queryMessagesLoadingController.safeAdd(true); - } - try { - final oldMessages = List.from(messageResponses ?? []); - - final response = await client.search( - filter, - sort: sort, - query: query, - paginationParams: pagination, - messageFilters: messageFilter, - ); - - final next = response.next; - final previous = response.previous; - - nextId = next != null && next.isNotEmpty - ? next - : /*reset nextId if we get nothing*/ null; - previousId = previous != null && previous.isNotEmpty - ? previous - : /*reset previousId if we get nothing*/ null; - - final newMessages = response.results; - if (clear) { - _messageResponses.safeAdd(newMessages); - } else { - final temp = oldMessages + newMessages; - _messageResponses.safeAdd(temp); - } - if (_messageResponses.hasValue && _queryMessagesLoadingController.value) { - _queryMessagesLoadingController.safeAdd(false); - } - if (newMessages.isEmpty || newMessages.length < pagination.limit) { - _paginationEnded = true; - } - } catch (e, stk) { - // reset loading controller - _queryMessagesLoadingController.safeAdd(false); - if (_messageResponses.hasValue) { - _queryMessagesLoadingController.safeAddError(e, stk); - } else { - _messageResponses.safeAddError(e, stk); - } - } - } - - @override - Widget build(BuildContext context) { - super.build(context); - return widget.child; - } - - @override - void didChangeDependencies() { - _streamChatCoreState = StreamChatCore.of(context); - super.didChangeDependencies(); - } - - @override - void dispose() { - _messageResponses.close(); - _queryMessagesLoadingController.close(); - super.dispose(); - } - - @override - bool get wantKeepAlive => true; -} diff --git a/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart b/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart deleted file mode 100644 index f528a4da0..000000000 --- a/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart +++ /dev/null @@ -1,236 +0,0 @@ -// ignore_for_file: deprecated_member_use_from_same_package - -import 'dart:convert'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_flutter_core/src/better_stream_builder.dart'; -import 'package:stream_chat_flutter_core/src/message_search_bloc.dart'; -import 'package:stream_chat_flutter_core/src/typedef.dart'; - -/// -/// [MessageSearchListCore] is a simplified class that allows searching for -/// messages across channels while exposing UI builders. -/// A [MessageSearchListController] is used to load and paginate data. -/// -/// ```dart -/// class MessageSearchPage extends StatelessWidget { -/// @override -/// Widget build(BuildContext context) { -/// return Scaffold( -/// body: MessageSearchListCore( -/// messageQuery: _messageFilter, -/// filters: _channelsFilter, -/// limit: 20, -/// ), -/// ); -/// } -/// } -/// ``` -/// -/// Make sure to have a [MessageSearchBloc] ancestor in order to provide the -/// information about the messages. -/// The widget uses a [ListView.separated] to render the list of messages. -/// -@Deprecated(''' -MessageSearchListCore is deprecated and will be removed in the next -major version. Use StreamMessageSearchListController instead to create your custom list. -More details here https://getstream.io/chat/docs/sdk/flutter/stream_chat_flutter_core/stream_message_search_list_controller -''') -class MessageSearchListCore extends StatefulWidget { - /// Instantiate a new [MessageSearchListView]. - /// The following parameters must be supplied and not null: - /// * [emptyBuilder] - /// * [errorBuilder] - /// * [loadingBuilder] - /// * [childBuilder] - MessageSearchListCore({ - super.key, - required this.emptyBuilder, - required this.errorBuilder, - required this.loadingBuilder, - required this.childBuilder, - required this.filters, - this.messageQuery, - this.sortOptions, - @Deprecated( - "'pagination' is deprecated and shouldn't be used. " - "This property is no longer used, Please use 'limit' instead", - ) - this.paginationParams, - this.messageFilters, - this.messageSearchListController, - int? limit, - }) : assert( - messageQuery != null || messageFilters != null, - 'Provide at least `query` or `messageFilters`', - ), - assert( - messageQuery == null || messageFilters == null, - "Can't provide both `query` and `messageFilters` at the same time", - ), - assert( - paginationParams?.offset == null || - paginationParams?.offset == 0 || - sortOptions == null, - 'Cannot specify `offset` with `sortOptions` parameter', - ), - limit = limit ?? paginationParams?.limit ?? 30; - - /// A [MessageSearchListController] allows reloading and pagination. - /// Use [MessageSearchListController.loadData] and - /// [MessageSearchListController.paginateData] respectively for reloading and - /// pagination. - final MessageSearchListController? messageSearchListController; - - /// Message String to search on - final String? messageQuery; - - /// The query filters to use. - /// You can query on any of the custom fields you've defined on the [Channel]. - /// You can also filter other built-in channel fields. - final Filter filters; - - /// The sorting used for the channels matching the filters. - /// Sorting is based on field and direction, multiple sorting options can be - /// provided. - /// You can sort based on last_updated, last_message_at, updated_at, created_ - /// at or member_count. Direction can be ascending or descending. - final List? sortOptions; - - /// Pagination parameters - /// limit: the number of messages to return (max is 30) - /// offset: the offset (max is 1000) - @Deprecated( - "'pagination' is deprecated and shouldn't be used. " - "This property is no longer used, Please use 'limit' instead", - ) - final PaginationParams? paginationParams; - - /// The amount of messages requested per API call. - final int limit; - - /// The message query filters to use. - /// You can query on any of the custom fields you've defined on the [Channel]. - /// You can also filter other built-in channel fields. - final Filter? messageFilters; - - /// The builder that is used when the search messages are fetched - final Widget Function(List) childBuilder; - - /// The builder used when the channel list is empty. - final WidgetBuilder emptyBuilder; - - /// The builder that will be used in case of error - final ErrorBuilder errorBuilder; - - /// The builder that will be used in case of loading - final WidgetBuilder loadingBuilder; - - @override - MessageSearchListCoreState createState() => MessageSearchListCoreState(); -} - -/// The current state of the [MessageSearchListCore]. -class MessageSearchListCoreState extends State { - MessageSearchBlocState? _messageSearchBloc; - - @override - void didChangeDependencies() { - final newMessageSearchBloc = MessageSearchBloc.of(context); - - if (newMessageSearchBloc != _messageSearchBloc) { - _messageSearchBloc = newMessageSearchBloc; - loadData(); - } - - super.didChangeDependencies(); - } - - void _setupController() { - if (widget.messageSearchListController != null) { - widget.messageSearchListController!.loadData = loadData; - widget.messageSearchListController!.paginateData = paginateData; - } - } - - @override - void initState() { - super.initState(); - _setupController(); - } - - @override - Widget build(BuildContext context) => _buildListView(_messageSearchBloc!); - - Widget _buildListView(MessageSearchBlocState messageSearchBloc) => - BetterStreamBuilder>( - stream: messageSearchBloc.messagesStream, - errorBuilder: widget.errorBuilder, - noDataBuilder: widget.loadingBuilder, - builder: (context, items) { - if (items.isEmpty) { - return widget.emptyBuilder(context); - } - return widget.childBuilder(items); - }, - ); - - /// Fetches initial messages and updates the widget - Future loadData() => _messageSearchBloc!.search( - filter: widget.filters, - sort: widget.sortOptions, - query: widget.messageQuery, - messageFilter: widget.messageFilters, - pagination: PaginationParams(limit: widget.limit), - ); - - /// Fetches more messages with updated pagination and updates the widget - Future paginateData() { - var pagination = PaginationParams(limit: widget.limit); - if (widget.sortOptions != null) { - pagination = pagination.copyWith( - next: _messageSearchBloc?.nextId, - ); - } else { - pagination = pagination.copyWith( - offset: _messageSearchBloc?.messageResponses?.length, - ); - } - return _messageSearchBloc!.search( - filter: widget.filters, - sort: widget.sortOptions, - pagination: pagination, - query: widget.messageQuery, - messageFilter: widget.messageFilters, - ); - } - - @override - void didUpdateWidget(MessageSearchListCore oldWidget) { - super.didUpdateWidget(oldWidget); - if (jsonEncode(widget.filters) != jsonEncode(oldWidget.filters) || - jsonEncode(widget.sortOptions) != jsonEncode(oldWidget.sortOptions) || - widget.messageQuery != oldWidget.messageQuery || - jsonEncode(widget.messageFilters) != - jsonEncode(oldWidget.messageFilters) || - widget.limit != oldWidget.limit) { - loadData(); - } - - if (widget.messageSearchListController != - oldWidget.messageSearchListController) { - _setupController(); - } - } -} - -/// Controller used for paginating data in [ChannelListView] -class MessageSearchListController { - /// Call this function to reload data - AsyncCallback? loadData; - - /// Call this function to load further data - AsyncCallback? paginateData; -} diff --git a/packages/stream_chat_flutter_core/lib/src/paged_value_notifier.freezed.dart b/packages/stream_chat_flutter_core/lib/src/paged_value_notifier.freezed.dart index a7ea0ea3c..9e3255310 100644 --- a/packages/stream_chat_flutter_core/lib/src/paged_value_notifier.freezed.dart +++ b/packages/stream_chat_flutter_core/lib/src/paged_value_notifier.freezed.dart @@ -1,5 +1,6 @@ // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target part of 'paged_value_notifier.dart'; @@ -11,34 +12,7 @@ part of 'paged_value_notifier.dart'; T _$identity(T value) => value; final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -/// @nodoc -class _$PagedValueTearOff { - const _$PagedValueTearOff(); - - Success call( - {required List items, Key? nextPageKey, StreamChatError? error}) { - return Success( - items: items, - nextPageKey: nextPageKey, - error: error, - ); - } - - Loading loading() { - return Loading(); - } - - Error error(StreamChatError error) { - return Error( - error, - ); - } -} - -/// @nodoc -const $PagedValue = _$PagedValueTearOff(); + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); /// @nodoc mixin _$PagedValue { @@ -112,23 +86,23 @@ class _$PagedValueCopyWithImpl } /// @nodoc -abstract class $SuccessCopyWith { - factory $SuccessCopyWith( - Success value, $Res Function(Success) then) = - _$SuccessCopyWithImpl; +abstract class _$$SuccessCopyWith { + factory _$$SuccessCopyWith(_$Success value, + $Res Function(_$Success) then) = + __$$SuccessCopyWithImpl; $Res call({List items, Key? nextPageKey, StreamChatError? error}); } /// @nodoc -class _$SuccessCopyWithImpl +class __$$SuccessCopyWithImpl extends _$PagedValueCopyWithImpl - implements $SuccessCopyWith { - _$SuccessCopyWithImpl( - Success _value, $Res Function(Success) _then) - : super(_value, (v) => _then(v as Success)); + implements _$$SuccessCopyWith { + __$$SuccessCopyWithImpl( + _$Success _value, $Res Function(_$Success) _then) + : super(_value, (v) => _then(v as _$Success)); @override - Success get _value => super._value as Success; + _$Success get _value => super._value as _$Success; @override $Res call({ @@ -136,9 +110,9 @@ class _$SuccessCopyWithImpl Object? nextPageKey = freezed, Object? error = freezed, }) { - return _then(Success( + return _then(_$Success( items: items == freezed - ? _value.items + ? _value._items : items // ignore: cast_nullable_to_non_nullable as List, nextPageKey: nextPageKey == freezed @@ -157,20 +131,27 @@ class _$SuccessCopyWithImpl class _$Success extends Success with DiagnosticableTreeMixin { - const _$Success({required this.items, this.nextPageKey, this.error}) - : super._(); + const _$Success( + {required final List items, this.nextPageKey, this.error}) + : _items = items, + super._(); - @override + /// List with all items loaded so far. + final List _items; /// List with all items loaded so far. - final List items; @override + List get items { + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_items); + } /// The key for the next page to be fetched. - final Key? nextPageKey; @override + final Key? nextPageKey; /// The current error, if any. + @override final StreamChatError? error; @override @@ -192,8 +173,8 @@ class _$Success extends Success bool operator ==(dynamic other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is Success && - const DeepCollectionEquality().equals(other.items, items) && + other is _$Success && + const DeepCollectionEquality().equals(other._items, _items) && const DeepCollectionEquality() .equals(other.nextPageKey, nextPageKey) && const DeepCollectionEquality().equals(other.error, error)); @@ -202,14 +183,15 @@ class _$Success extends Success @override int get hashCode => Object.hash( runtimeType, - const DeepCollectionEquality().hash(items), + const DeepCollectionEquality().hash(_items), const DeepCollectionEquality().hash(nextPageKey), const DeepCollectionEquality().hash(error)); @JsonKey(ignore: true) @override - $SuccessCopyWith> get copyWith => - _$SuccessCopyWithImpl>(this, _$identity); + _$$SuccessCopyWith> get copyWith => + __$$SuccessCopyWithImpl>( + this, _$identity); @override @optionalTypeArgs @@ -288,41 +270,41 @@ class _$Success extends Success abstract class Success extends PagedValue { const factory Success( - {required List items, - Key? nextPageKey, - StreamChatError? error}) = _$Success; + {required final List items, + final Key? nextPageKey, + final StreamChatError? error}) = _$Success; const Success._() : super._(); /// List with all items loaded so far. - List get items; + List get items => throw _privateConstructorUsedError; /// The key for the next page to be fetched. - Key? get nextPageKey; + Key? get nextPageKey => throw _privateConstructorUsedError; /// The current error, if any. - StreamChatError? get error; + StreamChatError? get error => throw _privateConstructorUsedError; @JsonKey(ignore: true) - $SuccessCopyWith> get copyWith => + _$$SuccessCopyWith> get copyWith => throw _privateConstructorUsedError; } /// @nodoc -abstract class $LoadingCopyWith { - factory $LoadingCopyWith( - Loading value, $Res Function(Loading) then) = - _$LoadingCopyWithImpl; +abstract class _$$LoadingCopyWith { + factory _$$LoadingCopyWith(_$Loading value, + $Res Function(_$Loading) then) = + __$$LoadingCopyWithImpl; } /// @nodoc -class _$LoadingCopyWithImpl +class __$$LoadingCopyWithImpl extends _$PagedValueCopyWithImpl - implements $LoadingCopyWith { - _$LoadingCopyWithImpl( - Loading _value, $Res Function(Loading) _then) - : super(_value, (v) => _then(v as Loading)); + implements _$$LoadingCopyWith { + __$$LoadingCopyWithImpl( + _$Loading _value, $Res Function(_$Loading) _then) + : super(_value, (v) => _then(v as _$Loading)); @override - Loading get _value => super._value as Loading; + _$Loading get _value => super._value as _$Loading; } /// @nodoc @@ -340,13 +322,13 @@ class _$Loading extends Loading void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty('type', 'PagedValue<$Key, $Value>.loading')); + .add(DiagnosticsProperty('type', 'PagedValue<$Key, $Value>.loading')); } @override bool operator ==(dynamic other) { return identical(this, other) || - (other.runtimeType == runtimeType && other is Loading); + (other.runtimeType == runtimeType && other is _$Loading); } @override @@ -433,29 +415,29 @@ abstract class Loading extends PagedValue { } /// @nodoc -abstract class $ErrorCopyWith { - factory $ErrorCopyWith( - Error value, $Res Function(Error) then) = - _$ErrorCopyWithImpl; +abstract class _$$ErrorCopyWith { + factory _$$ErrorCopyWith( + _$Error value, $Res Function(_$Error) then) = + __$$ErrorCopyWithImpl; $Res call({StreamChatError error}); } /// @nodoc -class _$ErrorCopyWithImpl +class __$$ErrorCopyWithImpl extends _$PagedValueCopyWithImpl - implements $ErrorCopyWith { - _$ErrorCopyWithImpl( - Error _value, $Res Function(Error) _then) - : super(_value, (v) => _then(v as Error)); + implements _$$ErrorCopyWith { + __$$ErrorCopyWithImpl( + _$Error _value, $Res Function(_$Error) _then) + : super(_value, (v) => _then(v as _$Error)); @override - Error get _value => super._value as Error; + _$Error get _value => super._value as _$Error; @override $Res call({ Object? error = freezed, }) { - return _then(Error( + return _then(_$Error( error == freezed ? _value.error : error // ignore: cast_nullable_to_non_nullable @@ -490,7 +472,7 @@ class _$Error extends Error bool operator ==(dynamic other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is Error && + other is _$Error && const DeepCollectionEquality().equals(other.error, error)); } @@ -500,8 +482,8 @@ class _$Error extends Error @JsonKey(ignore: true) @override - $ErrorCopyWith> get copyWith => - _$ErrorCopyWithImpl>(this, _$identity); + _$$ErrorCopyWith> get copyWith => + __$$ErrorCopyWithImpl>(this, _$identity); @override @optionalTypeArgs @@ -579,11 +561,11 @@ class _$Error extends Error } abstract class Error extends PagedValue { - const factory Error(StreamChatError error) = _$Error; + const factory Error(final StreamChatError error) = _$Error; const Error._() : super._(); - StreamChatError get error; + StreamChatError get error => throw _privateConstructorUsedError; @JsonKey(ignore: true) - $ErrorCopyWith> get copyWith => + _$$ErrorCopyWith> get copyWith => throw _privateConstructorUsedError; } 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 18e494e2e..fea9a64cd 100644 --- a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart +++ b/packages/stream_chat_flutter_core/lib/src/stream_channel.dart @@ -220,8 +220,6 @@ class StreamChannelState extends State { /// Loads channel at specific message Future loadChannelAtMessage( String? messageId, { - @Deprecated('before is deprecated, use limit instead') int before = 20, - @Deprecated('after is deprecated, use limit instead') int after = 20, int limit = 20, bool preferOffline = false, }) => @@ -261,8 +259,6 @@ class StreamChannelState extends State { /// Future queryAroundMessage( String messageId, { - @Deprecated('before is deprecated, use limit instead') int before = 20, - @Deprecated('after is deprecated, use limit instead') int after = 20, int limit = 20, bool preferOffline = false, }) => diff --git a/packages/stream_chat_flutter_core/lib/src/user_list_core.dart b/packages/stream_chat_flutter_core/lib/src/user_list_core.dart deleted file mode 100644 index 678415690..000000000 --- a/packages/stream_chat_flutter_core/lib/src/user_list_core.dart +++ /dev/null @@ -1,286 +0,0 @@ -// ignore_for_file: deprecated_member_use_from_same_package - -import 'dart:convert'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// -/// [UserListCore] is a simplified class that allows fetching users while -/// exposing UI builders. -/// A [UserListController] is used to load and paginate data. -/// -/// ```dart -/// class UsersListPage extends StatelessWidget { -/// @override -/// Widget build(BuildContext context) { -/// return Scaffold( -/// body: UsersListCore( -/// filter: { -/// 'members': { -/// '\$in': [StreamChat.of(context).user.id], -/// } -/// }, -/// sort: [SortOption('last_message_at')], -/// pagination: PaginationParams( -/// limit: 20, -/// ), -/// errorBuilder: (err) { -/// return Center( -/// child: Text('An error has occured'), -/// ); -/// }, -/// emptyBuilder: (context) { -/// return Center( -/// child: Text('Nothing here...'), -/// ); -/// }, -/// emptyBuilder: (context) { -/// return Center( -/// child: CircularProgressIndicator(), -/// ); -/// }, -/// listBuilder: (context, list) { -/// return UsersPage(list); -/// } -/// ), -/// ); -/// } -/// } -/// ``` -/// -/// [UsersBloc] must be the ancestor of this widget. This is necessary since -/// [UserListCore] depends on functionality contained within [UsersBloc]. -/// -/// The parameters [listBuilder], [loadingBuilder], [emptyBuilder] and -/// [errorBuilder] must all be supplied and not null. -@Deprecated(''' -UserListCore is deprecated and will be removed in the next -major version. Use StreamUserListController instead to create your custom list. -More details here https://getstream.io/chat/docs/sdk/flutter/stream_chat_flutter_core/stream_user_list_controller -''') -class UserListCore extends StatefulWidget { - /// Instantiate a new [UserListCore] - const UserListCore({ - required this.errorBuilder, - required this.emptyBuilder, - required this.loadingBuilder, - required this.listBuilder, - super.key, - this.filter = const Filter.empty(), - this.sort, - this.presence, - this.groupAlphabetically = false, - this.userListController, - this.limit = 30, - }); - - /// A [UserListController] allows reloading and pagination. - /// Use [UserListController.loadData] and [UserListController.paginateData] - /// respectively for reloading and pagination. - final UserListController? userListController; - - /// The builder that will be used in case of error - final ErrorBuilder errorBuilder; - - /// The builder that will be used to build the list - final Widget Function(BuildContext context, List users) listBuilder; - - /// The builder that will be used for loading - final WidgetBuilder loadingBuilder; - - /// The builder used when the channel list is empty. - final WidgetBuilder emptyBuilder; - - /// The query filters to use. - /// You can query on any of the custom fields you've defined on the [Channel]. - /// You can also filter other built-in channel fields. - final Filter filter; - - /// The sorting used for the channels matching the filters. - /// Sorting is based on field and direction, multiple sorting options can be - /// provided. You can sort based on last_updated, last_message_at, updated_at, - /// created_at or member_count. Direction can be ascending or descending. - final List? sort; - - /// If true you’ll receive user presence updates via the websocket events - final bool? presence; - - /// The amount of users requested per API call. - final int limit; - - /// Set it to true to group users by their first character - /// - /// defaults to false - final bool groupAlphabetically; - - @override - UserListCoreState createState() => UserListCoreState(); -} - -/// The current state of the [UserListCore]. -class UserListCoreState extends State - with WidgetsBindingObserver { - UsersBlocState? _usersBloc; - - @override - void didChangeDependencies() { - final newUsersBloc = UsersBloc.of(context); - if (newUsersBloc != _usersBloc) { - _usersBloc = newUsersBloc; - loadData(); - } - super.didChangeDependencies(); - } - - @override - void initState() { - super.initState(); - _setupController(); - } - - void _setupController() { - if (widget.userListController != null) { - widget.userListController!.loadData = loadData; - widget.userListController!.paginateData = paginateData; - } - } - - @override - Widget build(BuildContext context) => _buildListView(); - - bool get _isListAlreadySorted => - widget.sort?.any((e) => e.field == 'name' && e.direction == 1) ?? false; - - Stream> _buildUserStream() => _usersBloc!.usersStream.map( - (users) { - if (widget.groupAlphabetically) { - var temp = users; - if (!_isListAlreadySorted) { - temp = users - ..sort((curr, next) => curr.name.compareTo(next.name)); - } - final groupedUsers = >{}; - for (final e in temp) { - final alphabet = e.name[0].toUpperCase(); - groupedUsers[alphabet] = [...groupedUsers[alphabet] ?? [], e]; - } - final items = []; - for (final key in groupedUsers.keys) { - items - ..add(ListHeaderItem(key)) - ..addAll(groupedUsers[key]!.map(ListUserItem.new)); - } - return items; - } - return users.map(ListUserItem.new).toList(); - }, - ); - - BetterStreamBuilder> _buildListView() => BetterStreamBuilder( - stream: _buildUserStream(), - errorBuilder: widget.errorBuilder, - noDataBuilder: widget.loadingBuilder, - builder: (context, items) { - if (items.isEmpty) { - return widget.emptyBuilder(context); - } - return widget.listBuilder(context, items); - }, - ); - - /// Fetches initial users and updates the widget - Future loadData() => _usersBloc!.queryUsers( - filter: widget.filter, - sort: widget.sort, - presence: widget.presence, - pagination: PaginationParams(limit: widget.limit), - ); - - /// Fetches more users with updated pagination and updates the widget - Future paginateData() => _usersBloc!.queryUsers( - filter: widget.filter, - sort: widget.sort, - presence: widget.presence, - pagination: PaginationParams( - limit: widget.limit, - offset: _usersBloc!.users?.length ?? 0, - ), - ); - - @override - void didUpdateWidget(UserListCore oldWidget) { - super.didUpdateWidget(oldWidget); - if (jsonEncode(widget.filter) != jsonEncode(oldWidget.filter) || - jsonEncode(widget.sort) != jsonEncode(oldWidget.sort) || - widget.presence != oldWidget.presence || - widget.limit != oldWidget.limit) { - loadData(); - } - - if (widget.userListController != oldWidget.userListController) { - _setupController(); - } - } -} - -/// Represents an item in a the user stream list. -/// Header items are prefixed with the key `HEADER` While users are prefixed -/// with `USER`. -abstract class ListItem { - /// Unique key per list item - String? get key { - if (this is ListHeaderItem) { - final header = (this as ListHeaderItem).heading; - return 'HEADER-${header.toLowerCase()}'; - } - if (this is ListUserItem) { - final user = (this as ListUserItem).user; - return 'USER-${user.id}'; - } - return null; - } - - /// Helper function to build widget based on ListItem type - // ignore: missing_return - Widget when({ - required Widget Function(String heading) headerItem, - required Widget Function(User user) userItem, - }) { - if (this is ListHeaderItem) { - return headerItem((this as ListHeaderItem).heading); - } - if (this is ListUserItem) { - return userItem((this as ListUserItem).user); - } - return Container(); - } -} - -/// Header Item -class ListHeaderItem extends ListItem { - /// Constructs a new [ListHeaderItem] - ListHeaderItem(this.heading); - - /// Heading used to build the item. - final String heading; -} - -/// User Item -class ListUserItem extends ListItem { - /// Constructs a new [ListUserItem] - ListUserItem(this.user); - - /// [User] used to build the item. - final User user; -} - -/// Controller used for paginating data in [ChannelListView] -class UserListController { - /// Call this function to reload data - AsyncCallback? loadData; - - /// Call this function to load further data - AsyncCallback? paginateData; -} diff --git a/packages/stream_chat_flutter_core/lib/src/users_bloc.dart b/packages/stream_chat_flutter_core/lib/src/users_bloc.dart deleted file mode 100644 index 5db4f5f5d..000000000 --- a/packages/stream_chat_flutter_core/lib/src/users_bloc.dart +++ /dev/null @@ -1,145 +0,0 @@ -// ignore_for_file: deprecated_member_use_from_same_package - -import 'package:flutter/material.dart'; -import 'package:rxdart/rxdart.dart'; -import 'package:stream_chat_flutter_core/src/stream_controller_extension.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// Widget dedicated to the management of a users list with pagination. -/// -/// [UsersBloc] can be access at anytime by using the static [of] method -/// using Flutter's [BuildContext]. -/// -/// API docs: https://getstream.io/chat/docs/flutter-dart/init_and_users/ -@Deprecated("Use 'StreamUserListController' instead") -class UsersBloc extends StatefulWidget { - /// Instantiate a new [UsersBloc]. The parameter [child] must be supplied and - /// not null. - const UsersBloc({ - required this.child, - super.key, - }); - - /// The widget child - final Widget child; - - @override - UsersBlocState createState() => UsersBlocState(); - - /// Use this method to get the current [UsersBlocState] instance - static UsersBlocState of(BuildContext context) { - UsersBlocState? state; - - state = context.findAncestorStateOfType(); - - assert( - state != null, - 'You must have a UsersBloc widget as ancestor', - ); - - return state!; - } -} - -/// The current state of the [UsersBloc] -class UsersBlocState extends State - with AutomaticKeepAliveClientMixin { - /// The current users list - List? get users => _usersController.valueOrNull; - - /// The current users list as a stream - Stream> get usersStream => _usersController.stream; - - final _usersController = BehaviorSubject>(); - - final _queryUsersLoadingController = BehaviorSubject.seeded(false); - - /// The stream notifying the state of queryUsers call - Stream get queryUsersLoading => _queryUsersLoadingController.stream; - - late StreamChatCoreState _streamChatCore; - - bool _paginationEnded = false; - - /// The Query Users method allows you to search for users and see if they are - /// online/offline. - /// [API Reference](https://getstream.io/chat/docs/flutter-dart/query_users/?language=dart) - Future queryUsers({ - Filter? filter, - List? sort, - bool? presence, - PaginationParams pagination = const PaginationParams(limit: 30), - }) async { - final client = _streamChatCore.client; - - final offset = pagination.offset; - final clear = offset == null || offset == 0; - - if (clear && _paginationEnded) { - _paginationEnded = false; - } - - if ((!clear && _paginationEnded) || _queryUsersLoadingController.value) { - return; - } - - if (_usersController.hasValue) { - _queryUsersLoadingController.safeAdd(true); - } - - try { - final oldUsers = List.from(users ?? []); - - final usersResponse = await client.queryUsers( - filter: filter, - sort: sort, - presence: presence, - pagination: pagination, - ); - - final newUsers = usersResponse.users; - if (clear) { - _usersController.safeAdd(usersResponse.users); - } else { - final temp = oldUsers + usersResponse.users; - _usersController.safeAdd(temp); - } - if (_usersController.hasValue && _queryUsersLoadingController.value) { - _queryUsersLoadingController.safeAdd(false); - } - if (newUsers.isEmpty || newUsers.length < pagination.limit) { - _paginationEnded = true; - } - } catch (e, stk) { - // reset loading controller - _queryUsersLoadingController.safeAdd(false); - if (_usersController.hasValue) { - _queryUsersLoadingController.safeAddError(e, stk); - } else { - _usersController.safeAddError(e, stk); - } - } - } - - @override - void didChangeDependencies() { - _streamChatCore = StreamChatCore.of(context); - super.didChangeDependencies(); - } - - @override - Widget build(BuildContext context) { - super.build(context); - return widget.child; - } - - @override - void dispose() { - _usersController.close(); - _queryUsersLoadingController.close(); - super.dispose(); - } - - @override - bool get wantKeepAlive => true; -} diff --git a/packages/stream_chat_flutter_core/lib/stream_chat_flutter_core.dart b/packages/stream_chat_flutter_core/lib/stream_chat_flutter_core.dart index 34cbaaa14..5a5107bf8 100644 --- a/packages/stream_chat_flutter_core/lib/stream_chat_flutter_core.dart +++ b/packages/stream_chat_flutter_core/lib/stream_chat_flutter_core.dart @@ -4,12 +4,8 @@ export 'package:connectivity_plus/connectivity_plus.dart'; export 'package:stream_chat/stream_chat.dart'; export 'src/better_stream_builder.dart'; -export 'src/channel_list_core.dart' hide ChannelListCoreState; -export 'src/channels_bloc.dart'; export 'src/lazy_load_scroll_view.dart'; export 'src/message_list_core.dart' hide MessageListCoreState; -export 'src/message_search_bloc.dart'; -export 'src/message_search_list_core.dart' hide MessageSearchListCoreState; export 'src/message_text_field_controller.dart'; export 'src/paged_value_notifier.dart' show PagedValueListenableBuilder, PagedValue, PagedValueNotifier; @@ -22,5 +18,3 @@ export 'src/stream_message_input_controller.dart'; export 'src/stream_message_search_list_controller.dart'; export 'src/stream_user_list_controller.dart'; export 'src/typedef.dart'; -export 'src/user_list_core.dart' hide UserListCoreState; -export 'src/users_bloc.dart'; diff --git a/packages/stream_chat_flutter_core/pubspec.yaml b/packages/stream_chat_flutter_core/pubspec.yaml index 3365e7247..be01b69a5 100644 --- a/packages/stream_chat_flutter_core/pubspec.yaml +++ b/packages/stream_chat_flutter_core/pubspec.yaml @@ -18,6 +18,7 @@ dependencies: meta: ^1.3.0 rxdart: ^0.27.0 stream_chat: ^4.3.0 + dev_dependencies: build_runner: ^2.0.1 dart_code_metrics: ^4.4.0 diff --git a/packages/stream_chat_flutter_core/test/channel_list_core_test.dart b/packages/stream_chat_flutter_core/test/channel_list_core_test.dart deleted file mode 100644 index f94ab3c62..000000000 --- a/packages/stream_chat_flutter_core/test/channel_list_core_test.dart +++ /dev/null @@ -1,526 +0,0 @@ -// ignore_for_file: deprecated_member_use_from_same_package - -import 'dart:async'; - -import 'package:flutter/widgets.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter_core/src/channel_list_core.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -import 'mocks.dart'; - -void main() { - const pagination = PaginationParams(limit: 3, offset: 0); - - List _generateChannels( - StreamChatClient client, { - int count = 3, - int offset = 0, - }) => - List.generate( - count, - (index) { - index = index + offset; - return Channel( - client, - 'testType$index', - 'testId$index', - extraData: {'extra_data_key': 'extra_data_value_$index'}, - ); - }, - ); - - testWidgets( - 'should throw if ChannelListCore is used where ChannelsBloc is not present ' - 'in the widget tree', - (tester) async { - const channelListCoreKey = Key('channelListCore'); - final channelListCore = ChannelListCore( - key: channelListCoreKey, - listBuilder: (_, __) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - ); - - await tester.pumpWidget(channelListCore); - - expect(find.byKey(channelListCoreKey), findsNothing); - expect(tester.takeException(), isInstanceOf()); - }, - ); - - testWidgets( - 'should render ChannelListCore if used with ChannelsBloc as an ancestor', - (tester) async { - const channelListCoreKey = Key('channelListCore'); - final channelListCore = ChannelListCore( - key: channelListCoreKey, - listBuilder: (_, __) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - ); - - final mockClient = MockClient(); - - when(() => mockClient.on(any(), any(), any(), any())) - .thenAnswer((_) => const Stream.empty()); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: ChannelsBloc( - child: channelListCore, - ), - ), - ); - - expect(find.byKey(channelListCoreKey), findsOneWidget); - }, - ); - - testWidgets( - 'should assign loadData and paginateData callback to ' - 'ChannelListController if passed', - (tester) async { - const channelListCoreKey = Key('channelListCore'); - final controller = ChannelListController(); - final channelListCore = ChannelListCore( - key: channelListCoreKey, - listBuilder: (_, __) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - channelListController: controller, - ); - - expect(controller.loadData, isNull); - expect(controller.paginateData, isNull); - - final mockClient = MockClient(); - - when(() => mockClient.on(any(), any(), any(), any())) - .thenAnswer((_) => const Stream.empty()); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: ChannelsBloc( - child: channelListCore, - ), - ), - ); - - expect(find.byKey(channelListCoreKey), findsOneWidget); - expect(controller.loadData, isNotNull); - expect(controller.paginateData, isNotNull); - }, - ); - - testWidgets( - 'should build error widget if channelsBlocState.channelsStream emits error', - (tester) async { - const channelListCoreKey = Key('channelListCore'); - const errorWidgetKey = Key('errorWidget'); - final channelListCore = ChannelListCore( - key: channelListCoreKey, - listBuilder: (_, __) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => - Container(key: errorWidgetKey), - limit: pagination.limit, - ); - - final mockClient = MockClient(); - - when(() => mockClient.on(any(), any(), any(), any())) - .thenAnswer((_) => const Stream.empty()); - - const error = 'Error! Error! Error!'; - when(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: pagination, - )).thenThrow(error); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: ChannelsBloc( - child: channelListCore, - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(errorWidgetKey), findsOneWidget); - - verify(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: pagination, - )).called(1); - }, - ); - - testWidgets( - '''should build empty widget if channelsBlocState.channelsStream emits empty data''', - (tester) async { - const channelListCoreKey = Key('channelListCore'); - const emptyWidgetKey = Key('emptyWidget'); - final channelListCore = ChannelListCore( - key: channelListCoreKey, - listBuilder: (_, __) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => Container(key: emptyWidgetKey), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - limit: pagination.limit, - ); - - final mockClient = MockClient(); - - when(() => mockClient.on(any(), any(), any(), any())) - .thenAnswer((_) => const Stream.empty()); - - const channels = []; - when(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: pagination, - )).thenAnswer((_) => Stream.value(channels)); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: ChannelsBloc( - child: channelListCore, - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(emptyWidgetKey), findsOneWidget); - - verify(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: pagination, - )).called(1); - }, - ); - - testWidgets( - '''should build list widget if channelsBlocState.channelsStream emits some data''', - (tester) async { - const channelListCoreKey = Key('channelListCore'); - const listWidgetKey = Key('listWidget'); - final channelListCore = ChannelListCore( - key: channelListCoreKey, - listBuilder: (_, __) => Container(key: listWidgetKey), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - limit: pagination.limit, - ); - - final mockClient = MockClient(); - - when(() => mockClient.on(any(), any(), any(), any())) - .thenAnswer((_) => const Stream.empty()); - - final channels = _generateChannels(mockClient); - when(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: pagination, - )).thenAnswer((_) => Stream.value(channels)); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: ChannelsBloc( - child: channelListCore, - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(listWidgetKey), findsOneWidget); - - verify(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: pagination, - )).called(1); - }, - ); - - testWidgets( - 'should build list widget with paginated data ' - 'on calling channelListCoreState.paginateData', - (tester) async { - const channelListCoreKey = Key('channelListCore'); - const listWidgetKey = Key('listWidget'); - final channelListCore = ChannelListCore( - key: channelListCoreKey, - listBuilder: (_, channels) => Container( - key: listWidgetKey, - child: Text( - channels.map((e) => e.cid).join(','), - ), - ), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - limit: pagination.limit, - ); - - final mockClient = MockClient(); - - when(() => mockClient.on(any(), any(), any(), any())) - .thenAnswer((_) => const Stream.empty()); - - final channels = _generateChannels(mockClient); - when(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: pagination, - )).thenAnswer((_) => Stream.value(channels)); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: StreamChatCore( - client: mockClient, - child: ChannelsBloc( - child: channelListCore, - ), - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(listWidgetKey), findsOneWidget); - expect(find.text(channels.map((e) => e.cid).join(',')), findsOneWidget); - - verify(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: pagination, - )).called(1); - - final channelListCoreState = tester.state( - find.byKey(channelListCoreKey), - ); - - final offset = channels.length; - final paginatedChannels = _generateChannels(mockClient, offset: offset); - final updatedPagination = pagination.copyWith(offset: offset); - when(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: updatedPagination, - )).thenAnswer((_) => Stream.value(paginatedChannels)); - - await channelListCoreState.paginateData(); - - await tester.pumpAndSettle(); - - expect(find.byKey(listWidgetKey), findsOneWidget); - expect( - find.text([ - ...channels, - ...paginatedChannels, - ].map((e) => e.cid).join(',')), - findsOneWidget, - ); - - verify(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: updatedPagination, - )).called(1); - }, - ); - - testWidgets( - 'should rebuild ChannelListCore with updated widget data ' - 'on calling setState()', - (tester) async { - StateSetter? _stateSetter; - var limit = pagination.limit; - - const channelListCoreKey = Key('channelListCore'); - const listWidgetKey = Key('listWidget'); - - ChannelListCore channelListCoreBuilder(int limit) => ChannelListCore( - key: channelListCoreKey, - listBuilder: (_, channels) => Container( - key: listWidgetKey, - child: Text( - channels.map((e) => e.cid).join(','), - ), - ), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => - const Offstage(), - limit: limit, - ); - - final mockClient = MockClient(); - - when(() => mockClient.on(any(), any(), any(), any())) - .thenAnswer((_) => const Stream.empty()); - - final channels = _generateChannels(mockClient); - when(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: pagination, - )).thenAnswer((_) => Stream.value(channels)); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: StreamChatCore( - client: mockClient, - child: ChannelsBloc( - child: StatefulBuilder(builder: (context, stateSetter) { - // Assigning stateSetter for rebuilding ChannelListCore - _stateSetter = stateSetter; - return channelListCoreBuilder(limit); - }), - ), - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(listWidgetKey), findsOneWidget); - expect(find.text(channels.map((e) => e.cid).join(',')), findsOneWidget); - - verify(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: pagination, - )).called(1); - - // Rebuilding ChannelListCore with new pagination limit - _stateSetter?.call(() => limit = 6); - - final updatedChannels = _generateChannels(mockClient, count: limit); - final updatedPagination = PaginationParams(limit: limit, offset: 0); - when(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: updatedPagination, - )).thenAnswer((_) => Stream.value(updatedChannels)); - - await tester.pumpAndSettle(); - - expect(find.byKey(listWidgetKey), findsOneWidget); - expect( - find.text(updatedChannels.map((e) => e.cid).join(',')), - findsOneWidget, - ); - - verify(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: updatedPagination, - )).called(1); - }, - ); - - test('`widget.limit` should match `widget.pagination.limit`', () { - const pagination = PaginationParams(limit: 30); - final channelListCore = ChannelListCore( - listBuilder: (_, __) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - limit: pagination.limit, - ); - - expect(channelListCore.limit, pagination.limit); - }); -} diff --git a/packages/stream_chat_flutter_core/test/channels_bloc_test.dart b/packages/stream_chat_flutter_core/test/channels_bloc_test.dart deleted file mode 100644 index 59d87e667..000000000 --- a/packages/stream_chat_flutter_core/test/channels_bloc_test.dart +++ /dev/null @@ -1,857 +0,0 @@ -// ignore_for_file: deprecated_member_use_from_same_package - -import 'dart:async'; - -import 'package:flutter/widgets.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -import 'matchers/channel_matcher.dart'; -import 'mocks.dart'; - -void main() { - setUpAll(() { - registerFallbackValue(const PaginationParams()); - }); - - List _generateChannels( - StreamChatClient client, { - int count = 3, - int offset = 0, - }) => - List.generate( - count, - (index) { - index = index + offset; - return Channel( - client, - 'testType$index', - 'testId$index', - extraData: {'extra_data_key': 'extra_data_value_$index'}, - ); - }, - ); - - testWidgets( - '''should throw if ChannelsBloc is used where StreamChat is not present in the widget tree''', - (tester) async { - const channelsBlocKey = Key('channelsBloc'); - const childKey = Key('child'); - const channelsBloc = ChannelsBloc( - key: channelsBlocKey, - child: Offstage(key: childKey), - ); - - await tester.pumpWidget(channelsBloc); - - expect(find.byKey(channelsBlocKey), findsNothing); - expect(find.byKey(childKey), findsNothing); - expect(tester.takeException(), isInstanceOf()); - }, - ); - - testWidgets( - 'should render ChannelsBloc if used with StreamChatCore as an ancestor', - (tester) async { - const channelsBlocKey = Key('channelsBloc'); - const childKey = Key('child'); - const channelsBloc = ChannelsBloc( - key: channelsBlocKey, - child: Offstage(key: childKey), - ); - - final mockClient = MockClient(); - - when(() => mockClient.on(any(), any(), any(), any())) - .thenAnswer((_) => const Stream.empty()); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: channelsBloc, - ), - ); - - expect(find.byKey(channelsBlocKey), findsOneWidget); - expect(find.byKey(childKey), findsOneWidget); - }, - ); - - testWidgets( - 'channelsBlocState.queryChannels() should emit data through channelsStream', - (tester) async { - const channelsBlocKey = Key('channelsBloc'); - const childKey = Key('child'); - final channelsBloc = ChannelsBloc( - key: channelsBlocKey, - child: Builder( - key: childKey, - builder: (context) => const Offstage(), - ), - ); - - final mockClient = MockClient(); - - when(() => mockClient.on(any(), any(), any(), any())) - .thenAnswer((_) => const Stream.empty()); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: channelsBloc, - ), - ); - - final channelsBlocState = tester.state( - find.byKey(channelsBlocKey), - ); - - final offlineChannels = _generateChannels(mockClient); - final onlineChannels = _generateChannels(mockClient, offset: 3); - - when(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: any(named: 'paginationParams'), - )).thenAnswer( - (_) => Stream.fromIterable([offlineChannels, onlineChannels]), - ); - - channelsBlocState.queryChannels(); - - await expectLater( - channelsBlocState.channelsStream, - emitsInOrder([ - isSameChannelListAs(offlineChannels), - isSameChannelListAs(onlineChannels), - ]), - ); - - verify(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: any(named: 'paginationParams'), - )).called(1); - }, - ); - - testWidgets( - 'channelsBlocState.channelsStream should emit error ' - 'if client.queryChannels() throws', - (tester) async { - const channelsBlocKey = Key('channelsBloc'); - const childKey = Key('child'); - final channelsBloc = ChannelsBloc( - key: channelsBlocKey, - child: Builder( - key: childKey, - builder: (context) => const Offstage(), - ), - ); - - final mockClient = MockClient(); - - when(() => mockClient.on(any(), any(), any(), any())) - .thenAnswer((_) => const Stream.empty()); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: channelsBloc, - ), - ); - - final channelsBlocState = tester.state( - find.byKey(channelsBlocKey), - ); - - const error = 'Error! Error! Error!'; - - when(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: any(named: 'paginationParams'), - )).thenThrow(error); - - channelsBlocState.queryChannels(); - - await expectLater( - channelsBlocState.channelsStream, - emitsError(error), - ); - - verify(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: any(named: 'paginationParams'), - )).called(1); - }, - ); - - testWidgets( - 'calling channelsBlocState.queryChannels() again with an offset ' - 'should emit new data through channelsStream and also emit loading state ' - 'through queryChannelsLoading', - (tester) async { - const channelsBlocKey = Key('channelsBloc'); - const channelsBloc = ChannelsBloc( - key: channelsBlocKey, - child: Offstage(), - ); - - final mockClient = MockClient(); - - when(() => mockClient.on(any(), any(), any(), any())) - .thenAnswer((_) => const Stream.empty()); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: channelsBloc, - ), - ); - - final channelsBlocState = tester.state( - find.byKey(channelsBlocKey), - ); - - final channels = _generateChannels(mockClient); - - when(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: any(named: 'paginationParams'), - )).thenAnswer((_) => Stream.value(channels)); - - const pagination = PaginationParams(limit: 3); - channelsBlocState.queryChannels( - paginationParams: pagination, - ); - - await expectLater( - channelsBlocState.channelsStream, - emits(isSameChannelListAs(channels)), - ); - - verify(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: any(named: 'paginationParams'), - )).called(1); - - final offset = channels.length; - final paginationParams = pagination.copyWith(offset: offset); - - final newChannels = _generateChannels(mockClient, offset: offset); - - when(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: paginationParams, - )).thenAnswer( - (_) => Stream.value(newChannels), - ); - - channelsBlocState.queryChannels(paginationParams: paginationParams); - - await Future.wait([ - expectLater( - channelsBlocState.queryChannelsLoading, - emitsInOrder([true, false]), - ), - expectLater( - channelsBlocState.channelsStream, - emits(isSameChannelListAs(channels + newChannels)), - ), - ]); - - verify(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: paginationParams, - )).called(1); - }, - ); - - testWidgets( - 'calling channelsBlocState.queryChannels() again with an offset ' - 'should emit error through queryChannelsLoading if ' - 'client.queryChannels() throws', - (tester) async { - const channelsBlocKey = Key('channelsBloc'); - const channelsBloc = ChannelsBloc( - key: channelsBlocKey, - child: Offstage(), - ); - - final mockClient = MockClient(); - - when(() => mockClient.on(any(), any(), any(), any())) - .thenAnswer((_) => const Stream.empty()); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: channelsBloc, - ), - ); - - final channelsBlocState = tester.state( - find.byKey(channelsBlocKey), - ); - - final channels = _generateChannels(mockClient); - const paginationParams = PaginationParams( - limit: 3, - ); - - when(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: paginationParams, - )).thenAnswer((_) => Stream.value(channels)); - - channelsBlocState.queryChannels( - paginationParams: paginationParams, - ); - - await expectLater( - channelsBlocState.channelsStream, - emits(isSameChannelListAs(channels)), - ); - - verify(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: paginationParams, - )).called(1); - - const error = 'Error! Error! Error!'; - - when(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: paginationParams, - )).thenThrow(error); - - channelsBlocState.queryChannels(paginationParams: paginationParams); - - await expectLater( - channelsBlocState.queryChannelsLoading, - emitsError(error), - ); - - verify(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: paginationParams, - )).called(1); - }, - ); - - group('event controller test', () { - late StreamController eventController; - setUp(() { - eventController = StreamController.broadcast(); - }); - - testWidgets( - 'channel should get hide when EventType.channelHidden event is received', - (tester) async { - final mockClient = MockClient(); - const channelsBlocKey = Key('channelsBloc'); - const channelsBloc = ChannelsBloc( - key: channelsBlocKey, - child: Offstage(), - ); - - when(() => mockClient.on(any(), any(), any(), any())) - .thenAnswer((_) => const Stream.empty()); - - when(() => mockClient.on( - EventType.channelHidden, - )).thenAnswer((_) => eventController.stream); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: channelsBloc, - ), - ); - - final channelsBlocState = tester.state( - find.byKey(channelsBlocKey), - ); - - final channels = _generateChannels(mockClient); - - when(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: any(named: 'paginationParams'), - )).thenAnswer( - (_) => Stream.value(channels), - ); - - await channelsBlocState.queryChannels(); - - verify(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: any(named: 'paginationParams'), - )).called(1); - - final channelHiddenEvent = Event( - type: EventType.channelHidden, - cid: channels.first.cid, - ); - - eventController.add(channelHiddenEvent); - - final newChannels = [...channels] - ..removeWhere((it) => it.cid == channelHiddenEvent.cid); - - await expectLater( - channelsBlocState.channelsStream, - emitsInOrder([ - isSameChannelListAs(channels), - isSameChannelListAs(newChannels), - ]), - ); - - verify(() => mockClient.on(EventType.channelHidden)).called(1); - }, - ); - - testWidgets( - 'event channel should be moved to top of the list if present when ' - 'EventType.messageNew event is received', - (tester) async { - final mockClient = MockClient(); - const channelsBlocKey = Key('channelsBloc'); - const channelsBloc = ChannelsBloc( - key: channelsBlocKey, - child: Offstage(), - ); - - when(() => mockClient.on(any(), any(), any(), any())) - .thenAnswer((_) => const Stream.empty()); - - when(() => mockClient.on( - EventType.messageNew, - )).thenAnswer((_) => eventController.stream); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: channelsBloc, - ), - ); - - final channelsBlocState = tester.state( - find.byKey(channelsBlocKey), - ); - - final channels = _generateChannels(mockClient); - - when(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: any(named: 'paginationParams'), - )).thenAnswer( - (_) => Stream.value(channels), - ); - - await channelsBlocState.queryChannels(); - - verify(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: any(named: 'paginationParams'), - )).called(1); - - final messageNewEvent = Event( - type: EventType.messageNew, - cid: channels.last.cid, - ); - - eventController.add(messageNewEvent); - - final channelCid = messageNewEvent.cid; - final index = channels.indexWhere((it) => it.cid == channelCid); - final updatedChannel = channels[index]; - final newChannels = [...channels] - ..removeAt(index) - ..insert(0, updatedChannel); - - await expectLater( - channelsBlocState.channelsStream, - emitsInOrder([ - isSameChannelListAs(channels), - isSameChannelListAs(newChannels), - ]), - ); - - verify(() => mockClient.on(EventType.messageNew)).called(1); - }, - ); - - testWidgets( - 'event channel should be moved to top of the list if present inside ' - 'hiddenChannels list and shouldAddChannel is true when ' - 'EventType.messageNew event is received', - (tester) async { - final hiddenChannelEventController = StreamController(); - - addTearDown(hiddenChannelEventController.close); - - final mockClient = MockClient(); - final channels = _generateChannels(mockClient); - const channelsBlocKey = Key('channelsBloc'); - final channelsBloc = ChannelsBloc( - key: channelsBlocKey, - shouldAddChannel: (e) => channels.map((it) => it.cid).contains(e.cid), - child: const Offstage(), - ); - - when(() => mockClient.on(any(), any(), any(), any())) - .thenAnswer((_) => const Stream.empty()); - - when(() => mockClient.on( - EventType.channelHidden, - )).thenAnswer((_) => hiddenChannelEventController.stream); - - when(() => mockClient.on( - EventType.messageNew, - )).thenAnswer((_) => eventController.stream); - - final messageNewEvent = Event( - type: EventType.messageNew, - cid: channels.last.cid, - ); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: channelsBloc, - ), - ); - - final channelsBlocState = tester.state( - find.byKey(channelsBlocKey), - ); - - when(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: any(named: 'paginationParams'), - )).thenAnswer( - (_) => Stream.value(channels), - ); - - await channelsBlocState.queryChannels(); - - verify(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: any(named: 'paginationParams'), - )).called(1); - - final channelHiddenEvent = Event( - type: EventType.channelHidden, - cid: channels.last.cid, - ); - - // Hiding the channel before passing messageNew event - hiddenChannelEventController.add(channelHiddenEvent); - - final channelsAfterHiddenEvent = [...channels]..removeLast(); - - eventController.add(messageNewEvent); - - final channelCid = messageNewEvent.cid; - final index = channels.indexWhere((it) => it.cid == channelCid); - final newChannels = [...channels] - ..removeAt(index) - ..insert(0, channels[index]); - - await expectLater( - channelsBlocState.channelsStream, - emitsInOrder([ - isSameChannelListAs(channels), - isSameChannelListAs(channelsAfterHiddenEvent), - isSameChannelListAs(newChannels), - ]), - ); - - verify(() => mockClient.on(EventType.channelHidden)).called(1); - verify(() => mockClient.on(EventType.messageNew)).called(1); - }, - ); - - testWidgets( - 'event channel should be moved to top of the list if present inside ' - 'channel state and shouldAddChannel is true when ' - 'EventType.messageNew event is received', - (tester) async { - final mockClient = MockClient(); - final channels = _generateChannels(mockClient); - final stateChannels = { - for (var c in _generateChannels(mockClient, offset: 5)) c.cid!: c - }; - const channelsBlocKey = Key('channelsBloc'); - final channelsBloc = ChannelsBloc( - key: channelsBlocKey, - shouldAddChannel: (_) => true, - child: const Offstage(), - ); - - when(() => mockClient.state.channels).thenReturn(stateChannels); - - when(() => mockClient.on(any(), any(), any(), any())) - .thenAnswer((_) => const Stream.empty()); - - when(() => mockClient.on( - EventType.messageNew, - )).thenAnswer((_) => eventController.stream); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: channelsBloc, - ), - ); - - final channelsBlocState = tester.state( - find.byKey(channelsBlocKey), - ); - - when(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: any(named: 'paginationParams'), - )).thenAnswer( - (_) => Stream.value(channels), - ); - - await channelsBlocState.queryChannels(); - - verify(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: any(named: 'paginationParams'), - )).called(1); - - final messageNewEvent = Event( - type: EventType.messageNew, - cid: stateChannels.keys.first, - ); - - eventController.add(messageNewEvent); - - final newChannels = [...channels] - ..insert(0, stateChannels[stateChannels.keys.first]!); - - await expectLater( - channelsBlocState.channelsStream, - emitsInOrder([ - isSameChannelListAs(channels), - isSameChannelListAs(newChannels), - ]), - ); - - verify(() => mockClient.on(EventType.messageNew)).called(1); - }, - ); - - testWidgets( - 'channels should get sorted according to channelsComparator when ' - 'EventType.messageNew event is received', - (tester) async { - final mockClient = MockClient(); - final channels = _generateChannels(mockClient); - int channelComparator(Channel a, Channel b) { - final aData = a.extraData['extra_data_key'].toString(); - final bData = b.extraData['extra_data_key'].toString(); - return bData.compareTo(aData); - } - - const channelsBlocKey = Key('channelsBloc'); - final channelsBloc = ChannelsBloc( - key: channelsBlocKey, - shouldAddChannel: (_) => true, - channelsComparator: channelComparator, - child: const Offstage(), - ); - - when(() => mockClient.on(any(), any(), any(), any())) - .thenAnswer((_) => const Stream.empty()); - - when(() => mockClient.on( - EventType.messageNew, - )).thenAnswer((_) => eventController.stream); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: channelsBloc, - ), - ); - - final channelsBlocState = tester.state( - find.byKey(channelsBlocKey), - ); - - when(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: any(named: 'paginationParams'), - )).thenAnswer( - (_) => Stream.value(channels), - ); - - await channelsBlocState.queryChannels(); - - verify(() => mockClient.queryChannels( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - memberLimit: any(named: 'memberLimit'), - messageLimit: any(named: 'messageLimit'), - paginationParams: any(named: 'paginationParams'), - )).called(1); - - final messageNewEvent = Event( - type: EventType.messageNew, - cid: channels.first.cid, - ); - - eventController.add(messageNewEvent); - - final newChannels = [...channels]..sort(channelComparator); - - await expectLater( - channelsBlocState.channelsStream, - emitsInOrder([ - isSameChannelListAs(channels), - isSameChannelListAs(newChannels), - ]), - ); - - verify(() => mockClient.on(EventType.messageNew)).called(1); - }, - ); - - tearDown(() { - eventController.close(); - }); - }); -} diff --git a/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart b/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart deleted file mode 100644 index c1465731a..000000000 --- a/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart +++ /dev/null @@ -1,400 +0,0 @@ -// ignore_for_file: deprecated_member_use_from_same_package - -import 'package:flutter/widgets.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -import 'matchers/get_message_response_matcher.dart'; -import 'mocks.dart'; - -const testFilter = Filter.custom(key: 'test', value: 'testValue'); - -void main() { - List _generateMessages({ - int count = 3, - int offset = 0, - }) => - List.generate( - count, - (index) { - index = index + offset; - return GetMessageResponse() - ..message = Message( - id: 'testId$index', - text: 'testTextData$index', - ) - ..channel = ChannelModel( - cid: 'testCid:id', - ); - }, - ); - - testWidgets( - '''messageSearchBlocState.search() should throw if used where StreamChat is not present in the widget tree''', - (tester) async { - const messageSearchBloc = MessageSearchBloc( - child: Offstage(), - ); - - await tester.pumpWidget(messageSearchBloc); - expect(tester.takeException(), isInstanceOf()); - }, - ); - - testWidgets( - 'messageSearchBlocState.search() should emit data through usersStream', - (tester) async { - const messageSearchBlocKey = Key('messageSearchBloc'); - const childKey = Key('child'); - const messageSearchBloc = MessageSearchBloc( - key: messageSearchBlocKey, - child: Offstage(key: childKey), - ); - - final mockClient = MockClient(); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: messageSearchBloc, - ), - ); - final messageSearchBlocState = tester.state( - find.byKey(messageSearchBlocKey), - ); - - final messageResponseList = _generateMessages(); - - when(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), - paginationParams: any(named: 'paginationParams'), - )).thenAnswer( - (_) async => SearchMessagesResponse() - ..results = messageResponseList - ..next = null - ..previous = null, - ); - - messageSearchBlocState.search(filter: testFilter); - - await expectLater( - messageSearchBlocState.messagesStream, - emits(isSameMessageResponseListAs(messageResponseList)), - ); - - verify(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), - paginationParams: any(named: 'paginationParams'), - )).called(1); - }, - ); - - testWidgets( - '''messageSearchBlocState.messagesStream should emit error if client.search() throws''', - (tester) async { - const messageSearchBlocKey = Key('messageSearchBloc'); - const childKey = Key('child'); - const messageSearchBloc = MessageSearchBloc( - key: messageSearchBlocKey, - child: Offstage(key: childKey), - ); - - final mockClient = MockClient(); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: messageSearchBloc, - ), - ); - final messageSearchBlocState = tester.state( - find.byKey(messageSearchBlocKey), - ); - - const error = 'Error! Error! Error!'; - when(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), - paginationParams: any(named: 'paginationParams'), - )).thenThrow(error); - - messageSearchBlocState.search(filter: testFilter); - - await expectLater( - messageSearchBlocState.messagesStream, - emitsError(error), - ); - - verify(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), - paginationParams: any(named: 'paginationParams'), - )).called(1); - }, - ); - - testWidgets( - '''calling messageSearchBlocState.search() again with an offset should emit new data through messagesStream and also emit loading state through queryMessagesLoading''', - (tester) async { - const messageSearchBlocKey = Key('messageSearchBloc'); - const childKey = Key('child'); - const messageSearchBloc = MessageSearchBloc( - key: messageSearchBlocKey, - child: Offstage(key: childKey), - ); - - final mockClient = MockClient(); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: messageSearchBloc, - ), - ); - - final messageSearchBlocState = tester.state( - find.byKey(messageSearchBlocKey), - ); - - const pagination = PaginationParams(limit: 25); - final messageResponseList = _generateMessages(count: 25); - - when(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), - paginationParams: pagination, - )).thenAnswer( - (_) async => SearchMessagesResponse() - ..results = messageResponseList - ..next = null - ..previous = null, - ); - - messageSearchBlocState.search(pagination: pagination, filter: testFilter); - - await expectLater( - messageSearchBlocState.messagesStream, - emits(isSameMessageResponseListAs(messageResponseList)), - ); - - verify(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), - paginationParams: pagination, - )).called(1); - - final offset = messageResponseList.length; - final paginatedMessageResponseList = _generateMessages(offset: offset); - final newPagination = pagination.copyWith(offset: offset); - - when(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), - paginationParams: newPagination, - )).thenAnswer( - (_) async => SearchMessagesResponse() - ..results = paginatedMessageResponseList - ..next = null - ..previous = null, - ); - - messageSearchBlocState.search(pagination: pagination, filter: testFilter); - - await Future.wait([ - expectLater( - messageSearchBlocState.queryMessagesLoading, - emitsInOrder([true, false]), - ), - expectLater( - messageSearchBlocState.messagesStream, - emits(isSameMessageResponseListAs( - messageResponseList + paginatedMessageResponseList, - )), - ), - ]); - - verify(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), - paginationParams: pagination, - )).called(1); - }, - ); - - testWidgets( - '''calling messageSearchBlocState.search() again with an offset should emit error through queryUsersLoading if client.search() throws''', - (tester) async { - const messageSearchBlocKey = Key('messageSearchBloc'); - const childKey = Key('child'); - const messageSearchBloc = MessageSearchBloc( - key: messageSearchBlocKey, - child: Offstage(key: childKey), - ); - - final mockClient = MockClient(); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: messageSearchBloc, - ), - ); - - final messageSearchBlocState = tester.state( - find.byKey(messageSearchBlocKey), - ); - - const pagination = PaginationParams(limit: 25); - final messageResponseList = _generateMessages(count: 25); - - when(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), - paginationParams: pagination, - )).thenAnswer( - (_) async => SearchMessagesResponse() - ..results = messageResponseList - ..next = null - ..previous = null, - ); - - messageSearchBlocState.search(pagination: pagination, filter: testFilter); - - await expectLater( - messageSearchBlocState.messagesStream, - emits(isSameMessageResponseListAs(messageResponseList)), - ); - - verify(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), - paginationParams: pagination, - )).called(1); - - final offset = messageResponseList.length; - final newPagination = pagination.copyWith(offset: offset); - - const error = 'Error! Error! Error!'; - when(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), - paginationParams: newPagination, - )).thenThrow(error); - - messageSearchBlocState.search( - pagination: newPagination, - filter: testFilter, - ); - - await expectLater( - messageSearchBlocState.queryMessagesLoading, - emitsError(error), - ); - - verify(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), - paginationParams: newPagination, - )).called(1); - }, - ); - - testWidgets( - '''calling messageSearchBlocState.search() again with an offset should do nothing and return if pagination is completed''', - (tester) async { - const messageSearchBlocKey = Key('messageSearchBloc'); - const childKey = Key('child'); - const messageSearchBloc = MessageSearchBloc( - key: messageSearchBlocKey, - child: Offstage(key: childKey), - ); - - final mockClient = MockClient(); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: messageSearchBloc, - ), - ); - - final messageSearchBlocState = tester.state( - find.byKey(messageSearchBlocKey), - ); - - const pagination = PaginationParams(limit: 25); - - final messageResponseList = _generateMessages(count: 20); - - when(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), - paginationParams: pagination, - )).thenAnswer( - (_) async => SearchMessagesResponse() - ..results = messageResponseList - ..next = null - ..previous = null, - ); - - messageSearchBlocState.search(pagination: pagination, filter: testFilter); - - await expectLater( - messageSearchBlocState.messagesStream, - emits(isSameMessageResponseListAs(messageResponseList)), - ); - - verify(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), - paginationParams: pagination, - )).called(1); - - final offset = messageResponseList.length; - final newPagination = pagination.copyWith(offset: offset); - - messageSearchBlocState.search( - filter: testFilter, - pagination: newPagination, - ); - - // should emit nothing. - await expectLater( - // skipping the initial data (behaviorSubject). - messageSearchBlocState.messagesStream.skip(1), - emitsInOrder([]), - ); - }, - ); -} diff --git a/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart b/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart deleted file mode 100644 index c56e377bb..000000000 --- a/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart +++ /dev/null @@ -1,571 +0,0 @@ -// ignore_for_file: deprecated_member_use_from_same_package - -import 'package:flutter/widgets.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter_core/src/message_search_list_core.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -import 'mocks.dart'; - -const testFilter = Filter.custom(key: 'test', value: 'testValue'); -const testMessageFilter = Filter.custom(key: 'test', value: 'testValue'); - -void main() { - List _generateMessages({ - int count = 3, - int offset = 0, - }) => - List.generate( - count, - (index) { - index = index + offset; - return GetMessageResponse() - ..message = Message( - id: 'testId$index', - text: 'testTextData$index', - ) - ..channel = ChannelModel( - cid: 'test:Cid', - ); - }, - ); - - testWidgets( - 'should throw if both `messageQuery` and `messageFilters` are provided', - (tester) async { - expect( - () => MessageSearchListCore( - childBuilder: (_) => const Offstage(), - loadingBuilder: (_) => const Offstage(), - emptyBuilder: (_) => const Offstage(), - errorBuilder: (_, __) => const Offstage(), - filters: testFilter, - messageFilters: testMessageFilter, - messageQuery: 'test', - ), - throwsAssertionError, - ); - }, - ); - - testWidgets( - 'should throw if both `messageQuery` and `messageFilters` are not provided', - (tester) async { - expect( - () => MessageSearchListCore( - childBuilder: (_) => const Offstage(), - loadingBuilder: (_) => const Offstage(), - emptyBuilder: (_) => const Offstage(), - errorBuilder: (_, __) => const Offstage(), - filters: testFilter, - ), - throwsAssertionError, - ); - }, - ); - - testWidgets( - 'should throw if MessageSearchListCore is used where MessageSearchBloc ' - 'is not present in the widget tree', - (tester) async { - const messageSearchListCoreKey = Key('messageSearchListCore'); - final messageSearchListCore = MessageSearchListCore( - key: messageSearchListCoreKey, - childBuilder: (List? messages) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object? error) => const Offstage(), - filters: testFilter, - messageFilters: testMessageFilter, - ); - - await tester.pumpWidget(messageSearchListCore); - - expect(find.byKey(messageSearchListCoreKey), findsNothing); - expect(tester.takeException(), isInstanceOf()); - }, - ); - - testWidgets( - 'should render MessageSearchListCore if used with ' - 'MessageSearchListCore as an ancestor', - (tester) async { - const messageSearchListCoreKey = Key('messageSearchListCore'); - final messageSearchListCore = MessageSearchListCore( - key: messageSearchListCoreKey, - childBuilder: (List messages) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object? error) => const Offstage(), - filters: testFilter, - messageFilters: testMessageFilter, - ); - - final mockClient = MockClient(); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: MessageSearchBloc( - child: messageSearchListCore, - ), - ), - ); - - expect(find.byKey(messageSearchListCoreKey), findsOneWidget); - }, - ); - - testWidgets( - 'should assign loadData and paginateData callback to ' - 'UserListController if passed', - (tester) async { - const messageSearchListCoreKey = Key('messageSearchListCore'); - final controller = MessageSearchListController(); - final messageSearchListCore = MessageSearchListCore( - key: messageSearchListCoreKey, - childBuilder: (List messages) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - messageSearchListController: controller, - filters: testFilter, - messageFilters: testMessageFilter, - ); - - expect(controller.loadData, isNull); - expect(controller.paginateData, isNull); - - final mockClient = MockClient(); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: MessageSearchBloc( - child: messageSearchListCore, - ), - ), - ); - await tester.pumpAndSettle(); - - expect(find.byKey(messageSearchListCoreKey), findsOneWidget); - expect(controller.loadData, isNotNull); - expect(controller.paginateData, isNotNull); - }, - ); - - testWidgets( - 'should build error widget if messageSearchBloc.messagesStream emits error', - (tester) async { - const messageSearchListCoreKey = Key('messageSearchListCore'); - const errorWidgetKey = Key('errorWidget'); - final messageSearchListCore = MessageSearchListCore( - key: messageSearchListCoreKey, - childBuilder: (List messages) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage( - key: errorWidgetKey, - ), - filters: testFilter, - messageFilters: testMessageFilter, - ); - - final mockClient = MockClient(); - - const error = 'Error! Error! Error!'; - when(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: testMessageFilter, - paginationParams: any(named: 'paginationParams'), - )).thenThrow(error); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: MessageSearchBloc( - child: messageSearchListCore, - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(errorWidgetKey), findsOneWidget); - - verify(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: testMessageFilter, - paginationParams: any(named: 'paginationParams'), - )).called(1); - }, - ); - - testWidgets( - 'should build empty widget if messageSearchBloc.messagesStream ' - 'emits empty data', - (tester) async { - const messageSearchListCoreKey = Key('messageSearchListCore'); - const emptyWidgetKey = Key('emptyWidget'); - final messageSearchListCore = MessageSearchListCore( - key: messageSearchListCoreKey, - childBuilder: (List messages) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => - const Offstage(key: emptyWidgetKey), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - filters: testFilter, - messageFilters: testMessageFilter, - ); - - final mockClient = MockClient(); - - final messageResponseList = []; - when(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: testMessageFilter, - paginationParams: any(named: 'paginationParams'), - )).thenAnswer( - (_) async => SearchMessagesResponse() - ..results = messageResponseList - ..next = null - ..previous = null, - ); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: MessageSearchBloc( - child: messageSearchListCore, - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(emptyWidgetKey), findsOneWidget); - - verify(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: testMessageFilter, - paginationParams: any(named: 'paginationParams'), - )).called(1); - }, - ); - - testWidgets( - 'should build child widget if usersBlocState.usersStream emits some data', - (tester) async { - const messageSearchListCoreKey = Key('messageSearchListCore'); - const childWidgetKey = Key('childWidget'); - final messageSearchListCore = MessageSearchListCore( - key: messageSearchListCoreKey, - childBuilder: (List messages) => const Offstage( - key: childWidgetKey, - ), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - filters: testFilter, - messageFilters: testMessageFilter, - ); - - final mockClient = MockClient(); - - final messageResponseList = _generateMessages(); - when(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: testMessageFilter, - paginationParams: any(named: 'paginationParams'), - )).thenAnswer( - (_) async => SearchMessagesResponse() - ..results = messageResponseList - ..next = null - ..previous = null, - ); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: MessageSearchBloc( - child: messageSearchListCore, - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(childWidgetKey), findsOneWidget); - - verify(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: testMessageFilter, - paginationParams: any(named: 'paginationParams'), - )).called(1); - }, - ); - - testWidgets( - 'should build child widget with paginated data ' - 'on calling channelListCoreState.paginateData', - (tester) async { - const messageSearchListCoreKey = Key('messageSearchListCore'); - const childWidgetKey = Key('childWidget'); - const pagination = PaginationParams(limit: 25); - final messageSearchListCore = MessageSearchListCore( - key: messageSearchListCoreKey, - childBuilder: (List messages) => Container( - key: childWidgetKey, - child: Text( - messages.map((e) => '${e.channel?.cid}-${e.message.id}').join(','), - ), - ), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - limit: pagination.limit, - filters: testFilter, - messageFilters: testMessageFilter, - ); - - final mockClient = MockClient(); - - final messageResponseList = _generateMessages(count: 25); - when(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: testMessageFilter, - paginationParams: pagination, - )).thenAnswer( - (_) async => SearchMessagesResponse() - ..results = messageResponseList - ..next = null - ..previous = null, - ); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: StreamChatCore( - client: mockClient, - child: MessageSearchBloc( - child: messageSearchListCore, - ), - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(childWidgetKey), findsOneWidget); - expect( - find.text( - messageResponseList - .map((e) => '${e.channel?.cid}-${e.message.id}') - .join(','), - ), - findsOneWidget, - ); - - verify(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: testMessageFilter, - paginationParams: pagination, - )).called(1); - - final messageSearchListCoreState = - tester.state( - find.byKey(messageSearchListCoreKey), - ); - - final offset = messageResponseList.length; - final paginatedMessageResponseList = _generateMessages(offset: offset); - final updatedPagination = pagination.copyWith(offset: offset); - when(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: testMessageFilter, - paginationParams: updatedPagination, - )).thenAnswer( - (_) async => SearchMessagesResponse() - ..results = paginatedMessageResponseList - ..next = null - ..previous = null, - ); - - await messageSearchListCoreState.paginateData(); - - await tester.pumpAndSettle(); - - expect(find.byKey(childWidgetKey), findsOneWidget); - expect( - find.text([ - ...messageResponseList, - ...paginatedMessageResponseList, - ].map((e) => '${e.channel?.cid}-${e.message.id}').join(',')), - findsOneWidget, - ); - - verify(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: testMessageFilter, - paginationParams: updatedPagination, - )).called(1); - }, - ); - - testWidgets( - 'should rebuild MessageSearchListCore with updated widget data ' - 'on calling setState()', - (tester) async { - const pagination = PaginationParams(); - - StateSetter? _stateSetter; - var limit = pagination.limit; - - const messageSearchListCoreKey = Key('messageSearchListCore'); - const childWidgetKey = Key('childWidget'); - MessageSearchListCore messageSearchListCoreBuilder(int limit) => - MessageSearchListCore( - key: messageSearchListCoreKey, - childBuilder: (List messages) => Container( - key: childWidgetKey, - child: Text( - messages - .map((e) => '${e.channel?.cid}-${e.message.id}') - .join(','), - ), - ), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => - const Offstage(), - limit: limit, - filters: testFilter, - messageFilters: testMessageFilter, - ); - - final mockClient = MockClient(); - - final messageResponseList = _generateMessages(); - when(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: testMessageFilter, - paginationParams: pagination, - )).thenAnswer( - (_) async => SearchMessagesResponse() - ..results = messageResponseList - ..next = null - ..previous = null, - ); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: StreamChatCore( - client: mockClient, - child: MessageSearchBloc( - child: StatefulBuilder(builder: (context, stateSetter) { - // Assigning stateSetter for rebuilding UserListCore - _stateSetter = stateSetter; - return messageSearchListCoreBuilder(limit); - }), - ), - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(childWidgetKey), findsOneWidget); - expect( - find.text( - messageResponseList - .map((e) => '${e.channel?.cid}-${e.message.id}') - .join(','), - ), - findsOneWidget, - ); - - verify(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: testMessageFilter, - paginationParams: pagination, - )).called(1); - - // Rebuilding MessageSearchListCore with new pagination limit - _stateSetter?.call(() => limit = 6); - - final updatedMessageResponseList = _generateMessages(count: limit); - final updatedPagination = PaginationParams(limit: limit); - when(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: testMessageFilter, - paginationParams: updatedPagination, - )).thenAnswer( - (_) async => SearchMessagesResponse() - ..results = updatedMessageResponseList - ..next = null - ..previous = null, - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(childWidgetKey), findsOneWidget); - expect( - find.text(updatedMessageResponseList - .map((e) => '${e.channel?.cid}-${e.message.id}') - .join(',')), - findsOneWidget, - ); - - verify(() => mockClient.search( - testFilter, - query: any(named: 'query'), - sort: any(named: 'sort'), - messageFilters: testMessageFilter, - paginationParams: updatedPagination, - )).called(1); - }, - ); - - test('`widget.limit` should match `widget.pagination.limit`', () { - const pagination = PaginationParams(limit: 30); - final messageSearchListCore = MessageSearchListCore( - childBuilder: (List messages) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object? error) => const Offstage(), - filters: testFilter, - messageFilters: testMessageFilter, - limit: pagination.limit, - ); - - expect(messageSearchListCore.limit, pagination.limit); - }); -} diff --git a/packages/stream_chat_flutter_core/test/user_list_core_test.dart b/packages/stream_chat_flutter_core/test/user_list_core_test.dart deleted file mode 100644 index 89d0cdbda..000000000 --- a/packages/stream_chat_flutter_core/test/user_list_core_test.dart +++ /dev/null @@ -1,538 +0,0 @@ -// ignore_for_file: deprecated_member_use_from_same_package - -import 'package:flutter/widgets.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter_core/src/user_list_core.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -import 'mocks.dart'; - -void main() { - const alphabets = 'abcdefghijklmnopqrstuvwxyz'; - - List _generateUsers({ - int count = 3, - int offset = 0, - }) => - List.generate( - count, - (index) { - index = index + offset; - return User( - id: 'testId$index', - role: 'testRole$index', - createdAt: DateTime.now(), - updatedAt: DateTime.now(), - lastActive: DateTime.now(), - online: true, - extraData: { - 'name': '${alphabets[index]}-testName', - }, - ); - }, - ); - - testWidgets( - 'should throw if UserListCore is used where UsersBloc is not present ' - 'in the widget tree', - (tester) async { - const userListCoreKey = Key('userListCore'); - final userListCore = UserListCore( - key: userListCoreKey, - listBuilder: (_, __) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - ); - - await tester.pumpWidget(userListCore); - - expect(find.byKey(userListCoreKey), findsNothing); - expect(tester.takeException(), isInstanceOf()); - }, - ); - - testWidgets( - 'should render UserListCore if used with UsersBloc as an ancestor', - (tester) async { - const userListCoreKey = Key('userListCore'); - final userListCore = UserListCore( - key: userListCoreKey, - listBuilder: (_, __) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - ); - - final mockClient = MockClient(); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: UsersBloc( - child: userListCore, - ), - ), - ); - - expect(find.byKey(userListCoreKey), findsOneWidget); - }, - ); - - testWidgets( - 'should assign loadData and paginateData callback to ' - 'UserListController if passed', - (tester) async { - const userListCoreKey = Key('userListCore'); - final controller = UserListController(); - final userListCore = UserListCore( - key: userListCoreKey, - listBuilder: (_, __) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - userListController: controller, - ); - - expect(controller.loadData, isNull); - expect(controller.paginateData, isNull); - - final mockClient = MockClient(); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: UsersBloc( - child: userListCore, - ), - ), - ); - - expect(find.byKey(userListCoreKey), findsOneWidget); - expect(controller.loadData, isNotNull); - expect(controller.paginateData, isNotNull); - }, - ); - - testWidgets( - 'should build error widget if usersBlocState.usersStream emits error', - (tester) async { - const userListCoreKey = Key('userListCore'); - const errorWidgetKey = Key('errorWidget'); - final userListCore = UserListCore( - key: userListCoreKey, - listBuilder: (_, __) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => - Container(key: errorWidgetKey), - ); - - final mockClient = MockClient(); - - const error = 'Error! Error! Error!'; - when(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: any(named: 'pagination'), - )).thenThrow(error); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: UsersBloc( - child: userListCore, - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(errorWidgetKey), findsOneWidget); - - verify(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: any(named: 'pagination'), - )).called(1); - }, - ); - - testWidgets( - 'should build empty widget if usersBlocState.usersStream emits empty data', - (tester) async { - const userListCoreKey = Key('userListCore'); - const emptyWidgetKey = Key('emptyWidget'); - final userListCore = UserListCore( - key: userListCoreKey, - listBuilder: (_, __) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => Container(key: emptyWidgetKey), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - ); - - final mockClient = MockClient(); - - const users = []; - when(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: any(named: 'pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: UsersBloc( - child: userListCore, - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(emptyWidgetKey), findsOneWidget); - - verify(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: any(named: 'pagination'), - )).called(1); - }, - ); - - testWidgets( - 'should build list widget if usersBlocState.usersStream emits some data', - (tester) async { - const userListCoreKey = Key('userListCore'); - const listWidgetKey = Key('listWidget'); - final userListCore = UserListCore( - key: userListCoreKey, - listBuilder: (_, __) => Container(key: listWidgetKey), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - ); - - final mockClient = MockClient(); - - final users = _generateUsers(); - when(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: any(named: 'pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: UsersBloc( - child: userListCore, - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(listWidgetKey), findsOneWidget); - - verify(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: any(named: 'pagination'), - )).called(1); - }, - ); - - testWidgets( - 'should build list widget with grouped data if groupAlphabetically is true', - (tester) async { - const userListCoreKey = Key('userListCore'); - const listWidgetKey = Key('listWidget'); - final userListCore = UserListCore( - key: userListCoreKey, - listBuilder: (_, items) => Container( - key: listWidgetKey, - child: ListView( - children: items - .map((e) => Container( - key: Key(e.key ?? ''), - child: e.when( - headerItem: Text.new, - userItem: (user) => Text(user.id), - ), - )) - .toList(growable: false), - ), - ), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - groupAlphabetically: true, - ); - - final mockClient = MockClient(); - - final users = _generateUsers(); - when(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: any(named: 'pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: StreamChatCore( - client: mockClient, - child: UsersBloc( - child: userListCore, - ), - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(listWidgetKey), findsOneWidget); - for (final user in users) { - expect(find.byKey(Key('HEADER-${user.name[0]}')), findsOneWidget); - expect(find.byKey(Key('USER-${user.id}')), findsOneWidget); - } - - verify(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: any(named: 'pagination'), - )).called(1); - }, - ); - - testWidgets( - 'should build list widget with paginated data ' - 'on calling channelListCoreState.paginateData', - (tester) async { - const userListCoreKey = Key('userListCore'); - const listWidgetKey = Key('listWidget'); - const pagination = PaginationParams(limit: 15); - final userListCore = UserListCore( - key: userListCoreKey, - listBuilder: (_, items) => Container( - key: listWidgetKey, - child: ListView( - children: items - .map((e) => Container( - key: Key(e.key ?? ''), - child: e.when( - headerItem: Text.new, - userItem: (user) => Text(user.id), - ), - )) - .toList(growable: false), - ), - ), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - limit: pagination.limit, - groupAlphabetically: true, - ); - - final mockClient = MockClient(); - - final users = _generateUsers(count: 15); - when(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: any(named: 'pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: StreamChatCore( - client: mockClient, - child: UsersBloc( - child: userListCore, - ), - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(listWidgetKey), findsOneWidget); - for (final user in users) { - expect(find.byKey(Key('HEADER-${user.name[0]}')), findsOneWidget); - expect(find.byKey(Key('USER-${user.id}')), findsOneWidget); - } - - verify(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: any(named: 'pagination'), - )).called(1); - - final userListCoreState = tester.state( - find.byKey(userListCoreKey), - ); - - final offset = users.length; - final paginatedUsers = _generateUsers(offset: offset); - final updatedPagination = pagination.copyWith(offset: offset); - when(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: updatedPagination, - )) - .thenAnswer( - (_) async => QueryUsersResponse()..users = paginatedUsers); - - await userListCoreState.paginateData(); - - await tester.pumpAndSettle(); - - for (final user in users + paginatedUsers) { - expect(find.byKey(Key('HEADER-${user.name[0]}')), findsOneWidget); - expect(find.byKey(Key('USER-${user.id}')), findsOneWidget); - } - - verify(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: updatedPagination, - )).called(1); - }, - ); - - testWidgets( - 'should rebuild UserListCore with updated widget data ' - 'on calling setState()', - (tester) async { - const pagination = PaginationParams(); - - StateSetter? _stateSetter; - var limit = pagination.limit; - - const userListCoreKey = Key('userListCore'); - const listWidgetKey = Key('listWidget'); - UserListCore userListCoreBuilder(int limit) => UserListCore( - key: userListCoreKey, - listBuilder: (_, items) => Container( - key: listWidgetKey, - child: ListView( - children: items - .map((e) => Container( - key: Key(e.key ?? ''), - child: e.when( - headerItem: Text.new, - userItem: (user) => Text(user.id), - ), - )) - .toList(growable: false), - ), - ), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => - const Offstage(), - limit: limit, - groupAlphabetically: true, - ); - - final mockClient = MockClient(); - - final users = _generateUsers(); - when(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: any(named: 'pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: StreamChatCore( - client: mockClient, - child: UsersBloc( - child: StatefulBuilder(builder: (context, stateSetter) { - // Assigning stateSetter for rebuilding UserListCore - _stateSetter = stateSetter; - return userListCoreBuilder(limit); - }), - ), - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(listWidgetKey), findsOneWidget); - for (final user in users) { - expect(find.byKey(Key('HEADER-${user.name[0]}')), findsOneWidget); - expect(find.byKey(Key('USER-${user.id}')), findsOneWidget); - } - - verify(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: any(named: 'pagination'), - )).called(1); - - // Rebuilding UserListCore with new pagination limit - _stateSetter?.call(() => limit = 6); - - final updatedUsers = _generateUsers(count: limit); - final updatedPagination = PaginationParams(limit: limit); - when(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: updatedPagination, - )) - .thenAnswer((_) async => QueryUsersResponse()..users = updatedUsers); - - await tester.pumpAndSettle(); - - for (final user in updatedUsers) { - expect(find.byKey(Key('HEADER-${user.name[0]}')), findsOneWidget); - expect(find.byKey(Key('USER-${user.id}')), findsOneWidget); - } - - verify(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: updatedPagination, - )).called(1); - }, - ); - - test('`widget.limit` should match `widget.pagination.limit`', () { - const limit = 20; - final userListCore = UserListCore( - listBuilder: (_, __) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - limit: limit, - ); - - expect(userListCore.limit, limit); - }); -} diff --git a/packages/stream_chat_flutter_core/test/users_bloc_test.dart b/packages/stream_chat_flutter_core/test/users_bloc_test.dart deleted file mode 100644 index ce28c2f80..000000000 --- a/packages/stream_chat_flutter_core/test/users_bloc_test.dart +++ /dev/null @@ -1,365 +0,0 @@ -// ignore_for_file: deprecated_member_use_from_same_package - -import 'package:flutter/widgets.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_flutter_core/src/stream_chat_core.dart'; -import 'package:stream_chat_flutter_core/src/users_bloc.dart'; - -import 'matchers/users_matcher.dart'; -import 'mocks.dart'; - -void main() { - List _generateUsers({ - int count = 3, - int offset = 0, - }) => - List.generate( - count, - (index) { - index = index + offset; - return User( - id: 'testId$index', - role: 'testRole$index', - createdAt: DateTime.now(), - updatedAt: DateTime.now(), - lastActive: DateTime.now(), - online: true, - extraData: const {'extra_data_key': 'extraDataValue'}, - ); - }, - ); - - testWidgets( - 'usersBlocState.queryUsers() should throw if used where ' - 'StreamChat is not present in the widget tree', - (tester) async { - const usersBloc = UsersBloc( - child: Offstage(), - ); - - await tester.pumpWidget(usersBloc); - expect(tester.takeException(), isInstanceOf()); - }, - ); - - testWidgets( - 'usersBlocState.queryUsers() should emit data through usersStream', - (tester) async { - const usersBlocKey = Key('usersBloc'); - const childKey = Key('child'); - const usersBloc = UsersBloc( - key: usersBlocKey, - child: Offstage(key: childKey), - ); - - final mockClient = MockClient(); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: usersBloc, - ), - ); - - final usersBlocState = tester.state( - find.byKey(usersBlocKey), - ); - - final users = _generateUsers(); - - when(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: any(named: 'pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); - - usersBlocState.queryUsers(); - - await expectLater( - usersBlocState.usersStream, - emits(isSameUserListAs(users)), - ); - - verify(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: any(named: 'pagination'), - )).called(1); - }, - ); - - testWidgets( - 'usersBlocState.usersStream should emit error ' - 'if client.queryUsers() throws', - (tester) async { - const usersBlocKey = Key('usersBloc'); - const childKey = Key('child'); - const usersBloc = UsersBloc( - key: usersBlocKey, - child: Offstage(key: childKey), - ); - - final mockClient = MockClient(); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: usersBloc, - ), - ); - - final usersBlocState = tester.state( - find.byKey(usersBlocKey), - ); - - const error = 'Error! Error! Error!'; - - when(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: any(named: 'pagination'), - )).thenThrow(error); - - usersBlocState.queryUsers(); - - await expectLater( - usersBlocState.usersStream, - emitsError(error), - ); - - verify(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: any(named: 'pagination'), - )).called(1); - }, - ); - - testWidgets( - 'calling usersBlocState.queryUsers() again with an offset ' - 'should emit new data through usersStream and also emit loading state ' - 'through queryUsersLoading', - (tester) async { - const usersBlocKey = Key('usersBloc'); - const childKey = Key('child'); - const usersBloc = UsersBloc( - key: usersBlocKey, - child: Offstage(key: childKey), - ); - - final mockClient = MockClient(); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: usersBloc, - ), - ); - - final usersBlocState = tester.state( - find.byKey(usersBlocKey), - ); - - const pagination = PaginationParams(limit: 25); - final users = _generateUsers(count: 25); - - when(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: pagination, - )).thenAnswer((_) async => QueryUsersResponse()..users = users); - - usersBlocState.queryUsers(pagination: pagination); - - await expectLater( - usersBlocState.usersStream, - emits(isSameUserListAs(users)), - ); - - verify(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: pagination, - )).called(1); - - final offset = users.length; - final paginatedUsers = _generateUsers(offset: offset); - final newPagination = pagination.copyWith(offset: offset); - - when(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: newPagination, - )).thenAnswer( - (_) async => QueryUsersResponse()..users = paginatedUsers, - ); - - usersBlocState.queryUsers(pagination: newPagination); - - await Future.wait([ - expectLater( - usersBlocState.queryUsersLoading, - emitsInOrder([true, false]), - ), - expectLater( - usersBlocState.usersStream, - emits(isSameUserListAs(users + paginatedUsers)), - ), - ]); - - verify(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: newPagination, - )).called(1); - }, - ); - - testWidgets( - 'calling usersBlocState.queryUsers() again with an offset ' - 'should emit error through queryUsersLoading if ' - 'client.queryUsers() throws', - (tester) async { - const usersBlocKey = Key('usersBloc'); - const childKey = Key('child'); - const usersBloc = UsersBloc( - key: usersBlocKey, - child: Offstage(key: childKey), - ); - - final mockClient = MockClient(); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: usersBloc, - ), - ); - - final usersBlocState = tester.state( - find.byKey(usersBlocKey), - ); - - const pagination = PaginationParams(limit: 25); - final users = _generateUsers(count: 25); - - when(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: pagination, - )).thenAnswer((_) async => QueryUsersResponse()..users = users); - - usersBlocState.queryUsers(pagination: pagination); - - await expectLater( - usersBlocState.usersStream, - emits(isSameUserListAs(users)), - ); - - verify(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: pagination, - )).called(1); - - final offset = users.length; - final newPagination = pagination.copyWith(offset: offset); - - const error = 'Error! Error! Error!'; - - when(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: newPagination, - )).thenThrow(error); - - usersBlocState.queryUsers(pagination: newPagination); - - await expectLater( - usersBlocState.queryUsersLoading, - emitsError(error), - ); - - verify(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: newPagination, - )).called(1); - }, - ); - - testWidgets( - '''calling usersBlocState.queryUsers() again with an offset should do nothing and return if pagination is completed''', - (tester) async { - const usersBlocKey = Key('usersBloc'); - const childKey = Key('child'); - const usersBloc = UsersBloc( - key: usersBlocKey, - child: Offstage(key: childKey), - ); - - final mockClient = MockClient(); - - await tester.pumpWidget( - StreamChatCore( - client: mockClient, - child: usersBloc, - ), - ); - - final usersBlocState = tester.state( - find.byKey(usersBlocKey), - ); - - const pagination = PaginationParams(limit: 30); - final users = _generateUsers(count: 25); - - when(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: pagination, - )).thenAnswer((_) async => QueryUsersResponse()..users = users); - - usersBlocState.queryUsers(); - - await expectLater( - usersBlocState.usersStream, - emits(isSameUserListAs(users)), - ); - - verify(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: any(named: 'pagination'), - )).called(1); - - final offset = users.length; - final newPagination = pagination.copyWith(offset: offset); - - usersBlocState.queryUsers(pagination: newPagination); - - // should emit nothing. - await expectLater( - // skipping the initial data (behaviorSubject). - usersBlocState.usersStream, - emitsInOrder([]), - ); - }, - ); -} diff --git a/packages/stream_chat_persistence/example/linux/flutter/generated_plugins.cmake b/packages/stream_chat_persistence/example/linux/flutter/generated_plugins.cmake index 51436ae8c..2e1de87a7 100644 --- a/packages/stream_chat_persistence/example/linux/flutter/generated_plugins.cmake +++ b/packages/stream_chat_persistence/example/linux/flutter/generated_plugins.cmake @@ -5,6 +5,9 @@ list(APPEND FLUTTER_PLUGIN_LIST ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -13,3 +16,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/stream_chat_persistence/example/windows/flutter/generated_plugins.cmake b/packages/stream_chat_persistence/example/windows/flutter/generated_plugins.cmake index 4d10c2518..b93c4c30c 100644 --- a/packages/stream_chat_persistence/example/windows/flutter/generated_plugins.cmake +++ b/packages/stream_chat_persistence/example/windows/flutter/generated_plugins.cmake @@ -5,6 +5,9 @@ list(APPEND FLUTTER_PLUGIN_LIST ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -13,3 +16,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) 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 af0dc874d..f84fbd6be 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 @@ -3942,9 +3942,6 @@ class MemberEntity extends DataClass implements Insertable { /// The channel cid of which this user is part of final String channelCid; - /// The role of the user in the channel - final String? role; - /// The role of the user in the channel final String? channelRole; @@ -3974,7 +3971,6 @@ class MemberEntity extends DataClass implements Insertable { MemberEntity( {required this.userId, required this.channelCid, - this.role, this.channelRole, this.inviteAcceptedAt, this.inviteRejectedAt, @@ -3991,8 +3987,6 @@ class MemberEntity extends DataClass implements Insertable { .mapFromDatabaseResponse(data['${effectivePrefix}user_id'])!, channelCid: const StringType() .mapFromDatabaseResponse(data['${effectivePrefix}channel_cid'])!, - role: const StringType() - .mapFromDatabaseResponse(data['${effectivePrefix}role']), channelRole: const StringType() .mapFromDatabaseResponse(data['${effectivePrefix}channel_role']), inviteAcceptedAt: const DateTimeType().mapFromDatabaseResponse( @@ -4018,9 +4012,6 @@ class MemberEntity extends DataClass implements Insertable { final map = {}; map['user_id'] = Variable(userId); map['channel_cid'] = Variable(channelCid); - if (!nullToAbsent || role != null) { - map['role'] = Variable(role); - } if (!nullToAbsent || channelRole != null) { map['channel_role'] = Variable(channelRole); } @@ -4045,7 +4036,6 @@ class MemberEntity extends DataClass implements Insertable { return MemberEntity( 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']), @@ -4065,7 +4055,6 @@ class MemberEntity extends DataClass implements Insertable { return { 'userId': serializer.toJson(userId), 'channelCid': serializer.toJson(channelCid), - 'role': serializer.toJson(role), 'channelRole': serializer.toJson(channelRole), 'inviteAcceptedAt': serializer.toJson(inviteAcceptedAt), 'inviteRejectedAt': serializer.toJson(inviteRejectedAt), @@ -4081,7 +4070,6 @@ class MemberEntity extends DataClass implements Insertable { MemberEntity copyWith( {String? userId, String? channelCid, - Value role = const Value.absent(), Value channelRole = const Value.absent(), Value inviteAcceptedAt = const Value.absent(), Value inviteRejectedAt = const Value.absent(), @@ -4094,7 +4082,6 @@ class MemberEntity extends DataClass implements Insertable { MemberEntity( 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 @@ -4114,7 +4101,6 @@ class MemberEntity extends DataClass implements Insertable { return (StringBuffer('MemberEntity(') ..write('userId: $userId, ') ..write('channelCid: $channelCid, ') - ..write('role: $role, ') ..write('channelRole: $channelRole, ') ..write('inviteAcceptedAt: $inviteAcceptedAt, ') ..write('inviteRejectedAt: $inviteRejectedAt, ') @@ -4132,7 +4118,6 @@ class MemberEntity extends DataClass implements Insertable { int get hashCode => Object.hash( userId, channelCid, - role, channelRole, inviteAcceptedAt, inviteRejectedAt, @@ -4148,7 +4133,6 @@ class MemberEntity extends DataClass implements Insertable { (other is MemberEntity && other.userId == this.userId && other.channelCid == this.channelCid && - other.role == this.role && other.channelRole == this.channelRole && other.inviteAcceptedAt == this.inviteAcceptedAt && other.inviteRejectedAt == this.inviteRejectedAt && @@ -4163,7 +4147,6 @@ class MemberEntity extends DataClass implements Insertable { class MembersCompanion extends UpdateCompanion { final Value userId; final Value channelCid; - final Value role; final Value channelRole; final Value inviteAcceptedAt; final Value inviteRejectedAt; @@ -4176,7 +4159,6 @@ class MembersCompanion extends UpdateCompanion { const MembersCompanion({ 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(), @@ -4190,7 +4172,6 @@ class MembersCompanion extends UpdateCompanion { MembersCompanion.insert({ 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(), @@ -4205,7 +4186,6 @@ class MembersCompanion extends UpdateCompanion { static Insertable custom({ Expression? userId, Expression? channelCid, - Expression? role, Expression? channelRole, Expression? inviteAcceptedAt, Expression? inviteRejectedAt, @@ -4219,7 +4199,6 @@ class MembersCompanion extends UpdateCompanion { return RawValuesInsertable({ 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, @@ -4235,7 +4214,6 @@ class MembersCompanion extends UpdateCompanion { MembersCompanion copyWith( {Value? userId, Value? channelCid, - Value? role, Value? channelRole, Value? inviteAcceptedAt, Value? inviteRejectedAt, @@ -4248,7 +4226,6 @@ class MembersCompanion extends UpdateCompanion { return MembersCompanion( userId: userId ?? this.userId, channelCid: channelCid ?? this.channelCid, - role: role ?? this.role, channelRole: channelRole ?? this.channelRole, inviteAcceptedAt: inviteAcceptedAt ?? this.inviteAcceptedAt, inviteRejectedAt: inviteRejectedAt ?? this.inviteRejectedAt, @@ -4270,9 +4247,6 @@ class MembersCompanion extends UpdateCompanion { if (channelCid.present) { map['channel_cid'] = Variable(channelCid.value); } - if (role.present) { - map['role'] = Variable(role.value); - } if (channelRole.present) { map['channel_role'] = Variable(channelRole.value); } @@ -4308,7 +4282,6 @@ class MembersCompanion extends UpdateCompanion { return (StringBuffer('MembersCompanion(') ..write('userId: $userId, ') ..write('channelCid: $channelCid, ') - ..write('role: $role, ') ..write('channelRole: $channelRole, ') ..write('inviteAcceptedAt: $inviteAcceptedAt, ') ..write('inviteRejectedAt: $inviteRejectedAt, ') @@ -4341,11 +4314,6 @@ class $MembersTable extends Members type: const StringType(), requiredDuringInsert: true, $customConstraints: 'REFERENCES channels(cid) ON DELETE CASCADE'); - final VerificationMeta _roleMeta = const VerificationMeta('role'); - @override - late final GeneratedColumn role = GeneratedColumn( - 'role', aliasedName, true, - type: const StringType(), requiredDuringInsert: false); final VerificationMeta _channelRoleMeta = const VerificationMeta('channelRole'); @override @@ -4416,7 +4384,6 @@ class $MembersTable extends Members List get $columns => [ userId, channelCid, - role, channelRole, inviteAcceptedAt, inviteRejectedAt, @@ -4450,10 +4417,6 @@ class $MembersTable extends Members } else if (isInserting) { context.missing(_channelCidMeta); } - if (data.containsKey('role')) { - context.handle( - _roleMeta, role.isAcceptableOrUnknown(data['role']!, _roleMeta)); - } if (data.containsKey('channel_role')) { context.handle( _channelRoleMeta, diff --git a/packages/stream_chat_persistence/lib/src/entity/members.dart b/packages/stream_chat_persistence/lib/src/entity/members.dart index 371cb2be5..b4e8181b7 100644 --- a/packages/stream_chat_persistence/lib/src/entity/members.dart +++ b/packages/stream_chat_persistence/lib/src/entity/members.dart @@ -11,10 +11,6 @@ class Members extends Table { TextColumn get channelCid => 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()(); 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 05975ea40..e1b3bac2a 100644 --- a/packages/stream_chat_persistence/lib/src/mapper/member_mapper.dart +++ b/packages/stream_chat_persistence/lib/src/mapper/member_mapper.dart @@ -11,7 +11,6 @@ extension MemberEntityX on MemberEntity { shadowBanned: shadowBanned, updatedAt: updatedAt, createdAt: createdAt, - role: role, channelRole: channelRole, inviteAcceptedAt: inviteAcceptedAt, invited: invited, @@ -33,8 +32,6 @@ 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 065be5cc9..007cb7281 100644 --- a/packages/stream_chat_persistence/pubspec.yaml +++ b/packages/stream_chat_persistence/pubspec.yaml @@ -20,6 +20,7 @@ dependencies: path_provider: ^2.0.1 sqlite3_flutter_libs: ^0.5.0 stream_chat: ^4.3.0 + dev_dependencies: build_runner: ^2.0.1 dart_code_metrics: ^4.4.0 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 1082967e3..1d4d151b5 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 @@ -30,7 +30,7 @@ void main() { isModerator: math.Random().nextBool(), invited: math.Random().nextBool(), inviteAcceptedAt: DateTime.now(), - role: 'testRole', + channelRole: 'testRole', updatedAt: DateTime.now(), ), ); @@ -108,7 +108,7 @@ void main() { isModerator: math.Random().nextBool(), invited: math.Random().nextBool(), inviteAcceptedAt: DateTime.now(), - role: 'testRole', + channelRole: 'testRole', updatedAt: DateTime.now(), ); await database.userDao.updateUsers([newUser]); 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 e327e7520..206651bd0 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 @@ -15,7 +15,7 @@ void main() { channelCid: 'testCid', createdAt: DateTime.now(), updatedAt: DateTime.now(), - role: 'testRole', + channelRole: 'testRole', inviteAcceptedAt: DateTime.now(), inviteRejectedAt: DateTime.now(), invited: math.Random().nextBool(), @@ -44,7 +44,7 @@ void main() { user: user, createdAt: DateTime.now(), updatedAt: DateTime.now(), - role: 'testRole', + channelRole: 'testRole', inviteAcceptedAt: DateTime.now(), inviteRejectedAt: DateTime.now(), invited: math.Random().nextBool(),