From 472dc7891be738dbec78acf71937a1c7bb13f99c Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 20 Jun 2023 15:39:37 +0530 Subject: [PATCH 01/48] fix(ui): pass `onConfirmDeleteTap` to MessageActionsModal. Signed-off-by: xsahil03x --- .../lib/src/message_widget/message_widget.dart | 1 + 1 file changed, 1 insertion(+) 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 61332a3cd..963b2ac50 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 @@ -1202,6 +1202,7 @@ class _StreamMessageWidgetState extends State messageTheme: widget.messageTheme, reverse: widget.reverse, showDeleteMessage: shouldShowDeleteAction, + onConfirmDeleteTap: widget.onConfirmDeleteTap, message: widget.message, editMessageInputBuilder: widget.editMessageInputBuilder, onReplyTap: widget.onReplyTap, From e3e838c1620d7b2df0f2549441a5c8d2c3fdca1d Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 20 Jun 2023 15:40:46 +0530 Subject: [PATCH 02/48] chore: update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat_flutter/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 2e6c73f0f..2eb6c6812 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,3 +1,10 @@ +## Upcoming + +🐞 Fixed + +- [[#1620]](https://github.com/GetStream/stream-chat-flutter/issues/1620) Fixed messages Are Not Hard Deleting even + after overriding the `onConfirmDeleteTap` callback. + ## 6.4.0 🐞 Fixed From f31deb389ca03495b0f1fc92a5b941d7595fccc2 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 20 Jun 2023 16:40:47 +0530 Subject: [PATCH 03/48] fix(ui): fix null check error. Signed-off-by: xsahil03x --- .../lib/src/message_widget/sending_indicator_builder.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_widget/sending_indicator_builder.dart b/packages/stream_chat_flutter/lib/src/message_widget/sending_indicator_builder.dart index 52ecc1cf4..616737521 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget/sending_indicator_builder.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget/sending_indicator_builder.dart @@ -76,7 +76,7 @@ class SendingIndicatorBuilder extends StatelessWidget { Widget child = StreamSendingIndicator( message: message, isMessageRead: isMessageRead, - size: style!.fontSize, + size: style?.fontSize, ); if (isMessageRead) { @@ -86,7 +86,7 @@ class SendingIndicatorBuilder extends StatelessWidget { if (memberCount > 2) Text( readList.length.toString(), - style: style.copyWith( + style: style?.copyWith( color: streamChatTheme.colorTheme.accentPrimary, ), ), From 573acc6702d6b1fcd0b82e95270a09434f8d5d27 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 20 Jun 2023 16:48:53 +0530 Subject: [PATCH 04/48] chore: update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat_flutter/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 2eb6c6812..c5cc4660a 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -4,6 +4,8 @@ - [[#1620]](https://github.com/GetStream/stream-chat-flutter/issues/1620) Fixed messages Are Not Hard Deleting even after overriding the `onConfirmDeleteTap` callback. +- [[#1621]](https://github.com/GetStream/stream-chat-flutter/issues/1621) Fixed `createdAtStyle` null check error + in `SendingIndicatorBuilder`. ## 6.4.0 From b0de43794953fb3329b6370d72d0f1f92e772fbd Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 21 Jun 2023 20:13:51 +0530 Subject: [PATCH 05/48] feat(ui): add support for customizing message input attachments. Signed-off-by: xsahil03x --- .../message_input/stream_message_input.dart | 279 +++++-------- .../stream_message_input_attachment_list.dart | 388 ++++++++++++++++++ .../lib/stream_chat_flutter.dart | 1 + 3 files changed, 491 insertions(+), 177 deletions(-) create mode 100644 packages/stream_chat_flutter/lib/src/message_input/stream_message_input_attachment_list.dart diff --git a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart index d4581ea05..4f60ae709 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart @@ -3,14 +3,10 @@ import 'dart:async'; import 'dart:math'; -import 'package:cached_network_image/cached_network_image.dart' - hide ErrorListener; import 'package:desktop_drop/desktop_drop.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:photo_manager/photo_manager.dart'; -import 'package:shimmer/shimmer.dart'; import 'package:stream_chat_flutter/platform_widget_builder/src/platform_widget_builder.dart'; import 'package:stream_chat_flutter/src/message_input/attachment_button.dart'; import 'package:stream_chat_flutter/src/message_input/command_button.dart'; @@ -19,7 +15,6 @@ 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/video/video_thumbnail_image.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; const _kCommandTrigger = '/'; @@ -110,6 +105,12 @@ class StreamMessageInput extends StatefulWidget { this.messageInputController, this.actions = const [], this.actionsLocation = ActionsLocation.left, + this.attachmentListBuilder, + this.fileAttachmentListBuilder, + this.mediaAttachmentListBuilder, + this.fileAttachmentBuilder, + this.mediaAttachmentBuilder, + @Deprecated('Use `mediaAttachmentBuilder` instead.') this.attachmentThumbnailBuilders, this.focusNode, this.sendButtonLocation = SendButtonLocation.outside, @@ -209,7 +210,32 @@ class StreamMessageInput extends StatefulWidget { /// The location of the custom actions. final ActionsLocation actionsLocation; + /// Builder used to build the attachment list present in the message input. + /// + /// In case you want to customize only sub-parts of the attachment list, + /// consider using [fileAttachmentListBuilder], [mediaAttachmentListBuilder]. + final AttachmentListBuilder? attachmentListBuilder; + + /// Builder used to build the file type attachment list. + /// + /// In case you want to customize the attachment item, consider using + /// [fileAttachmentBuilder]. + final AttachmentListBuilder? fileAttachmentListBuilder; + + /// Builder used to build the media type attachment list. + /// + /// In case you want to customize the attachment item, consider using + /// [mediaAttachmentBuilder]. + final AttachmentListBuilder? mediaAttachmentListBuilder; + + /// Builder used to build the file attachment item. + final AttachmentItemBuilder? fileAttachmentBuilder; + + /// Builder used to build the media attachment item. + final AttachmentItemBuilder? mediaAttachmentBuilder; + /// Map that defines a thumbnail builder for an attachment type. + @Deprecated('Use `mediaAttachmentBuilder` instead.') final Map? attachmentThumbnailBuilders; /// The focus node associated to the TextField. @@ -1152,188 +1178,87 @@ class StreamMessageInputState extends State } Widget _buildAttachments() { - final nonOGAttachments = _effectiveController.attachments.where( - (it) => it.titleLink == null, - ); + final attachments = _effectiveController.attachments; + final nonOGAttachments = attachments.where((it) { + return it.titleLink == null; + }).toList(growable: false); + + // If there are no attachments, return an empty widget if (nonOGAttachments.isEmpty) return const Offstage(); - final fileAttachments = nonOGAttachments - .where((it) => it.type == 'file') - .toList(growable: false); - final remainingAttachments = nonOGAttachments - .where((it) => it.type != 'file') - .toList(growable: false); - return Column( - children: [ - if (fileAttachments.isNotEmpty) - Padding( - padding: const EdgeInsets.fromLTRB(8, 8, 8, 0), - child: LimitedBox( - maxHeight: 136, - child: ListView( - reverse: true, - shrinkWrap: true, - children: fileAttachments.reversed - .map( - (e) => ClipRRect( - borderRadius: BorderRadius.circular(10), - child: StreamFileAttachment( - message: Message(), // dummy message - attachment: e, - constraints: BoxConstraints.loose(Size( - MediaQuery.of(context).size.width * 0.65, - 56, - )), - trailing: Padding( - padding: const EdgeInsets.all(8), - child: _buildRemoveButton(e), - ), - ), - ), - ) - .insertBetween(const SizedBox(height: 8)), - ), - ), - ), - if (remainingAttachments.isNotEmpty) - Padding( - padding: const EdgeInsets.fromLTRB(8, 8, 8, 0), - child: LimitedBox( - maxHeight: 104, - child: ListView( - scrollDirection: Axis.horizontal, - children: remainingAttachments - .map( - (attachment) => ClipRRect( - borderRadius: BorderRadius.circular(10), - child: Stack( - children: [ - AspectRatio( - aspectRatio: 1, - child: SizedBox( - height: 104, - width: 104, - child: _buildAttachment(attachment), - ), - ), - Positioned( - top: 8, - right: 8, - child: _buildRemoveButton(attachment), - ), - ], - ), - ), - ) - .insertBetween(const SizedBox(width: 8)), - ), - ), - ), - ], - ); - } - Widget _buildRemoveButton(Attachment attachment) { - return SizedBox( - height: 24, - width: 24, - child: RawMaterialButton( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - elevation: 0, - highlightElevation: 0, - focusElevation: 0, - hoverElevation: 0, - onPressed: () async { - final file = attachment.file; - final uploadState = attachment.uploadState; - - if (file != null && !uploadState.isSuccess && !isWeb) { - await StreamAttachmentHandler.instance.deleteAttachmentFile( - attachmentFile: file, - ); - } + // Default callback for removing an attachment. + Future onAttachmentRemovePressed(Attachment attachment) async { + final file = attachment.file; + final uploadState = attachment.uploadState; - _effectiveController.removeAttachmentById(attachment.id); - }, - fillColor: - _streamChatTheme.colorTheme.textHighEmphasis.withOpacity(0.5), - child: Center( - child: StreamSvgIcon.close( - size: 24, - color: _streamChatTheme.colorTheme.barsBg, - ), - ), - ), - ); - } + if (file != null && !uploadState.isSuccess && !isWeb) { + await StreamAttachmentHandler.instance.deleteAttachmentFile( + attachmentFile: file, + ); + } - Widget _buildAttachment(Attachment attachment) { - if (widget.attachmentThumbnailBuilders?.containsKey(attachment.type) == - true) { - return widget.attachmentThumbnailBuilders![attachment.type!]!( + _effectiveController.removeAttachmentById(attachment.id); + } + + // If the user has provided a custom attachment list builder, use that. + final attachmentListBuilder = widget.attachmentListBuilder; + if (attachmentListBuilder != null) { + return attachmentListBuilder( context, - attachment, + nonOGAttachments, + onAttachmentRemovePressed, ); } - switch (attachment.type) { - case 'image': - case 'giphy': - return attachment.file != null - ? Image.memory( - attachment.file!.bytes!, - fit: BoxFit.cover, - errorBuilder: (context, _, __) => Image.asset( - 'images/placeholder.png', - package: 'stream_chat_flutter', - ), - ) - : CachedNetworkImage( - imageUrl: attachment.imageUrl ?? - attachment.assetUrl ?? - attachment.thumbUrl!, - fit: BoxFit.cover, - errorWidget: (_, obj, trace) => - getFileTypeImage(attachment.extraData['other'] as String?), - placeholder: (context, _) => Shimmer.fromColors( - baseColor: _streamChatTheme.colorTheme.disabled, - highlightColor: _streamChatTheme.colorTheme.inputBg, - child: Image.asset( - 'images/placeholder.png', - fit: BoxFit.cover, - package: 'stream_chat_flutter', - ), + // Otherwise, use the default attachment list builder. + return LimitedBox( + maxHeight: 240, + child: StreamMessageInputAttachmentList( + attachments: nonOGAttachments, + onRemovePressed: onAttachmentRemovePressed, + fileAttachmentListBuilder: widget.fileAttachmentListBuilder, + mediaAttachmentListBuilder: widget.mediaAttachmentListBuilder, + fileAttachmentBuilder: widget.fileAttachmentBuilder, + mediaAttachmentBuilder: widget.mediaAttachmentBuilder ?? + // For backward compatibility. + // TODO: Remove in the next major release. + (context, attachment, onRemovePressed) { + final Widget mediaAttachmentThumbnail; + + final builder = + widget.attachmentThumbnailBuilders?[attachment.type]; + if (builder != null) { + mediaAttachmentThumbnail = builder(context, attachment); + } else { + mediaAttachmentThumbnail = MessageInputMediaAttachmentThumbnail( + attachment: attachment, + ); + } + + return ClipRRect( + key: Key(attachment.id), + borderRadius: BorderRadius.circular(10), + child: Stack( + children: [ + AspectRatio( + aspectRatio: 1, + child: mediaAttachmentThumbnail, + ), + Positioned( + top: 8, + right: 8, + child: RemoveAttachmentButton( + onPressed: onRemovePressed != null + ? () => onRemovePressed(attachment) + : null, + ), + ), + ], ), ); - case 'video': - return Stack( - children: [ - StreamVideoThumbnailImage( - constraints: BoxConstraints.loose( - const Size( - 104, - 104, - ), - ), - video: attachment.file?.path ?? attachment.assetUrl, - ), - Positioned( - left: 8, - bottom: 10, - child: SvgPicture.asset( - 'svgs/video_call_icon.svg', - package: 'stream_chat_flutter', - ), - ), - ], - ); - default: - return const ColoredBox( - color: Colors.black26, - child: Icon(Icons.insert_drive_file), - ); - } + }, + ), + ); } Widget _buildCommandButton(BuildContext context) { diff --git a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input_attachment_list.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input_attachment_list.dart new file mode 100644 index 000000000..a849bffc2 --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input_attachment_list.dart @@ -0,0 +1,388 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/attachment/attachment.dart'; +import 'package:stream_chat_flutter/src/misc/stream_svg_icon.dart'; +import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; +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'; + +/// WidgetBuilder used to build the message input attachment list. +/// +/// see more: +/// - [StreamMessageInputAttachmentList] +typedef AttachmentListBuilder = Widget Function( + BuildContext context, + List attachments, + ValueSetter? onRemovePressed, +); + +/// WidgetBuilder used to build the message input attachment item. +/// +/// see more: +/// - [StreamMessageInputAttachmentList] +typedef AttachmentItemBuilder = Widget Function( + BuildContext context, + Attachment attachment, + ValueSetter? onRemovePressed, +); + +/// {@template stream_message_input_attachment_list} +/// Widget used to display the list of attachments added to the message input. +/// +/// By default, it displays the list of file attachments and media attachments +/// separately. +/// +/// You can customize the list of file attachments and media attachments using +/// [fileAttachmentListBuilder] and [mediaAttachmentListBuilder] respectively. +/// +/// You can also customize the attachment item using [fileAttachmentBuilder] and +/// [mediaAttachmentBuilder] respectively. +/// +/// You can override the default action of removing an attachment by providing +/// [onRemovePressed]. +/// {@endtemplate} +class StreamMessageInputAttachmentList extends StatefulWidget { + /// {@macro stream_message_input_attachment_list} + const StreamMessageInputAttachmentList({ + super.key, + required this.attachments, + this.onRemovePressed, + this.fileAttachmentBuilder, + this.mediaAttachmentBuilder, + this.fileAttachmentListBuilder, + this.mediaAttachmentListBuilder, + }); + + /// List of attachments to display thumbnails for. + /// + /// Open graph should be filtered out. + final Iterable attachments; + + /// Builder used to build the file attachment item. + final AttachmentItemBuilder? fileAttachmentBuilder; + + /// Builder used to build the media attachment item. + final AttachmentItemBuilder? mediaAttachmentBuilder; + + /// Builder used to build the file attachment list. + final AttachmentListBuilder? fileAttachmentListBuilder; + + /// Builder used to build the media attachment list. + final AttachmentListBuilder? mediaAttachmentListBuilder; + + /// Callback called when the remove button is pressed. + final ValueSetter? onRemovePressed; + + @override + State createState() => + _StreamMessageInputAttachmentListState(); +} + +class _StreamMessageInputAttachmentListState + extends State { + List fileAttachments = []; + List mediaAttachments = []; + + void _updateAttachments() { + // Clear the lists. + fileAttachments.clear(); + mediaAttachments.clear(); + + // Split the attachments into file and media attachments. + for (final attachment in widget.attachments) { + if (attachment.type == 'file') { + fileAttachments.add(attachment); + } else { + mediaAttachments.add(attachment); + } + } + } + + @override + void initState() { + super.initState(); + _updateAttachments(); + } + + @override + void didUpdateWidget(covariant StreamMessageInputAttachmentList oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.attachments != widget.attachments) { + _updateAttachments(); + } + } + + @override + Widget build(BuildContext context) { + // If there are no attachments, return an empty box. + if (fileAttachments.isEmpty && mediaAttachments.isEmpty) { + return const SizedBox(); + } + + return SingleChildScrollView( + padding: const EdgeInsets.only(top: 8), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (mediaAttachments.isNotEmpty) + Flexible( + child: widget.mediaAttachmentListBuilder?.call( + context, + mediaAttachments, + widget.onRemovePressed, + ) ?? + MessageInputMediaAttachments( + attachments: mediaAttachments, + attachmentBuilder: widget.mediaAttachmentBuilder, + onRemovePressed: widget.onRemovePressed, + ), + ), + if (fileAttachments.isNotEmpty) + Flexible( + child: widget.fileAttachmentListBuilder?.call( + context, + fileAttachments, + widget.onRemovePressed, + ) ?? + MessageInputFileAttachments( + attachments: fileAttachments, + attachmentBuilder: widget.fileAttachmentBuilder, + onRemovePressed: widget.onRemovePressed, + ), + ), + ].insertBetween( + Divider( + height: 16, + indent: 16, + endIndent: 16, + thickness: 1, + color: StreamChatTheme.of(context).colorTheme.disabled, + ), + ), + ), + ); + } +} + +/// Widget used to display the list of file type attachments added to the +/// message input. +class MessageInputFileAttachments extends StatelessWidget { + /// Creates a new FileAttachments widget. + const MessageInputFileAttachments({ + super.key, + required this.attachments, + this.attachmentBuilder, + this.onRemovePressed, + }); + + /// List of file type attachments to display thumbnails for. + final List attachments; + + /// Builder used to build the file type attachment item. + final AttachmentItemBuilder? attachmentBuilder; + + /// Callback called when the remove button is pressed. + final ValueSetter? onRemovePressed; + + @override + Widget build(BuildContext context) { + return ListView( + reverse: true, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.symmetric(horizontal: 8), + children: attachments.reversed.map( + (attachment) { + // If a custom builder is provided, use it. + final builder = attachmentBuilder; + if (builder != null) { + return builder(context, attachment, onRemovePressed); + } + + // Otherwise, use the default builder. + return ClipRRect( + key: Key(attachment.id), + borderRadius: BorderRadius.circular(10), + child: StreamFileAttachment( + message: Message(), // dummy message + attachment: attachment, + constraints: BoxConstraints.loose(Size( + MediaQuery.sizeOf(context).width * 0.65, + 56, + )), + trailing: Padding( + padding: const EdgeInsets.all(8), + child: RemoveAttachmentButton( + onPressed: onRemovePressed != null + ? () => onRemovePressed!(attachment) + : null, + ), + ), + ), + ); + }, + ).insertBetween(const SizedBox(height: 8)), + ); + } +} + +/// Widget used to display the list of media type attachments added to the +/// message input. +class MessageInputMediaAttachments extends StatelessWidget { + /// Creates a new MediaAttachments widget. + const MessageInputMediaAttachments({ + super.key, + required this.attachments, + this.attachmentBuilder, + this.onRemovePressed, + }); + + /// List of media type attachments to display thumbnails for. + /// + /// Only attachments of type `image`, `video` and `giphy` are supported. Shows + /// a placeholder for other types of attachments. + final List attachments; + + /// Builder used to build the media type attachment item. + final AttachmentItemBuilder? attachmentBuilder; + + /// Callback called when the remove button is pressed. + final ValueSetter? onRemovePressed; + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 104, + child: ListView( + scrollDirection: Axis.horizontal, + padding: const EdgeInsets.symmetric(horizontal: 8), + children: attachments.map( + (attachment) { + // If a custom builder is provided, use it. + final builder = attachmentBuilder; + if (builder != null) { + return builder(context, attachment, onRemovePressed); + } + + return ClipRRect( + key: Key(attachment.id), + borderRadius: BorderRadius.circular(10), + child: Stack( + children: [ + AspectRatio( + aspectRatio: 1, + child: MessageInputMediaAttachmentThumbnail( + attachment: attachment, + ), + ), + Positioned( + top: 8, + right: 8, + child: RemoveAttachmentButton( + onPressed: onRemovePressed != null + ? () => onRemovePressed!(attachment) + : null, + ), + ), + ], + ), + ); + }, + ).insertBetween(const SizedBox(width: 8)), + ), + ); + } +} + +/// A widget that displays a thumbnail for a media attachment. +class MessageInputMediaAttachmentThumbnail extends StatelessWidget { + /// Creates a new media attachment widget. + const MessageInputMediaAttachmentThumbnail({ + super.key, + required this.attachment, + }); + + /// The attachment to display. + final Attachment attachment; + + @override + Widget build(BuildContext context) { + switch (attachment.type) { + case 'image': + case 'giphy': + return attachment.file != null + ? Image.memory( + attachment.file!.bytes!, + fit: BoxFit.cover, + errorBuilder: (context, _, __) => Image.asset( + 'images/placeholder.png', + package: 'stream_chat_flutter', + ), + ) + : CachedNetworkImage( + imageUrl: attachment.imageUrl ?? + attachment.assetUrl ?? + attachment.thumbUrl!, + fit: BoxFit.cover, + errorWidget: (_, obj, trace) => Image.asset( + 'images/placeholder.png', + package: 'stream_chat_flutter', + ), + ); + case 'video': + return Stack( + children: [ + StreamVideoThumbnailImage( + video: attachment.file?.path ?? attachment.assetUrl, + ), + Positioned( + left: 8, + bottom: 10, + child: StreamSvgIcon.videoCall(), + ), + ], + ); + default: + return const ColoredBox( + color: Colors.black26, + child: Icon(Icons.insert_drive_file), + ); + } + } +} + +/// Material Button used for removing attachments. +class RemoveAttachmentButton extends StatelessWidget { + /// Creates a new remove attachment button. + const RemoveAttachmentButton({super.key, this.onPressed}); + + /// Callback when the remove attachment button is pressed. + final VoidCallback? onPressed; + + @override + Widget build(BuildContext context) { + final theme = StreamChatTheme.of(context); + final colorTheme = theme.colorTheme; + + return SizedBox( + width: 24, + height: 24, + child: RawMaterialButton( + elevation: 0, + focusElevation: 0, + hoverElevation: 0, + highlightElevation: 0, + onPressed: onPressed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + fillColor: colorTheme.textHighEmphasis.withOpacity(0.5), + child: StreamSvgIcon.close( + size: 24, + color: colorTheme.barsBg, + ), + ), + ); + } +} diff --git a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart index c2a0bc4dc..8899dbaf9 100644 --- a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart +++ b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart @@ -48,6 +48,7 @@ export 'src/message_input/attachment_picker/stream_attachment_picker_bottom_shee export 'src/message_input/countdown_button.dart'; export 'src/message_input/enums.dart'; export 'src/message_input/stream_message_input.dart'; +export 'src/message_input/stream_message_input_attachment_list.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'; From 04f13bee4d54dfcafa015a1077ca13bbee5b78e8 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 21 Jun 2023 20:14:36 +0530 Subject: [PATCH 06/48] feat: introduce `quotedMessageAttachmentThumbnailBuilders`. Signed-off-by: xsahil03x --- .../lib/src/message_input/stream_message_input.dart | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart index 4f60ae709..97391d6a6 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart @@ -131,6 +131,7 @@ class StreamMessageInput extends StatefulWidget { this.mentionAllAppUsers = false, this.sendButtonBuilder, this.quotedMessageBuilder, + this.quotedMessageAttachmentThumbnailBuilders, this.shouldKeepFocusAfterMessage, this.validator = _defaultValidator, this.restorationId, @@ -238,6 +239,13 @@ class StreamMessageInput extends StatefulWidget { @Deprecated('Use `mediaAttachmentBuilder` instead.') final Map? attachmentThumbnailBuilders; + /// Map that defines a thumbnail builder for an attachment type. + /// + /// This is used to build the thumbnail for the attachment in the quoted + /// message. + final Map? + quotedMessageAttachmentThumbnailBuilders; + /// The focus node associated to the TextField. final FocusNode? focusNode; @@ -1173,7 +1181,8 @@ class StreamMessageInputState extends State messageTheme: _streamChatTheme.otherMessageTheme, padding: const EdgeInsets.fromLTRB(8, 8, 8, 0), onQuotedMessageClear: widget.onQuotedMessageCleared, - attachmentThumbnailBuilders: widget.attachmentThumbnailBuilders, + attachmentThumbnailBuilders: + widget.quotedMessageAttachmentThumbnailBuilders, ); } From 28c33a0972a96675b721a7395846b282dec6ed44 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 21 Jun 2023 20:22:11 +0530 Subject: [PATCH 07/48] chore: update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat_flutter/CHANGELOG.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index c5cc4660a..49b324d67 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -7,6 +7,26 @@ - [[#1621]](https://github.com/GetStream/stream-chat-flutter/issues/1621) Fixed `createdAtStyle` null check error in `SendingIndicatorBuilder`. +✅ Added + +- Added support for customizing attachments in `StreamMessageInput`. Use various properties mentioned + below. [#1511](https://github.com/GetStream/stream-chat-flutter/issues/1511) + * `StreamMessageInput.attachmentListBuilder` to customize the attachment list. + * `StreamMessageInput.fileAttachmentListBuilder` to customize the file attachment list. + * `StreamMessageInput.mediaAttachmentListBuilder` to customize the media attachment list. Includes images, videos + and gifs. + * `StreamMessageInput.fileAttachmentBuilder` to customize the file attachment item shown in `FileAttachmentList`. + * `StreamMessageInput.mediaAttachmentBuilder` to customize the media attachment item shown in + `MediaAttachmentList`. + + +- Added `StreamMessageInput.quotedMessageAttachmentThumbnailBuilders` to customize the thumbnail builders for quoted + message attachments. + +🔄 Changed + +- Deprecated `StreamMessageInput.attachmentThumbnailBuilders` in favor of `StreamMessageInput.mediaAttachmentBuilder`. + ## 6.4.0 🐞 Fixed From daa5ac44f80c7064bc6e894086e509103c66f406 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 21 Jun 2023 20:27:45 +0530 Subject: [PATCH 08/48] chore: export `StreamVideoThumbnailImage`. Signed-off-by: xsahil03x --- packages/stream_chat_flutter/lib/stream_chat_flutter.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart index 8899dbaf9..e5ca5a1ed 100644 --- a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart +++ b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart @@ -97,3 +97,4 @@ export 'src/utils/device_segmentation.dart'; export 'src/utils/extensions.dart'; export 'src/utils/helpers.dart'; export 'src/utils/typedefs.dart'; +export 'src/video/video_thumbnail_image.dart'; From 4e4b7fd81e4909bfada85c389ed66917273055ba Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 21 Jun 2023 20:43:03 +0530 Subject: [PATCH 09/48] chore: fix analysis. Signed-off-by: xsahil03x --- .../stream_chat_flutter/lib/src/attachment/video_attachment.dart | 1 - packages/stream_chat_flutter/lib/src/gallery/gallery_footer.dart | 1 - .../lib/src/message_input/quoted_message_widget.dart | 1 - 3 files changed, 3 deletions(-) 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 10efca0b8..0d5d4ecd9 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/video_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/video_attachment.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; 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'; /// {@template streamVideoAttachment} 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 98ae7df25..e7384fd92 100644 --- a/packages/stream_chat_flutter/lib/src/gallery/gallery_footer.dart +++ b/packages/stream_chat_flutter/lib/src/gallery/gallery_footer.dart @@ -5,7 +5,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; import 'package:share_plus/share_plus.dart'; -import 'package:stream_chat_flutter/src/video/video_thumbnail_image.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// {@template streamGalleryFooter} 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 a61ced633..ab90710ae 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 @@ -2,7 +2,6 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/platform_widget_builder/platform_widget_builder.dart'; import 'package:stream_chat_flutter/src/message_input/clear_input_item_button.dart'; -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'; From 9bee33dd49bf38db79ffc440d2a149fc526f5948 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 22 Jun 2023 00:09:54 +0530 Subject: [PATCH 10/48] chore(ui): minor refactoring. Signed-off-by: xsahil03x --- .../message_input/stream_message_input.dart | 73 ++++++++++--------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart index 97391d6a6..e8ce137e5 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart @@ -1167,23 +1167,30 @@ class StreamMessageInputState extends State Widget _buildReplyToMessage() { if (!_hasQuotedMessage) return const Offstage(); - final containsUrl = _effectiveController.message.quotedMessage!.attachments - .any((element) => element.titleLink != null); - - return widget.quotedMessageBuilder?.call( - context, - _effectiveController.message.quotedMessage!, - ) ?? - StreamQuotedMessageWidget( - reverse: true, - showBorder: !containsUrl, - message: _effectiveController.message.quotedMessage!, - messageTheme: _streamChatTheme.otherMessageTheme, - padding: const EdgeInsets.fromLTRB(8, 8, 8, 0), - onQuotedMessageClear: widget.onQuotedMessageCleared, - attachmentThumbnailBuilders: - widget.quotedMessageAttachmentThumbnailBuilders, - ); + final quotedMessage = _effectiveController.message.quotedMessage!; + + final quotedMessageBuilder = widget.quotedMessageBuilder; + if (quotedMessageBuilder != null) { + return quotedMessageBuilder( + context, + _effectiveController.message.quotedMessage!, + ); + } + + final containsUrl = quotedMessage.attachments.any((it) { + return it.titleLink != null; + }); + + return StreamQuotedMessageWidget( + reverse: true, + showBorder: !containsUrl, + message: quotedMessage, + messageTheme: _streamChatTheme.otherMessageTheme, + padding: const EdgeInsets.fromLTRB(8, 8, 8, 0), + onQuotedMessageClear: widget.onQuotedMessageCleared, + attachmentThumbnailBuilders: + widget.quotedMessageAttachmentThumbnailBuilders, + ); } Widget _buildAttachments() { @@ -1195,27 +1202,13 @@ class StreamMessageInputState extends State // If there are no attachments, return an empty widget if (nonOGAttachments.isEmpty) return const Offstage(); - // Default callback for removing an attachment. - Future onAttachmentRemovePressed(Attachment attachment) async { - final file = attachment.file; - final uploadState = attachment.uploadState; - - if (file != null && !uploadState.isSuccess && !isWeb) { - await StreamAttachmentHandler.instance.deleteAttachmentFile( - attachmentFile: file, - ); - } - - _effectiveController.removeAttachmentById(attachment.id); - } - // If the user has provided a custom attachment list builder, use that. final attachmentListBuilder = widget.attachmentListBuilder; if (attachmentListBuilder != null) { return attachmentListBuilder( context, nonOGAttachments, - onAttachmentRemovePressed, + _onAttachmentRemovePressed, ); } @@ -1224,7 +1217,7 @@ class StreamMessageInputState extends State maxHeight: 240, child: StreamMessageInputAttachmentList( attachments: nonOGAttachments, - onRemovePressed: onAttachmentRemovePressed, + onRemovePressed: _onAttachmentRemovePressed, fileAttachmentListBuilder: widget.fileAttachmentListBuilder, mediaAttachmentListBuilder: widget.mediaAttachmentListBuilder, fileAttachmentBuilder: widget.fileAttachmentBuilder, @@ -1270,6 +1263,20 @@ class StreamMessageInputState extends State ); } + // Default callback for removing an attachment. + Future _onAttachmentRemovePressed(Attachment attachment) async { + final file = attachment.file; + final uploadState = attachment.uploadState; + + if (file != null && !uploadState.isSuccess && !isWeb) { + await StreamAttachmentHandler.instance.deleteAttachmentFile( + attachmentFile: file, + ); + } + + _effectiveController.removeAttachmentById(attachment.id); + } + Widget _buildCommandButton(BuildContext context) { final s = _effectiveController.text.trim(); final isCommandOptionsVisible = s.startsWith(_kCommandTrigger); From 0ba6f013572d4806b45cada1ccec8d5a8318094f Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 22 Jun 2023 14:30:17 +0530 Subject: [PATCH 11/48] chore(repo): bump example sdk constraints for `dart 3.0`. Signed-off-by: xsahil03x --- packages/stream_chat/example/pubspec.yaml | 6 +++--- packages/stream_chat_flutter/example/pubspec.yaml | 15 ++++----------- .../stream_chat_flutter_core/example/pubspec.yaml | 4 ++-- .../example/pubspec.yaml | 15 ++++----------- .../stream_chat_persistence/example/pubspec.yaml | 6 +++--- 5 files changed, 16 insertions(+), 30 deletions(-) diff --git a/packages/stream_chat/example/pubspec.yaml b/packages/stream_chat/example/pubspec.yaml index b8a852eb8..dfedc5b2f 100644 --- a/packages/stream_chat/example/pubspec.yaml +++ b/packages/stream_chat/example/pubspec.yaml @@ -5,14 +5,14 @@ publish_to: "none" version: 1.0.0+1 environment: - sdk: '>=2.17.0 <3.0.0' + sdk: '>=2.17.0 <4.0.0' flutter: ">=1.17.0" dependencies: - cupertino_icons: ^1.0.0 + cupertino_icons: ^1.0.5 flutter: sdk: flutter - stream_chat: ^6.0.0 + stream_chat: ^6.4.0 dev_dependencies: flutter_test: diff --git a/packages/stream_chat_flutter/example/pubspec.yaml b/packages/stream_chat_flutter/example/pubspec.yaml index 7e051f96c..dfc36a084 100644 --- a/packages/stream_chat_flutter/example/pubspec.yaml +++ b/packages/stream_chat_flutter/example/pubspec.yaml @@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: '>=2.17.0 <3.0.0' + sdk: '>=2.17.0 <4.0.0' flutter: ">=1.17.0" dependencies: @@ -29,21 +29,14 @@ dependencies: flutter: sdk: flutter responsive_builder: ^0.7.0 - stream_chat_flutter: - path: ../ - stream_chat_localizations: - path: ../../stream_chat_localizations - stream_chat_persistence: - path: ../../stream_chat_persistence + stream_chat_flutter: ^6.4.0 + stream_chat_localizations: ^5.4.0 + stream_chat_persistence: ^6.4.0 dev_dependencies: flutter_test: sdk: flutter -dependency_overrides: - stream_chat_flutter: - path: ../ - # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/packages/stream_chat_flutter_core/example/pubspec.yaml b/packages/stream_chat_flutter_core/example/pubspec.yaml index 0abc0cdc0..a67d6c9b9 100644 --- a/packages/stream_chat_flutter_core/example/pubspec.yaml +++ b/packages/stream_chat_flutter_core/example/pubspec.yaml @@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: '>=2.17.0 <3.0.0' + sdk: '>=2.17.0 <4.0.0' flutter: ">=1.17.0" dependencies: @@ -27,7 +27,7 @@ dependencies: cupertino_icons: ^1.0.3 flutter: sdk: flutter - stream_chat_flutter_core: ^5.1.0 + stream_chat_flutter_core: ^6.4.0 dev_dependencies: flutter_test: diff --git a/packages/stream_chat_localizations/example/pubspec.yaml b/packages/stream_chat_localizations/example/pubspec.yaml index 5fcac8de3..0d98bfa71 100644 --- a/packages/stream_chat_localizations/example/pubspec.yaml +++ b/packages/stream_chat_localizations/example/pubspec.yaml @@ -1,25 +1,18 @@ name: stream_chat_localizations_example description: A new Flutter project. -publish_to: 'none' +publish_to: 'none' version: 1.0.0+1 environment: - sdk: '>=2.17.0 <3.0.0' + sdk: '>=2.17.0 <4.0.0' dependencies: cupertino_icons: ^1.0.3 flutter: sdk: flutter - stream_chat_flutter: - path: ../../stream_chat_flutter - stream_chat_localizations: - path: ../ - - -dependency_overrides: - stream_chat_flutter: - path: ../../stream_chat_flutter + stream_chat_flutter: ^6.4.0 + stream_chat_localizations: ^5.4.0 dev_dependencies: flutter_test: diff --git a/packages/stream_chat_persistence/example/pubspec.yaml b/packages/stream_chat_persistence/example/pubspec.yaml index 80a35fc75..4f20ad85e 100644 --- a/packages/stream_chat_persistence/example/pubspec.yaml +++ b/packages/stream_chat_persistence/example/pubspec.yaml @@ -5,15 +5,15 @@ publish_to: 'none' version: 1.0.0+1 environment: - sdk: '>=2.17.0 <3.0.0' + sdk: '>=2.17.0 <4.0.0' flutter: ">=1.17.0" dependencies: cupertino_icons: ^1.0.3 flutter: sdk: flutter - stream_chat: ^5.1.0 - stream_chat_persistence: ^5.1.0 + stream_chat: ^6.4.0 + stream_chat_persistence: ^6.4.0 dev_dependencies: flutter_test: From 13d9ad396123aec28216251b643800d384787df9 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 23 Jun 2023 17:17:08 +0530 Subject: [PATCH 12/48] feat(ui): Swipeable v2, migrate message widget. Signed-off-by: xsahil03x --- .../message_list_view/message_list_view.dart | 94 +++- .../misc/animated_circle_border_painter.dart | 72 +++ .../lib/src/misc/swipeable.dart | 523 ++++++++++++++---- 3 files changed, 553 insertions(+), 136 deletions(-) create mode 100644 packages/stream_chat_flutter/lib/src/misc/animated_circle_border_painter.dart 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 8863c991d..c1ad1b044 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 @@ -1,5 +1,7 @@ // ignore_for_file: lines_longer_than_80_chars import 'dart:async'; +import 'dart:math' as math; +import 'dart:ui'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; @@ -10,6 +12,7 @@ import 'package:stream_chat_flutter/src/message_list_view/loading_indicator.dart import 'package:stream_chat_flutter/src/message_list_view/mlv_utils.dart'; import 'package:stream_chat_flutter/src/message_list_view/thread_separator.dart'; import 'package:stream_chat_flutter/src/message_list_view/unread_messages_separator.dart'; +import 'package:stream_chat_flutter/src/misc/animated_circle_border_painter.dart'; import 'package:stream_chat_flutter/src/misc/swipeable.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; @@ -1226,26 +1229,6 @@ class _StreamMessageListViewState extends State { } var child = messageWidget; - if (!message.isDeleted && - !message.isSystem && - !message.isEphemeral && - widget.onMessageSwiped != null) { - child = Container( - decoration: const BoxDecoration(), - clipBehavior: Clip.hardEdge, - child: Swipeable( - onSwipeEnd: () { - FocusScope.of(context).unfocus(); - widget.onMessageSwiped?.call(message); - }, - backgroundIcon: StreamSvgIcon.reply( - color: _streamTheme.colorTheme.accentPrimary, - ), - child: child, - ), - ); - } - if (!initialMessageHighlightComplete && widget.highlightInitialMessage && isInitialMessage(message.id, streamChannel)) { @@ -1269,6 +1252,77 @@ class _StreamMessageListViewState extends State { ), ); } + + // Add swipeable if the callback is provided and the message is not deleted, + // system or ephemeral. + final onMessageSwiped = widget.onMessageSwiped; + if (onMessageSwiped != null && + !message.isDeleted && + !message.isSystem && + !message.isEphemeral) { + // The threshold after which the message is considered swiped. + const threshold = 0.2; + + // The direction in which the message can be swiped. + final swipeDirection = isMyMessage + ? SwipeDirection.endToStart // + : SwipeDirection.startToEnd; + + child = Swipeable( + key: ValueKey(message.id), + direction: swipeDirection, + swipeThreshold: threshold, + onSwiped: (_) => onMessageSwiped(message), + backgroundBuilder: (context, details) { + // The alignment of the swipe action. + final alignment = isMyMessage + ? Alignment.centerRight // + : Alignment.centerLeft; + + // The progress of the swipe action. + final progress = math.min(details.progress, threshold) / threshold; + + // The offset for the reply icon. + var offset = Offset.lerp( + const Offset(-24, 0), + const Offset(12, 0), + progress, + )!; + + // If the message is mine, we need to flip the offset. + if (isMyMessage) { + offset = Offset(-offset.dx, -offset.dy); + } + + return Align( + alignment: alignment, + child: Transform.translate( + offset: offset, + child: Opacity( + opacity: progress, + child: SizedBox.square( + dimension: 30, + child: CustomPaint( + painter: AnimatedCircleBorderPainter( + progress: progress, + color: _streamTheme.colorTheme.borders, + ), + child: Center( + child: StreamSvgIcon.reply( + size: lerpDouble(0, 18, progress), + color: _streamTheme.colorTheme.accentPrimary, + ), + ), + ), + ), + ), + ), + ); + }, + child: child, + ); + } + return child; } diff --git a/packages/stream_chat_flutter/lib/src/misc/animated_circle_border_painter.dart b/packages/stream_chat_flutter/lib/src/misc/animated_circle_border_painter.dart new file mode 100644 index 000000000..bd02678fe --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/misc/animated_circle_border_painter.dart @@ -0,0 +1,72 @@ +import 'dart:math' as math; + +import 'package:flutter/widgets.dart'; + +/// A custom painter that animates a circle border or fills it based on a +/// progress value. +/// +/// This painter draws a circle with a border that can be animated to fill the +/// circle or stroke its outline based on a given progress value. The progress +/// value is a double between 0.0 and 1.0, representing the completion of the +/// animation. +/// +/// When the progress is 0.0, the circle is completely empty, and when the +/// progress is 1.0, the circle is fully filled or stroked. +/// +/// The color of the arc/circle can be customized by providing a [color]. +/// +/// Example usage: +/// ```dart +/// AnimatedCircleBorderPainter painter = AnimatedCircleBorderPainter( +/// progress: 0.5, +/// color: Colors.blue, +/// ); +/// +/// CustomPaint( +/// painter: painter, +/// size: Size(200, 200), +/// // ... other properties +/// ) +/// ``` +class AnimatedCircleBorderPainter extends CustomPainter { + /// Creates an [AnimatedCircleBorderPainter] with the specified [progress] + /// and [color]. + const AnimatedCircleBorderPainter({ + required this.progress, + required this.color, + }); + + /// The progress of the animation, as a value between 0.0 and 1.0. + final double progress; + + /// The color of the arc/circle. + final Color color; + + @override + void paint(Canvas canvas, Size size) { + final style = progress == 1.0 ? PaintingStyle.fill : PaintingStyle.stroke; + + final arcPaint = Paint() + ..style = style + ..color = color + ..strokeWidth = 2.0 + ..strokeCap = StrokeCap.round; + + final radius = size.width / 2; + final center = Offset(size.width / 2, size.height / 2); + final sweepAngle = math.pi * 2 * progress; + + canvas.drawArc( + Rect.fromCircle(center: center, radius: radius), + -math.pi / 2, + sweepAngle, + false, + arcPaint, + ); + } + + @override + bool shouldRepaint(AnimatedCircleBorderPainter oldPainter) { + return oldPainter.progress != progress || oldPainter.color != color; + } +} diff --git a/packages/stream_chat_flutter/lib/src/misc/swipeable.dart b/packages/stream_chat_flutter/lib/src/misc/swipeable.dart index ded8520bb..45b9b2dce 100644 --- a/packages/stream_chat_flutter/lib/src/misc/swipeable.dart +++ b/packages/stream_chat_flutter/lib/src/misc/swipeable.dart @@ -1,173 +1,464 @@ import 'dart:math' as math; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -/// {@template swipeable} -/// A swipeable tile in a list. Swiping on the tile will reveal actions that -/// can be taken. -/// {@endtemplate} +/// Signature used by [swipeable] to indicate that it has been swiped in +/// the given `direction`. +/// +/// Used by [Swipeable.onSwiped]. +typedef SwipeDirectionCallback = void Function(SwipeDirection direction); + +/// Signature for a function that builds a widget given the progress of the +/// dismissing action. +/// +/// Used by [Swipeable.backgroundBuilder]. +typedef BackgroundWidgetBuilder = Widget Function( + BuildContext context, + SwipeUpdateDetails details, +); + +/// The direction in which a [Swipeable] can be swiped. +enum SwipeDirection { + /// The [Swipeable] can be swiped by dragging either left or right. + horizontal, + + /// The [Swipeable] can be swiped by dragging in the reverse of the + /// reading direction (e.g., from right to left in left-to-right languages). + endToStart, + + /// The [Swipeable] can be swiped by dragging in the reading direction + /// (e.g., from left to right in left-to-right languages). + startToEnd, + + /// The [Swipeable] cannot be swiped by dragging. + none +} + +/// A widget that can be swiped in a specified direction. +/// +/// The `Swipeable` widget allows its child to be swiped by the user in a +/// specified direction. +/// +/// It provides options for customizing the swipe behavior, including the +/// ability to specify a background widget that appears during the swipe, +/// callbacks for handling swipe completion, and more. +/// +/// Example usage: +/// ```dart +/// Swipeable( +/// child: Container( +/// height: 100, +/// width: 200, +/// color: Colors.blue, +/// child: Center( +/// child: Text('Swipe me'), +/// ), +/// ), +/// backgroundBuilder: (context, details) { +/// final direction = details.direction; +/// return Container( +/// color: Colors.red, +/// child: Center( +/// child: Text( +/// direction == SwipeDirection.left ? 'Swipe left' : 'Swipe right', +/// style: TextStyle( +/// color: Colors.white, +/// fontWeight: FontWeight.bold, +/// ), +/// ), +/// ), +/// ); +/// }, +/// onSwiped: (direction) { +/// if (direction == SwipeDirection.left) { +/// // Handle left swipe +/// } else if (direction == SwipeDirection.right) { +/// // Handle right swipe +/// } +/// }, +/// direction: SwipeDirection.horizontal, +/// swipeThreshold: 0.4, +/// movementDuration: Duration(milliseconds: 200), +/// ) +/// ``` class Swipeable extends StatefulWidget { - /// {@macro swipeable} + /// Creates a widget that can be swiped . const Swipeable({ - super.key, + required super.key, required this.child, - required this.backgroundIcon, - this.onSwipeStart, - this.onSwipeCancel, - this.onSwipeEnd, - this.threshold = 82.0, - }); + this.backgroundBuilder, + this.onSwiped, + this.direction = SwipeDirection.horizontal, + this.swipeThreshold = 0.4, + this.movementDuration = const Duration(milliseconds: 200), + this.dragStartBehavior = DragStartBehavior.start, + this.behavior = HitTestBehavior.opaque, + }) : assert( + swipeThreshold >= 0.0 && swipeThreshold <= 1.0, + 'swipeThreshold must be between 0.0 and 1.0', + ); - /// Child to make swipeable + /// The widget below this widget in the tree. + /// + /// {@macro flutter.widgets.ProxyWidget.child} final Widget child; - /// Background icon after swipe - final Widget backgroundIcon; + /// A widget that is stacked behind the child. If secondaryBackground is also + /// specified then this widget only appears when the child has been dragged + /// down or to the right. + final BackgroundWidgetBuilder? backgroundBuilder; + + /// Called when the widget has been successfully swiped based on the + /// [direction] and [swipeThreshold]. + final SwipeDirectionCallback? onSwiped; - /// The action to perform when the swipe starts - final VoidCallback? onSwipeStart; + /// The direction in which the widget can be swiped. + final SwipeDirection direction; - /// The action to perform when the swipe is cancelled - final VoidCallback? onSwipeCancel; + /// The offset threshold the item has to be dragged in order to be considered + /// swiped. + /// + /// Represented as a fraction, e.g. if it is 0.4 (the default), then the item + /// has to be dragged at least 40% towards one direction to be considered + /// swiped. + /// + /// See also: + /// + /// * [direction], which controls the directions in which the items can + /// be swiped. + final double swipeThreshold; - /// The action to perform when the swipe ends - final VoidCallback? onSwipeEnd; + /// Defines the duration for card to come back to original position. + final Duration movementDuration; - /// Threshold for swipe - final double threshold; + /// Determines the way that drag start behavior is handled. + /// + /// If set to [DragStartBehavior.start], the drag gesture used to dismiss a + /// swipeable will begin at the position where the drag gesture won the + /// arena. + /// + /// If set to [DragStartBehavior.down] it will begin at the position where + /// a down event is first detected. + /// + /// In general, setting this to [DragStartBehavior.start] will make drag + /// animation smoother and setting it to [DragStartBehavior.down] will make + /// drag behavior feel slightly more reactive. + /// + /// By default, the drag start behavior is [DragStartBehavior.start]. + /// + /// See also: + /// + /// * [DragGestureRecognizer.dragStartBehavior], which gives an example for + /// the different behaviors. + final DragStartBehavior dragStartBehavior; + + /// How to behave during hit tests. + /// + /// This defaults to [HitTestBehavior.opaque]. + final HitTestBehavior behavior; @override - State createState() => _SwipeableState(); + _SwipeableState createState() => _SwipeableState(); } -class _SwipeableState extends State with TickerProviderStateMixin { - double _dragExtent = 0; - late AnimationController _moveController; - late AnimationController _iconMoveController; - late Animation _moveAnimation; - late Animation _iconTransitionAnimation; - late Animation _iconFadeAnimation; - bool _pastThreshold = false; +/// Details for [DismissUpdateCallback]. +/// +/// See also: +/// +/// * [swipeable.onUpdate], which receives this information. +class SwipeUpdateDetails { + /// Create a new instance of [SwipeUpdateDetails]. + SwipeUpdateDetails({ + this.direction = SwipeDirection.horizontal, + this.reached = false, + this.previousReached = false, + this.progress = 0.0, + }); + + /// The direction that the swipeable is being dragged. + final SwipeDirection direction; + + /// Whether the swipe threshold is currently reached. + final bool reached; + + /// Whether the swipe threshold was reached the last time this callback was + /// invoked. + /// + /// This can be used in conjunction with [SwipeUpdateDetails.reached] to catch + /// the moment that the [Swipeable] is dragged across the threshold. + final bool previousReached; + + /// The offset ratio of the swipeable in its parent container. + /// + /// A value of 0.0 represents the normal position and 1.0 means the child is + /// completely outside its parent. + /// + /// This can be used to synchronize other elements to what the swipeable is + /// doing on screen, e.g. using this value to set the opacity thereby fading + /// swipeable as it's dragged offscreen. + final double progress; +} + +class _SwipeableClipper extends CustomClipper { + _SwipeableClipper({ + required this.moveAnimation, + }) : super(reclip: moveAnimation); + + final Animation moveAnimation; + + @override + Rect getClip(Size size) { + final offset = moveAnimation.value.dx * size.width; + if (offset < 0) { + return Rect.fromLTRB(size.width + offset, 0, size.width, size.height); + } + return Rect.fromLTRB(0, 0, offset, size.height); + } - final _animationDuration = const Duration(milliseconds: 200); + @override + Rect getApproximateClipRect(Size size) => getClip(size); + @override + bool shouldReclip(_SwipeableClipper oldClipper) { + return oldClipper.moveAnimation.value != moveAnimation.value; + } +} + +class _SwipeableState extends State + with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin { @override void initState() { super.initState(); - _moveController = - AnimationController(duration: _animationDuration, vsync: this); - _iconMoveController = - AnimationController(duration: _animationDuration, vsync: this); - _moveAnimation = Tween(begin: Offset.zero, end: const Offset(1, 0)) - .animate(_moveController); - _iconTransitionAnimation = - Tween(begin: const Offset(-0.1, 0), end: const Offset(0.4, 0)) - .animate(_moveController); - _iconFadeAnimation = - Tween(begin: 0.7, end: 1).animate(_iconMoveController); - - const controllerValue = 0.0; - _moveController.animateTo(controllerValue); - _iconMoveController.animateTo(controllerValue); + _moveController = AnimationController( + duration: widget.movementDuration, + vsync: this, + )..addStatusListener((_) => updateKeepAlive()); + _updateMoveAnimation(); } + AnimationController? _moveController; + late Animation _moveAnimation; + + double _dragExtent = 0; + bool _dragUnderway = false; + bool _swipeThresholdReached = false; + + final GlobalKey _contentKey = GlobalKey(); + + @override + bool get wantKeepAlive => _moveController?.isAnimating ?? false; + @override void dispose() { - _moveController.dispose(); - _iconMoveController.dispose(); + _moveController?.dispose(); super.dispose(); } + SwipeDirection _extentToDirection(double extent) { + if (extent == 0.0) { + return SwipeDirection.none; + } + switch (Directionality.of(context)) { + case TextDirection.rtl: + return extent < 0 + ? SwipeDirection.startToEnd + : SwipeDirection.endToStart; + case TextDirection.ltr: + return extent > 0 + ? SwipeDirection.startToEnd + : SwipeDirection.endToStart; + } + } + + SwipeDirection get _swipeDirection => _extentToDirection(_dragExtent); + + bool get _isActive => _dragUnderway || _moveController!.isAnimating; + + double get _overallDragAxisExtent => context.size!.width; + void _handleDragStart(DragStartDetails details) { - widget.onSwipeStart?.call(); + _dragUnderway = true; + if (_moveController!.isAnimating) { + _dragExtent = + _moveController!.value * _overallDragAxisExtent * _dragExtent.sign; + _moveController!.stop(); + } else { + _dragExtent = 0.0; + _moveController!.value = 0.0; + } + setState(_updateMoveAnimation); } void _handleDragUpdate(DragUpdateDetails details) { - final delta = details.primaryDelta!; - _dragExtent += delta; + if (!_isActive || _moveController!.isAnimating) { + return; + } - if (_dragExtent.isNegative) return; + final delta = details.primaryDelta!; + final oldDragExtent = _dragExtent; + switch (widget.direction) { + case SwipeDirection.horizontal: + _dragExtent += delta; + break; + case SwipeDirection.endToStart: + switch (Directionality.of(context)) { + case TextDirection.rtl: + if (_dragExtent + delta > 0) { + _dragExtent += delta; + } + break; + case TextDirection.ltr: + if (_dragExtent + delta < 0) { + _dragExtent += delta; + } + break; + } + break; + case SwipeDirection.startToEnd: + switch (Directionality.of(context)) { + case TextDirection.rtl: + if (_dragExtent + delta < 0) { + _dragExtent += delta; + } + break; + case TextDirection.ltr: + if (_dragExtent + delta > 0) { + _dragExtent += delta; + } + break; + } + break; + case SwipeDirection.none: + _dragExtent = 0; + break; + } - final movePastThresholdPixels = widget.threshold; - var newPos = _dragExtent.abs() / context.size!.width; + if (oldDragExtent.sign != _dragExtent.sign) { + setState(_updateMoveAnimation); + } - if (_dragExtent.abs() > movePastThresholdPixels) { - // how many "thresholds" past the threshold we are. 1 = the threshold 2 - // = two thresholds. - final n = _dragExtent.abs() / movePastThresholdPixels; + if (!_moveController!.isAnimating) { + final currentDragExtent = _dragExtent.abs(); + final overallDragExtent = _overallDragAxisExtent; + final movePastThresholdExtent = widget.swipeThreshold * overallDragExtent; - // Take the number of thresholds past the threshold, and reduce this - // number - final reducedThreshold = math.pow(n, 0.3); + final double newPos; + if (currentDragExtent > movePastThresholdExtent) { + // How many "thresholds" past the threshold we are. + final n = currentDragExtent / movePastThresholdExtent; - final adjustedPixelPos = movePastThresholdPixels * reducedThreshold; - newPos = adjustedPixelPos / context.size!.width; + // Take the number of thresholds past the threshold, and reduce it by + // the threshold amount, then normalize it to the drag extents. + final reducedThreshold = math.pow(n, 0.3); + final adjustedDragExtent = movePastThresholdExtent * reducedThreshold; - if (_dragExtent > 0 && !_pastThreshold) { - _iconMoveController.value = 1; - _pastThreshold = true; + newPos = adjustedDragExtent / overallDragExtent; + } else { + newPos = currentDragExtent / overallDragExtent; } - } else { - // Send a cancel event if the user has swiped back underneath the - // threshold - if (_pastThreshold && widget.onSwipeCancel != null) { - widget.onSwipeCancel!(); - } - _pastThreshold = false; - } - if (!_pastThreshold || newPos < _moveController.value) { - _iconMoveController.value = newPos; + + _moveController!.value = newPos; } - _moveController.value = newPos; + } + + SwipeUpdateDetails _currentSwipeUpdateDetails() { + final oldSwipeThresholdReached = _swipeThresholdReached; + _swipeThresholdReached = _moveController!.value > widget.swipeThreshold; + + return SwipeUpdateDetails( + direction: _swipeDirection, + reached: _swipeThresholdReached, + previousReached: oldSwipeThresholdReached, + progress: _moveController!.value, + ); + } + + void _updateMoveAnimation() { + final end = _dragExtent.sign; + _moveAnimation = _moveController!.drive( + Tween( + begin: Offset.zero, + end: Offset(end, 0), + ), + ); } void _handleDragEnd(DragEndDetails details) { - _moveController.animateTo(0, duration: _animationDuration); - _iconMoveController.animateTo(0, duration: _animationDuration); - _dragExtent = 0.0; - if (_pastThreshold && widget.onSwipeEnd != null) { - widget.onSwipeEnd!(); + if (!_isActive || _moveController!.isAnimating) return; + _dragUnderway = false; + + // Once dragging ends, animate back to the initial offset. + _moveController!.reverse(); + + // If the threshold was reached, report it. + if (_moveController!.value > widget.swipeThreshold) { + if (widget.onSwiped != null) { + final direction = _swipeDirection; + widget.onSwiped!(direction); + } } } @override Widget build(BuildContext context) { - return GestureDetector( - onHorizontalDragStart: _handleDragStart, - onHorizontalDragUpdate: _handleDragUpdate, - onHorizontalDragEnd: _handleDragEnd, - behavior: HitTestBehavior.opaque, - child: Stack( - alignment: Alignment.center, + super.build(context); // See AutomaticKeepAliveClientMixin. + + assert( + debugCheckHasDirectionality(context), + 'Swipeable must be inside of a Directionality widget.', + ); + + Widget? background; + final backgroundBuilder = widget.backgroundBuilder; + if (backgroundBuilder != null) { + background = AnimatedBuilder( + animation: _moveAnimation, + builder: (context, _) { + final updateDetails = _currentSwipeUpdateDetails(); + return backgroundBuilder.call(context, updateDetails); + }, + ); + } + + Widget content = SlideTransition( + position: _moveAnimation, + child: KeyedSubtree(key: _contentKey, child: widget.child), + ); + + if (background != null) { + content = Stack( fit: StackFit.passthrough, - children: [ - SlideTransition( - position: _iconTransitionAnimation, - child: Row( - children: [ - FadeTransition( - opacity: _iconFadeAnimation, - child: Container( - padding: const EdgeInsets.all(4), - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: StreamChatTheme.of(context).colorTheme.disabled, - ), - ), - child: widget.backgroundIcon, - ), + alignment: Alignment.center, + children: [ + if (!_moveAnimation.isDismissed) + Positioned.fill( + child: ClipRect( + clipper: _SwipeableClipper( + moveAnimation: _moveAnimation, ), - ], + child: background, + ), ), - ), - SlideTransition( - position: _moveAnimation, - child: widget.child, - ), + content, ], - ), + ); + } + + // If the SwipeDirection is none, we do not add drag gestures because the + // content cannot be dragged. + if (widget.direction == SwipeDirection.none) { + return content; + } + + // We are not resizing but we may be being dragging in widget.direction. + return GestureDetector( + onHorizontalDragStart: _handleDragStart, + onHorizontalDragUpdate: _handleDragUpdate, + onHorizontalDragEnd: _handleDragEnd, + behavior: widget.behavior, + dragStartBehavior: widget.dragStartBehavior, + child: content, ); } } From 1890d316f930bae3c3aab60af8713983afdd6e21 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 23 Jun 2023 17:19:39 +0530 Subject: [PATCH 13/48] feat(ui): export `Swipeable`, `AnimatedCircleBorderPainter`. Signed-off-by: xsahil03x --- packages/stream_chat_flutter/lib/stream_chat_flutter.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart index e5ca5a1ed..50977784b 100644 --- a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart +++ b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart @@ -58,6 +58,7 @@ export 'src/message_widget/message_text.dart'; export 'src/message_widget/message_widget.dart'; export 'src/message_widget/reactions/reaction_picker.dart'; export 'src/message_widget/text_bubble.dart'; +export 'src/misc/animated_circle_border_painter.dart'; export 'src/misc/back_button.dart'; export 'src/misc/connection_status_builder.dart'; export 'src/misc/date_divider.dart'; @@ -66,6 +67,7 @@ export 'src/misc/option_list_tile.dart'; export 'src/misc/reaction_icon.dart'; export 'src/misc/stream_neumorphic_button.dart'; export 'src/misc/stream_svg_icon.dart'; +export 'src/misc/swipeable.dart'; export 'src/misc/system_message.dart'; export 'src/misc/thread_header.dart'; export 'src/misc/visible_footnote.dart'; From 92307a270e773d976fd7dac9fcc67660fe63451b Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 23 Jun 2023 17:30:01 +0530 Subject: [PATCH 14/48] chore(ui): deprecate `MessageListView.onMessageSwiped`. Signed-off-by: xsahil03x --- .../lib/src/message_list_view/message_list_view.dart | 4 ++++ 1 file changed, 4 insertions(+) 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 c1ad1b044..68c19036e 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 @@ -99,6 +99,10 @@ class StreamMessageListView extends StatefulWidget { this.initialAlignment, this.scrollController, this.itemPositionListener, + @Deprecated( + 'Try wrapping the `MessageWidget` with a `Swipeable`, `Dismissible` or a ' + 'custom widget to achieve the swipe to reply behaviour.', + ) this.onMessageSwiped, this.highlightInitialMessage = false, this.messageHighlightColor, From 59c4307205e84bf9f411e8a73e658368b316b1dc Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 23 Jun 2023 17:52:15 +0530 Subject: [PATCH 15/48] chore: update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat_flutter/CHANGELOG.md | 51 +++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 49b324d67..11ca3d099 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -6,6 +6,9 @@ after overriding the `onConfirmDeleteTap` callback. - [[#1621]](https://github.com/GetStream/stream-chat-flutter/issues/1621) Fixed `createdAtStyle` null check error in `SendingIndicatorBuilder`. +- [[#1069]](https://github.com/GetStream/stream-chat-flutter/issues/1069) Fixed message swipe to reply using same + direction for both current user and other users. It now uses `SwipeDirection.startToEnd` for current user + and `SwipeDirection.endToStart` for other users. ✅ Added @@ -26,6 +29,54 @@ 🔄 Changed - Deprecated `StreamMessageInput.attachmentThumbnailBuilders` in favor of `StreamMessageInput.mediaAttachmentBuilder`. +- Deprecated `StreamMessageListView.onMessageSwiped`. Try wrapping the `MessageWidget` with a `Swipeable`, `Dismissible` + or a custom widget to achieve the swipe to reply behaviour. + + ```dart + // Migration from onMessageSwiped to Swipeable. + StreamMessageListView( + ..., + messageBuilder: (context, messageDetails, messages, defaultWidget) { + // The threshold after which the message should be considered as swiped. + const threshold = 0.2; + + // The direction in which the message should be swiped to reply. + final swipeDirection = messageDetails.isMyMessage + ? SwipeDirection.endToStart // + : SwipeDirection.startToEnd; + + return Swipeable( + key: ValueKey(messageDetails.message.id), + direction: swipeDirection, + swipeThreshold: threshold, + onSwiped: (direction) { + // Handle the swipe action here. + }, + backgroundBuilder: (context, details) { + // The alignment of the swipe action. + final alignment = messageDetails.isMyMessage + ? Alignment.centerRight // + : Alignment.centerLeft; + + // The progress of the swipe action. + final progress = math.min(details.progress, threshold) / threshold; + + return Align( + alignment: alignment, + child: Opacity( + opacity: progress, + child: const Icon( + Icons.reply, + color: Colors.white, + ), + ), + ); + }, + child: defaultWidget, + ); + }, + ) + ``` ## 6.4.0 From a681ebfd683f5ca9810030b6ada78ccdc34ca0e8 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 23 Jun 2023 18:07:07 +0530 Subject: [PATCH 16/48] chore: fix analysis. Signed-off-by: xsahil03x --- .../stream_chat_flutter/example/lib/main.dart | 83 +++++++++++++++++-- .../message_list_view/message_list_view.dart | 2 - 2 files changed, 75 insertions(+), 10 deletions(-) diff --git a/packages/stream_chat_flutter/example/lib/main.dart b/packages/stream_chat_flutter/example/lib/main.dart index f0acc0fa5..332d813bf 100644 --- a/packages/stream_chat_flutter/example/lib/main.dart +++ b/packages/stream_chat_flutter/example/lib/main.dart @@ -1,6 +1,8 @@ // ignore_for_file: public_member_api_docs import 'dart:async'; +import 'dart:math' as math; +import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:responsive_builder/responsive_builder.dart'; @@ -277,19 +279,84 @@ class _ChannelPageState extends State { children: [ Expanded( child: StreamMessageListView( - onMessageSwiped: - (CurrentPlatform.isAndroid || CurrentPlatform.isIos) - ? reply - : null, threadBuilder: (context, parent) { return ThreadPage( parent: parent!, ); }, - messageBuilder: - (context, details, messages, defaultWidget) { - return defaultWidget.copyWith( - onReplyTap: reply, + messageBuilder: ( + context, + messageDetails, + messages, + defaultWidget, + ) { + // The threshold after which the message is considered + // swiped. + const threshold = 0.2; + + final isMyMessage = messageDetails.isMyMessage; + + // The direction in which the message can be swiped. + final swipeDirection = isMyMessage + ? SwipeDirection.endToStart // + : SwipeDirection.startToEnd; + + return Swipeable( + key: ValueKey(messageDetails.message.id), + direction: swipeDirection, + swipeThreshold: threshold, + onSwiped: (details) => reply(messageDetails.message), + backgroundBuilder: (context, details) { + // The alignment of the swipe action. + final alignment = isMyMessage + ? Alignment.centerRight // + : Alignment.centerLeft; + + // The progress of the swipe action. + final progress = + math.min(details.progress, threshold) / threshold; + + // The offset for the reply icon. + var offset = Offset.lerp( + const Offset(-24, 0), + const Offset(12, 0), + progress, + )!; + + // If the message is mine, we need to flip the offset. + if (isMyMessage) { + offset = Offset(-offset.dx, -offset.dy); + } + + final _streamTheme = StreamChatTheme.of(context); + + return Align( + alignment: alignment, + child: Transform.translate( + offset: offset, + child: Opacity( + opacity: progress, + child: SizedBox.square( + dimension: 30, + child: CustomPaint( + painter: AnimatedCircleBorderPainter( + progress: progress, + color: _streamTheme.colorTheme.borders, + ), + child: Center( + child: StreamSvgIcon.reply( + size: lerpDouble(0, 18, progress), + color: _streamTheme + .colorTheme.accentPrimary, + ), + ), + ), + ), + ), + ), + ); + }, + child: defaultWidget.copyWith(onReplyTap: reply), ); }, ), 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 68c19036e..5a5075f88 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 @@ -12,8 +12,6 @@ import 'package:stream_chat_flutter/src/message_list_view/loading_indicator.dart import 'package:stream_chat_flutter/src/message_list_view/mlv_utils.dart'; import 'package:stream_chat_flutter/src/message_list_view/thread_separator.dart'; import 'package:stream_chat_flutter/src/message_list_view/unread_messages_separator.dart'; -import 'package:stream_chat_flutter/src/misc/animated_circle_border_painter.dart'; -import 'package:stream_chat_flutter/src/misc/swipeable.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// Spacing Types (These are properties of a message to help inform the decision From 723a7368b545fc9d1afb2eee8543950d2727686a Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 27 Jun 2023 17:14:54 +0530 Subject: [PATCH 17/48] fix(ui): not able to hide reaction picker. Signed-off-by: xsahil03x --- .../message_actions_modal.dart | 18 +- .../src/message_widget/message_widget.dart | 169 ++++++++++++------ .../message_widget_content.dart | 63 ++----- .../reactions/message_reactions_modal.dart | 9 +- .../reactions/reactions_align.dart | 20 +-- .../message_reactions_modal_test.dart | 2 +- 6 files changed, 153 insertions(+), 128 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_actions_modal/message_actions_modal.dart b/packages/stream_chat_flutter/lib/src/message_actions_modal/message_actions_modal.dart index 7fcda8a92..2af8e5e6d 100644 --- a/packages/stream_chat_flutter/lib/src/message_actions_modal/message_actions_modal.dart +++ b/packages/stream_chat_flutter/lib/src/message_actions_modal/message_actions_modal.dart @@ -15,7 +15,7 @@ class MessageActionsModal extends StatefulWidget { required this.message, required this.messageWidget, required this.messageTheme, - this.showReactions = true, + this.showReactionPicker = true, this.showDeleteMessage = true, this.showEditMessage = true, this.onReplyTap, @@ -54,8 +54,8 @@ class MessageActionsModal extends StatefulWidget { /// [StreamMessageThemeData] for message final StreamMessageThemeData messageTheme; - /// Flag for showing reactions - final bool showReactions; + /// Flag for showing reaction picker. + final bool showReactionPicker; /// Callback when copy is tapped final OnMessageTap? onCopyTap; @@ -105,6 +105,10 @@ class _MessageActionsModalState extends State { final user = StreamChat.of(context).currentUser; final orientation = mediaQueryData.orientation; + final _userPermissions = StreamChannel.of(context).channel.ownCapabilities; + final hasReactionPermission = + _userPermissions.contains(PermissionType.sendReaction); + final fontSize = widget.messageTheme.messageTextStyle?.fontSize; final streamChatThemeData = StreamChatTheme.of(context); @@ -115,12 +119,10 @@ class _MessageActionsModalState extends State { child: Padding( padding: const EdgeInsets.all(8), child: Column( - crossAxisAlignment: widget.reverse - ? CrossAxisAlignment.end - : CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - if (widget.showReactions && - (widget.message.status == MessageSendingStatus.sent)) + if (widget.showReactionPicker && hasReactionPermission) LayoutBuilder( builder: (context, constraints) { return Align( 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 963b2ac50..614a0f38c 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 @@ -2,6 +2,7 @@ import 'package:contextmenu/contextmenu.dart'; import 'package:flutter/material.dart' hide ButtonStyle; import 'package:flutter/services.dart'; import 'package:flutter_portal/flutter_portal.dart'; +import 'package:meta/meta.dart'; import 'package:stream_chat_flutter/conditional_parent_builder/conditional_parent_builder.dart'; import 'package:stream_chat_flutter/platform_widget_builder/platform_widget_builder.dart'; import 'package:stream_chat_flutter/src/context_menu_items/context_menu_reaction_picker.dart'; @@ -54,7 +55,10 @@ class StreamMessageWidget extends StatefulWidget { this.attachmentBorderRadiusGeometry, this.onMentionTap, this.onMessageTap, - this.showReactionPickerIndicator = false, + bool? showReactionPicker, + @Deprecated('Use `showReactionPicker` instead') + bool showReactionPickerIndicator = true, + @internal this.showReactionPickerTail = false, this.showUserAvatar = DisplayWidget.show, this.showSendingIndicator = true, this.showThreadReplyIndicator = false, @@ -114,6 +118,7 @@ class StreamMessageWidget extends StatefulWidget { bottomRowBuilder == null || bottomRowBuilderWithDefaultWidget == null, 'You can only use one of the two bottom row builders', ), + showReactionPicker = showReactionPicker ?? showReactionPickerIndicator, attachmentBuilders = { 'image': (context, message, attachments) { final border = RoundedRectangleBorder( @@ -460,10 +465,22 @@ class StreamMessageWidget extends StatefulWidget { /// {@endtemplate} final void Function(String)? onLinkTap; + /// {@template showReactionPicker} + /// Whether or not to show the reaction picker. + /// Used in [StreamMessageReactionsModal] and [MessageActionsModal]. + /// {@endtemplate} + final bool showReactionPicker; + /// {@template showReactionPickerIndicator} /// Used in [StreamMessageReactionsModal] and [MessageActionsModal] + /// {@endtemplate} @Deprecated('Use `showReactionPicker` instead') + bool get showReactionPickerIndicator => showReactionPicker; + + /// {@template showReactionPickerTail} + /// Whether or not to show the reaction picker tail /// {@endtemplate} - final bool showReactionPickerIndicator; + @internal + final bool showReactionPickerTail; /// {@template onShowMessage} /// Callback when show message is tapped @@ -619,7 +636,10 @@ class StreamMessageWidget extends StatefulWidget { bool? showInChannelIndicator, void Function(User)? onUserAvatarTap, void Function(String)? onLinkTap, + bool? showReactionPicker, + @Deprecated('Use `showReactionPicker` instead') bool? showReactionPickerIndicator, + @internal bool? showReactionPickerTail, List? readList, ShowMessageCallback? onShowMessage, bool? showUsername, @@ -703,8 +723,11 @@ class StreamMessageWidget extends StatefulWidget { showInChannelIndicator ?? this.showInChannelIndicator, onUserAvatarTap: onUserAvatarTap ?? this.onUserAvatarTap, onLinkTap: onLinkTap ?? this.onLinkTap, - showReactionPickerIndicator: - showReactionPickerIndicator ?? this.showReactionPickerIndicator, + showReactionPicker: showReactionPicker ?? + showReactionPickerIndicator ?? + this.showReactionPicker, + showReactionPickerTail: + showReactionPickerTail ?? this.showReactionPickerTail, onShowMessage: onShowMessage ?? this.onShowMessage, showUsername: showUsername ?? this.showUsername, showTimestamp: showTimestamp ?? this.showTimestamp, @@ -885,12 +908,12 @@ class _StreamMessageWidgetState extends State if (!widget.message.isDeleted) { return ContextMenuArea( verticalPadding: 0, - builder: (context) => _buildContextMenu(), + builder: (_) => _buildContextMenu(), child: child, ); - } else { - return child; } + + return child; }, child: Material( type: MaterialType.transparency, @@ -963,9 +986,9 @@ class _StreamMessageWidgetState extends State messageWidget: widget, showBottomRow: showBottomRow, showPinHighlight: widget.showPinHighlight, - showReactionPickerIndicator: - widget.showReactionPickerIndicator, + showReactionPickerTail: widget.showReactionPickerTail, showReactions: showReactions, + onReactionsTap: () => _showMessageReactionsModal(context), showUserAvatar: widget.showUserAvatar, streamChat: _streamChat, translateUserAvatar: widget.translateUserAvatar, @@ -996,14 +1019,15 @@ class _StreamMessageWidgetState extends State final channel = StreamChannel.of(context).channel; return [ - StreamChatContextMenuItem( - child: StreamChannel( - channel: channel, - child: ContextMenuReactionPicker( - message: widget.message, + if (widget.showReactionPicker) + StreamChatContextMenuItem( + child: StreamChannel( + channel: channel, + child: ContextMenuReactionPicker( + message: widget.message, + ), ), ), - ), if (shouldShowReplyAction) ...[ StreamChatContextMenuItem( leading: StreamSvgIcon.reply(), @@ -1149,21 +1173,7 @@ class _StreamMessageWidgetState extends State ]; } - void onLongPress(BuildContext context) { - if (widget.message.isEphemeral || - widget.message.status == MessageSendingStatus.sending) { - return; - } - - if (widget.onMessageActions != null) { - widget.onMessageActions!(context, widget.message); - } else { - _showMessageActionModalBottomSheet(context); - } - return; - } - - void _showMessageActionModalBottomSheet(BuildContext context) { + void _showMessageReactionsModal(BuildContext context) { final channel = StreamChannel.of(context).channel; showDialog( @@ -1173,7 +1183,8 @@ class _StreamMessageWidgetState extends State barrierColor: _streamChatTheme.colorTheme.overlay, builder: (context) => StreamChannel( channel: channel, - child: MessageActionsModal( + child: StreamMessageReactionsModal( + showReactionPicker: widget.showReactionPicker, messageWidget: widget.copyWith( key: const Key('MessageWidget'), message: widget.message.copyWith( @@ -1187,37 +1198,93 @@ class _StreamMessageWidgetState extends State translateUserAvatar: false, showSendingIndicator: false, padding: EdgeInsets.zero, - showReactionPickerIndicator: widget.showReactions && - (widget.message.status == MessageSendingStatus.sent), + // Show the tail if the reaction picker is visible. + showReactionPickerTail: widget.showReactionPicker, showPinHighlight: false, showUserAvatar: widget.message.user!.id == channel.client.state.currentUser!.id ? DisplayWidget.gone : DisplayWidget.show, ), - onCopyTap: (message) { - final text = message.text; - if (text != null) Clipboard.setData(ClipboardData(text: text)); - }, + onUserAvatarTap: widget.onUserAvatarTap, messageTheme: widget.messageTheme, reverse: widget.reverse, - showDeleteMessage: shouldShowDeleteAction, - onConfirmDeleteTap: widget.onConfirmDeleteTap, message: widget.message, - editMessageInputBuilder: widget.editMessageInputBuilder, - onReplyTap: widget.onReplyTap, - onThreadReplyTap: widget.onThreadTap, - showResendMessage: shouldShowResendAction, - showCopyMessage: shouldShowCopyAction, - showEditMessage: shouldShowEditAction, - showReactions: widget.showReactions, - showReplyMessage: shouldShowReplyAction, - showThreadReplyMessage: shouldShowThreadReplyAction, - showFlagButton: widget.showFlagButton, - showPinButton: widget.showPinButton, - customActions: widget.customActions, ), ), ); } + + void onLongPress(BuildContext context) { + if (widget.message.isEphemeral || + widget.message.status == MessageSendingStatus.sending) { + return; + } + + if (widget.onMessageActions != null) { + return widget.onMessageActions!(context, widget.message); + } + + return _showMessageActionModalBottomSheet(context); + } + + void _showMessageActionModalBottomSheet(BuildContext context) { + final channel = StreamChannel.of(context).channel; + + showDialog( + useRootNavigator: false, + context: context, + useSafeArea: false, + barrierColor: _streamChatTheme.colorTheme.overlay, + builder: (context) { + return StreamChannel( + channel: channel, + child: MessageActionsModal( + messageWidget: widget.copyWith( + key: const Key('MessageWidget'), + message: widget.message.copyWith( + text: (widget.message.text?.length ?? 0) > 200 + ? '${widget.message.text!.substring(0, 200)}...' + : widget.message.text, + ), + showReactions: false, + showUsername: false, + showTimestamp: false, + translateUserAvatar: false, + showSendingIndicator: false, + padding: EdgeInsets.zero, + // Show both the tail and indicator if the indicator is shown. + showReactionPickerTail: widget.showReactionPickerIndicator, + showPinHighlight: false, + showUserAvatar: widget.message.user!.id == + channel.client.state.currentUser!.id + ? DisplayWidget.gone + : DisplayWidget.show, + ), + onCopyTap: (message) { + final text = message.text; + if (text != null) Clipboard.setData(ClipboardData(text: text)); + }, + messageTheme: widget.messageTheme, + reverse: widget.reverse, + showDeleteMessage: shouldShowDeleteAction, + onConfirmDeleteTap: widget.onConfirmDeleteTap, + message: widget.message, + editMessageInputBuilder: widget.editMessageInputBuilder, + onReplyTap: widget.onReplyTap, + onThreadReplyTap: widget.onThreadTap, + showResendMessage: shouldShowResendAction, + showCopyMessage: shouldShowCopyAction, + showEditMessage: shouldShowEditAction, + showReactionPicker: widget.showReactionPickerIndicator, + showReplyMessage: shouldShowReplyAction, + showThreadReplyMessage: shouldShowThreadReplyAction, + showFlagButton: widget.showFlagButton, + showPinButton: widget.showPinButton, + customActions: widget.customActions, + ), + ); + }, + ); + } } diff --git a/packages/stream_chat_flutter/lib/src/message_widget/message_widget_content.dart b/packages/stream_chat_flutter/lib/src/message_widget/message_widget_content.dart index f85430137..2910ee62a 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget/message_widget_content.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget/message_widget_content.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_portal/flutter_portal.dart'; +import 'package:meta/meta.dart'; import 'package:stream_chat_flutter/src/message_widget/message_widget_content_components.dart'; import 'package:stream_chat_flutter/src/message_widget/reactions/desktop_reactions_builder.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; @@ -21,6 +22,7 @@ typedef BottomRowBuilderWithDefaultWidget = Widget Function( /// /// Should not be used outside of [MessageWidget. /// {@endtemplate} +@internal class MessageWidgetContent extends StatelessWidget { /// {@macro messageWidgetContent} const MessageWidgetContent({ @@ -33,6 +35,7 @@ class MessageWidgetContent extends StatelessWidget { required this.showUserAvatar, required this.avatarWidth, required this.showReactions, + required this.onReactionsTap, required this.messageTheme, required this.shouldShowReactions, required this.streamChatTheme, @@ -45,7 +48,7 @@ class MessageWidgetContent extends StatelessWidget { required this.attachmentBuilders, required this.attachmentPadding, required this.textPadding, - required this.showReactionPickerIndicator, + required this.showReactionPickerTail, required this.translateUserAvatar, required this.bottomRowPadding, required this.showInChannel, @@ -111,6 +114,11 @@ class MessageWidgetContent extends StatelessWidget { /// {@macro showReactions} final bool showReactions; + /// Callback called when the reactions icon is tapped. + /// + /// Do not confuse this with the tap action on the reactions picker. + final VoidCallback onReactionsTap; + /// {@macro messageTheme} final StreamMessageThemeData messageTheme; @@ -174,8 +182,8 @@ class MessageWidgetContent extends StatelessWidget { /// {@macro quotedMessageBuilder} final Widget Function(BuildContext, Message)? quotedMessageBuilder; - /// {@macro showReactionPickerIndicator} - final bool showReactionPickerIndicator; + /// {@macro showReactionPickerTail} + final bool showReactionPickerTail; /// {@macro translateUserAvatar} final bool translateUserAvatar; @@ -288,9 +296,7 @@ class MessageWidgetContent extends StatelessWidget { ownId: streamChat.currentUser!.id, reverse: reverse, shouldShowReactions: shouldShowReactions, - onTap: () => _showMessageReactionsModal( - context, - ), + onTap: onReactionsTap, ) : null, anchor: Aligned( @@ -363,7 +369,8 @@ class MessageWidgetContent extends StatelessWidget { shape: shape, ), ), - if (showReactionPickerIndicator) + // TODO: Make tail part of the Reaction Picker. + if (showReactionPickerTail) Positioned( right: reverse ? null : 4, left: reverse ? 4 : null, @@ -374,6 +381,7 @@ class MessageWidgetContent extends StatelessWidget { Colors.transparent, Colors.transparent, tailCirclesSpace: 1, + flipTail: !reverse, ), ), ), @@ -434,47 +442,6 @@ class MessageWidgetContent extends StatelessWidget { ); } - void _showMessageReactionsModal(BuildContext context) { - final channel = StreamChannel.of(context).channel; - showDialog( - useRootNavigator: false, - context: context, - useSafeArea: false, - barrierColor: streamChatTheme.colorTheme.overlay, - builder: (context) => StreamChannel( - channel: channel, - child: StreamMessageReactionsModal( - messageWidget: messageWidget.copyWith( - key: const Key('MessageWidget'), - message: message.copyWith( - text: (message.text?.length ?? 0) > 200 - ? '${message.text!.substring(0, 200)}...' - : message.text, - ), - showReactions: false, - showUsername: false, - showTimestamp: false, - translateUserAvatar: false, - showSendingIndicator: false, - padding: EdgeInsets.zero, - showReactionPickerIndicator: - showReactions && (message.status == MessageSendingStatus.sent), - showPinHighlight: false, - showUserAvatar: - message.user!.id == channel.client.state.currentUser!.id - ? DisplayWidget.gone - : DisplayWidget.show, - ), - onUserAvatarTap: onUserAvatarTap, - messageTheme: messageTheme, - reverse: reverse, - message: message, - showReactions: showReactions, - ), - ), - ); - } - Widget _buildBottomRow(BuildContext context) { final defaultWidget = BottomRow( message: message, 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 cd66d08de..8dadc47d8 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 @@ -15,7 +15,7 @@ class StreamMessageReactionsModal extends StatelessWidget { required this.message, required this.messageWidget, required this.messageTheme, - this.showReactions, + this.showReactionPicker = true, this.reverse = false, this.onUserAvatarTap, }); @@ -32,8 +32,8 @@ class StreamMessageReactionsModal extends StatelessWidget { /// {@macro reverse} final bool reverse; - /// {@macro showReactions} - final bool? showReactions; + /// Flag for showing reaction picker. + final bool showReactionPicker; /// {@macro onUserAvatarTap} final void Function(User)? onUserAvatarTap; @@ -56,8 +56,7 @@ class StreamMessageReactionsModal extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - if ((showReactions ?? hasReactionPermission) && - (message.status == MessageSendingStatus.sent)) + if (showReactionPicker && hasReactionPermission) LayoutBuilder( builder: (context, constraints) { return Align( diff --git a/packages/stream_chat_flutter/lib/src/message_widget/reactions/reactions_align.dart b/packages/stream_chat_flutter/lib/src/message_widget/reactions/reactions_align.dart index e4e1ad489..a7baad7ee 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget/reactions/reactions_align.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget/reactions/reactions_align.dart @@ -43,19 +43,9 @@ double calculateReactionsHorizontalAlignment( final signal = user?.id == message.user?.id ? 1 : -1; final result = signal * (1 - divFactor * 2.0); - return _capResult(result); -} - -// Ensure reactions don't get pushed past the edge of the screen. -// -// This happens if divFactor is really big. When this happens, we can simply -// move the model all the way to the end of screen. -double _capResult(double result) { - if (result > 1.0) { - return 1; - } else if (result < -1.0) { - return -1; - } else { - return result; - } + // Ensure reactions don't get pushed past the edge of the screen. + // + // This happens if divFactor is really big. When this happens, we can simply + // move the model all the way to the end of screen. + return result.clamp(-1, 1); } diff --git a/packages/stream_chat_flutter/test/src/message_reactions_modal/message_reactions_modal_test.dart b/packages/stream_chat_flutter/test/src/message_reactions_modal/message_reactions_modal_test.dart index 54f3675b1..20ecd58e8 100644 --- a/packages/stream_chat_flutter/test/src/message_reactions_modal/message_reactions_modal_test.dart +++ b/packages/stream_chat_flutter/test/src/message_reactions_modal/message_reactions_modal_test.dart @@ -104,7 +104,7 @@ void main() { message: message, messageTheme: streamTheme.ownMessageTheme, reverse: true, - showReactions: false, + showReactionPicker: false, onUserAvatarTap: onUserAvatarTap, ), ), From f29774e262e6a565dac01b767401064a4aeb46c0 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 27 Jun 2023 17:30:57 +0530 Subject: [PATCH 18/48] chore: update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat_flutter/CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 11ca3d099..ea75a9c85 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -9,6 +9,8 @@ - [[#1069]](https://github.com/GetStream/stream-chat-flutter/issues/1069) Fixed message swipe to reply using same direction for both current user and other users. It now uses `SwipeDirection.startToEnd` for current user and `SwipeDirection.endToStart` for other users. +- [[#1590]](https://github.com/GetStream/stream-chat-flutter/issues/1590) + Fixed `StreamMessageWidget.showReactionPickerIndicator` not toggling the reaction picker indicator visibility. ✅ Added @@ -77,6 +79,14 @@ }, ) ``` +- Deprecated `StreamMessageWidget.showReactionPickerIndicator` in favor of `StreamMessageWidget.showReactionPicker`. + + ```diff + StreamMessageWidget( + - showReactionPickerIndicator: true/false, + + showReactionPicker: true/false, + ) + ``` ## 6.4.0 From b6c662ee087965834149d7f34bddaac1a4ac2745 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 27 Jun 2023 18:01:37 +0530 Subject: [PATCH 19/48] chore: fix deprecation Signed-off-by: xsahil03x --- .../handler/stream_attachment_handler_io.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart index 0b04ee615..1df1b4115 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart @@ -29,14 +29,16 @@ class StreamAttachmentHandlerDesktop extends StreamAttachmentHandler { options: options, ); - // Open the native file browser so the user can select the download path. - final path = await getSavePath(suggestedName: data.fileName); - - if (path == null) { + // Open the native file browser so the user can select the save location. + final saveLocation = await getSaveLocation(suggestedName: data.fileName); + if (saveLocation == null) { // Operation was canceled by the user. return null; } + // Get the path to the user's selected location. + final path = saveLocation.path; + // Create an XFile for proper file saving. final file = data.toXFile(path: path); From e1a90ec50c4f624525b8aa3d1089073c220ea6a2 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 27 Jun 2023 18:22:59 +0530 Subject: [PATCH 20/48] test(ui): add tests for message_input_attachment_list.dart Signed-off-by: xsahil03x --- .../ios/Runner.xcodeproj/project.pbxproj | 1 + .../stream_chat_flutter/example/lib/main.dart | 6 +- .../message_input_attachment_list_test.dart | 190 ++++++++++++++++++ 3 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 packages/stream_chat_flutter/test/src/message_input/message_input_attachment_list_test.dart diff --git a/packages/stream_chat_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/packages/stream_chat_flutter/example/ios/Runner.xcodeproj/project.pbxproj index e5d8f7436..4c7de605b 100644 --- a/packages/stream_chat_flutter/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/stream_chat_flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -204,6 +204,7 @@ files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( diff --git a/packages/stream_chat_flutter/example/lib/main.dart b/packages/stream_chat_flutter/example/lib/main.dart index 332d813bf..1db7f73ec 100644 --- a/packages/stream_chat_flutter/example/lib/main.dart +++ b/packages/stream_chat_flutter/example/lib/main.dart @@ -356,7 +356,11 @@ class _ChannelPageState extends State { ), ); }, - child: defaultWidget.copyWith(onReplyTap: reply), + child: defaultWidget.copyWith( + onReplyTap: reply, + // showReactionPicker: true, + // showReactionPickerIndicator: false, + ), ); }, ), diff --git a/packages/stream_chat_flutter/test/src/message_input/message_input_attachment_list_test.dart b/packages/stream_chat_flutter/test/src/message_input/message_input_attachment_list_test.dart new file mode 100644 index 000000000..a7afeea85 --- /dev/null +++ b/packages/stream_chat_flutter/test/src/message_input/message_input_attachment_list_test.dart @@ -0,0 +1,190 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; + +import '../mocks.dart'; + +Widget wrapWithStreamChat( + Widget child, { + StreamChatClient? client, +}) { + return MaterialApp( + home: StreamChat( + client: client ?? MockClient(), + child: child, + ), + ); +} + +void main() { + group('StreamMessageInputAttachmentList tests', () { + testWidgets( + 'StreamMessageInputAttachmentList should render attachments', + (WidgetTester tester) async { + final attachments = [ + Attachment(type: 'file', id: 'file1'), + Attachment(type: 'file', id: 'file2'), + Attachment(type: 'media', id: 'media1'), + ]; + + await tester.pumpWidget( + wrapWithStreamChat( + StreamMessageInputAttachmentList( + attachments: attachments, + ), + ), + ); + + // Expect 2 file attachments and 1 media attachment + expect(find.byType(MessageInputFileAttachments), findsOneWidget); + expect(find.byType(MessageInputMediaAttachments), findsOneWidget); + }, + ); + + testWidgets( + 'StreamMessageInputAttachmentList should call onRemovePressed callback', + (WidgetTester tester) async { + Attachment? removedAttachment; + + final attachments = [ + Attachment(type: 'file', id: 'file1'), + Attachment(type: 'file', id: 'file2'), + ]; + + await tester.pumpWidget( + wrapWithStreamChat( + StreamMessageInputAttachmentList( + attachments: attachments, + onRemovePressed: (attachment) { + removedAttachment = attachment; + }, + ), + ), + ); + + final removeButtons = find.byType(RemoveAttachmentButton); + + // Tap the first remove button + await tester.tap(removeButtons.first); + await tester.pump(); + + // Expect the onRemovePressed callback to be called with the second + // attachment as they are reversed in the UI. + expect(removedAttachment, attachments[1]); + }, + ); + + testWidgets( + '''StreamMessageInputAttachmentList should display empty box if no attachments''', + (WidgetTester tester) async { + final attachments = []; + + await tester.pumpWidget( + wrapWithStreamChat( + StreamMessageInputAttachmentList( + attachments: attachments, + ), + ), + ); + + // Expect an empty box + expect(find.byType(SizedBox), findsOneWidget); + }, + ); + }); + + group('MessageInputFileAttachments tests', () { + testWidgets( + 'MessageInputFileAttachments should render file attachments', + (WidgetTester tester) async { + final attachments = [ + Attachment(type: 'file', id: 'file1'), + Attachment(type: 'file', id: 'file2'), + ]; + + await tester.pumpWidget( + wrapWithStreamChat( + MessageInputFileAttachments( + attachments: attachments, + ), + ), + ); + + // Expect 2 file attachments + expect(find.byType(ClipRRect), findsNWidgets(2)); + }, + ); + + testWidgets( + 'MessageInputFileAttachments should call onRemovePressed callback', + (WidgetTester tester) async { + Attachment? removedAttachment; + + final attachments = [ + Attachment(type: 'file', id: 'file1'), + ]; + + await tester.pumpWidget( + wrapWithStreamChat( + MessageInputFileAttachments( + attachments: attachments, + onRemovePressed: (attachment) { + removedAttachment = attachment; + }, + ), + ), + ); + + final removeButton = find.byType(RemoveAttachmentButton); + + // Tap the remove button + await tester.tap(removeButton); + await tester.pump(); + + // Expect the onRemovePressed callback to be called with the attachment + expect(removedAttachment, attachments.first); + }, + ); + }); + + group('MessageInputMediaAttachments tests', () { + testWidgets( + 'MessageInputMediaAttachments should render media attachments', + (WidgetTester tester) async { + final attachments = [ + Attachment(type: 'media', id: 'media1'), + Attachment(type: 'media', id: 'media2'), + ]; + + await tester.pumpWidget( + wrapWithStreamChat( + MessageInputMediaAttachments( + attachments: attachments, + ), + ), + ); + + // Expect 2 media attachments + expect(find.byType(Stack), findsNWidgets(2)); + }, + ); + + testWidgets( + 'MessageInputMediaAttachments should display empty box if no attachments', + (WidgetTester tester) async { + final attachments = []; + + await tester.pumpWidget( + wrapWithStreamChat( + MessageInputMediaAttachments( + attachments: attachments, + ), + ), + ); + + // Expect an empty box + expect(find.byType(SizedBox), findsOneWidget); + }, + ); + }); +} From 5171d9c208d36746e205164ee13c40f069ef1719 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 27 Jun 2023 18:46:26 +0530 Subject: [PATCH 21/48] chore(ui): revert example changes Signed-off-by: xsahil03x --- .../example/ios/Runner.xcodeproj/project.pbxproj | 1 - packages/stream_chat_flutter/example/lib/main.dart | 6 +----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/stream_chat_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/packages/stream_chat_flutter/example/ios/Runner.xcodeproj/project.pbxproj index 4c7de605b..e5d8f7436 100644 --- a/packages/stream_chat_flutter/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/stream_chat_flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -204,7 +204,6 @@ files = ( ); inputPaths = ( - "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( diff --git a/packages/stream_chat_flutter/example/lib/main.dart b/packages/stream_chat_flutter/example/lib/main.dart index 1db7f73ec..332d813bf 100644 --- a/packages/stream_chat_flutter/example/lib/main.dart +++ b/packages/stream_chat_flutter/example/lib/main.dart @@ -356,11 +356,7 @@ class _ChannelPageState extends State { ), ); }, - child: defaultWidget.copyWith( - onReplyTap: reply, - // showReactionPicker: true, - // showReactionPickerIndicator: false, - ), + child: defaultWidget.copyWith(onReplyTap: reply), ); }, ), From f6c11b39794d5f870ea5235fe1e1aceebceb88d4 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 28 Jun 2023 14:28:00 +0530 Subject: [PATCH 22/48] fix(ui): save attachments to gallery. Signed-off-by: xsahil03x --- .../handler/stream_attachment_handler_io.dart | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart index 1df1b4115..e4d0b35e4 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:file_picker/file_picker.dart'; import 'package:file_selector/file_selector.dart'; +import 'package:image_gallery_saver/image_gallery_saver.dart'; import 'package:image_picker/image_picker.dart'; import 'package:path_provider/path_provider.dart'; import 'package:stream_chat_flutter/src/attachment/handler/common.dart'; @@ -142,7 +143,6 @@ class StreamAttachmentHandler extends StreamAttachmentHandlerBase { final tempFilePath = tempPath .resolve(fileName!) .toFilePath(windows: CurrentPlatform.isWindows); - print(tempFilePath); final attachmentFileBytes = attachmentFile.bytes; if (attachmentFileBytes == null) { @@ -191,8 +191,26 @@ class StreamAttachmentHandler extends StreamAttachmentHandlerBase { // Create an XFile for proper file saving. final file = data.toXFile(path: path); - // Save the file to the user's selected path. + // Save the file to a temporary location. await file.saveTo(path); + + // Now that the file is saved, we need to copy it to the user's gallery + // because the gallery only shows files that are in the gallery folder. + await ImageGallerySaver.saveFile(path); + + // Once the file is copied to the gallery, we can delete the temporary file. + await file.delete(); + return path; } } + +extension on XFile { + /// Deletes this xfile from the file system. + Future delete() async { + final file = File(path); + if (file.existsSync()) { + await file.delete(); + } + } +} From 18e5b5406888061872e9ad50edacde51a07e2360 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 28 Jun 2023 14:42:08 +0530 Subject: [PATCH 23/48] chore: update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat_flutter/CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index ea75a9c85..d2ca7e283 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -11,6 +11,11 @@ and `SwipeDirection.endToStart` for other users. - [[#1590]](https://github.com/GetStream/stream-chat-flutter/issues/1590) Fixed `StreamMessageWidget.showReactionPickerIndicator` not toggling the reaction picker indicator visibility. +- [[#1639]](https://github.com/GetStream/stream-chat-flutter/issues/1639) Fixed attachments not showing in gallery view + even after saving them to the device. + > **Note** + > This fix depends on the [image_gallery_saver](https://pub.dev/packages/image_gallery_saver) plugin. Make sure to add + necessary permissions in your App as per the plugin documentation. ✅ Added @@ -79,6 +84,7 @@ }, ) ``` + - Deprecated `StreamMessageWidget.showReactionPickerIndicator` in favor of `StreamMessageWidget.showReactionPicker`. ```diff From 4f1a2029946063880a0bfeef50c5faee1a341ce9 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 28 Jun 2023 16:26:24 +0530 Subject: [PATCH 24/48] doc: remove incorrect info. Signed-off-by: xsahil03x --- .../05-push-notifications/adding_push_notifications_v2.mdx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docusaurus/docs/Flutter/05-guides/05-push-notifications/adding_push_notifications_v2.mdx b/docusaurus/docs/Flutter/05-guides/05-push-notifications/adding_push_notifications_v2.mdx index c0678e3b0..c440daaf9 100644 --- a/docusaurus/docs/Flutter/05-guides/05-push-notifications/adding_push_notifications_v2.mdx +++ b/docusaurus/docs/Flutter/05-guides/05-push-notifications/adding_push_notifications_v2.mdx @@ -40,12 +40,6 @@ Push message delivery behaves according to these rules: - The message doesn't contain the flag `skip_push` as true. - `push_notifications` is enabled (default) on the channel type for message is sent. -:::info - -If you would like get push notifications only when users are offline, please contact support. - -::: - :::caution Push notifications require membership. Watching a channel isn't enough. From a311463cab9df4a28ff6ac173c68d577d69c9bb1 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 29 Jun 2023 16:23:16 +0530 Subject: [PATCH 25/48] fix(ui): fix `MessageWidget.widthFactor` not working on web/desktop. Signed-off-by: xsahil03x --- .../lib/src/message_widget/message_card.dart | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) 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 5cd93efc4..9491b0da9 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 @@ -188,19 +188,16 @@ class _MessageCardState extends State { attachmentPadding: widget.attachmentPadding, ), if (!widget.isGiphy) - ConstrainedBox( - constraints: BoxConstraints.loose(const Size.fromWidth(500)), - child: TextBubble( - messageTheme: widget.messageTheme, - message: widget.message, - textPadding: widget.textPadding, - textBuilder: widget.textBuilder, - isOnlyEmoji: widget.isOnlyEmoji, - hasQuotedMessage: widget.hasQuotedMessage, - hasUrlAttachments: widget.hasUrlAttachments, - onLinkTap: widget.onLinkTap, - onMentionTap: widget.onMentionTap, - ), + TextBubble( + messageTheme: widget.messageTheme, + message: widget.message, + textPadding: widget.textPadding, + textBuilder: widget.textBuilder, + isOnlyEmoji: widget.isOnlyEmoji, + hasQuotedMessage: widget.hasQuotedMessage, + hasUrlAttachments: widget.hasUrlAttachments, + onLinkTap: widget.onLinkTap, + onMentionTap: widget.onMentionTap, ), if (widget.hasUrlAttachments && !widget.hasQuotedMessage) _buildUrlAttachment(), From cb22267c7181a69f91974335a40e07bfe3b73f72 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 29 Jun 2023 16:24:44 +0530 Subject: [PATCH 26/48] chore: update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat_flutter/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index d2ca7e283..ceaf1683e 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -16,6 +16,8 @@ > **Note** > This fix depends on the [image_gallery_saver](https://pub.dev/packages/image_gallery_saver) plugin. Make sure to add necessary permissions in your App as per the plugin documentation. +- [[#1642]](https://github.com/GetStream/stream-chat-flutter/issues/1642) Fixed `StreamMessageWidget.widthFactor` not + working on web and desktop platforms. ✅ Added From 4359538c041bf60981f51b30e29365a8ce649f16 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 30 Jun 2023 12:18:23 +0530 Subject: [PATCH 27/48] feat(repo): add dependabot --- .github/dependabot.yml | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index fbe1ab130..e6183303a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,11 +1,26 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - version: 2 updates: - - package-ecosystem: "pub" # See documentation for possible values - directory: "/" # Location of package manifests + - package-ecosystem: "github-actions" + directory: "/" schedule: - interval: "weekly" + interval: "daily" + - package-ecosystem: "pub" + directory: "/packages/stream_chat" + schedule: + interval: "daily" + - package-ecosystem: "pub" + directory: "/packages/stream_chat_flutter_core" + schedule: + interval: "daily" + - package-ecosystem: "pub" + directory: "/packages/stream_chat_flutter" + schedule: + interval: "daily" + - package-ecosystem: "pub" + directory: "/packages/stream_chat_persistence" + schedule: + interval: "daily" + - package-ecosystem: "pub" + directory: "/packages/stream_chat_localizations" + schedule: + interval: "daily" From ca1f937e5bb3d13f4d4a09a9b50857905230ce88 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 30 Jun 2023 13:11:12 +0530 Subject: [PATCH 28/48] Update dependabot.yml --- .github/dependabot.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e6183303a..508508a7e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,24 +3,24 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "daily" + interval: "weekly" - package-ecosystem: "pub" directory: "/packages/stream_chat" schedule: - interval: "daily" + interval: "weekly" - package-ecosystem: "pub" directory: "/packages/stream_chat_flutter_core" schedule: - interval: "daily" + interval: "weekly" - package-ecosystem: "pub" directory: "/packages/stream_chat_flutter" schedule: - interval: "daily" + interval: "weekly" - package-ecosystem: "pub" directory: "/packages/stream_chat_persistence" schedule: - interval: "daily" + interval: "weekly" - package-ecosystem: "pub" directory: "/packages/stream_chat_localizations" schedule: - interval: "daily" + interval: "weekly" From 7121a87f77e20b994a91c5a880c4e87b1967c7f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Jun 2023 07:42:51 +0000 Subject: [PATCH 29/48] chore(deps): bump benc-uk/workflow-dispatch from 1 to 121 Bumps [benc-uk/workflow-dispatch](https://github.com/benc-uk/workflow-dispatch) from 1 to 121. - [Release notes](https://github.com/benc-uk/workflow-dispatch/releases) - [Commits](https://github.com/benc-uk/workflow-dispatch/compare/v1...v121) --- updated-dependencies: - dependency-name: benc-uk/workflow-dispatch dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/dispatch_workflows.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dispatch_workflows.yml b/.github/workflows/dispatch_workflows.yml index 6d626fdb1..afd04f992 100644 --- a/.github/workflows/dispatch_workflows.yml +++ b/.github/workflows/dispatch_workflows.yml @@ -14,7 +14,7 @@ jobs: if: github.ref == 'refs/heads/develop' runs-on: ubuntu-latest steps: - - uses: benc-uk/workflow-dispatch@v1 + - uses: benc-uk/workflow-dispatch@v121 with: workflow: build_nightly repo: GetStream/flutter-samples @@ -23,7 +23,7 @@ jobs: if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest steps: - - uses: benc-uk/workflow-dispatch@v1 + - uses: benc-uk/workflow-dispatch@v121 with: repo: GetStream/flutter-samples workflow: build From 46a1eecf50cf5a2e391409042362e116d0bbb079 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Jun 2023 07:47:15 +0000 Subject: [PATCH 30/48] chore(deps): bump codecov/codecov-action from 1 to 3 Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 1 to 3. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v1...v3) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/stream_flutter_workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stream_flutter_workflow.yml b/.github/workflows/stream_flutter_workflow.yml index e42174adc..d57922952 100644 --- a/.github/workflows/stream_flutter_workflow.yml +++ b/.github/workflows/stream_flutter_workflow.yml @@ -100,7 +100,7 @@ jobs: - name: "Collect Coverage" run: melos run coverage:ignore-file --no-select - name: "Upload Coverage" - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3 with: token: ${{secrets.CODECOV_TOKEN}} files: packages/*/coverage/lcov.info From 2d1e07f6b01c1e402c5e3e33c0702106c5d67321 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Jun 2023 06:51:36 +0000 Subject: [PATCH 31/48] chore(deps): bump amannn/action-semantic-pull-request Bumps [amannn/action-semantic-pull-request](https://github.com/amannn/action-semantic-pull-request) from 3.4.0 to 5.2.0. - [Release notes](https://github.com/amannn/action-semantic-pull-request/releases) - [Changelog](https://github.com/amannn/action-semantic-pull-request/blob/main/CHANGELOG.md) - [Commits](https://github.com/amannn/action-semantic-pull-request/compare/v3.4.0...v5.2.0) --- updated-dependencies: - dependency-name: amannn/action-semantic-pull-request dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/pr_title.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_title.yml b/.github/workflows/pr_title.yml index bd4e36cb0..20b3f60eb 100644 --- a/.github/workflows/pr_title.yml +++ b/.github/workflows/pr_title.yml @@ -16,7 +16,7 @@ jobs: conventional_pr_title: runs-on: ubuntu-latest steps: - - uses: amannn/action-semantic-pull-request@v3.4.0 + - uses: amannn/action-semantic-pull-request@v5.2.0 with: scopes: | llc From 2d48391ff6638ee81ebfc7ee06eb33d22b936abc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Jun 2023 06:51:32 +0000 Subject: [PATCH 32/48] chore(deps): bump VeryGoodOpenSource/very_good_coverage Bumps [VeryGoodOpenSource/very_good_coverage](https://github.com/verygoodopensource/very_good_coverage) from 1.1.1 to 2.1.0. - [Release notes](https://github.com/verygoodopensource/very_good_coverage/releases) - [Changelog](https://github.com/VeryGoodOpenSource/very_good_coverage/blob/main/CHANGELOG.md) - [Commits](https://github.com/verygoodopensource/very_good_coverage/compare/v1.1.1...v2.1.0) --- updated-dependencies: - dependency-name: VeryGoodOpenSource/very_good_coverage dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/stream_flutter_workflow.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/stream_flutter_workflow.yml b/.github/workflows/stream_flutter_workflow.yml index d57922952..d1a479554 100644 --- a/.github/workflows/stream_flutter_workflow.yml +++ b/.github/workflows/stream_flutter_workflow.yml @@ -105,27 +105,27 @@ jobs: token: ${{secrets.CODECOV_TOKEN}} files: packages/*/coverage/lcov.info - name: "Stream Chat Coverage Check" - uses: VeryGoodOpenSource/very_good_coverage@v1.1.1 + uses: VeryGoodOpenSource/very_good_coverage@v2.1.0 with: path: packages/stream_chat/coverage/lcov.info min_coverage: 79 - name: "Stream Chat Persistence Coverage Check" - uses: VeryGoodOpenSource/very_good_coverage@v1.1.1 + uses: VeryGoodOpenSource/very_good_coverage@v2.1.0 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 + uses: VeryGoodOpenSource/very_good_coverage@v2.1.0 with: path: packages/stream_chat_persistence/coverage/lcov.info min_coverage: 95 - name: "Stream Chat Flutter Core Coverage Check" - uses: VeryGoodOpenSource/very_good_coverage@v1.1.1 + uses: VeryGoodOpenSource/very_good_coverage@v2.1.0 with: path: packages/stream_chat_flutter_core/coverage/lcov.info min_coverage: 30 - name: "Stream Chat Flutter Coverage Check" - uses: VeryGoodOpenSource/very_good_coverage@v1.1.1 + uses: VeryGoodOpenSource/very_good_coverage@v2.1.0 with: path: packages/stream_chat_flutter/coverage/lcov.info min_coverage: 44 From eebdc5a20aa5a80b71b8b013771bd5275ab113f3 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 30 Jun 2023 13:33:41 +0530 Subject: [PATCH 33/48] Update stream_flutter_workflow.yml --- .github/workflows/stream_flutter_workflow.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/stream_flutter_workflow.yml b/.github/workflows/stream_flutter_workflow.yml index d1a479554..3b8b6f44a 100644 --- a/.github/workflows/stream_flutter_workflow.yml +++ b/.github/workflows/stream_flutter_workflow.yml @@ -8,6 +8,7 @@ on: pull_request: paths: - 'packages/**' + - '.github/workflows/stream_flutter_workflow.yml' types: - opened - reopened @@ -137,4 +138,4 @@ jobs: steps: - name: Run a one-line script - run: echo Draft PR, you are good. \ No newline at end of file + run: echo Draft PR, you are good. From 8850a684a6da259ce9ca34deaa9460f9f5eff410 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 30 Jun 2023 13:49:45 +0530 Subject: [PATCH 34/48] chore(localization): update translations_test.dart --- packages/stream_chat_localizations/test/translations_test.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stream_chat_localizations/test/translations_test.dart b/packages/stream_chat_localizations/test/translations_test.dart index 4004994a3..c59b07626 100644 --- a/packages/stream_chat_localizations/test/translations_test.dart +++ b/packages/stream_chat_localizations/test/translations_test.dart @@ -14,6 +14,7 @@ void main() { expect(localizations.launchUrlError, isNotNull); expect(localizations.loadingUsersError, isNotNull); expect(localizations.noUsersLabel, isNotNull); + expect(localizations.noPhotoOrVideoLabel, isNotNull); expect(localizations.retryLabel, isNotNull); expect(localizations.userLastOnlineText, isNotNull); expect(localizations.userOnlineText, isNotNull); From 2900e5f3a2cdd51a0b15301d0f03eec08ad7e6a9 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 30 Jun 2023 13:51:02 +0530 Subject: [PATCH 35/48] chore: update stream_flutter_workflow.yml --- .github/workflows/stream_flutter_workflow.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stream_flutter_workflow.yml b/.github/workflows/stream_flutter_workflow.yml index 3b8b6f44a..99312316c 100644 --- a/.github/workflows/stream_flutter_workflow.yml +++ b/.github/workflows/stream_flutter_workflow.yml @@ -110,11 +110,11 @@ jobs: with: path: packages/stream_chat/coverage/lcov.info min_coverage: 79 - - name: "Stream Chat Persistence Coverage Check" + - name: "Stream Chat Localizations Coverage Check" uses: VeryGoodOpenSource/very_good_coverage@v2.1.0 with: path: packages/stream_chat_localizations/coverage/lcov.info - min_coverage: 88 + min_coverage: 100 - name: "Stream Chat Persistence Coverage Check" uses: VeryGoodOpenSource/very_good_coverage@v2.1.0 with: From eddd8a3d125c027495efe5ff0027e0bba7bb740c Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 30 Jun 2023 14:20:40 +0530 Subject: [PATCH 36/48] chore(ui): bump and migrate video_player to 2.7.0. Signed-off-by: xsahil03x --- .../lib/src/fullscreen_media/full_screen_media.dart | 8 ++++++-- .../lib/src/message_input/quoted_message_widget.dart | 5 +++-- packages/stream_chat_flutter/pubspec.yaml | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media.dart b/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media.dart index a6feaa5e9..2a791954f 100644 --- a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media.dart +++ b/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media.dart @@ -446,8 +446,12 @@ class VideoPackage { }) : _showControls = showControls, _autoInitialize = autoInitialize, _videoPlayerController = _attachment.localUri != null - ? VideoPlayerController.file(File.fromUri(_attachment.localUri!)) - : VideoPlayerController.network(_attachment.assetUrl!); + ? VideoPlayerController.file( + File.fromUri(_attachment.localUri!), + ) + : VideoPlayerController.networkUrl( + Uri.parse(_attachment.assetUrl!), + ); final Attachment _attachment; final bool _showControls; 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 ab90710ae..3bec19759 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 @@ -382,8 +382,9 @@ class _VideoAttachmentThumbnailState extends State<_VideoAttachmentThumbnail> { @override void initState() { super.initState(); - _controller = VideoPlayerController.network(widget.attachment.assetUrl!) - ..initialize().then((_) { + _controller = VideoPlayerController.networkUrl( + Uri.parse(widget.attachment.assetUrl!), + )..initialize().then((_) { // ignore: no-empty-block setState(() {}); //when your thumbnail will show. }); diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index 45f43a5f0..c67b8f60d 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -42,7 +42,7 @@ dependencies: synchronized: ^3.0.0 thumblr: ^0.0.4 url_launcher: ^6.1.0 - video_player: ^2.4.5 + video_player: ^2.7.0 video_player_macos: ^2.0.1 video_thumbnail: ^0.5.0 From af1de4c311e4d3d38fbb654044922f236dacfb9a Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 30 Jun 2023 14:23:21 +0530 Subject: [PATCH 37/48] chore: update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat_flutter/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index ceaf1683e..50b976c6d 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -86,7 +86,6 @@ }, ) ``` - - Deprecated `StreamMessageWidget.showReactionPickerIndicator` in favor of `StreamMessageWidget.showReactionPicker`. ```diff @@ -95,6 +94,7 @@ + showReactionPicker: true/false, ) ``` +- Updated `video_player` dependency to `^2.7.0`. ## 6.4.0 From cbce833804f1fc1e485731935b12e8f7e4791cbb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Jun 2023 07:00:23 +0000 Subject: [PATCH 38/48] chore(deps): bump image_picker in /packages/stream_chat_flutter Bumps [image_picker](https://github.com/flutter/packages/tree/main/packages/image_picker) from 0.8.9 to 1.0.0. - [Release notes](https://github.com/flutter/packages/releases) - [Commits](https://github.com/flutter/packages/commits/image_picker-v1.0.0/packages/image_picker) --- updated-dependencies: - dependency-name: image_picker dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- packages/stream_chat_flutter/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index c67b8f60d..b4131a9f9 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -28,7 +28,7 @@ dependencies: flutter_svg: ^2.0.4 http_parser: ^4.0.0 image_gallery_saver: ^2.0.1 - image_picker: ^0.8.2 + image_picker: ">=0.8.2 <2.0.0" jiffy: ^5.0.0 lottie: ^2.0.0 meta: ^1.8.0 From d7715ea9dcb08c9885c1197d9fc368e55b2e44f5 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 30 Jun 2023 14:41:48 +0530 Subject: [PATCH 39/48] Revert "feat(ui): add support for `StreamMessageInput.contentInsertionConfiguration`." --- packages/stream_chat_flutter/CHANGELOG.md | 15 --------------- .../src/message_input/stream_message_input.dart | 6 ------ .../message_input/stream_message_text_field.dart | 8 -------- 3 files changed, 29 deletions(-) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 50b976c6d..9ac7a0c76 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -178,21 +178,6 @@ ) ``` -- Added support for `StreamMessageInput.contentInsertionConfiguration` to specify the content insertion configuration. - [#1613](https://github.com/GetStream/stream-chat-flutter/issues/1613) - - ```dart - StreamMessageInput( - ..., - contentInsertionConfiguration: ContentInsertionConfiguration( - onContentInserted: (content) { - // Do something with the content. - controller.addAttachment(...); - }, - ), - ) - ``` - ## 6.3.0 🐞 Fixed diff --git a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart index e8ce137e5..ad5790b42 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart @@ -147,7 +147,6 @@ class StreamMessageInput extends StatefulWidget { _defaultClearQuotedMessageKeyPredicate, this.ogPreviewFilter = _defaultOgPreviewFilter, this.hintGetter = _defaultHintGetter, - this.contentInsertionConfiguration, }); /// The predicate used to send a message on desktop/web @@ -341,9 +340,6 @@ class StreamMessageInput extends StatefulWidget { /// Returns the hint text for the message input. final HintGetter hintGetter; - /// {@macro flutter.widgets.editableText.contentInsertionConfiguration} - final ContentInsertionConfiguration? contentInsertionConfiguration; - static String? _defaultHintGetter( BuildContext context, HintType type, @@ -909,8 +905,6 @@ class StreamMessageInputState extends State decoration: _getInputDecoration(context), textCapitalization: widget.textCapitalization, autocorrect: widget.autoCorrect, - contentInsertionConfiguration: - widget.contentInsertionConfiguration, ), ), ), diff --git a/packages/stream_chat_flutter/lib/src/message_input/stream_message_text_field.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_text_field.dart index 292c9141f..a04ab829d 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/stream_message_text_field.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/stream_message_text_field.dart @@ -120,7 +120,6 @@ class StreamMessageTextField extends StatefulWidget { this.restorationId, this.scribbleEnabled = true, this.enableIMEPersonalizedLearning = true, - this.contentInsertionConfiguration, }) : assert(obscuringCharacter.length == 1, ''), smartDashesType = smartDashesType ?? (obscureText ? SmartDashesType.disabled : SmartDashesType.enabled), @@ -527,9 +526,6 @@ class StreamMessageTextField extends StatefulWidget { /// {@macro flutter.services.TextInputConfiguration.enableIMEPersonalizedLearning} final bool enableIMEPersonalizedLearning; - /// {@macro flutter.widgets.editableText.contentInsertionConfiguration} - final ContentInsertionConfiguration? contentInsertionConfiguration; - @override _StreamMessageTextFieldState createState() => _StreamMessageTextFieldState(); @@ -626,9 +622,6 @@ class StreamMessageTextField extends StatefulWidget { properties.add(DiagnosticsProperty( 'enableIMEPersonalizedLearning', enableIMEPersonalizedLearning, defaultValue: true)); - properties.add(DiagnosticsProperty( - 'contentInsertionConfiguration', contentInsertionConfiguration, - defaultValue: null)); } } @@ -734,7 +727,6 @@ class _StreamMessageTextFieldState extends State restorationId: widget.restorationId, scribbleEnabled: widget.scribbleEnabled, enableIMEPersonalizedLearning: widget.enableIMEPersonalizedLearning, - contentInsertionConfiguration: widget.contentInsertionConfiguration, ); @override From 3b09837f61ad07df981478355fa8284a997a6b15 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 4 Jul 2023 14:25:55 +0530 Subject: [PATCH 40/48] chore(repo): remove dart-code-metrics. Signed-off-by: xsahil03x --- .github/workflows/dart_code_metrics.yaml | 74 ------------------- analysis_options.yaml | 23 +----- packages/stream_chat/pubspec.yaml | 1 - packages/stream_chat_flutter/pubspec.yaml | 1 - .../stream_chat_flutter_core/pubspec.yaml | 1 - .../stream_chat_localizations/pubspec.yaml | 1 - packages/stream_chat_persistence/pubspec.yaml | 1 - 7 files changed, 1 insertion(+), 101 deletions(-) delete mode 100644 .github/workflows/dart_code_metrics.yaml diff --git a/.github/workflows/dart_code_metrics.yaml b/.github/workflows/dart_code_metrics.yaml deleted file mode 100644 index 504173f3d..000000000 --- a/.github/workflows/dart_code_metrics.yaml +++ /dev/null @@ -1,74 +0,0 @@ -name: Dart Code Metrics - -env: - flutter_version: "3.10.4" - folders: "lib, test" - -on: - pull_request: - paths: - - 'packages/**' - push: - branches: - - master - - develop - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - check: - name: dart-code-metrics - runs-on: ubuntu-latest - steps: - - name: "Git Checkout" - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: "Install Flutter" - uses: subosito/flutter-action@v2 - with: - cache: true - flutter-version: ${{ env.flutter_version }} - - - name: "Install Tools" - run: flutter pub global activate melos - - name: "Bootstrap Workspace" - run: melos bootstrap - - - name: "Stream Chat Metrics" - uses: dart-code-checker/dart-code-metrics-action@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - relative_path: 'packages/stream_chat' - folders: ${{ env.folders }} - - - name: "Stream Chat Flutter Core Metrics" - uses: dart-code-checker/dart-code-metrics-action@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - relative_path: 'packages/stream_chat_flutter_core' - folders: ${{ env.folders }} - - - name: "Stream Chat Flutter Metrics" - uses: dart-code-checker/dart-code-metrics-action@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - relative_path: 'packages/stream_chat_flutter' - folders: ${{ env.folders }} - - - name: "Stream Chat Localizations Metrics" - uses: dart-code-checker/dart-code-metrics-action@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - relative_path: 'packages/stream_chat_localizations' - folders: ${{ env.folders }} - - - name: "Stream Chat Persistence Metrics" - uses: dart-code-checker/dart-code-metrics-action@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - relative_path: 'packages/stream_chat_persistence' - folders: ${{ env.folders }} diff --git a/analysis_options.yaml b/analysis_options.yaml index 6386f1684..abb5d9797 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,6 +1,4 @@ analyzer: - plugins: - - dart_code_metrics exclude: - packages/*/lib/**/*.g.dart - packages/*/lib/scrollable_positioned_list/** @@ -159,23 +157,4 @@ linter: - unnecessary_overrides - prefer_null_aware_method_calls - use_named_constants - - use_raw_strings - -# https://dartcodemetrics.dev/docs/getting-started/introduction -dart_code_metrics: - rules: - # Dart Specific - - binary-expression-operand-order - - double-literal-format - - no-boolean-literal-compare - - no-equal-then-else - - no-empty-block: - exclude: - - packages/*/test/** - - prefer-trailing-comma: - exclude: - - packages/*/test/** - - # Flutter specific - - always-remove-listener - - avoid-unnecessary-setstate \ No newline at end of file + - use_raw_strings \ No newline at end of file diff --git a/packages/stream_chat/pubspec.yaml b/packages/stream_chat/pubspec.yaml index 3bd6b49ac..7156199c0 100644 --- a/packages/stream_chat/pubspec.yaml +++ b/packages/stream_chat/pubspec.yaml @@ -28,7 +28,6 @@ dependencies: dev_dependencies: build_runner: ^2.3.3 - dart_code_metrics: ^5.7.2 freezed: ^2.3.2 json_serializable: ^6.6.1 mocktail: ^0.3.0 diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index b4131a9f9..5514cd349 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -70,7 +70,6 @@ flutter: uses-material-design: true dev_dependencies: - dart_code_metrics: ^5.7.2 flutter_test: sdk: flutter golden_toolkit: ^0.15.0 diff --git a/packages/stream_chat_flutter_core/pubspec.yaml b/packages/stream_chat_flutter_core/pubspec.yaml index 852b0afcf..b3448b07b 100644 --- a/packages/stream_chat_flutter_core/pubspec.yaml +++ b/packages/stream_chat_flutter_core/pubspec.yaml @@ -21,7 +21,6 @@ dependencies: dev_dependencies: build_runner: ^2.3.3 - dart_code_metrics: ^5.7.2 fake_async: ^1.2.0 flutter_test: sdk: flutter diff --git a/packages/stream_chat_localizations/pubspec.yaml b/packages/stream_chat_localizations/pubspec.yaml index ecd1ae782..10fef73f5 100644 --- a/packages/stream_chat_localizations/pubspec.yaml +++ b/packages/stream_chat_localizations/pubspec.yaml @@ -17,7 +17,6 @@ dependencies: stream_chat_flutter: ^6.4.0 dev_dependencies: - dart_code_metrics: ^5.7.2 flutter_test: sdk: flutter diff --git a/packages/stream_chat_persistence/pubspec.yaml b/packages/stream_chat_persistence/pubspec.yaml index b4dfa792a..cb76edad6 100644 --- a/packages/stream_chat_persistence/pubspec.yaml +++ b/packages/stream_chat_persistence/pubspec.yaml @@ -22,7 +22,6 @@ dependencies: dev_dependencies: build_runner: ^2.3.3 - dart_code_metrics: ^5.7.2 drift_dev: ^2.7.0 flutter_test: sdk: flutter From 7f6cfda9bc33b6ecc19a5b0c2fdc0f50472f01ea Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Mon, 3 Jul 2023 22:54:59 +0530 Subject: [PATCH 41/48] chore(ui): bump chewie to ^1.6.0 Signed-off-by: xsahil03x --- packages/stream_chat_flutter/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index 5514cd349..b6e26a189 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -11,7 +11,7 @@ environment: dependencies: cached_network_image: ^3.0.0 - chewie: ^1.3.4 + chewie: ^1.6.0 collection: ^1.17.0 contextmenu: ^3.0.0 dart_vlc: ^0.4.0 From be9d82827e6a32c8282c32c6acc0e692f5b3302b Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Mon, 3 Jul 2023 22:55:46 +0530 Subject: [PATCH 42/48] chore(ui): bump share_plus to ^7.0.2 Signed-off-by: xsahil03x --- packages/stream_chat_flutter/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index b6e26a189..027a88449 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -36,7 +36,7 @@ dependencies: photo_manager: ^2.5.2 photo_view: ^0.14.0 rxdart: ^0.27.0 - share_plus: ^6.3.0 + share_plus: ^7.0.2 shimmer: ^3.0.0 stream_chat_flutter_core: ^6.4.0 synchronized: ^3.0.0 From 4e19f077325159ac3ddebb428178aef7bea823fc Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Mon, 3 Jul 2023 22:56:09 +0530 Subject: [PATCH 43/48] chore: update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat_flutter/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 9ac7a0c76..0d3d4e734 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -95,6 +95,8 @@ ) ``` - Updated `video_player` dependency to `^2.7.0`. +- Updated `chewie` dependency to `^1.6.0`. +- Updated `share_plus` dependency to `^7.0.2`. ## 6.4.0 From 79faac57e5761296f0bf93719a57985758db9629 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 22 Jun 2023 13:37:58 +0530 Subject: [PATCH 44/48] chore(ui): bump jiffy to v6.2.1 Signed-off-by: xsahil03x --- .gitignore | 1 + .../lib/src/channel/channel_info.dart | 3 ++- .../lib/src/channel/channel_preview.dart | 6 ++--- .../lib/src/localization/translations.dart | 8 ++++--- .../message_list_view/message_list_view.dart | 22 +++++++++++++------ .../lib/src/message_widget/bottom_row.dart | 2 +- .../lib/src/misc/date_divider.dart | 12 +++++----- .../stream_channel_list_tile.dart | 6 ++--- .../stream_message_search_list_tile.dart | 4 ++-- .../stream_user_list_tile.dart | 3 ++- .../lib/src/stream_chat.dart | 4 ++-- .../lib/src/user/user_item.dart | 4 +++- packages/stream_chat_flutter/pubspec.yaml | 2 +- 13 files changed, 46 insertions(+), 31 deletions(-) diff --git a/.gitignore b/.gitignore index 8f4fc6293..c3f339539 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ coverage .pub/ .dart_tool/ pubspec.lock +pubspec_overrides.yaml flutter_export_environment.sh examples/all_plugins/pubspec.yaml diff --git a/packages/stream_chat_flutter/lib/src/channel/channel_info.dart b/packages/stream_chat_flutter/lib/src/channel/channel_info.dart index 5c32d2789..abab5ad93 100644 --- a/packages/stream_chat_flutter/lib/src/channel/channel_info.dart +++ b/packages/stream_chat_flutter/lib/src/channel/channel_info.dart @@ -106,9 +106,10 @@ class _ConnectedTitleState extends StatelessWidget { style: textStyle, ); } else { + final lastActive = otherMember.user?.lastActive ?? DateTime.now(); alternativeWidget = Text( '${context.translations.userLastOnlineText} ' - '${Jiffy(otherMember.user?.lastActive).fromNow()}', + '${Jiffy.parseFromDateTime(lastActive).fromNow()}', style: textStyle, ); } diff --git a/packages/stream_chat_flutter/lib/src/channel/channel_preview.dart b/packages/stream_chat_flutter/lib/src/channel/channel_preview.dart index ae6247ea2..f64b9dc95 100644 --- a/packages/stream_chat_flutter/lib/src/channel/channel_preview.dart +++ b/packages/stream_chat_flutter/lib/src/channel/channel_preview.dart @@ -349,16 +349,16 @@ class _Date extends StatelessWidget { if (lastMessageAt.millisecondsSinceEpoch >= startOfDay.millisecondsSinceEpoch) { - stringDate = Jiffy(lastMessageAt.toLocal()).jm; + stringDate = Jiffy.parseFromDateTime(lastMessageAt.toLocal()).jm; } else if (lastMessageAt.millisecondsSinceEpoch >= startOfDay .subtract(const Duration(days: 1)) .millisecondsSinceEpoch) { stringDate = context.translations.yesterdayLabel; } else if (startOfDay.difference(lastMessageAt).inDays < 7) { - stringDate = Jiffy(lastMessageAt.toLocal()).EEEE; + stringDate = Jiffy.parseFromDateTime(lastMessageAt.toLocal()).EEEE; } else { - stringDate = Jiffy(lastMessageAt.toLocal()).yMd; + stringDate = Jiffy.parseFromDateTime(lastMessageAt.toLocal()).yMd; } return Text( diff --git a/packages/stream_chat_flutter/lib/src/localization/translations.dart b/packages/stream_chat_flutter/lib/src/localization/translations.dart index 61c3441a2..9863fbbcf 100644 --- a/packages/stream_chat_flutter/lib/src/localization/translations.dart +++ b/packages/stream_chat_flutter/lib/src/localization/translations.dart @@ -611,13 +611,15 @@ class DefaultTranslations implements Translations { } else if (date == yesterday) { return 'yesterday'; } else { - return 'on ${Jiffy(date).MMMd}'; + return 'on ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - 'Sent ${_getDay(date)} at ${Jiffy(time.toLocal()).format('HH:mm')}'; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return 'Sent ${_getDay(date)} at ${atTime.format(pattern: 'HH:mm')}'; + } @override String get todayLabel => 'Today'; 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 5a5075f88..f9de17876 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 @@ -669,14 +669,20 @@ class _StreamMessageListViewState extends State { final isPartOfThread = message.replyCount! > 0 || message.showInChannel == true; - final createdAt = message.createdAt.toLocal(); - final nextCreatedAt = nextMessage.createdAt.toLocal(); - if (!Jiffy(createdAt).isSame(nextCreatedAt, Units.DAY)) { + final createdAt = Jiffy.parseFromDateTime( + message.createdAt.toLocal(), + ); + + final nextCreatedAt = Jiffy.parseFromDateTime( + nextMessage.createdAt.toLocal(), + ); + + if (!createdAt.isSame(nextCreatedAt, unit: Unit.day)) { separator = _buildDateDivider(nextMessage); } else { - final hasTimeDiff = !Jiffy(createdAt).isSame( + final hasTimeDiff = !createdAt.isSame( nextCreatedAt, - Units.MINUTE, + unit: Unit.minute, ); final isNextUserSame = @@ -1071,10 +1077,12 @@ class _StreamMessageListViewState extends State { var hasTimeDiff = false; if (nextMessage != null) { - hasTimeDiff = !Jiffy(message.createdAt.toLocal()).isSame( + final createdAt = Jiffy.parseFromDateTime(message.createdAt.toLocal()); + final nextCreatedAt = Jiffy.parseFromDateTime( nextMessage.createdAt.toLocal(), - Units.MINUTE, ); + + hasTimeDiff = !createdAt.isSame(nextCreatedAt, unit: Unit.minute); } final hasFileAttachment = diff --git a/packages/stream_chat_flutter/lib/src/message_widget/bottom_row.dart b/packages/stream_chat_flutter/lib/src/message_widget/bottom_row.dart index 5ca2462a5..7092dadf5 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget/bottom_row.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget/bottom_row.dart @@ -191,7 +191,7 @@ class BottomRow extends StatelessWidget { ), if (showTimeStamp) Text( - Jiffy(message.createdAt.toLocal()).jm, + Jiffy.parseFromDateTime(message.createdAt.toLocal()).jm, style: messageTheme.createdAtStyle, ), if (showSendingIndicator) 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 24af4f3f2..24c0c9a73 100644 --- a/packages/stream_chat_flutter/lib/src/misc/date_divider.dart +++ b/packages/stream_chat_flutter/lib/src/misc/date_divider.dart @@ -20,17 +20,17 @@ class StreamDateDivider extends StatelessWidget { @override Widget build(BuildContext context) { - final createdAt = Jiffy(dateTime); - final now = Jiffy(DateTime.now()); + final createdAt = Jiffy.parseFromDateTime(dateTime); + final now = Jiffy.parseFromDateTime(DateTime.now()); var dayInfo = createdAt.MMMd; - if (createdAt.isSame(now, Units.DAY)) { + if (createdAt.isSame(now, unit: Unit.day)) { dayInfo = context.translations.todayLabel; - } else if (createdAt.isSame(now.subtract(days: 1), Units.DAY)) { + } else if (createdAt.isSame(now.subtract(days: 1), unit: Unit.day)) { dayInfo = context.translations.yesterdayLabel; - } else if (createdAt.isAfter(now.subtract(days: 7), Units.DAY)) { + } else if (createdAt.isAfter(now.subtract(days: 7), unit: Unit.day)) { dayInfo = createdAt.EEEE; - } else if (createdAt.isAfter(now.subtract(years: 1), Units.DAY)) { + } else if (createdAt.isAfter(now.subtract(years: 1), unit: Unit.day)) { dayInfo = createdAt.MMMd; } diff --git a/packages/stream_chat_flutter/lib/src/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 index 6b2a9b4fe..b5261802d 100644 --- a/packages/stream_chat_flutter/lib/src/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 @@ -287,16 +287,16 @@ class ChannelLastMessageDate extends StatelessWidget { if (lastMessageAt.millisecondsSinceEpoch >= startOfDay.millisecondsSinceEpoch) { - stringDate = Jiffy(lastMessageAt.toLocal()).jm; + stringDate = Jiffy.parseFromDateTime(lastMessageAt.toLocal()).jm; } else if (lastMessageAt.millisecondsSinceEpoch >= startOfDay .subtract(const Duration(days: 1)) .millisecondsSinceEpoch) { stringDate = context.translations.yesterdayLabel; } else if (startOfDay.difference(lastMessageAt).inDays < 7) { - stringDate = Jiffy(lastMessageAt.toLocal()).EEEE; + stringDate = Jiffy.parseFromDateTime(lastMessageAt.toLocal()).EEEE; } else { - stringDate = Jiffy(lastMessageAt.toLocal()).yMd; + stringDate = Jiffy.parseFromDateTime(lastMessageAt.toLocal()).yMd; } return Text( diff --git a/packages/stream_chat_flutter/lib/src/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 index 5aab3b2d4..3eb03aec1 100644 --- a/packages/stream_chat_flutter/lib/src/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 @@ -227,9 +227,9 @@ class MessageSearchTileMessageDate extends StatelessWidget { if (now.year != createdAt.year || now.month != createdAt.month || now.day != createdAt.day) { - stringDate = Jiffy(createdAt.toLocal()).yMd; + stringDate = Jiffy.parseFromDateTime(createdAt.toLocal()).yMd; } else { - stringDate = Jiffy(createdAt.toLocal()).jm; + stringDate = Jiffy.parseFromDateTime(createdAt.toLocal()).jm; } return Text( diff --git a/packages/stream_chat_flutter/lib/src/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 index 5ee49dccb..1b0251f6a 100644 --- a/packages/stream_chat_flutter/lib/src/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 @@ -176,11 +176,12 @@ class UserLastActive extends StatelessWidget { @override Widget build(BuildContext context) { final chatTheme = StreamChatTheme.of(context); + final lastActive = user.lastActive ?? DateTime.now(); return Text( user.online ? context.translations.userOnlineText : '${context.translations.userLastOnlineText} ' - '${Jiffy(user.lastActive).fromNow()}', + '${Jiffy.parseFromDateTime(lastActive).fromNow()}', style: chatTheme.textTheme.footnote.copyWith( color: chatTheme.colorTheme.textHighEmphasis.withOpacity(0.5), ), diff --git a/packages/stream_chat_flutter/lib/src/stream_chat.dart b/packages/stream_chat_flutter/lib/src/stream_chat.dart index 73a0d0a0a..6b0d0e609 100644 --- a/packages/stream_chat_flutter/lib/src/stream_chat.dart +++ b/packages/stream_chat_flutter/lib/src/stream_chat.dart @@ -165,9 +165,9 @@ class StreamChatState extends State { @override void didChangeDependencies() { final currentLocale = Localizations.localeOf(context).toString(); - final availableLocales = Jiffy.getAllAvailableLocales(); + final availableLocales = Jiffy.getSupportedLocales(); if (availableLocales.contains(currentLocale)) { - Jiffy.locale(currentLocale); + Jiffy.setLocale(currentLocale); } super.didChangeDependencies(); } 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 85d12a997..0c13d95ec 100644 --- a/packages/stream_chat_flutter/lib/src/user/user_item.dart +++ b/packages/stream_chat_flutter/lib/src/user/user_item.dart @@ -14,6 +14,7 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// [StreamChatTheme]. /// Modify it to change the widget's appearance. /// {@endtemplate} +@Deprecated('Use `StreamUserListTile` instead.') class StreamUserItem extends StatelessWidget { /// {@macro streamUserItem} const StreamUserItem({ @@ -73,11 +74,12 @@ class StreamUserItem extends StatelessWidget { Widget _buildLastActive(BuildContext context) { final chatTheme = StreamChatTheme.of(context); + final lastActive = user.lastActive ?? DateTime.now(); return Text( user.online ? context.translations.userOnlineText : '${context.translations.userLastOnlineText} ' - '${Jiffy(user.lastActive).fromNow()}', + '${Jiffy.parseFromDateTime(lastActive).fromNow()}', style: chatTheme.textTheme.footnote.copyWith( color: chatTheme.colorTheme.textHighEmphasis.withOpacity(0.5), ), diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index 027a88449..f4b079d55 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -29,7 +29,7 @@ dependencies: http_parser: ^4.0.0 image_gallery_saver: ^2.0.1 image_picker: ">=0.8.2 <2.0.0" - jiffy: ^5.0.0 + jiffy: ^6.2.1 lottie: ^2.0.0 meta: ^1.8.0 path_provider: ^2.0.9 From 677b71e728b095e6f2088aa1a694846c26bcdb7e Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 22 Jun 2023 14:12:29 +0530 Subject: [PATCH 45/48] chore(localization): bump stream_chat_flutter Signed-off-by: xsahil03x --- .../example/lib/add_new_lang.dart | 8 +++++--- .../lib/src/stream_chat_localizations_ca.dart | 8 +++++--- .../lib/src/stream_chat_localizations_de.dart | 8 +++++--- .../lib/src/stream_chat_localizations_en.dart | 8 +++++--- .../lib/src/stream_chat_localizations_es.dart | 8 +++++--- .../lib/src/stream_chat_localizations_fr.dart | 8 +++++--- .../lib/src/stream_chat_localizations_hi.dart | 8 +++++--- .../lib/src/stream_chat_localizations_it.dart | 8 +++++--- .../lib/src/stream_chat_localizations_ja.dart | 8 +++++--- .../lib/src/stream_chat_localizations_ko.dart | 8 +++++--- .../lib/src/stream_chat_localizations_no.dart | 8 +++++--- .../lib/src/stream_chat_localizations_pt.dart | 8 +++++--- 12 files changed, 60 insertions(+), 36 deletions(-) diff --git a/packages/stream_chat_localizations/example/lib/add_new_lang.dart b/packages/stream_chat_localizations/example/lib/add_new_lang.dart index bab8101b8..e1d1335ac 100644 --- a/packages/stream_chat_localizations/example/lib/add_new_lang.dart +++ b/packages/stream_chat_localizations/example/lib/add_new_lang.dart @@ -270,13 +270,15 @@ class NnStreamChatLocalizations extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return 'yesterday'; } else { - return 'on ${Jiffy(date).MMMd}'; + return 'on ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - 'Sent ${_getDay(date)} at ${Jiffy(time.toLocal()).format('HH:mm')}'; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return 'Sent ${_getDay(date)} at ${atTime.jm}'; + } @override String get todayLabel => 'Today'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ca.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ca.dart index 3c471e051..ed2d3d7ed 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ca.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ca.dart @@ -249,13 +249,15 @@ class StreamChatLocalizationsCa extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return 'ahir'; } else { - return 'el ${Jiffy(date).MMMd}'; + return 'el ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - '''Enviat el ${_getDay(date)} a les ${Jiffy(time.toLocal()).format('HH:mm')}'''; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return 'Enviat el ${_getDay(date)} a les ${atTime.jm}'; + } @override String get todayLabel => 'Avui'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_de.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_de.dart index df2a7766b..fe95f0f7f 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_de.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_de.dart @@ -239,13 +239,15 @@ class StreamChatLocalizationsDe extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return 'Gestern'; } else { - return 'am ${Jiffy(date).MMMd}'; + return 'am ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - 'Gesendet ${_getDay(date)} am ${Jiffy(time.toLocal()).format('HH:mm')}'; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return 'Gesendet ${_getDay(date)} am ${atTime.jm}'; + } @override String get todayLabel => 'Heute'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_en.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_en.dart index c55bbf49d..a744ff2ae 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_en.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_en.dart @@ -246,13 +246,15 @@ class StreamChatLocalizationsEn extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return 'yesterday'; } else { - return 'on ${Jiffy(date).MMMd}'; + return 'on ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - 'Sent ${_getDay(date)} at ${Jiffy(time.toLocal()).format('HH:mm')}'; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return 'Sent ${_getDay(date)} at ${atTime.jm}'; + } @override String get todayLabel => 'Today'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart index 107e26166..2d75b1028 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart @@ -250,13 +250,15 @@ class StreamChatLocalizationsEs extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return 'ayer'; } else { - return 'el ${Jiffy(date).MMMd}'; + return 'el ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - '''Enviado el ${_getDay(date)} a las ${Jiffy(time.toLocal()).format('HH:mm')}'''; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return 'Enviado el ${_getDay(date)} a las ${atTime.jm}'; + } @override String get todayLabel => 'Hoy'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_fr.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_fr.dart index 8b501184e..6a474b8a6 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_fr.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_fr.dart @@ -249,13 +249,15 @@ class StreamChatLocalizationsFr extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return 'hier'; } else { - return 'le ${Jiffy(date).MMMd}'; + return 'le ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - 'Envoyé ${_getDay(date)} à ${Jiffy(time.toLocal()).format('HH:mm')}'; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return 'Envoyé ${_getDay(date)} à ${atTime.jm}'; + } @override String get todayLabel => "Aujourd'hui"; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_hi.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_hi.dart index 4f27717a7..4e056ab22 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_hi.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_hi.dart @@ -243,13 +243,15 @@ class StreamChatLocalizationsHi extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return 'कल'; } else { - return '${Jiffy(date).MMMd} को'; + return '${Jiffy.parseFromDateTime(date).MMMd} को'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - '${_getDay(date)} ${Jiffy(time.toLocal()).format('HH:mm')} बजे भेजा गया'; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return '${_getDay(date)} ${atTime.jm} बजे भेजा गया'; + } @override String get todayLabel => 'आज'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_it.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_it.dart index b407629d1..51a808972 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_it.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_it.dart @@ -252,13 +252,15 @@ Il file è troppo grande per essere caricato. Il limite è di $limitInMB MB.'''; } else if (date == yesterday) { return 'ieri'; } else { - return 'il ${Jiffy(date).MMMd}'; + return 'il ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - "Inviato ${_getDay(date)} alle ${Jiffy(time.toLocal()).format('HH:mm')}"; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return 'Inviato ${_getDay(date)} alle ${atTime.jm}'; + } @override String get todayLabel => 'Oggi'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart index 26a5e8b3e..17285f6aa 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart @@ -236,13 +236,15 @@ class StreamChatLocalizationsJa extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return '昨日'; } else { - return '${Jiffy(date).MMMd}に'; + return '${Jiffy.parseFromDateTime(date).MMMd}に'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - '${_getDay(date)}の${Jiffy(time.toLocal()).format('HH:mm')}に送信しました '; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return '${_getDay(date)}の${atTime.jm}に送信しました '; + } @override String get todayLabel => '今日'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart index 7b907584e..acf1f79f9 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart @@ -235,13 +235,15 @@ class StreamChatLocalizationsKo extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return '어제'; } else { - return '${Jiffy(date).MMMd}에'; + return '${Jiffy.parseFromDateTime(date).MMMd}에'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - '${_getDay(date)} ${Jiffy(time.toLocal()).format('HH:mm')}에 보냈습니다'; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return '${_getDay(date)} ${atTime.jm}에 보냈습니다'; + } @override String get todayLabel => '오늘'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_no.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_no.dart index 88350979e..8a6c4db31 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_no.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_no.dart @@ -242,13 +242,15 @@ class StreamChatLocalizationsNo extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return 'i går'; } else { - return 'på ${Jiffy(date).MMMd}'; + return 'på ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - 'Sent ${_getDay(date)} kl. ${Jiffy(time.toLocal()).format('HH:mm')}'; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return 'Sent ${_getDay(date)} kl. ${atTime.jm}'; + } @override String get todayLabel => 'I dag'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_pt.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_pt.dart index 874eb1189..f359808b2 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_pt.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_pt.dart @@ -244,13 +244,15 @@ class StreamChatLocalizationsPt extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return 'Ontem'; } else { - return 'o ${Jiffy(date).MMMd}'; + return 'o ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - '''Enviado ${_getDay(date)} às ${Jiffy(time.toLocal()).format('HH:mm')}'''; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return 'Enviado ${_getDay(date)} às ${atTime.jm}'; + } @override String get todayLabel => 'Hoje'; From 4312687d15b1947b839cacd9e2b98fcb380c9559 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 22 Jun 2023 14:14:52 +0530 Subject: [PATCH 46/48] chore: update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat_flutter/CHANGELOG.md | 2 ++ packages/stream_chat_localizations/CHANGELOG.md | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 0d3d4e734..717045bd3 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -97,6 +97,8 @@ - Updated `video_player` dependency to `^2.7.0`. - Updated `chewie` dependency to `^1.6.0`. - Updated `share_plus` dependency to `^7.0.2`. +- Deprecated `StreamUserItem` in favor of `StreamUserListTile`. +- Updated `jiffy` dependency to `^6.2.1`. ## 6.4.0 diff --git a/packages/stream_chat_localizations/CHANGELOG.md b/packages/stream_chat_localizations/CHANGELOG.md index 628f82dc9..79aefad60 100644 --- a/packages/stream_chat_localizations/CHANGELOG.md +++ b/packages/stream_chat_localizations/CHANGELOG.md @@ -1,3 +1,7 @@ +## Upcoming + +* Updated `stream_chat_flutter` dependency to [`Upcoming`](https://pub.dev/packages/stream_chat_flutter/changelog). + ## 5.4.0 * Updated `stream_chat_flutter` dependency to [`6.4.0`](https://pub.dev/packages/stream_chat_flutter/changelog). From d1ce69d539f10207d6e14c62c6b56df096e0ad19 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 22 Jun 2023 14:18:41 +0530 Subject: [PATCH 47/48] chore: minor change. Signed-off-by: xsahil03x --- .../stream_chat_flutter/lib/src/localization/translations.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/lib/src/localization/translations.dart b/packages/stream_chat_flutter/lib/src/localization/translations.dart index 9863fbbcf..e6bb87d97 100644 --- a/packages/stream_chat_flutter/lib/src/localization/translations.dart +++ b/packages/stream_chat_flutter/lib/src/localization/translations.dart @@ -618,7 +618,7 @@ class DefaultTranslations implements Translations { @override String sentAtText({required DateTime date, required DateTime time}) { final atTime = Jiffy.parseFromDateTime(time.toLocal()); - return 'Sent ${_getDay(date)} at ${atTime.format(pattern: 'HH:mm')}'; + return 'Sent ${_getDay(date)} at ${atTime.jm}'; } @override From 4b2a7be3eba36e029c50cc748dd2a85ba3db5b9f Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 5 Jul 2023 15:15:48 +0530 Subject: [PATCH 48/48] chore(repo): prepare for release. Signed-off-by: xsahil03x --- packages/stream_chat_flutter/CHANGELOG.md | 2 +- packages/stream_chat_flutter/pubspec.yaml | 2 +- packages/stream_chat_localizations/CHANGELOG.md | 4 ++-- packages/stream_chat_localizations/pubspec.yaml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 717045bd3..8705e9c65 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,4 +1,4 @@ -## Upcoming +## 6.5.0 🐞 Fixed diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index f4b079d55..3d298d837 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat_flutter homepage: https://github.com/GetStream/stream-chat-flutter description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter. -version: 6.4.0 +version: 6.5.0 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues diff --git a/packages/stream_chat_localizations/CHANGELOG.md b/packages/stream_chat_localizations/CHANGELOG.md index 79aefad60..86b6a739b 100644 --- a/packages/stream_chat_localizations/CHANGELOG.md +++ b/packages/stream_chat_localizations/CHANGELOG.md @@ -1,6 +1,6 @@ -## Upcoming +## 5.5.0 -* Updated `stream_chat_flutter` dependency to [`Upcoming`](https://pub.dev/packages/stream_chat_flutter/changelog). +* Updated `stream_chat_flutter` dependency to [`6.5.0`](https://pub.dev/packages/stream_chat_flutter/changelog). ## 5.4.0 diff --git a/packages/stream_chat_localizations/pubspec.yaml b/packages/stream_chat_localizations/pubspec.yaml index 10fef73f5..85a2cd4d1 100644 --- a/packages/stream_chat_localizations/pubspec.yaml +++ b/packages/stream_chat_localizations/pubspec.yaml @@ -1,6 +1,6 @@ name: stream_chat_localizations description: The Official localizations for Stream Chat Flutter, a service for building chat applications -version: 5.4.0 +version: 5.5.0 homepage: https://github.com/GetStream/stream-chat-flutter repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues @@ -14,7 +14,7 @@ dependencies: sdk: flutter flutter_localizations: sdk: flutter - stream_chat_flutter: ^6.4.0 + stream_chat_flutter: ^6.5.0 dev_dependencies: flutter_test: