fix(ui): not able to hide reaction picker.

Signed-off-by: xsahil03x <[email protected]>
This commit is contained in:
Sahil Kumar
2023-06-27 17:14:54 +05:30
committed by xsahil03x
parent bd572dee55
commit 723a7368b5
6 changed files with 153 additions and 128 deletions
@@ -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,
@@ -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);
}
@@ -104,7 +104,7 @@ void main() {
message: message,
messageTheme: streamTheme.ownMessageTheme,
reverse: true,
showReactions: false,
showReactionPicker: false,
onUserAvatarTap: onUserAvatarTap,
),
),