fix(ui): not able to hide reaction picker.
Signed-off-by: xsahil03x <[email protected]>
This commit is contained in:
+10
-8
@@ -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<MessageActionsModal> {
|
||||
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<MessageActionsModal> {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Column(
|
||||
crossAxisAlignment: widget.reverse
|
||||
? CrossAxisAlignment.end
|
||||
: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
if (widget.showReactions &&
|
||||
(widget.message.status == MessageSendingStatus.sent))
|
||||
if (widget.showReactionPicker && hasReactionPermission)
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return Align(
|
||||
|
||||
@@ -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<Read>? 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<StreamMessageWidget>
|
||||
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<StreamMessageWidget>
|
||||
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<StreamMessageWidget>
|
||||
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<StreamMessageWidget>
|
||||
];
|
||||
}
|
||||
|
||||
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<StreamMessageWidget>
|
||||
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<StreamMessageWidget>
|
||||
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,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
+4
-5
@@ -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: <Widget>[
|
||||
if ((showReactions ?? hasReactionPermission) &&
|
||||
(message.status == MessageSendingStatus.sent))
|
||||
if (showReactionPicker && hasReactionPermission)
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return Align(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+1
-1
@@ -104,7 +104,7 @@ void main() {
|
||||
message: message,
|
||||
messageTheme: streamTheme.ownMessageTheme,
|
||||
reverse: true,
|
||||
showReactions: false,
|
||||
showReactionPicker: false,
|
||||
onUserAvatarTap: onUserAvatarTap,
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user