From f3a6ba68a406d898579d25adbfbbbc15178ba2b1 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 3 Mar 2020 11:03:36 +0100 Subject: [PATCH 1/8] add edit message feature --- example/lib/main.dart | 14 ++-- lib/src/message_input.dart | 151 ++++++++++++++++++++++++++---------- lib/src/message_widget.dart | 108 +++++++++++++++++++++++++- pubspec.yaml | 2 +- 4 files changed, 227 insertions(+), 48 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index a28a4320..15efe5e5 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -3,13 +3,13 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; void main() async { final client = Client( - 'b67pax5b2wdq', + 's2dxdhpxd94g', logLevel: Level.INFO, ); await client.setUser( User(id: 'falling-mountain-7'), - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZmFsbGluZy1tb3VudGFpbi03In0.AKgRXHMQQMz6vJAKszXdY8zMFfsAgkoUeZHlI-Szz9E', + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNDE5MjI5YTMtODRhMC00MDZiLTkzNzEtN2NlOWE0ZTBhNjc2In0.J3SjGH4e4v7b3cg5EgWkljTxXj_HeHpCWn5ujEVv_H8', ); runApp(MyApp(client)); @@ -38,11 +38,11 @@ class ChannelListPage extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( body: ChannelListView( - filter: { - 'members': { - '\$in': [StreamChat.of(context).user.id], - } - }, +// filter: { +// 'members': { +// '\$in': [StreamChat.of(context).user.id], +// } +// }, sort: [SortOption('last_message_at')], pagination: PaginationParams( limit: 20, diff --git a/lib/src/message_input.dart b/lib/src/message_input.dart index 6db8a574..eb95d6d5 100644 --- a/lib/src/message_input.dart +++ b/lib/src/message_input.dart @@ -8,10 +8,13 @@ import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_flutter/src/message_list_view.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; +import '../stream_chat_flutter.dart'; import 'stream_channel.dart'; +/// Inactive state /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/message_input.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/message_input_paint.png) +/// Focused state /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/message_input2.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/message_input2_paint.png) /// @@ -55,8 +58,12 @@ class MessageInput extends StatefulWidget { Key key, this.onMessageSent, this.parentMessage, + this.editMessage, }) : super(key: key); + /// Message to edit + final Message editMessage; + /// Function called after sending the message final void Function(Message) onMessageSent; @@ -68,11 +75,13 @@ class MessageInput extends StatefulWidget { } class _MessageInputState extends State { - final _textController = TextEditingController(); + TextEditingController _textController; bool _messageIsPresent = false; bool _typingStarted = false; final List<_SendingAttachment> _attachments = []; + final _focusNode = FocusNode(); + @override Widget build(BuildContext context) { return SafeArea( @@ -87,9 +96,7 @@ class _MessageInputState extends State { padding: EdgeInsets.all(2), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10.0), - gradient: _typingStarted - ? StreamChatTheme.of(context).channelTheme.inputGradient - : null, + gradient: _getGradient(context), ), child: Container( decoration: BoxDecoration( @@ -127,6 +134,7 @@ class _MessageInputState extends State { _sendMessage(context); }, controller: _textController, + focusNode: _focusNode, onChanged: (s) { StreamChannel.of(context).channel.keyStroke(); setState(() { @@ -167,6 +175,19 @@ class _MessageInputState extends State { ); } + Gradient _getGradient(BuildContext context) { + if (_typingStarted) { + if (widget.editMessage == null) { + return StreamChatTheme.of(context).channelTheme.inputGradient; + } + return LinearGradient( + colors: [Colors.lightGreen, Colors.green], + ); + } else { + return null; + } + } + Widget _buildAttachments() { return Wrap( direction: Axis.horizontal, @@ -234,10 +255,15 @@ class _MessageInputState extends State { Widget _buildAttachment(_SendingAttachment attachment) { switch (attachment.type) { case FileType.IMAGE: - return Image.file( - attachment.file, - fit: BoxFit.cover, - ); + return attachment.file != null + ? Image.file( + attachment.file, + fit: BoxFit.cover, + ) + : Image.network( + attachment.url, + fit: BoxFit.cover, + ); break; case FileType.VIDEO: return Container( @@ -414,50 +440,97 @@ class _MessageInputState extends State { }); FocusScope.of(context).unfocus(); - StreamChannel.of(context) - .channel - .sendMessage( - Message( - parentId: widget.parentMessage?.id, - text: text, - attachments: attachments.map((attachment) { - String type; - switch (attachment.type) { - case FileType.IMAGE: - type = 'image'; - break; - case FileType.VIDEO: - type = 'video'; - break; - default: - type = 'file'; - } - return Attachment( - imageUrl: - attachment.type == FileType.IMAGE ? attachment.url : null, - assetUrl: attachment.url, - type: type, - ); - }).toList(), - ), - ) - .then((_) { - if (widget.onMessageSent != null) { - widget.onMessageSent(Message(text: text)); + if (widget.editMessage != null) { + final message = widget.editMessage.copyWith( + parentId: widget.parentMessage?.id, + text: text, + attachments: _getAttachments(attachments).toList(), + ); + StreamChat.of(context).client.updateMessage(message).then((_) { + if (widget.onMessageSent != null) { + widget.onMessageSent(message); + } + }); + } else { + final message = Message( + parentId: widget.parentMessage?.id, + text: text, + attachments: _getAttachments(attachments).toList(), + ); + StreamChannel.of(context).channel.sendMessage(message).then((_) { + if (widget.onMessageSent != null) { + widget.onMessageSent(message); + } + }); + } + } + + Iterable _getAttachments(List<_SendingAttachment> attachments) { + return attachments.map((attachment) { + String type; + switch (attachment.type) { + case FileType.IMAGE: + type = 'image'; + break; + case FileType.VIDEO: + type = 'video'; + break; + default: + type = 'file'; } + return Attachment( + imageUrl: attachment.type == FileType.IMAGE ? attachment.url : null, + assetUrl: attachment.url, + type: type, + ); }); } + + @override + void initState() { + super.initState(); + + if (widget.editMessage != null) { + _textController = TextEditingController(text: widget.editMessage.text); + + _typingStarted = true; + _messageIsPresent = true; + + widget.editMessage.attachments.forEach((attachment) { + if (attachment.type == 'image') { + _attachments.add(_SendingAttachment( + type: FileType.IMAGE, + url: attachment.imageUrl, + uploaded: true, + )); + } + }); + } else { + _textController = TextEditingController(); + } + } + + bool _focused = false; + @override + void didChangeDependencies() { + if (widget.editMessage != null && !_focused) { + FocusScope.of(context).requestFocus(_focusNode); + _focused = true; + } + super.didChangeDependencies(); + } } class _SendingAttachment { final File file; final FileType type; String url; - bool uploaded = false; + bool uploaded; _SendingAttachment({ this.url, this.file, this.type, + this.uploaded = false, }); } diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index 7cb737ad..2570bdad 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -8,6 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:stream_chat/stream_chat.dart'; +import 'package:stream_chat_flutter/src/message_input.dart'; import 'package:stream_chat_flutter/src/message_list_view.dart'; import 'package:stream_chat_flutter/src/stream_channel.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; @@ -34,8 +35,12 @@ class MessageWidget extends StatefulWidget { @required this.nextMessage, this.onThreadTap, this.isParent = false, + this.onMessageEdit, }) : super(key: key); + /// Function called when editing a message + final Function(Message) onMessageEdit; + /// This message final Message message; @@ -399,7 +404,7 @@ class _MessageWidgetState extends State ), ), context: context, - builder: (context) { + builder: (_) { final fontSize = 30.0; final textStyle = TextStyle( fontSize: fontSize, @@ -478,6 +483,22 @@ class _MessageWidgetState extends State }, ) : SizedBox(), + isMyMessage + ? FlatButton( + child: Padding( + padding: const EdgeInsets.all(28.0), + child: Text( + 'Edit message', + style: Theme.of(context).textTheme.headline, + ), + ), + onPressed: () async { + await Navigator.pop(context); + + _showEditBottomSheet(context); + }, + ) + : SizedBox(), streamChannel.channel.config.replies ? FlatButton( child: Padding( @@ -499,6 +520,91 @@ class _MessageWidgetState extends State }); } + void _showEditBottomSheet(BuildContext context) { + showModalBottomSheet( + context: context, + elevation: 2, + clipBehavior: Clip.hardEdge, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(32), + topRight: Radius.circular(32), + ), + ), + builder: (context) { + return Flex( + direction: Axis.vertical, + mainAxisAlignment: MainAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.only( + top: 16.0, + left: 16.0, + right: 16.0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Edit message', + style: Theme.of(context).textTheme.title, + ), + Container( + height: 30, + padding: const EdgeInsets.all(2.0), + child: AspectRatio( + aspectRatio: 1, + child: RawMaterialButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + ), + elevation: 0, + highlightElevation: 0, + focusElevation: 0, + disabledElevation: 0, + hoverElevation: 0, + onPressed: () { + Navigator.of(context).pop(); + }, + fillColor: Colors.black.withOpacity(.1), + padding: EdgeInsets.all(4), + child: Icon( + Icons.close, + size: 15, + color: Colors.black, + ), + ), + ), + ), + ], + ), + ), + Padding( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).viewInsets.bottom, + ), + child: MessageInput( + editMessage: widget.message, + parentMessage: widget.isParent + ? StreamChannel.of(context) + .channel + .state + .messages + .firstWhere( + (message) => message.id == widget.message.parentId) + : null, + onMessageSent: (_) { + Navigator.pop(context); + }, + ), + ), + ], + ); + }, + ); + } + Widget _buildReactions(bool isMyMessage) { return GestureDetector( onTap: () { diff --git a/pubspec.yaml b/pubspec.yaml index 3662c3ce..491a1663 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -20,7 +20,7 @@ dependencies: chewie: ^0.9.8+1 file_picker: ^1.4.3+2 image_picker: ^0.6.3+4 - stream_chat: ^0.1.12 + stream_chat: ^0.1.13 dev_dependencies: pedantic: ^1.8.0+1 From 62e386b36b84d2f9a6a1fd14456ac3a3dd1a2705 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 3 Mar 2020 11:29:22 +0100 Subject: [PATCH 2/8] add reaction picker widget --- lib/src/message_widget.dart | 113 ++++++++------------------ lib/src/reaction_picker.dart | 78 ++++++++++++++++++ screenshots/reaction_picker.png | Bin 0 -> 46037 bytes screenshots/reaction_picker_paint.png | Bin 0 -> 49343 bytes 4 files changed, 112 insertions(+), 79 deletions(-) create mode 100644 lib/src/reaction_picker.dart create mode 100644 screenshots/reaction_picker.png create mode 100644 screenshots/reaction_picker_paint.png diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index 2570bdad..1b322a9f 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -10,6 +10,7 @@ import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_flutter/src/message_input.dart'; import 'package:stream_chat_flutter/src/message_list_view.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_theme.dart'; import 'package:stream_chat_flutter/src/user_avatar.dart'; @@ -34,12 +35,12 @@ class MessageWidget extends StatefulWidget { @required this.message, @required this.nextMessage, this.onThreadTap, + this.onMessageActions, this.isParent = false, - this.onMessageEdit, }) : super(key: key); - /// Function called when editing a message - final Function(Message) onMessageEdit; + /// Function called on long press + final Function(BuildContext, Message) onMessageActions; /// This message final Message message; @@ -68,6 +69,7 @@ class _MessageWidgetState extends State MessageTheme messageTheme; StreamChatState streamChat; StreamChannelState streamChannel; + bool isMyMessage; @override Widget build(BuildContext context) { @@ -80,9 +82,10 @@ class _MessageWidgetState extends State final messageUserId = widget.message.user.id; final previousUserId = widget.previousMessage?.user?.id; final nextUserId = widget.nextMessage?.user?.id; - final isMyMessage = messageUserId == currentUserId; final isLastUser = previousUserId == messageUserId; final isNextUser = nextUserId == messageUserId; + + isMyMessage = messageUserId == currentUserId; final alignment = isMyMessage ? Alignment.centerRight : Alignment.centerLeft; @@ -100,11 +103,11 @@ class _MessageWidgetState extends State crossAxisAlignment: isMyMessage ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [ - _buildBubble(context, isMyMessage, isLastUser), + _buildBubble(context, isLastUser), streamChannel.channel.config.replies - ? _buildThreadIndicator(context, isMyMessage) + ? _buildThreadIndicator(context) : SizedBox(), - isNextUser ? SizedBox() : _buildTimestamp(isMyMessage, alignment), + isNextUser ? SizedBox() : _buildTimestamp(alignment), ], ), isNextUser @@ -166,7 +169,7 @@ class _MessageWidgetState extends State ); } - Widget _buildThreadIndicator(BuildContext context, bool isMyMessage) { + Widget _buildThreadIndicator(BuildContext context) { var row = [ Text( 'Replies: ${widget.message.replyCount}', @@ -208,7 +211,6 @@ class _MessageWidgetState extends State Widget _buildBubble( BuildContext context, - bool isMyMessage, bool isLastUser, ) { var nOfAttachmentWidgets = 0; @@ -221,13 +223,13 @@ class _MessageWidgetState extends State Widget attachmentWidget; if (attachment.type == 'video') { - attachmentWidget = _buildVideo(attachment, isMyMessage, isLastUser); + attachmentWidget = _buildVideo(attachment, isLastUser); } else if (attachment.type == 'image' || attachment.type == 'giphy') { - attachmentWidget = _buildImage(isMyMessage, isLastUser, attachment); + attachmentWidget = _buildImage(isLastUser, attachment); } if (attachmentWidget != null) { - final boxDecoration = _buildBoxDecoration(isMyMessage, isLastUser) + final boxDecoration = _buildBoxDecoration(isLastUser) .copyWith(color: Color(0xffebebeb)); return Padding( padding: const EdgeInsets.only(bottom: 2.0), @@ -319,7 +321,7 @@ class _MessageWidgetState extends State children: [ streamChannel.channel.config.reactions ? Align( - child: _buildReactions(isMyMessage), + child: _buildReactions(), alignment: isMyMessage ? Alignment.centerLeft : Alignment.centerRight, @@ -344,7 +346,7 @@ class _MessageWidgetState extends State left: isMyMessage ? 8.0 : 0.0, ), child: Container( - decoration: _buildBoxDecoration(isMyMessage, + decoration: _buildBoxDecoration( isLastUser || nOfAttachmentWidgets > 0), padding: EdgeInsets.all(10), constraints: BoxConstraints.loose(Size.fromWidth(300)), @@ -385,11 +387,15 @@ class _MessageWidgetState extends State return GestureDetector( child: column, onLongPress: () { - _showMessageBottomSheet(context, isMyMessage); + if (widget.onMessageActions != null) { + widget.onMessageActions(context, widget.message); + } else { + _showMessageBottomSheet(context); + } }); } - void _showMessageBottomSheet(BuildContext context, bool isMyMessage) { + void _showMessageBottomSheet(BuildContext context) { if (!streamChannel.channel.config.reactions && !streamChannel.channel.config.replies) { return; @@ -405,10 +411,6 @@ class _MessageWidgetState extends State ), context: context, builder: (_) { - final fontSize = 30.0; - final textStyle = TextStyle( - fontSize: fontSize, - ); return SafeArea( child: Column( mainAxisSize: MainAxisSize.min, @@ -417,49 +419,10 @@ class _MessageWidgetState extends State Container( color: Colors.black87, child: streamChannel.channel.config.reactions - ? Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: reactionToEmoji.keys.map((reactionType) { - final ownReactionIndex = widget.message.ownReactions - .indexWhere((reaction) => - reaction.type == reactionType); - return Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - IconButton( - iconSize: fontSize + 10, - icon: Text( - reactionToEmoji[reactionType], - style: textStyle, - ), - onPressed: () { - if (ownReactionIndex != -1) { - removeReaction(reactionType); - } else { - sendReaction(reactionType); - } - }, - ), - ownReactionIndex != -1 - ? Padding( - padding: - const EdgeInsets.only(bottom: 4.0), - child: Text( - widget - .message - .ownReactions[ownReactionIndex] - .score - .toString(), - style: TextStyle(color: Colors.white), - ), - ) - : SizedBox(), - ], - ); - }).toList(), + ? ReactionPicker( + channel: StreamChannel.of(context).channel, + reactionToEmoji: reactionToEmoji, + message: widget.message, ) : SizedBox(), ), @@ -605,10 +568,14 @@ class _MessageWidgetState extends State ); } - Widget _buildReactions(bool isMyMessage) { + Widget _buildReactions() { return GestureDetector( onTap: () { - _showMessageBottomSheet(context, isMyMessage); + if (widget.onMessageActions != null) { + widget.onMessageActions(context, widget.message); + } else { + _showMessageBottomSheet(context); + } }, child: Padding( padding: EdgeInsets.symmetric( @@ -665,18 +632,7 @@ class _MessageWidgetState extends State 'wow': '😲', }; - void sendReaction(String reactionType) { - streamChannel.channel.sendReaction(widget.message.id, reactionType); - Navigator.of(context).pop(); - } - - void removeReaction(String reactionType) { - streamChannel.channel.deleteReaction(widget.message.id, reactionType); - Navigator.of(context).pop(); - } - Widget _buildImage( - bool isMyMessage, bool isLastUser, Attachment attachment, ) { @@ -689,7 +645,6 @@ class _MessageWidgetState extends State Widget _buildVideo( Attachment attachment, - bool isMyMessage, bool isLastUser, ) { VideoPlayerController videoController; @@ -759,7 +714,7 @@ class _MessageWidgetState extends State super.dispose(); } - Widget _buildTimestamp(bool isMyMessage, Alignment alignment) { + Widget _buildTimestamp(Alignment alignment) { return Padding( padding: const EdgeInsets.only(top: 5.0), child: Text( @@ -769,7 +724,7 @@ class _MessageWidgetState extends State ); } - BoxDecoration _buildBoxDecoration(bool isMyMessage, bool isLastUser) { + BoxDecoration _buildBoxDecoration(bool isLastUser) { return BoxDecoration( border: isMyMessage ? null : Border.all(color: Colors.black.withAlpha(8)), borderRadius: BorderRadius.only( diff --git a/lib/src/reaction_picker.dart b/lib/src/reaction_picker.dart new file mode 100644 index 00000000..c5186a93 --- /dev/null +++ b/lib/src/reaction_picker.dart @@ -0,0 +1,78 @@ +import 'package:flutter/material.dart'; + +import '../stream_chat_flutter.dart'; + +/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/reaction_picker.png) +/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/reaction_picker_paint.png) +/// +/// It shows a reaction picker +/// +/// Usually you don't use this widget as it's one of the default widgets used by [MessageWidget.onMessageActions]. +class ReactionPicker extends StatelessWidget { + const ReactionPicker({ + Key key, + @required this.reactionToEmoji, + @required this.message, + @required this.channel, + this.size = 40, + }) : super(key: key); + + final Map reactionToEmoji; + final Message message; + final double size; + final Channel channel; + + @override + Widget build(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: reactionToEmoji.keys.map((reactionType) { + final ownReactionIndex = message.ownReactions + .indexWhere((reaction) => reaction.type == reactionType); + return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + IconButton( + iconSize: size, + icon: Text( + reactionToEmoji[reactionType], + style: TextStyle( + fontSize: size - 10, + ), + ), + onPressed: () { + if (ownReactionIndex != -1) { + removeReaction(context, reactionType); + } else { + sendReaction(context, reactionType); + } + }, + ), + ownReactionIndex != -1 + ? Padding( + padding: const EdgeInsets.only(bottom: 4.0), + child: Text( + message.ownReactions[ownReactionIndex].score.toString(), + style: TextStyle(color: Colors.white), + ), + ) + : SizedBox(), + ], + ); + }).toList(), + ); + } + + void sendReaction(BuildContext context, String reactionType) { + channel.sendReaction(message.id, reactionType); + Navigator.of(context).pop(); + } + + void removeReaction(BuildContext context, String reactionType) { + channel.deleteReaction(message.id, reactionType); + Navigator.of(context).pop(); + } +} diff --git a/screenshots/reaction_picker.png b/screenshots/reaction_picker.png new file mode 100644 index 0000000000000000000000000000000000000000..eafefa3102ab3cbd83c2bb8070926cfc8f0620fc GIT binary patch literal 46037 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 Pux3;z literal 0 HcmV?d00001 diff --git a/screenshots/reaction_picker_paint.png b/screenshots/reaction_picker_paint.png new file mode 100644 index 0000000000000000000000000000000000000000..20028f4c38853a5b0363b2370b6cb64ac4af9c46 GIT binary patch literal 49343 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 Date: Tue, 3 Mar 2020 11:45:55 +0100 Subject: [PATCH 3/8] typo --- lib/src/message_widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index 1b322a9f..7053b482 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -456,7 +456,7 @@ class _MessageWidgetState extends State ), ), onPressed: () async { - await Navigator.pop(context); + Navigator.pop(context); _showEditBottomSheet(context); }, From e2f29a15bf5154e06459561b996555828cf6b6bc Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 3 Mar 2020 17:06:36 +0100 Subject: [PATCH 4/8] add commands --- lib/src/message_input.dart | 124 ++++++++++++- lib/src/message_widget.dart | 334 +++++++++++++++++++++++------------- 2 files changed, 332 insertions(+), 126 deletions(-) diff --git a/lib/src/message_input.dart b/lib/src/message_input.dart index eb95d6d5..90d4306d 100644 --- a/lib/src/message_input.dart +++ b/lib/src/message_input.dart @@ -4,6 +4,7 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:keyboard_visibility/keyboard_visibility.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_flutter/src/message_list_view.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; @@ -82,6 +83,10 @@ class _MessageInputState extends State { final _focusNode = FocusNode(); + bool _modalOn = false; + + OverlayEntry _commandsOverlay; + @override Widget build(BuildContext context) { return SafeArea( @@ -140,6 +145,19 @@ class _MessageInputState extends State { setState(() { _messageIsPresent = s.trim().isNotEmpty; }); + + if (s.startsWith('/')) { + _modalOn = true; + + _commandsOverlay?.remove(); + _commandsOverlay = null; + _commandsOverlay = _buildOverlayEntry(); + Overlay.of(context).insert(_commandsOverlay); + } else if (!s.startsWith('/') && _modalOn) { + _modalOn = false; + _commandsOverlay?.remove(); + _commandsOverlay = null; + } }, onTap: () { setState(() { @@ -175,6 +193,79 @@ class _MessageInputState extends State { ); } + OverlayEntry _buildOverlayEntry() { + final text = _textController.text; + final commands = StreamChannel.of(context) + .channel + .config + .commands + .where((c) => c.name.startsWith(text.substring(1))) + .toList(); + + RenderBox renderBox = context.findRenderObject(); + final size = renderBox.size; + final offset = renderBox.localToGlobal(Offset.zero); + + return OverlayEntry(builder: (_) { + return Positioned( + top: offset.dy - size.height, + left: 0, + right: 0, + child: Material( + color: StreamChatTheme.of(context).primaryColor, + child: Flex( + mainAxisSize: MainAxisSize.min, + direction: Axis.vertical, + children: [ + ListView( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + children: commands + .map((c) => ListTile( + title: Text.rich( + TextSpan( + text: '${c.name}', + style: TextStyle(fontWeight: FontWeight.bold), + children: [ + TextSpan( + text: ' ${c.args}', + style: TextStyle( + fontWeight: FontWeight.w300, + ), + ), + ], + ), + ), + subtitle: Text(c.description), + onTap: () { + _setCommand(c); + }, + )) + .toList(), + ), + ], + ), + ), + ); + }); + } + + void _setCommand(Command c) { + setState(() { + _textController.value = TextEditingValue( + text: '/${c.name}', + selection: TextSelection.fromPosition( + TextPosition( + offset: c.name.length + 1, + ), + ), + ); + }); + _modalOn = false; + _commandsOverlay?.remove(); + _commandsOverlay = null; + } + Gradient _getGradient(BuildContext context) { if (_typingStarted) { if (widget.editMessage == null) { @@ -438,6 +529,12 @@ class _MessageInputState extends State { _messageIsPresent = false; _typingStarted = false; }); + + _modalOn = false; + + _commandsOverlay?.remove(); + _commandsOverlay = null; + FocusScope.of(context).unfocus(); if (widget.editMessage != null) { @@ -486,10 +583,24 @@ class _MessageInputState extends State { }); } + int _keyboardListener; + @override void initState() { super.initState(); + _keyboardListener = KeyboardVisibilityNotification().addNewListener( + onChange: (_) { + if (_modalOn) { + _commandsOverlay?.remove(); + WidgetsBinding.instance.addPostFrameCallback((_) { + _commandsOverlay = _buildOverlayEntry(); + Overlay.of(context).insert(_commandsOverlay); + }); + } + }, + ); + if (widget.editMessage != null) { _textController = TextEditingController(text: widget.editMessage.text); @@ -510,12 +621,19 @@ class _MessageInputState extends State { } } - bool _focused = false; + @override + void dispose() { + _commandsOverlay?.remove(); + KeyboardVisibilityNotification().removeListener(_keyboardListener); + super.dispose(); + } + + bool _initialized = false; @override void didChangeDependencies() { - if (widget.editMessage != null && !_focused) { + if (widget.editMessage != null && !_initialized) { FocusScope.of(context).requestFocus(_focusNode); - _focused = true; + _initialized = true; } super.didChangeDependencies(); } diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index 7053b482..f8b58398 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -233,74 +233,124 @@ class _MessageWidgetState extends State .copyWith(color: Color(0xffebebeb)); return Padding( padding: const EdgeInsets.only(bottom: 2.0), - child: ClipRRect( - borderRadius: boxDecoration.borderRadius, - child: Container( - decoration: boxDecoration, - constraints: BoxConstraints.loose(Size.fromWidth(300)), - child: Stack( - children: [ - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ClipRRect( + borderRadius: boxDecoration.borderRadius, + child: Container( + decoration: boxDecoration, + constraints: BoxConstraints.loose(Size.fromWidth(300)), + child: Stack( children: [ - attachmentWidget, - attachment.title != null - ? Container( - constraints: - BoxConstraints.loose(Size(300, 500)), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - attachment.title, - overflow: TextOverflow.ellipsis, - style: - messageTheme.messageText.copyWith( - color: Colors.blue, - fontWeight: FontWeight.bold, - ), + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + attachmentWidget, + attachment.title != null + ? Container( + constraints: + BoxConstraints.loose(Size(300, 500)), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + attachment.title, + overflow: TextOverflow.ellipsis, + style: messageTheme.messageText + .copyWith( + color: Colors.blue, + fontWeight: FontWeight.bold, + ), + ), + Text( + Uri.parse(attachment.thumbUrl) + .authority + .split('.') + .reversed + .take(2) + .toList() + .reversed + .join('.'), + overflow: TextOverflow.ellipsis, + style: messageTheme.createdAt, + ), + ], ), - Text( - Uri.parse(attachment.thumbUrl) - .authority - .split('.') - .reversed - .take(2) - .toList() - .reversed - .join('.'), - overflow: TextOverflow.ellipsis, - style: messageTheme.createdAt, - ), - ], + ), + color: Color(0xffebebeb), + ) + : SizedBox(), + ], + ), + attachment.type == 'image' && + attachment.titleLink != null + ? Positioned.fill( + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: () => + _launchURL(attachment.titleLink), ), ), - color: Color(0xffebebeb), ) : SizedBox(), ], ), - attachment.type == 'image' && attachment.titleLink != null - ? Positioned.fill( - child: Material( - color: Colors.transparent, - child: InkWell( - onTap: () => _launchURL(attachment.titleLink), - ), + margin: EdgeInsets.only( + top: nOfAttachmentWidgets > 1 ? 5 : 0, + ), + ), + ), + if (attachment.actions != null) + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: attachment.actions?.map((action) { + if (action.style == 'primary') { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 4.0), + child: FlatButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), ), - ) - : SizedBox(), - ], - ), - margin: EdgeInsets.only( - top: nOfAttachmentWidgets > 1 ? 5 : 0, - ), - ), + child: Text('${action.text}'), + color: action.style == 'primary' + ? StreamChatTheme.of(context).accentColor + : null, + textColor: Colors.white, + onPressed: () { + streamChannel.channel + .sendAction(widget.message.id, { + action.name: action.value, + }); + }, + ), + ); + } + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 4.0), + child: OutlineButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Text('${action.text}'), + color: StreamChatTheme.of(context).accentColor, + onPressed: () { + streamChannel.channel + .sendAction(widget.message.id, { + action.name: action.value, + }); + }, + ), + ); + })?.toList(), + ), + ], ), ); } @@ -313,80 +363,96 @@ class _MessageWidgetState extends State if (widget.message.text.trim().isNotEmpty) { column.children.addAll( [ - IntrinsicWidth( - child: Column( - crossAxisAlignment: isMyMessage - ? CrossAxisAlignment.end - : CrossAxisAlignment.start, - children: [ - streamChannel.channel.config.reactions - ? Align( - child: _buildReactions(), - alignment: isMyMessage - ? Alignment.centerLeft - : Alignment.centerRight, - ) - : SizedBox(), - Stack( - overflow: Overflow.visible, - children: [ - widget.message.reactionCounts?.isNotEmpty == true - ? Positioned( - left: isMyMessage ? 8 : null, - right: !isMyMessage ? 8 : null, - top: -6, - child: CustomPaint( - painter: ReactionBubblePainter(), - ), - ) - : SizedBox(), - Padding( - padding: EdgeInsets.only( - right: isMyMessage ? 0.0 : 8.0, - left: isMyMessage ? 8.0 : 0.0, - ), - child: Container( - decoration: _buildBoxDecoration( - isLastUser || nOfAttachmentWidgets > 0), - padding: EdgeInsets.all(10), - constraints: BoxConstraints.loose(Size.fromWidth(300)), - child: MarkdownBody( - data: '${widget.message.text}', - onTapLink: (link) { - _launchURL(link); - }, - styleSheet: MarkdownStyleSheet.fromTheme( - Theme.of(context).copyWith( - textTheme: Theme.of(context).textTheme.apply( - bodyColor: messageTheme.messageText.color, - decoration: - messageTheme.messageText.decoration, - decorationColor: messageTheme - .messageText.decorationColor, - decorationStyle: messageTheme - .messageText.decorationStyle, - fontFamily: - messageTheme.messageText.fontFamily, - ), - ), - ).copyWith( - p: messageTheme.messageText, + Column( + crossAxisAlignment: + isMyMessage ? CrossAxisAlignment.end : CrossAxisAlignment.start, + children: [ + streamChannel.channel.config.reactions + ? Align( + child: _buildReactions(), + alignment: isMyMessage + ? Alignment.centerLeft + : Alignment.centerRight, + ) + : SizedBox(), + Stack( + overflow: Overflow.visible, + children: [ + _buildReactionPaint(), + Padding( + padding: EdgeInsets.only( + right: isMyMessage ? 0.0 : 8.0, + left: isMyMessage ? 8.0 : 0.0, + ), + child: Container( + decoration: _buildBoxDecoration( + isLastUser || nOfAttachmentWidgets > 0), + padding: EdgeInsets.all(10), + constraints: BoxConstraints.loose(Size.fromWidth(300)), + child: MarkdownBody( + data: '${widget.message.text}', + onTapLink: (link) { + _launchURL(link); + }, + styleSheet: MarkdownStyleSheet.fromTheme( + Theme.of(context).copyWith( + textTheme: Theme.of(context).textTheme.apply( + bodyColor: messageTheme.messageText.color, + decoration: + messageTheme.messageText.decoration, + decorationColor: + messageTheme.messageText.decorationColor, + decorationStyle: + messageTheme.messageText.decorationStyle, + fontFamily: + messageTheme.messageText.fontFamily, + ), ), + ).copyWith( + p: messageTheme.messageText, ), ), ), - ], - ), - ], - ), + ), + ], + ), + ], ), ], ); + } else { + column.children.insert( + 0, + streamChannel.channel.config.reactions + ? Align( + child: _buildReactions(), + alignment: + isMyMessage ? Alignment.centerLeft : Alignment.centerRight, + ) + : SizedBox(), + ); + column.children[1] = Stack( + overflow: Overflow.visible, + children: [ + Padding( + padding: EdgeInsets.only( + right: isMyMessage ? 0.0 : 8.0, + left: isMyMessage ? 8.0 : 0.0, + ), + child: column.children[1], + ), + _buildReactionPaint(), + ], + ); } return GestureDetector( - child: column, + child: IntrinsicWidth(child: column), onLongPress: () { + if (widget.message.type == 'ephemeral') { + return; + } + if (widget.onMessageActions != null) { widget.onMessageActions(context, widget.message); } else { @@ -395,6 +461,19 @@ class _MessageWidgetState extends State }); } + Widget _buildReactionPaint() { + return widget.message.reactionCounts?.isNotEmpty == true + ? Positioned( + left: isMyMessage ? 8 : null, + right: !isMyMessage ? 8 : null, + top: -6, + child: CustomPaint( + painter: _ReactionBubblePainter(), + ), + ) + : SizedBox(); + } + void _showMessageBottomSheet(BuildContext context) { if (!streamChannel.channel.config.reactions && !streamChannel.channel.config.replies) { @@ -640,6 +719,15 @@ class _MessageWidgetState extends State imageUrl: attachment.thumbUrl ?? attachment.imageUrl ?? attachment.assetUrl, fit: BoxFit.cover, + placeholder: (_, __) { + return SizedBox( + height: 200, + width: 300, + child: Center( + child: CircularProgressIndicator(), + ), + ); + }, ); } @@ -743,7 +831,7 @@ class _MessageWidgetState extends State } } -class ReactionBubblePainter extends CustomPainter { +class _ReactionBubblePainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final paint = Paint()..color = Colors.black; From f37b8d3cc43fd9b1cd7ddcf89505dce9c5ee07be Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 3 Mar 2020 17:13:02 +0100 Subject: [PATCH 5/8] update dart lib in pubspec --- pubspec.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 491a1663..cacce9fb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ description: Stream Chat official Flutter SDK. Build your own chat experience us version: 0.1.6+4 environment: - sdk: ">=2.1.0 <3.0.0" + sdk: ">=2.2.2 <3.0.0" dependencies: flutter: @@ -20,6 +20,7 @@ dependencies: chewie: ^0.9.8+1 file_picker: ^1.4.3+2 image_picker: ^0.6.3+4 + keyboard_visibility: ^0.5.6 stream_chat: ^0.1.13 dev_dependencies: From c686540306ce96252a7c2a01b9f7daa0d80d2c43 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 3 Mar 2020 18:01:55 +0100 Subject: [PATCH 6/8] fix edit message --- example/ios/Podfile.lock | 28 +++++ lib/src/message_input.dart | 236 ++++++++++++++++++++---------------- lib/src/message_widget.dart | 130 ++++++++++---------- 3 files changed, 226 insertions(+), 168 deletions(-) diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 134db5c4..e848b8ff 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,10 +1,20 @@ PODS: + - file_picker (0.0.1): + - Flutter - Flutter (1.0.0) + - flutter_plugin_android_lifecycle (0.0.1): + - Flutter - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) + - image_picker (0.0.1): + - Flutter + - keyboard_visibility (0.5.0): + - Flutter + - Reachability - path_provider (0.0.1): - Flutter + - Reachability (3.2) - screen (0.0.1): - Flutter - sqflite (0.0.1): @@ -22,7 +32,11 @@ PODS: - Flutter DEPENDENCIES: + - file_picker (from `.symlinks/plugins/file_picker/ios`) - Flutter (from `Flutter`) + - flutter_plugin_android_lifecycle (from `.symlinks/plugins/flutter_plugin_android_lifecycle/ios`) + - image_picker (from `.symlinks/plugins/image_picker/ios`) + - keyboard_visibility (from `.symlinks/plugins/keyboard_visibility/ios`) - path_provider (from `.symlinks/plugins/path_provider/ios`) - screen (from `.symlinks/plugins/screen/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) @@ -35,10 +49,19 @@ DEPENDENCIES: SPEC REPOS: trunk: - FMDB + - Reachability EXTERNAL SOURCES: + file_picker: + :path: ".symlinks/plugins/file_picker/ios" Flutter: :path: Flutter + flutter_plugin_android_lifecycle: + :path: ".symlinks/plugins/flutter_plugin_android_lifecycle/ios" + image_picker: + :path: ".symlinks/plugins/image_picker/ios" + keyboard_visibility: + :path: ".symlinks/plugins/keyboard_visibility/ios" path_provider: :path: ".symlinks/plugins/path_provider/ios" screen: @@ -57,9 +80,14 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/video_player_web/ios" SPEC CHECKSUMS: + file_picker: 408623be2125b79a4539cf703be3d4b3abe5e245 Flutter: 0e3d915762c693b495b44d77113d4970485de6ec + flutter_plugin_android_lifecycle: 47de533a02850f070f5696a623995e93eddcdb9b FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + image_picker: e3eacd46b94694dde7cf2705955cece853aa1a8f + keyboard_visibility: 96a24de806fe6823c3ad956c01ba2ec6d056616f path_provider: fb74bd0465e96b594bb3b5088ee4a4e7bb1f2a9d + Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 screen: abd91ca7bf3426e1cc3646d27e9b2358d6bf07b0 sqflite: 4001a31ff81d210346b500c55b17f4d6c7589dd0 url_launcher: a1c0cc845906122c4784c542523d8cacbded5626 diff --git a/lib/src/message_input.dart b/lib/src/message_input.dart index 90d4306d..79c9f55f 100644 --- a/lib/src/message_input.dart +++ b/lib/src/message_input.dart @@ -76,15 +76,12 @@ class MessageInput extends StatefulWidget { } class _MessageInputState extends State { + final List<_SendingAttachment> _attachments = []; + final _focusNode = FocusNode(); + TextEditingController _textController; bool _messageIsPresent = false; bool _typingStarted = false; - final List<_SendingAttachment> _attachments = []; - - final _focusNode = FocusNode(); - - bool _modalOn = false; - OverlayEntry _commandsOverlay; @override @@ -95,96 +92,12 @@ class _MessageInputState extends State { child: Stack( overflow: Overflow.visible, children: [ - Positioned.fill( - child: Container( - width: MediaQuery.of(context).size.width, - padding: EdgeInsets.all(2), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10.0), - gradient: _getGradient(context), - ), - child: Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(10.0), - ), - child: Container( - decoration: BoxDecoration( - color: StreamChatTheme.of(context) - .channelTheme - .inputBackground, - borderRadius: BorderRadius.circular(10.0), - border: Border.all( - color: _typingStarted - ? Colors.transparent - : Colors.black.withOpacity(.2)), - ), - ), - ), - ), - ), + _buildBorder(context), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildAttachments(), - Flex( - direction: Axis.horizontal, - children: [ - _buildAttachmentButton(), - Expanded( - child: TextField( - minLines: null, - maxLines: null, - onSubmitted: (_) { - _sendMessage(context); - }, - controller: _textController, - focusNode: _focusNode, - onChanged: (s) { - StreamChannel.of(context).channel.keyStroke(); - setState(() { - _messageIsPresent = s.trim().isNotEmpty; - }); - - if (s.startsWith('/')) { - _modalOn = true; - - _commandsOverlay?.remove(); - _commandsOverlay = null; - _commandsOverlay = _buildOverlayEntry(); - Overlay.of(context).insert(_commandsOverlay); - } else if (!s.startsWith('/') && _modalOn) { - _modalOn = false; - _commandsOverlay?.remove(); - _commandsOverlay = null; - } - }, - onTap: () { - setState(() { - _typingStarted = true; - }); - }, - style: Theme.of(context).textTheme.body1, - autofocus: false, - decoration: InputDecoration( - hintText: 'Write a message', - prefixText: ' ', - border: InputBorder.none, - ), - ), - ), - AnimatedCrossFade( - crossFadeState: - (_messageIsPresent || _attachments.isNotEmpty) - ? CrossFadeState.showFirst - : CrossFadeState.showSecond, - firstChild: _buildSendButton(context), - secondChild: SizedBox(), - duration: Duration(milliseconds: 300), - alignment: Alignment.center, - ), - ], - ), + _buildTextField(context), ], ), ], @@ -193,13 +106,110 @@ class _MessageInputState extends State { ); } + Flex _buildTextField(BuildContext context) { + return Flex( + direction: Axis.horizontal, + children: [ + _buildAttachmentButton(), + _buildTextInput(context), + _animateSendButton(context), + ], + ); + } + + AnimatedCrossFade _animateSendButton(BuildContext context) { + print( + '_messageIsPresent || _attachments.isNotEmpty: ${_messageIsPresent || _attachments.isNotEmpty}'); + print('_attachments.isNotEmpty: ${_attachments.isNotEmpty}'); + print('_messageIsPresent: ${_messageIsPresent}'); + return AnimatedCrossFade( + crossFadeState: (_messageIsPresent || _attachments.isNotEmpty) + ? CrossFadeState.showFirst + : CrossFadeState.showSecond, + firstChild: _buildSendButton(context), + secondChild: SizedBox(), + duration: Duration(milliseconds: 300), + alignment: Alignment.center, + ); + } + + Expanded _buildTextInput(BuildContext context) { + return Expanded( + child: TextField( + minLines: null, + maxLines: null, + onSubmitted: (_) { + _sendMessage(context); + }, + controller: _textController, + focusNode: _focusNode, + onChanged: (s) { + StreamChannel.of(context).channel.keyStroke(); + + setState(() { + _messageIsPresent = s.trim().isNotEmpty; + }); + + _commandsOverlay?.remove(); + _commandsOverlay = null; + + if (s.startsWith('/')) { + _commandsOverlay = _buildOverlayEntry(); + Overlay.of(context).insert(_commandsOverlay); + } + }, + onTap: () { + setState(() { + _typingStarted = true; + }); + }, + style: Theme.of(context).textTheme.body1, + autofocus: false, + decoration: InputDecoration( + hintText: 'Write a message', + prefixText: ' ', + border: InputBorder.none, + ), + ), + ); + } + + Positioned _buildBorder(BuildContext context) { + return Positioned.fill( + child: Container( + width: MediaQuery.of(context).size.width, + padding: EdgeInsets.all(2), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.0), + gradient: _getGradient(context), + ), + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10.0), + ), + child: Container( + decoration: BoxDecoration( + color: StreamChatTheme.of(context).channelTheme.inputBackground, + borderRadius: BorderRadius.circular(10.0), + border: Border.all( + color: _typingStarted + ? Colors.transparent + : Colors.black.withOpacity(.2)), + ), + ), + ), + ), + ); + } + OverlayEntry _buildOverlayEntry() { final text = _textController.text; final commands = StreamChannel.of(context) .channel .config .commands - .where((c) => c.name.startsWith(text.substring(1))) + .where((c) => c.name.startsWith(text.replaceFirst('/', ''))) .toList(); RenderBox renderBox = context.findRenderObject(); @@ -253,15 +263,14 @@ class _MessageInputState extends State { void _setCommand(Command c) { setState(() { _textController.value = TextEditingValue( - text: '/${c.name}', + text: '/${c.name} ', selection: TextSelection.fromPosition( TextPosition( - offset: c.name.length + 1, + offset: c.name.length + 2, ), ), ); }); - _modalOn = false; _commandsOverlay?.remove(); _commandsOverlay = null; } @@ -530,8 +539,6 @@ class _MessageInputState extends State { _typingStarted = false; }); - _modalOn = false; - _commandsOverlay?.remove(); _commandsOverlay = null; @@ -541,7 +548,10 @@ class _MessageInputState extends State { final message = widget.editMessage.copyWith( parentId: widget.parentMessage?.id, text: text, - attachments: _getAttachments(attachments).toList(), + attachments: widget.editMessage.attachments + .where((attachment) => attachment.type == 'giphy') + .toList() + + _getAttachments(attachments).toList(), ); StreamChat.of(context).client.updateMessage(message).then((_) { if (widget.onMessageSent != null) { @@ -589,17 +599,21 @@ class _MessageInputState extends State { void initState() { super.initState(); - _keyboardListener = KeyboardVisibilityNotification().addNewListener( - onChange: (_) { - if (_modalOn) { - _commandsOverlay?.remove(); + _keyboardListener = + KeyboardVisibilityNotification().addNewListener(onHide: () { + if (_commandsOverlay != null) { + _commandsOverlay.remove(); + } + }, onShow: () { + if (_commandsOverlay != null) { + if (_textController.text.startsWith('/')) { WidgetsBinding.instance.addPostFrameCallback((_) { _commandsOverlay = _buildOverlayEntry(); Overlay.of(context).insert(_commandsOverlay); }); } - }, - ); + } + }); if (widget.editMessage != null) { _textController = TextEditingController(text: widget.editMessage.text); @@ -614,6 +628,18 @@ class _MessageInputState extends State { url: attachment.imageUrl, uploaded: true, )); + } else if (attachment.type == 'video') { + _attachments.add(_SendingAttachment( + type: FileType.VIDEO, + url: attachment.assetUrl, + uploaded: true, + )); + } else if (attachment.type != 'giphy') { + _attachments.add(_SendingAttachment( + type: FileType.ANY, + url: attachment.assetUrl, + uploaded: true, + )); } }); } else { diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index f8b58398..1e3d7d02 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -574,74 +574,77 @@ class _MessageWidgetState extends State ), ), builder: (context) { - return Flex( - direction: Axis.vertical, - mainAxisAlignment: MainAxisAlignment.end, - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.only( - top: 16.0, - left: 16.0, - right: 16.0, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Edit message', - style: Theme.of(context).textTheme.title, - ), - Container( - height: 30, - padding: const EdgeInsets.all(2.0), - child: AspectRatio( - aspectRatio: 1, - child: RawMaterialButton( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(4), - ), - elevation: 0, - highlightElevation: 0, - focusElevation: 0, - disabledElevation: 0, - hoverElevation: 0, - onPressed: () { - Navigator.of(context).pop(); - }, - fillColor: Colors.black.withOpacity(.1), - padding: EdgeInsets.all(4), - child: Icon( - Icons.close, - size: 15, - color: Colors.black, + return StreamChannel( + channel: streamChannel.channel, + child: Flex( + direction: Axis.vertical, + mainAxisAlignment: MainAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.only( + top: 16.0, + left: 16.0, + right: 16.0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Edit message', + style: Theme.of(context).textTheme.title, + ), + Container( + height: 30, + padding: const EdgeInsets.all(2.0), + child: AspectRatio( + aspectRatio: 1, + child: RawMaterialButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + ), + elevation: 0, + highlightElevation: 0, + focusElevation: 0, + disabledElevation: 0, + hoverElevation: 0, + onPressed: () { + Navigator.of(context).pop(); + }, + fillColor: Colors.black.withOpacity(.1), + padding: EdgeInsets.all(4), + child: Icon( + Icons.close, + size: 15, + color: Colors.black, + ), ), ), ), - ), - ], + ], + ), ), - ), - Padding( - padding: EdgeInsets.only( - bottom: MediaQuery.of(context).viewInsets.bottom, + Padding( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).viewInsets.bottom, + ), + child: MessageInput( + editMessage: widget.message, + parentMessage: widget.isParent + ? StreamChannel.of(context) + .channel + .state + .messages + .firstWhere((message) => + message.id == widget.message.parentId) + : null, + onMessageSent: (_) { + Navigator.pop(context); + }, + ), ), - child: MessageInput( - editMessage: widget.message, - parentMessage: widget.isParent - ? StreamChannel.of(context) - .channel - .state - .messages - .firstWhere( - (message) => message.id == widget.message.parentId) - : null, - onMessageSent: (_) { - Navigator.pop(context); - }, - ), - ), - ], + ], + ), ); }, ); @@ -719,6 +722,7 @@ class _MessageWidgetState extends State imageUrl: attachment.thumbUrl ?? attachment.imageUrl ?? attachment.assetUrl, fit: BoxFit.cover, + placeholderFadeInDuration: Duration(milliseconds: 300), placeholder: (_, __) { return SizedBox( height: 200, From 1ec09bac639f665d490f073b5035f01c03a73690 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 3 Mar 2020 18:23:54 +0100 Subject: [PATCH 7/8] fix reaction layout --- example/android/settings_aar.gradle | 1 + lib/src/message_widget.dart | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 example/android/settings_aar.gradle diff --git a/example/android/settings_aar.gradle b/example/android/settings_aar.gradle new file mode 100644 index 00000000..e7b4def4 --- /dev/null +++ b/example/android/settings_aar.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index 1e3d7d02..b1f51ef0 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -367,7 +367,8 @@ class _MessageWidgetState extends State crossAxisAlignment: isMyMessage ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [ - streamChannel.channel.config.reactions + (streamChannel.channel.config.reactions && + nOfAttachmentWidgets == 0) ? Align( child: _buildReactions(), alignment: isMyMessage @@ -378,7 +379,9 @@ class _MessageWidgetState extends State Stack( overflow: Overflow.visible, children: [ - _buildReactionPaint(), + nOfAttachmentWidgets == 0 + ? _buildReactionPaint() + : SizedBox(), Padding( padding: EdgeInsets.only( right: isMyMessage ? 0.0 : 8.0, @@ -420,7 +423,9 @@ class _MessageWidgetState extends State ), ], ); - } else { + } + + if (nOfAttachmentWidgets > 0) { column.children.insert( 0, streamChannel.channel.config.reactions From 0f0226ca695e28494a7ffffb4e2932740f1c7835 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 4 Mar 2020 10:19:11 +0100 Subject: [PATCH 8/8] fix command layout --- example/ios/Podfile.lock | 6 + example/lib/main.dart | 14 +- lib/src/channel_preview.dart | 2 +- lib/src/message_input.dart | 244 ++++++++++++++++++----------------- lib/src/message_widget.dart | 33 +++-- 5 files changed, 159 insertions(+), 140 deletions(-) diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index e848b8ff..07bb3fa8 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -14,6 +14,8 @@ PODS: - Reachability - path_provider (0.0.1): - Flutter + - path_provider_macos (0.0.1): + - Flutter - Reachability (3.2) - screen (0.0.1): - Flutter @@ -38,6 +40,7 @@ DEPENDENCIES: - image_picker (from `.symlinks/plugins/image_picker/ios`) - keyboard_visibility (from `.symlinks/plugins/keyboard_visibility/ios`) - path_provider (from `.symlinks/plugins/path_provider/ios`) + - path_provider_macos (from `.symlinks/plugins/path_provider_macos/ios`) - screen (from `.symlinks/plugins/screen/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) - url_launcher (from `.symlinks/plugins/url_launcher/ios`) @@ -64,6 +67,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/keyboard_visibility/ios" path_provider: :path: ".symlinks/plugins/path_provider/ios" + path_provider_macos: + :path: ".symlinks/plugins/path_provider_macos/ios" screen: :path: ".symlinks/plugins/screen/ios" sqflite: @@ -87,6 +92,7 @@ SPEC CHECKSUMS: image_picker: e3eacd46b94694dde7cf2705955cece853aa1a8f keyboard_visibility: 96a24de806fe6823c3ad956c01ba2ec6d056616f path_provider: fb74bd0465e96b594bb3b5088ee4a4e7bb1f2a9d + path_provider_macos: f760a3c5b04357c380e2fddb6f9db6f3015897e0 Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 screen: abd91ca7bf3426e1cc3646d27e9b2358d6bf07b0 sqflite: 4001a31ff81d210346b500c55b17f4d6c7589dd0 diff --git a/example/lib/main.dart b/example/lib/main.dart index 15efe5e5..a28a4320 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -3,13 +3,13 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; void main() async { final client = Client( - 's2dxdhpxd94g', + 'b67pax5b2wdq', logLevel: Level.INFO, ); await client.setUser( User(id: 'falling-mountain-7'), - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNDE5MjI5YTMtODRhMC00MDZiLTkzNzEtN2NlOWE0ZTBhNjc2In0.J3SjGH4e4v7b3cg5EgWkljTxXj_HeHpCWn5ujEVv_H8', + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZmFsbGluZy1tb3VudGFpbi03In0.AKgRXHMQQMz6vJAKszXdY8zMFfsAgkoUeZHlI-Szz9E', ); runApp(MyApp(client)); @@ -38,11 +38,11 @@ class ChannelListPage extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( body: ChannelListView( -// filter: { -// 'members': { -// '\$in': [StreamChat.of(context).user.id], -// } -// }, + filter: { + 'members': { + '\$in': [StreamChat.of(context).user.id], + } + }, sort: [SortOption('last_message_at')], pagination: PaginationParams( limit: 20, diff --git a/lib/src/channel_preview.dart b/lib/src/channel_preview.dart index 92664339..8174c290 100644 --- a/lib/src/channel_preview.dart +++ b/lib/src/channel_preview.dart @@ -89,7 +89,7 @@ class ChannelPreview extends StatelessWidget { initialData: channel.state.typingEvents, builder: (context, snapshot) { final typings = snapshot.data; - final opacity = channel.state.unreadCount > .0 ? 1.0 : 0.5; + final opacity = channel.state.unreadCount > 0 ? 1.0 : 0.5; return typings.isNotEmpty ? _buildTypings(typings, context, opacity) : _buildLastMessage(context, opacity); diff --git a/lib/src/message_input.dart b/lib/src/message_input.dart index 79c9f55f..8df9670c 100644 --- a/lib/src/message_input.dart +++ b/lib/src/message_input.dart @@ -118,10 +118,6 @@ class _MessageInputState extends State { } AnimatedCrossFade _animateSendButton(BuildContext context) { - print( - '_messageIsPresent || _attachments.isNotEmpty: ${_messageIsPresent || _attachments.isNotEmpty}'); - print('_attachments.isNotEmpty: ${_attachments.isNotEmpty}'); - print('_messageIsPresent: ${_messageIsPresent}'); return AnimatedCrossFade( crossFadeState: (_messageIsPresent || _attachments.isNotEmpty) ? CrossFadeState.showFirst @@ -214,46 +210,58 @@ class _MessageInputState extends State { RenderBox renderBox = context.findRenderObject(); final size = renderBox.size; - final offset = renderBox.localToGlobal(Offset.zero); - return OverlayEntry(builder: (_) { + return OverlayEntry(builder: (context) { return Positioned( - top: offset.dy - size.height, + bottom: size.height + MediaQuery.of(context).viewInsets.bottom, left: 0, right: 0, child: Material( color: StreamChatTheme.of(context).primaryColor, - child: Flex( - mainAxisSize: MainAxisSize.min, - direction: Axis.vertical, - children: [ - ListView( - padding: const EdgeInsets.all(0), - shrinkWrap: true, - children: commands - .map((c) => ListTile( - title: Text.rich( - TextSpan( - text: '${c.name}', - style: TextStyle(fontWeight: FontWeight.bold), - children: [ - TextSpan( - text: ' ${c.args}', - style: TextStyle( - fontWeight: FontWeight.w300, + child: Container( + constraints: BoxConstraints.loose(Size.fromHeight(400)), + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + spreadRadius: -8, + blurRadius: 5.0, + offset: Offset(0, -4), + ), + ], + color: StreamChatTheme.of(context).primaryColor, + ), + child: Flex( + mainAxisSize: MainAxisSize.min, + direction: Axis.vertical, + children: [ + ListView( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + children: commands + .map((c) => ListTile( + title: Text.rich( + TextSpan( + text: '${c.name}', + style: TextStyle(fontWeight: FontWeight.bold), + children: [ + TextSpan( + text: ' ${c.args}', + style: TextStyle( + fontWeight: FontWeight.w300, + ), ), - ), - ], + ], + ), ), - ), - subtitle: Text(c.description), - onTap: () { - _setCommand(c); - }, - )) - .toList(), - ), - ], + subtitle: Text(c.description), + onTap: () { + _setCommand(c); + }, + )) + .toList(), + ), + ], + ), ), ), ); @@ -388,71 +396,7 @@ class _MessageInputState extends State { color: Colors.transparent, child: IconButton( onPressed: () { - showModalBottomSheet( - clipBehavior: Clip.hardEdge, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(32), - topRight: Radius.circular(32), - ), - ), - context: context, - builder: (_) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - title: Text( - 'Add a file', - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ), - ), - ListTile( - leading: Icon(Icons.image), - title: Text('Upload a photo'), - onTap: () { - _pickFile(FileType.IMAGE, false); - Navigator.pop(context); - }, - ), - ListTile( - leading: Icon(Icons.video_library), - title: Text('Upload a video'), - onTap: () { - _pickFile(FileType.VIDEO, false); - Navigator.pop(context); - }, - ), - ListTile( - leading: Icon(Icons.camera_alt), - title: Text('Photo from camera'), - onTap: () { - ImagePicker.pickImage(source: ImageSource.camera); - _pickFile(FileType.IMAGE, true); - Navigator.pop(context); - }, - ), - ListTile( - leading: Icon(Icons.videocam), - title: Text('Video from camera'), - onTap: () { - _pickFile(FileType.VIDEO, true); - Navigator.pop(context); - }, - ), - ListTile( - leading: Icon(Icons.insert_drive_file), - title: Text('Upload a file'), - onTap: () { - _pickFile(FileType.ANY, false); - Navigator.pop(context); - }, - ), - ], - ); - }); + _showAttachmentModal(); }, icon: Icon( Icons.add_circle_outline, @@ -461,6 +405,74 @@ class _MessageInputState extends State { ); } + void _showAttachmentModal() { + showModalBottomSheet( + clipBehavior: Clip.hardEdge, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(32), + topRight: Radius.circular(32), + ), + ), + context: context, + builder: (_) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + title: Text( + 'Add a file', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + ListTile( + leading: Icon(Icons.image), + title: Text('Upload a photo'), + onTap: () { + _pickFile(FileType.IMAGE, false); + Navigator.pop(context); + }, + ), + ListTile( + leading: Icon(Icons.video_library), + title: Text('Upload a video'), + onTap: () { + _pickFile(FileType.VIDEO, false); + Navigator.pop(context); + }, + ), + ListTile( + leading: Icon(Icons.camera_alt), + title: Text('Photo from camera'), + onTap: () { + ImagePicker.pickImage(source: ImageSource.camera); + _pickFile(FileType.IMAGE, true); + Navigator.pop(context); + }, + ), + ListTile( + leading: Icon(Icons.videocam), + title: Text('Video from camera'), + onTap: () { + _pickFile(FileType.VIDEO, true); + Navigator.pop(context); + }, + ), + ListTile( + leading: Icon(Icons.insert_drive_file), + title: Text('Upload a file'), + onTap: () { + _pickFile(FileType.ANY, false); + Navigator.pop(context); + }, + ), + ], + ); + }); + } + void _pickFile(FileType type, bool camera) async { File file; @@ -599,21 +611,23 @@ class _MessageInputState extends State { void initState() { super.initState(); - _keyboardListener = - KeyboardVisibilityNotification().addNewListener(onHide: () { - if (_commandsOverlay != null) { - _commandsOverlay.remove(); - } - }, onShow: () { - if (_commandsOverlay != null) { - if (_textController.text.startsWith('/')) { - WidgetsBinding.instance.addPostFrameCallback((_) { - _commandsOverlay = _buildOverlayEntry(); - Overlay.of(context).insert(_commandsOverlay); - }); + _keyboardListener = KeyboardVisibilityNotification().addNewListener( + onHide: () { + if (_commandsOverlay != null) { + _commandsOverlay.remove(); } - } - }); + }, + onShow: () { + if (_commandsOverlay != null) { + if (_textController.text.startsWith('/')) { + WidgetsBinding.instance.addPostFrameCallback((_) { + _commandsOverlay = _buildOverlayEntry(); + Overlay.of(context).insert(_commandsOverlay); + }); + } + } + }, + ); if (widget.editMessage != null) { _textController = TextEditingController(text: widget.editMessage.text); diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index b1f51ef0..7e81cd51 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -687,26 +687,25 @@ class _MessageWidgetState extends State } Row _buildReactionRow() { - final List children = - widget.message.reactionCounts.keys.map((reactionType) { - return Text( - reactionToEmoji[reactionType] ?? '?', - ) as Widget; - }).toList(); - - children.add(Padding( - padding: const EdgeInsets.only(left: 4.0), - child: Text( - widget.message.reactionCounts.values - .fold(0, (t, v) => v + t) - .toString(), - style: TextStyle(color: Colors.white), - ), - )); return Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, - children: children, + children: [ + ...widget.message.reactionCounts.keys.map((reactionType) { + return Text( + reactionToEmoji[reactionType] ?? '?', + ); + }), + Padding( + padding: const EdgeInsets.only(left: 4.0), + child: Text( + widget.message.reactionCounts.values + .fold(0, (t, v) => v + t) + .toString(), + style: TextStyle(color: Colors.white), + ), + ), + ], ); }