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 e9dff50cd..6c2c9767b 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 @@ -113,6 +113,8 @@ class StreamMessageListView extends StatefulWidget { this.onViewInChannelTap, this.onEditMessageTap, this.onReplyTap, + this.onShowMessage, + this.attachmentActionsModalBuilder, this.swipeToReply = false, this.onUserAvatarTap, this.onReactionsTap, @@ -240,6 +242,18 @@ class StreamMessageListView extends StatefulWidget { /// Forwarded to each [StreamMessageWidget] in the list. final void Function(Message)? onReplyTap; + /// Called when the "show in chat" action is tapped in the full-screen + /// media gallery. + /// + /// Forwarded to each [StreamMessageWidget] in the list. + final ShowMessageCallback? onShowMessage; + + /// Widget builder for the attachment actions modal shown in the full-screen + /// media gallery. + /// + /// Forwarded to each [StreamMessageWidget] in the list. + final AttachmentActionsBuilder? attachmentActionsModalBuilder; + /// Whether swiping a message triggers a quoted-reply action. /// /// Forwarded to each [StreamMessageWidget] in the list via @@ -1152,6 +1166,8 @@ class _StreamMessageListViewState extends State { onMessageLongPress: widget.onMessageLongPress, onEditMessageTap: widget.onEditMessageTap, onReplyTap: widget.onReplyTap, + onShowMessage: widget.onShowMessage, + attachmentActionsModalBuilder: widget.attachmentActionsModalBuilder, onUserAvatarTap: widget.onUserAvatarTap, onReactionsTap: widget.onReactionsTap, onQuotedMessageTap: widget.onQuotedMessageTap, @@ -1302,6 +1318,14 @@ class _StreamMessageListViewState extends State { onMessageLongPress: widget.onMessageLongPress, onEditMessageTap: widget.onEditMessageTap, onReplyTap: widget.onReplyTap, + onShowMessage: switch (widget.onShowMessage) { + final onTap? => onTap, + _ => (message, _) => _moveToAndHighlight( + messageId: message.id, + messages: messages, + ), + }, + attachmentActionsModalBuilder: widget.attachmentActionsModalBuilder, onUserAvatarTap: widget.onUserAvatarTap, onReactionsTap: widget.onReactionsTap, onMessageLinkTap: widget.onMessageLinkTap, diff --git a/packages/stream_chat_flutter/lib/src/message_widget/components/stream_message_content.dart b/packages/stream_chat_flutter/lib/src/message_widget/components/stream_message_content.dart index f0471d04c..ab4c18db8 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget/components/stream_message_content.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget/components/stream_message_content.dart @@ -6,6 +6,7 @@ import 'package:stream_chat_flutter/src/message_widget/components/stream_message import 'package:stream_chat_flutter/src/message_widget/components/stream_message_reactions.dart'; import 'package:stream_chat_flutter/src/message_widget/components/stream_message_text.dart'; import 'package:stream_chat_flutter/src/message_widget/parse_attachments.dart'; +import 'package:stream_chat_flutter/src/utils/typedefs.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:stream_core_flutter/stream_core_flutter.dart' as core; @@ -41,6 +42,9 @@ class StreamMessageContent extends StatefulWidget { this.onReactionsTap, this.onQuotedMessageTap, this.reactionSorting, + this.onShowMessage, + this.onReplyTap, + this.attachmentActionsModalBuilder, }); /// The message to display. @@ -95,6 +99,17 @@ class StreamMessageContent extends StatefulWidget { /// Passed through to [StreamMessageReactions.sorting]. final Comparator? reactionSorting; + /// Called when the "show in chat" action is tapped in the full-screen + /// media gallery. + final ShowMessageCallback? onShowMessage; + + /// Called when the reply action is tapped in the full-screen media gallery. + final void Function(Message)? onReplyTap; + + /// Widget builder for the attachment actions modal in the full-screen + /// media gallery. + final AttachmentActionsBuilder? attachmentActionsModalBuilder; + @override State createState() => _StreamMessageContentState(); } @@ -179,6 +194,9 @@ class _StreamMessageContentState extends State { key: attachmentsKey, message: widget.message, attachmentBuilders: widget.attachmentBuilders, + onShowMessage: widget.onShowMessage, + onReplyTap: widget.onReplyTap, + attachmentActionsModalBuilder: widget.attachmentActionsModalBuilder, ), if (widget.message.text case final text? when text.isNotEmpty) StreamMessageText( 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 544334c9a..57a0f92d9 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 @@ -93,6 +93,8 @@ class StreamMessageWidget extends StatelessWidget { void Function(BuildContext, Message)? onBouncedErrorMessageActions, void Function(Message)? onEditMessageTap, List? attachmentBuilders, + ShowMessageCallback? onShowMessage, + AttachmentActionsBuilder? attachmentActionsModalBuilder, }) : props = .new( message: message, padding: padding, @@ -116,6 +118,8 @@ class StreamMessageWidget extends StatelessWidget { onBouncedErrorMessageActions: onBouncedErrorMessageActions, onEditMessageTap: onEditMessageTap, attachmentBuilders: attachmentBuilders, + onShowMessage: onShowMessage, + attachmentActionsModalBuilder: attachmentActionsModalBuilder, ); /// Creates a chat message widget from pre-built [props]. @@ -171,6 +175,8 @@ class StreamMessageWidgetProps { this.onBouncedErrorMessageActions, this.onEditMessageTap, this.attachmentBuilders, + this.onShowMessage, + this.attachmentActionsModalBuilder, }); /// The message to display. @@ -334,6 +340,20 @@ class StreamMessageWidgetProps { /// priority for attachment types they can handle. final List? attachmentBuilders; + /// Called when the "show in chat" action is tapped in the full-screen + /// media gallery. + /// + /// Receives the [Message] and its [Channel] so the caller can scroll to + /// the message in the channel view. + final ShowMessageCallback? onShowMessage; + + /// Widget builder for the attachment actions modal shown in the full-screen + /// media gallery. + /// + /// When non-null, allows customizing the [AttachmentActionsModal] displayed + /// when the user taps the actions button in the gallery header. + final AttachmentActionsBuilder? attachmentActionsModalBuilder; + /// Returns a copy of this [StreamMessageWidgetProps] with the given fields /// replaced with new values. StreamMessageWidgetProps copyWith({ @@ -359,6 +379,8 @@ class StreamMessageWidgetProps { void Function(BuildContext, Message)? onBouncedErrorMessageActions, void Function(Message)? onEditMessageTap, List? attachmentBuilders, + ShowMessageCallback? onShowMessage, + AttachmentActionsBuilder? attachmentActionsModalBuilder, }) { return StreamMessageWidgetProps( message: message ?? this.message, @@ -383,6 +405,8 @@ class StreamMessageWidgetProps { onBouncedErrorMessageActions: onBouncedErrorMessageActions ?? this.onBouncedErrorMessageActions, onEditMessageTap: onEditMessageTap ?? this.onEditMessageTap, attachmentBuilders: attachmentBuilders ?? this.attachmentBuilders, + onShowMessage: onShowMessage ?? this.onShowMessage, + attachmentActionsModalBuilder: attachmentActionsModalBuilder ?? this.attachmentActionsModalBuilder, ); } } @@ -484,6 +508,9 @@ class DefaultStreamMessage extends StatelessWidget { attachmentBuilders: props.attachmentBuilders, reactionSorting: props.reactionSorting, onQuotedMessageTap: props.onQuotedMessageTap, + onShowMessage: props.onShowMessage, + onReplyTap: props.onReplyTap, + attachmentActionsModalBuilder: props.attachmentActionsModalBuilder, onLinkTap: (_, href, __) { if (href == null) return; if (props.onMessageLinkTap case final onTap?) return onTap(message, href);