From c4a890d0697a5a0960b6096b1eafa18b5186d6bc Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Thu, 15 Oct 2020 13:34:59 +0200 Subject: [PATCH] add reactions modal --- example/lib/main.dart | 4 +- example/pubspec.yaml | 2 +- lib/src/message_reactions_modal.dart | 165 +++++++++++++++++++++++++++ lib/src/message_widget.dart | 31 ++++- 4 files changed, 197 insertions(+), 5 deletions(-) create mode 100644 lib/src/message_reactions_modal.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index ed474701..89ed9381 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -75,7 +75,9 @@ void main() async { ); await client.setUser( - User(id: 'super-band-9'), + User(id: 'super-band-9', extraData: { + 'name': 'John Doe', + }), 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A', ); diff --git a/example/pubspec.yaml b/example/pubspec.yaml index c3ab3b46..457e72e4 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,7 +1,7 @@ name: example description: A new Flutter project. -version: 1.0.28+30 +version: 1.0.29+31 environment: sdk: ">=2.2.2 <3.0.0" diff --git a/lib/src/message_reactions_modal.dart b/lib/src/message_reactions_modal.dart new file mode 100644 index 00000000..538fbd92 --- /dev/null +++ b/lib/src/message_reactions_modal.dart @@ -0,0 +1,165 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:stream_chat/stream_chat.dart'; +import 'package:stream_chat_flutter/src/reaction_bubble.dart'; +import 'package:stream_chat_flutter/src/reaction_picker.dart'; +import 'package:stream_chat_flutter/src/stream_channel.dart'; +import 'package:stream_chat_flutter/src/stream_chat.dart'; +import 'package:stream_chat_flutter/src/user_avatar.dart'; + +import 'message_widget.dart'; +import 'stream_chat_theme.dart'; + +class MessageReactionsModal extends StatelessWidget { + final Widget Function(BuildContext, Message) editMessageInputBuilder; + final void Function(Message) onThreadTap; + final Message message; + final MessageTheme messageTheme; + final bool showReactions; + final bool showDeleteMessage; + final bool showEditMessage; + final bool showReply; + final bool reverse; + final ShapeBorder messageShape; + + const MessageReactionsModal({ + Key key, + @required this.message, + @required this.messageTheme, + this.showReactions, + this.showDeleteMessage, + this.showEditMessage, + this.onThreadTap, + this.showReply, + this.editMessageInputBuilder, + this.messageShape, + this.reverse, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final channel = StreamChannel.of(context).channel; + return Stack( + children: [ + Positioned.fill( + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.pop(context); + }, + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaX: 10, + sigmaY: 10, + ), + child: Container( + color: Colors.transparent, + ), + ), + ), + ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + if (showReactions && + (message.status == MessageSendingStatus.SENT || + message.status == null)) + Center( + child: ReactionPicker( + channel: channel, + message: message, + messageTheme: messageTheme, + ), + ), + AbsorbPointer( + child: MessageWidget( + reverse: reverse, + message: message, + messageTheme: messageTheme, + showReactions: false, + showUsername: false, + showReplyIndicator: false, + showTimestamp: false, + showSendingIndicator: DisplayWidget.gone, + shape: messageShape, + ), + ), + SizedBox( + height: 8, + ), + if (message.latestReactions.isNotEmpty) + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + ), + child: Card( + clipBehavior: Clip.hardEdge, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: GridView.builder( + shrinkWrap: true, + padding: const EdgeInsets.all(16.0), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 4, + crossAxisSpacing: 16, + childAspectRatio: 0.8, + mainAxisSpacing: 22, + ), + itemCount: message.latestReactions.length, + itemBuilder: (context, i) { + final reaction = message.latestReactions[i]; + final isCurrentUser = + reaction.user.id == StreamChat.of(context).user.id; + return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Stack( + children: [ + UserAvatar( + user: reaction.user, + constraints: BoxConstraints.tightFor( + height: 64, + width: 64, + ), + borderRadius: BorderRadius.circular(32), + ), + Positioned( + child: ReactionBubble( + reactions: [reaction], + borderColor: isCurrentUser + ? messageTheme.ownReactionsBorderColor + : messageTheme.otherReactionsBorderColor, + backgroundColor: isCurrentUser + ? messageTheme.ownReactionsBackgroundColor + : messageTheme + .otherReactionsBackgroundColor, + flipTail: !isCurrentUser, + ), + bottom: 0, + left: isCurrentUser ? 0 : null, + right: isCurrentUser ? 0 : null, + ), + ], + ), + Text( + reaction.user.name, + style: Theme.of(context).textTheme.subtitle2, + textAlign: TextAlign.center, + ), + ], + ); + }, + ), + ), + ) + ], + ), + ], + ); + } +} diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index 13b31f21..fccaa1e4 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -8,6 +8,7 @@ import 'package:flutter/rendering.dart'; import 'package:flutter_portal/flutter_portal.dart'; import 'package:jiffy/jiffy.dart'; import 'package:stream_chat_flutter/src/message_actions_modal.dart'; +import 'package:stream_chat_flutter/src/message_reactions_modal.dart'; import 'package:stream_chat_flutter/src/reaction_bubble.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; @@ -437,7 +438,7 @@ class _MessageWidgetState extends State { !widget.message.isDeleted) ? Container( child: GestureDetector( - onTap: () => onLongPress(context), + onTap: () => _showMessageReactionsModalBottomSheet(context), child: FractionallySizedBox( widthFactor: 0.5, child: Row( @@ -452,7 +453,7 @@ class _MessageWidgetState extends State { ); } - void _showMessageModalBottomSheet(BuildContext context) { + void _showMessageActionModalBottomSheet(BuildContext context) { final channel = StreamChannel.of(context).channel; showDialog( context: context, @@ -476,6 +477,30 @@ class _MessageWidgetState extends State { }); } + void _showMessageReactionsModalBottomSheet(BuildContext context) { + final channel = StreamChannel.of(context).channel; + showDialog( + context: context, + builder: (context) { + return StreamChannel( + channel: channel, + child: MessageReactionsModal( + messageTheme: widget.messageTheme, + messageShape: widget.shape ?? _getDefaultShape(context), + reverse: widget.reverse, + showDeleteMessage: widget.showDeleteMessage, + message: widget.message, + editMessageInputBuilder: widget.editMessageInputBuilder, + onThreadTap: widget.onThreadTap, + showEditMessage: widget.showEditMessage, + showReactions: widget.showReactions, + showReply: + widget.showReplyIndicator && widget.onThreadTap != null, + ), + ); + }); + } + ContinuousRectangleBorder _getDefaultShape(BuildContext context) { return ContinuousRectangleBorder( side: widget.attachmentBorderSide ?? @@ -581,7 +606,7 @@ class _MessageWidgetState extends State { if (widget.onMessageActions != null) { widget.onMessageActions(context, widget.message); } else { - _showMessageModalBottomSheet(context); + _showMessageActionModalBottomSheet(context); } return; }