From b93d616c86ec15b6c49eea57421ff2209c1593fe Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Fri, 8 Jan 2021 22:00:54 +0530 Subject: [PATCH 01/19] feat: Added message flagging --- lib/src/message_actions_modal.dart | 285 ++++++++++++++++++++++++----- lib/src/message_list_view.dart | 1 + lib/src/message_widget.dart | 3 + 3 files changed, 239 insertions(+), 50 deletions(-) diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index c7ff56e0..8390ce97 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -1,5 +1,6 @@ import 'dart:ui'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -13,7 +14,7 @@ import 'message_widget.dart'; import 'stream_chat.dart'; import 'stream_chat_theme.dart'; -class MessageActionsModal extends StatelessWidget { +class MessageActionsModal extends StatefulWidget { final Widget Function(BuildContext, Message) editMessageInputBuilder; final void Function(Message) onThreadReplyTap; final void Function(Message) onReplyTap; @@ -26,6 +27,7 @@ class MessageActionsModal extends StatelessWidget { final bool showResendMessage; final bool showReply; final bool showThreadReply; + final bool showFlagButton; final bool reverse; final ShapeBorder messageShape; final DisplayWidget showUserAvatar; @@ -43,22 +45,38 @@ class MessageActionsModal extends StatelessWidget { this.showReply = true, this.showResendMessage = true, this.showThreadReply = true, + this.showFlagButton = true, this.showUserAvatar = DisplayWidget.show, this.editMessageInputBuilder, this.messageShape, this.reverse = false, }) : super(key: key); + @override + _MessageActionsModalState createState() => _MessageActionsModalState(); +} + +class _MessageActionsModalState extends State { + bool showConfirmFlagModal = false; + @override Widget build(BuildContext context) { + if (showConfirmFlagModal) { + return _showFlagDialog(); + } else { + return _showMessageOptionsModal(); + } + } + + Widget _showMessageOptionsModal() { final size = MediaQuery.of(context).size; final user = StreamChat.of(context).user; final roughMaxSize = 2 * size.width / 3; - var messageTextLength = message.text.length; - if (message.quotedMessage != null) { - var quotedMessageLength = message.quotedMessage.text.length + 40; - if (message.quotedMessage.attachments?.isNotEmpty == true) { + var messageTextLength = widget.message.text.length; + if (widget.message.quotedMessage != null) { + var quotedMessageLength = widget.message.quotedMessage.text.length + 40; + if (widget.message.quotedMessage.attachments?.isNotEmpty == true) { quotedMessageLength += 40; } if (quotedMessageLength > messageTextLength) { @@ -66,8 +84,8 @@ class MessageActionsModal extends StatelessWidget { } } final roughSentenceSize = - messageTextLength * messageTheme.messageText.fontSize * 1.2; - final divFactor = message.attachments?.isNotEmpty == true + messageTextLength * widget.messageTheme.messageText.fontSize * 1.2; + final divFactor = widget.message.attachments?.isNotEmpty == true ? 1 : (roughSentenceSize == 0 ? 1 : (roughSentenceSize / roughMaxSize)); @@ -95,18 +113,18 @@ class MessageActionsModal extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - if (showReactions && - (message.status == MessageSendingStatus.SENT || - message.status == null)) + if (widget.showReactions && + (widget.message.status == MessageSendingStatus.SENT || + widget.message.status == null)) Align( alignment: Alignment( - user.id == message.user.id + user.id == widget.message.user.id ? (divFactor > 1.0 ? 0.0 : (1.0 - divFactor)) : (divFactor > 1.0 ? 0.0 : -(1.0 - divFactor)), 0.0), child: ReactionPicker( - message: message, - messageTheme: messageTheme, + message: widget.message, + messageTheme: widget.messageTheme, ), ), TweenAnimationBuilder( @@ -118,27 +136,28 @@ class MessageActionsModal extends StatelessWidget { child: IgnorePointer( child: MessageWidget( key: Key('MessageWidget'), - reverse: reverse, - message: message.copyWith( - text: message.text.length > 200 - ? '${message.text.substring(0, 200)}...' - : message.text, + reverse: widget.reverse, + message: widget.message.copyWith( + text: widget.message.text.length > 200 + ? '${widget.message.text.substring(0, 200)}...' + : widget.message.text, ), - messageTheme: messageTheme, + messageTheme: widget.messageTheme, showReactions: false, showUsername: false, showThreadReplyIndicator: false, showReplyIndicator: false, - showUserAvatar: showUserAvatar, + showUserAvatar: widget.showUserAvatar, showTimestamp: false, translateUserAvatar: false, - showReactionPickerIndicator: showReactions && - (message.status == - MessageSendingStatus.SENT || - message.status == null), + showReactionPickerIndicator: + widget.showReactions && + (widget.message.status == + MessageSendingStatus.SENT || + widget.message.status == null), showInChannelIndicator: false, showSendingIndicator: DisplayWidget.gone, - shape: messageShape, + shape: widget.messageShape, ), ), ); @@ -172,26 +191,28 @@ class MessageActionsModal extends StatelessWidget { children: ListTile.divideTiles( context: context, tiles: [ - if (showReply && - (message.status == + if (widget.showReply && + (widget.message.status == MessageSendingStatus.SENT || - message.status == null) && - message.parentId == null) + widget.message.status == null) && + widget.message.parentId == null) _buildReplyButton(context), - if (showThreadReply && - (message.status == + if (widget.showThreadReply && + (widget.message.status == MessageSendingStatus.SENT || - message.status == null) && - message.parentId == null) + widget.message.status == null) && + widget.message.parentId == null) _buildThreadReplyButton(context), - if (showResendMessage) + if (widget.showResendMessage) _buildResendMessage(context), - if (showEditMessage) + if (widget.showEditMessage) _buildEditMessage(context), - if (showDeleteMessage) + if (widget.showDeleteMessage) _buildDeleteButton(context), - if (showCopyMessage) + if (widget.showCopyMessage) _buildCopyButton(context), + if (widget.showCopyMessage) + _buildFlagButton(context), ], ).toList(), ), @@ -209,6 +230,150 @@ class MessageActionsModal extends StatelessWidget { ); } + Widget _showFlagDialog() { + return GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () => Navigator.maybePop(context), + child: Stack( + children: [ + Positioned.fill( + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaX: 10, + sigmaY: 10, + ), + child: Container( + color: StreamChatTheme.of(context).colorTheme.overlay, + ), + ), + ), + Center( + child: Opacity( + opacity: 0.9, + child: Material( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16.0), + ), + clipBehavior: Clip.antiAlias, + child: Container( + width: 270.0, + height: 156.0, + alignment: Alignment.center, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + height: 19.0, + ), + Text( + 'Flag Message', + style: StreamChatTheme.of(context) + .textTheme + .bodyBold + .copyWith(fontSize: 17.0), + ), + SizedBox( + height: 4.0, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Text( + 'Do you want to send a copy of this \nmessage to a moderator for further \ninvestigation?', + style: TextStyle(fontSize: 13.5), + textAlign: TextAlign.center, + ), + ), + SizedBox( + height: 21.0, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + child: Center( + child: InkWell( + child: Container( + height: 44.0, + alignment: Alignment.center, + child: Text( + 'Cancel', + style: StreamChatTheme.of(context) + .textTheme + .body + .copyWith( + fontSize: 17.0, + color: StreamChatTheme.of(context) + .colorTheme + .accentBlue), + ), + decoration: BoxDecoration( + border: Border.symmetric( + horizontal: BorderSide( + color: StreamChatTheme.of(context) + .colorTheme + .grey), + ), + ), + ), + onTap: () { + setState(() { + showConfirmFlagModal = false; + }); + }, + ), + ), + ), + Expanded( + child: Center( + child: InkWell( + child: Container( + height: 44.0, + alignment: Alignment.center, + child: Text( + 'Flag', + style: StreamChatTheme.of(context) + .textTheme + .body + .copyWith( + fontWeight: FontWeight.bold, + fontSize: 17.0, + color: StreamChatTheme.of(context) + .colorTheme + .accentBlue), + ), + decoration: BoxDecoration( + border: Border.fromBorderSide( + BorderSide( + color: StreamChatTheme.of(context) + .colorTheme + .grey), + ), + ), + ), + onTap: () { + setState(() { + showConfirmFlagModal = false; + }); + final client = StreamChat.of(context).client; + client.flagMessage(widget.message.id); + }, + ), + ), + ), + ], + ), + ], + ), + ), + ), + ), + ), + ], + ), + ); + } + Widget _buildReplyButton(BuildContext context) { return ListTile( title: Text( @@ -220,15 +385,34 @@ class MessageActionsModal extends StatelessWidget { ), onTap: () { Navigator.pop(context); - if (onReplyTap != null) { - onReplyTap(message); + if (widget.onReplyTap != null) { + widget.onReplyTap(widget.message); } }, ); } + Widget _buildFlagButton(BuildContext context) { + return ListTile( + title: Text( + 'Flag', + style: Theme.of(context).textTheme.headline6, + ), + leading: StreamSvgIcon.flag( + color: StreamChatTheme.of(context).primaryIconTheme.color, + size: 24.0, + ), + onTap: () { + setState(() { + showConfirmFlagModal = true; + }); + }, + ); + } + Widget _buildDeleteButton(BuildContext context) { - final isDeleteFailed = message.status == MessageSendingStatus.FAILED_DELETE; + final isDeleteFailed = + widget.message.status == MessageSendingStatus.FAILED_DELETE; return ListTile( title: Text( isDeleteFailed ? 'Retry deleting message' : 'Delete message', @@ -241,7 +425,7 @@ class MessageActionsModal extends StatelessWidget { onTap: () { Navigator.pop(context); StreamChat.of(context).client.deleteMessage( - message, + widget.message, StreamChannel.of(context).channel.cid, ); }, @@ -258,7 +442,7 @@ class MessageActionsModal extends StatelessWidget { color: StreamChatTheme.of(context).primaryIconTheme.color, ), onTap: () async { - await Clipboard.setData(ClipboardData(text: message.text)); + await Clipboard.setData(ClipboardData(text: widget.message.text)); Navigator.pop(context); }, ); @@ -281,7 +465,8 @@ class MessageActionsModal extends StatelessWidget { } Widget _buildResendMessage(BuildContext context) { - final isUpdateFailed = message.status == MessageSendingStatus.FAILED_UPDATE; + final isUpdateFailed = + widget.message.status == MessageSendingStatus.FAILED_UPDATE; return ListTile( title: Text( isUpdateFailed ? 'Resend edited message' : 'Resend', @@ -295,9 +480,9 @@ class MessageActionsModal extends StatelessWidget { final client = StreamChat.of(context).client; final channel = StreamChannel.of(context).channel; if (isUpdateFailed) { - client.updateMessage(message, channel.cid); + client.updateMessage(widget.message, channel.cid); } else { - channel.sendMessage(message); + channel.sendMessage(widget.message); } }, ); @@ -354,10 +539,10 @@ class MessageActionsModal extends StatelessWidget { padding: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom, ), - child: editMessageInputBuilder != null - ? editMessageInputBuilder(context, message) + child: widget.editMessageInputBuilder != null + ? widget.editMessageInputBuilder(context, widget.message) : MessageInput( - editMessage: message, + editMessage: widget.message, preMessageSending: (m) { FocusScope.of(context).unfocus(); Navigator.pop(context); @@ -383,8 +568,8 @@ class MessageActionsModal extends StatelessWidget { ), onTap: () { Navigator.pop(context); - if (onThreadReplyTap != null) { - onThreadReplyTap(message); + if (widget.onThreadReplyTap != null) { + widget.onThreadReplyTap(widget.message); } }, ); diff --git a/lib/src/message_list_view.dart b/lib/src/message_list_view.dart index 9f4cbc0d..21aa9ed1 100644 --- a/lib/src/message_list_view.dart +++ b/lib/src/message_list_view.dart @@ -800,6 +800,7 @@ class _MessageListViewState extends State { showTimestamp: !isNextUser || readList?.isNotEmpty == true, showEditMessage: isMyMessage, showDeleteMessage: isMyMessage, + showFlagButton: !isMyMessage, borderSide: isMyMessage ? BorderSide.none : null, onThreadTap: _onThreadTap, onReplyTap: widget.onReplyTap, diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index 1200f38d..c687f36f 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -133,6 +133,7 @@ class MessageWidget extends StatefulWidget { final bool showTimestamp; final bool showDeleteMessage; final bool showEditMessage; + final bool showFlagButton; final Map attachmentBuilders; /// Center user avatar with bottom of the message @@ -168,6 +169,7 @@ class MessageWidget extends StatefulWidget { this.showReactions = true, this.showDeleteMessage = true, this.showEditMessage = true, + this.showFlagButton = true, this.onUserAvatarTap, this.onLinkTap, this.onMessageActions, @@ -718,6 +720,7 @@ class _MessageWidgetState extends State { widget.onReplyTap != null, showThreadReply: widget.showThreadReplyIndicator && widget.onThreadTap != null, + showFlagButton: widget.showFlagButton, ), ); }); From bd73a8b6db86b66051b4ee85e6bd51fa494c14c8 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 11 Jan 2021 14:37:53 +0530 Subject: [PATCH 02/19] feat: Added end dialog and animations --- lib/src/message_actions_modal.dart | 450 +++++++++++++++++++---------- lib/src/message_widget.dart | 5 +- 2 files changed, 309 insertions(+), 146 deletions(-) diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index 8390ce97..cc29527e 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -14,6 +14,12 @@ import 'message_widget.dart'; import 'stream_chat.dart'; import 'stream_chat_theme.dart'; +enum ActionsModalState { + modal, + confirmDialog, + dismissMessage, +} + class MessageActionsModal extends StatefulWidget { final Widget Function(BuildContext, Message) editMessageInputBuilder; final void Function(Message) onThreadReplyTap; @@ -57,14 +63,20 @@ class MessageActionsModal extends StatefulWidget { } class _MessageActionsModalState extends State { - bool showConfirmFlagModal = false; + ActionsModalState state = ActionsModalState.modal; @override Widget build(BuildContext context) { - if (showConfirmFlagModal) { - return _showFlagDialog(); - } else { - return _showMessageOptionsModal(); + switch (state) { + case ActionsModalState.modal: + return _showMessageOptionsModal(); + break; + case ActionsModalState.confirmDialog: + return _showFlagDialog(); + break; + case ActionsModalState.dismissMessage: + return _showDismissAlert(); + break; } } @@ -211,7 +223,7 @@ class _MessageActionsModalState extends State { _buildDeleteButton(context), if (widget.showCopyMessage) _buildCopyButton(context), - if (widget.showCopyMessage) + if (widget.showFlagButton) _buildFlagButton(context), ], ).toList(), @@ -231,147 +243,295 @@ class _MessageActionsModalState extends State { } Widget _showFlagDialog() { - return GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () => Navigator.maybePop(context), - child: Stack( - children: [ - Positioned.fill( - child: BackdropFilter( - filter: ImageFilter.blur( - sigmaX: 10, - sigmaY: 10, - ), - child: Container( - color: StreamChatTheme.of(context).colorTheme.overlay, - ), - ), - ), - Center( - child: Opacity( - opacity: 0.9, - child: Material( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - ), - clipBehavior: Clip.antiAlias, - child: Container( - width: 270.0, - height: 156.0, - alignment: Alignment.center, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox( - height: 19.0, - ), - Text( - 'Flag Message', - style: StreamChatTheme.of(context) - .textTheme - .bodyBold - .copyWith(fontSize: 17.0), - ), - SizedBox( - height: 4.0, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Text( - 'Do you want to send a copy of this \nmessage to a moderator for further \ninvestigation?', - style: TextStyle(fontSize: 13.5), - textAlign: TextAlign.center, - ), - ), - SizedBox( - height: 21.0, - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Expanded( - child: Center( - child: InkWell( - child: Container( - height: 44.0, - alignment: Alignment.center, - child: Text( - 'Cancel', - style: StreamChatTheme.of(context) - .textTheme - .body - .copyWith( - fontSize: 17.0, - color: StreamChatTheme.of(context) - .colorTheme - .accentBlue), - ), - decoration: BoxDecoration( - border: Border.symmetric( - horizontal: BorderSide( - color: StreamChatTheme.of(context) - .colorTheme - .grey), - ), - ), - ), - onTap: () { - setState(() { - showConfirmFlagModal = false; - }); - }, - ), - ), - ), - Expanded( - child: Center( - child: InkWell( - child: Container( - height: 44.0, - alignment: Alignment.center, - child: Text( - 'Flag', - style: StreamChatTheme.of(context) - .textTheme - .body - .copyWith( - fontWeight: FontWeight.bold, - fontSize: 17.0, - color: StreamChatTheme.of(context) - .colorTheme - .accentBlue), - ), - decoration: BoxDecoration( - border: Border.fromBorderSide( - BorderSide( - color: StreamChatTheme.of(context) - .colorTheme - .grey), - ), - ), - ), - onTap: () { - setState(() { - showConfirmFlagModal = false; - }); - final client = StreamChat.of(context).client; - client.flagMessage(widget.message.id); - }, - ), - ), - ), - ], - ), - ], + return TweenAnimationBuilder( + tween: Tween(begin: 0.0, end: 1.0), + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + builder: (context, value, _) { + return GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () => Navigator.maybePop(context), + child: Stack( + children: [ + Positioned.fill( + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaX: 10, + sigmaY: 10, + ), + child: Container( + color: StreamChatTheme.of(context).colorTheme.overlay, + ), ), ), - ), + Transform.scale( + scale: value, + child: Center( + child: Opacity( + opacity: 0.9, + child: Material( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16.0), + ), + clipBehavior: Clip.antiAlias, + child: Container( + width: 270.0, + height: 156.0, + alignment: Alignment.center, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + height: 19.0, + ), + Text( + 'Flag Message', + style: StreamChatTheme.of(context) + .textTheme + .bodyBold + .copyWith(fontSize: 17.0), + ), + SizedBox( + height: 4.0, + ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0), + child: Text( + 'Do you want to send a copy of this \nmessage to a moderator for further \ninvestigation?', + style: TextStyle(fontSize: 13.5), + textAlign: TextAlign.center, + ), + ), + SizedBox( + height: 21.0, + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + child: Center( + child: InkWell( + child: Container( + height: 44.0, + alignment: Alignment.center, + child: Text( + 'Cancel', + style: StreamChatTheme.of(context) + .textTheme + .body + .copyWith( + fontSize: 17.0, + color: StreamChatTheme.of( + context) + .colorTheme + .accentBlue), + ), + decoration: BoxDecoration( + border: Border( + top: BorderSide( + color: StreamChatTheme.of( + context) + .colorTheme + .grey + .withOpacity(0.8)), + right: BorderSide( + color: StreamChatTheme.of( + context) + .colorTheme + .grey + .withOpacity(0.8)), + ), + ), + ), + onTap: () { + setState(() { + state = ActionsModalState.modal; + }); + }, + ), + ), + ), + Expanded( + child: Center( + child: InkWell( + child: Container( + height: 44.0, + alignment: Alignment.center, + child: Text( + 'Flag', + style: StreamChatTheme.of(context) + .textTheme + .body + .copyWith( + fontWeight: FontWeight.bold, + fontSize: 17.0, + color: StreamChatTheme.of( + context) + .colorTheme + .accentBlue), + ), + decoration: BoxDecoration( + border: Border( + top: BorderSide( + color: StreamChatTheme.of( + context) + .colorTheme + .grey + .withOpacity(0.8)), + ), + ), + ), + onTap: () { + setState(() { + state = ActionsModalState + .dismissMessage; + }); + final client = + StreamChat.of(context).client; + client.flagMessage(widget.message.id); + }, + ), + ), + ), + ], + ), + ], + ), + ), + ), + ), + ), + ), + ], ), - ), - ], - ), - ); + ); + }); + } + + Widget _showDismissAlert() { + return TweenAnimationBuilder( + key: GlobalKey(), + tween: Tween(begin: 0.0, end: 1.0), + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + builder: (context, value, _) { + return GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () => Navigator.maybePop(context), + child: Stack( + children: [ + Positioned.fill( + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaX: 10, + sigmaY: 10, + ), + child: Container( + color: StreamChatTheme.of(context).colorTheme.overlay, + ), + ), + ), + Transform.scale( + scale: value, + child: Center( + child: Opacity( + opacity: 0.9, + child: Material( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16.0), + ), + clipBehavior: Clip.antiAlias, + child: Container( + width: 270.0, + height: 156.0, + alignment: Alignment.center, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + height: 19.0, + ), + Text( + 'Message Flagged', + style: StreamChatTheme.of(context) + .textTheme + .bodyBold + .copyWith(fontSize: 17.0), + ), + SizedBox( + height: 4.0, + ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0), + child: Text( + 'This message has been reported to a\nmoderator.', + style: TextStyle(fontSize: 13.5), + textAlign: TextAlign.center, + ), + ), + SizedBox( + height: 21.0, + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + child: Center( + child: InkWell( + child: Container( + height: 44.0, + alignment: Alignment.center, + child: Text( + 'Dismiss', + style: StreamChatTheme.of(context) + .textTheme + .body + .copyWith( + fontWeight: FontWeight.bold, + fontSize: 17.0, + color: StreamChatTheme.of( + context) + .colorTheme + .accentBlue), + ), + decoration: BoxDecoration( + border: Border( + top: BorderSide( + color: StreamChatTheme.of( + context) + .colorTheme + .grey + .withOpacity(0.8)), + ), + ), + ), + onTap: () { + setState(() { + state = ActionsModalState.modal; + }); + }, + ), + ), + ), + ], + ), + ], + ), + ), + ), + ), + ), + ), + ], + ), + ); + }); } Widget _buildReplyButton(BuildContext context) { @@ -404,7 +564,7 @@ class _MessageActionsModalState extends State { ), onTap: () { setState(() { - showConfirmFlagModal = true; + state = ActionsModalState.confirmDialog; }); }, ); diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index c687f36f..e26524f8 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -686,6 +686,9 @@ class _MessageWidgetState extends State { void _showMessageActionModalBottomSheet(BuildContext context) { final channel = StreamChannel.of(context).channel; + final isMyMessage = + widget.message.user.id == StreamChat.of(context).user.id; + showDialog( context: context, barrierColor: StreamChatTheme.of(context).colorTheme.overlay, @@ -720,7 +723,7 @@ class _MessageWidgetState extends State { widget.onReplyTap != null, showThreadReply: widget.showThreadReplyIndicator && widget.onThreadTap != null, - showFlagButton: widget.showFlagButton, + showFlagButton: widget.showFlagButton && !isMyMessage, ), ); }); From aca3918786f6e77626bacae8fa2443d550b95ec8 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 11 Jan 2021 14:50:31 +0530 Subject: [PATCH 03/19] fix: Fixed dialog styling --- lib/src/message_actions_modal.dart | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index cc29527e..486bc83d 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -268,8 +268,12 @@ class _MessageActionsModalState extends State { scale: value, child: Center( child: Opacity( - opacity: 0.9, + opacity: 0.8, child: Material( + color: StreamChatTheme.of(context) + .colorTheme + .whiteSnow + .withOpacity(0.8), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16.0), ), @@ -332,12 +336,14 @@ class _MessageActionsModalState extends State { decoration: BoxDecoration( border: Border( top: BorderSide( + width: 0.5, color: StreamChatTheme.of( context) .colorTheme .grey .withOpacity(0.8)), right: BorderSide( + width: 0.5, color: StreamChatTheme.of( context) .colorTheme @@ -376,6 +382,7 @@ class _MessageActionsModalState extends State { decoration: BoxDecoration( border: Border( top: BorderSide( + width: 0.5, color: StreamChatTheme.of( context) .colorTheme @@ -438,8 +445,12 @@ class _MessageActionsModalState extends State { scale: value, child: Center( child: Opacity( - opacity: 0.9, + opacity: 0.8, child: Material( + color: StreamChatTheme.of(context) + .colorTheme + .whiteSnow + .withOpacity(0.8), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16.0), ), @@ -503,6 +514,7 @@ class _MessageActionsModalState extends State { decoration: BoxDecoration( border: Border( top: BorderSide( + width: 0.5, color: StreamChatTheme.of( context) .colorTheme From 43006b61848086d02dd3972c8352e4b229b86019 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 11 Jan 2021 15:13:01 +0530 Subject: [PATCH 04/19] fix: merge and tile styling change --- lib/src/message_actions_modal.dart | 54 +++++++++++++++++------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index 37c0aeae..0953c789 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -123,7 +123,7 @@ class _MessageActionsModalState extends State { child: Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), child: Column( - crossAxisAlignment: reverse + crossAxisAlignment: widget.reverse ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [ @@ -185,13 +185,13 @@ class _MessageActionsModalState extends State { transform: Matrix4.identity() ..scale(val) ..rotateZ(-1.0 + val), - alignment: reverse + alignment: widget.reverse ? Alignment.topRight : Alignment.topLeft, child: Padding( padding: EdgeInsets.only( - right: reverse ? 16 : 0, - left: reverse ? 0 : 48, + right: widget.reverse ? 16 : 0, + left: widget.reverse ? 0 : 48, ), child: SizedBox( width: MediaQuery.of(context).size.width * 0.75, @@ -212,27 +212,29 @@ class _MessageActionsModalState extends State { .greyWhisper, context: context, tiles: [ - if (showReply && - (message.status == + if (widget.showReply && + (widget.message.status == MessageSendingStatus.SENT || - message.status == null) && - message.parentId == null) + widget.message.status == + null) && + widget.message.parentId == null) _buildReplyButton(context), - if (showThreadReply && - (message.status == + if (widget.showThreadReply && + (widget.message.status == MessageSendingStatus.SENT || - message.status == null) && - message.parentId == null) + widget.message.status == + null) && + widget.message.parentId == null) _buildThreadReplyButton(context), - if (showResendMessage) + if (widget.showResendMessage) _buildResendMessage(context), - if (showEditMessage) + if (widget.showEditMessage) _buildEditMessage(context), - if (showCopyMessage) + if (widget.showCopyMessage) _buildCopyButton(context), if (widget.showFlagButton) _buildFlagButton(context), - if (showDeleteMessage) + if (widget.showDeleteMessage) _buildDeleteButton(context), ], ).toList(), @@ -570,13 +572,19 @@ class _MessageActionsModalState extends State { Widget _buildFlagButton(BuildContext context) { return ListTile( - title: Text( - 'Flag', - style: Theme.of(context).textTheme.headline6, - ), - leading: StreamSvgIcon.flag( - color: StreamChatTheme.of(context).primaryIconTheme.color, - size: 24.0, + dense: true, + title: Row( + children: [ + StreamSvgIcon.flag( + color: StreamChatTheme.of(context).primaryIconTheme.color, + size: 23.0, + ), + const SizedBox(width: 16), + Text( + 'Flag', + style: StreamChatTheme.of(context).textTheme.headline, + ), + ], ), onTap: () { setState(() { From 2879b491fff44894b4525f78fc9ae0b6863d310f Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 11 Jan 2021 16:20:42 +0530 Subject: [PATCH 05/19] fix: Added correct icon in modal --- example/ios/Podfile.lock | 2 +- lib/src/message_actions_modal.dart | 3 +-- lib/src/stream_svg_icon.dart | 12 ++++++++++++ lib/svgs/icon_flag.svg | 3 +++ 4 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 lib/svgs/icon_flag.svg diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index f266b656..af57742c 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -296,4 +296,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: eb001256612a59f8f9e4d083ad8b9671e69dd184 -COCOAPODS: 1.10.0 +COCOAPODS: 1.8.3 diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index 0953c789..5c2f16bb 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -575,9 +575,8 @@ class _MessageActionsModalState extends State { dense: true, title: Row( children: [ - StreamSvgIcon.flag( + StreamSvgIcon.icon_flag( color: StreamChatTheme.of(context).primaryIconTheme.color, - size: 23.0, ), const SizedBox(width: 16), Text( diff --git a/lib/src/stream_svg_icon.dart b/lib/src/stream_svg_icon.dart index 3e8cfae2..e8af3436 100644 --- a/lib/src/stream_svg_icon.dart +++ b/lib/src/stream_svg_icon.dart @@ -877,4 +877,16 @@ class StreamSvgIcon extends StatelessWidget { height: size, ); } + + factory StreamSvgIcon.icon_flag({ + double size, + Color color, + }) { + return StreamSvgIcon( + assetName: 'icon_flag.svg', + color: color, + width: size, + height: size, + ); + } } diff --git a/lib/svgs/icon_flag.svg b/lib/svgs/icon_flag.svg new file mode 100644 index 00000000..0f382e94 --- /dev/null +++ b/lib/svgs/icon_flag.svg @@ -0,0 +1,3 @@ + + + From 0eb482e96669165d3ecd79aaf405eb160f58be02 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 11 Jan 2021 16:21:15 +0530 Subject: [PATCH 06/19] fix: Added correct icon in modal --- example/ios/Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index af57742c..f266b656 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -296,4 +296,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: eb001256612a59f8f9e4d083ad8b9671e69dd184 -COCOAPODS: 1.8.3 +COCOAPODS: 1.10.0 From 7b32379d07e5b4f9705216cf89381d71680f0dda Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 11 Jan 2021 17:20:29 +0530 Subject: [PATCH 07/19] fix: Removed unnecessary check --- lib/src/message_widget.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index 38d2f1e2..ebf57c94 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -694,8 +694,6 @@ class _MessageWidgetState extends State { void _showMessageActionModalBottomSheet(BuildContext context) { final channel = StreamChannel.of(context).channel; - final isMyMessage = - widget.message.user.id == StreamChat.of(context).user.id; showDialog( context: context, @@ -731,7 +729,7 @@ class _MessageWidgetState extends State { widget.onReplyTap != null, showThreadReply: widget.showThreadReplyIndicator && widget.onThreadTap != null, - showFlagButton: widget.showFlagButton && !isMyMessage, + showFlagButton: widget.showFlagButton, ), ); }); From c987066329c740aa03aa08fbbffa379592a3d973 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 11 Jan 2021 21:22:32 +0530 Subject: [PATCH 08/19] fix: Added modals instead --- lib/src/message_actions_modal.dart | 444 ++++++++++------------------- lib/src/utils.dart | 73 ++++- 2 files changed, 216 insertions(+), 301 deletions(-) diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index 5c2f16bb..e4db71f5 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -1,4 +1,3 @@ -import 'dart:math'; import 'dart:ui'; import 'package:flutter/cupertino.dart'; @@ -9,6 +8,7 @@ import 'package:stream_chat/stream_chat.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_svg_icon.dart'; +import 'package:stream_chat_flutter/src/utils.dart'; import 'message_input.dart'; import 'message_widget.dart'; @@ -64,21 +64,9 @@ class MessageActionsModal extends StatefulWidget { } class _MessageActionsModalState extends State { - ActionsModalState state = ActionsModalState.modal; - @override Widget build(BuildContext context) { - switch (state) { - case ActionsModalState.modal: - return _showMessageOptionsModal(); - break; - case ActionsModalState.confirmDialog: - return _showFlagDialog(); - break; - case ActionsModalState.dismissMessage: - return _showDismissAlert(); - break; - } + return _showMessageOptionsModal(); } Widget _showMessageOptionsModal() { @@ -254,296 +242,154 @@ class _MessageActionsModalState extends State { ); } - Widget _showFlagDialog() { - return TweenAnimationBuilder( - tween: Tween(begin: 0.0, end: 1.0), - duration: Duration(milliseconds: 300), - curve: Curves.easeInOut, - builder: (context, value, _) { - return GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () => Navigator.maybePop(context), - child: Stack( - children: [ - Positioned.fill( - child: BackdropFilter( - filter: ImageFilter.blur( - sigmaX: 10, - sigmaY: 10, - ), - child: Container( - color: StreamChatTheme.of(context).colorTheme.overlay, - ), - ), - ), - Transform.scale( - scale: value, - child: Center( - child: Opacity( - opacity: 0.9, - child: Material( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - ), - clipBehavior: Clip.antiAlias, - child: Container( - width: 270.0, - height: 156.0, - alignment: Alignment.center, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox( - height: 19.0, - ), - Text( - 'Flag Message', - style: StreamChatTheme.of(context) - .textTheme - .bodyBold - .copyWith(fontSize: 17.0), - ), - SizedBox( - height: 4.0, - ), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0), - child: Text( - 'Do you want to send a copy of this \nmessage to a moderator for further \ninvestigation?', - style: TextStyle(fontSize: 13.5), - textAlign: TextAlign.center, - ), - ), - SizedBox( - height: 21.0, - ), - Row( - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, - children: [ - Expanded( - child: Center( - child: InkWell( - child: Container( - height: 44.0, - alignment: Alignment.center, - child: Text( - 'Cancel', - style: StreamChatTheme.of(context) - .textTheme - .body - .copyWith( - fontSize: 17.0, - color: StreamChatTheme.of( - context) - .colorTheme - .accentBlue), - ), - decoration: BoxDecoration( - border: Border( - top: BorderSide( - color: StreamChatTheme.of( - context) - .colorTheme - .grey - .withOpacity(0.8)), - right: BorderSide( - color: StreamChatTheme.of( - context) - .colorTheme - .grey - .withOpacity(0.8)), - ), - ), - ), - onTap: () { - setState(() { - state = ActionsModalState.modal; - }); - }, - ), - ), - ), - Expanded( - child: Center( - child: InkWell( - child: Container( - height: 44.0, - alignment: Alignment.center, - child: Text( - 'Flag', - style: StreamChatTheme.of(context) - .textTheme - .body - .copyWith( - fontWeight: FontWeight.bold, - fontSize: 17.0, - color: StreamChatTheme.of( - context) - .colorTheme - .accentBlue), - ), - decoration: BoxDecoration( - border: Border( - top: BorderSide( - color: StreamChatTheme.of( - context) - .colorTheme - .grey - .withOpacity(0.8)), - ), - ), - ), - onTap: () { - setState(() { - state = ActionsModalState - .dismissMessage; - }); - final client = - StreamChat.of(context).client; - client.flagMessage(widget.message.id); - }, - ), - ), - ), - ], - ), - ], - ), - ), - ), - ), - ), - ), - ], - ), - ); - }); + void _showFlagDialog() async { + final client = StreamChat.of(context).client; + + var answer = await showConfirmationDialog(context, + title: 'Flag Message', + icon: StreamSvgIcon.flag( + color: StreamChatTheme.of(context).colorTheme.accentRed, + size: 24.0, + ), + question: + 'Do you want to send a copy of this message to a\nmoderator for further investigation?', + okText: 'FLAG', + cancelText: 'CANCEL'); + + if (answer) { + try { + await client.flagMessage(widget.message.id); + _showDismissAlert(); + } catch (err) { + _showErrorAlert(); + } + } else { + Navigator.pop(context); + } } - Widget _showDismissAlert() { - return TweenAnimationBuilder( - key: GlobalKey(), - tween: Tween(begin: 0.0, end: 1.0), - duration: Duration(milliseconds: 300), - curve: Curves.easeInOut, - builder: (context, value, _) { - return GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () => Navigator.maybePop(context), - child: Stack( + void _showDismissAlert() { + showModalBottomSheet( + backgroundColor: StreamChatTheme.of(context).colorTheme.white, + context: context, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(16.0), + topRight: Radius.circular(16.0), + )), + builder: (context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: 26.0, + ), + StreamSvgIcon.flag( + color: StreamChatTheme.of(context).colorTheme.accentRed, + size: 24.0, + ), + SizedBox( + height: 26.0, + ), + Text( + 'Message flagged', + style: StreamChatTheme.of(context).textTheme.headlineBold, + ), + SizedBox( + height: 7.0, + ), + Text('The message has been reported to a moderator.'), + SizedBox( + height: 36.0, + ), + Container( + color: + StreamChatTheme.of(context).colorTheme.black.withOpacity(.08), + height: 1.0, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, children: [ - Positioned.fill( - child: BackdropFilter( - filter: ImageFilter.blur( - sigmaX: 10, - sigmaY: 10, - ), - child: Container( - color: StreamChatTheme.of(context).colorTheme.overlay, - ), - ), - ), - Transform.scale( - scale: value, - child: Center( - child: Opacity( - opacity: 0.9, - child: Material( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - ), - clipBehavior: Clip.antiAlias, - child: Container( - width: 270.0, - height: 156.0, - alignment: Alignment.center, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox( - height: 19.0, - ), - Text( - 'Message Flagged', - style: StreamChatTheme.of(context) - .textTheme - .bodyBold - .copyWith(fontSize: 17.0), - ), - SizedBox( - height: 4.0, - ), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0), - child: Text( - 'This message has been reported to a\nmoderator.', - style: TextStyle(fontSize: 13.5), - textAlign: TextAlign.center, - ), - ), - SizedBox( - height: 21.0, - ), - Row( - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, - children: [ - Expanded( - child: Center( - child: InkWell( - child: Container( - height: 44.0, - alignment: Alignment.center, - child: Text( - 'Dismiss', - style: StreamChatTheme.of(context) - .textTheme - .body - .copyWith( - fontWeight: FontWeight.bold, - fontSize: 17.0, - color: StreamChatTheme.of( - context) - .colorTheme - .accentBlue), - ), - decoration: BoxDecoration( - border: Border( - top: BorderSide( - color: StreamChatTheme.of( - context) - .colorTheme - .grey - .withOpacity(0.8)), - ), - ), - ), - onTap: () { - setState(() { - state = ActionsModalState.modal; - }); - }, - ), - ), - ), - ], - ), - ], - ), - ), - ), - ), + FlatButton( + child: Text( + 'OK', + style: TextStyle( + color: + StreamChatTheme.of(context).colorTheme.accentBlue, + fontWeight: FontWeight.w400), ), + onPressed: () { + Navigator.of(context).pop(); + }, ), ], ), - ); - }); + ], + ); + }, + ); + } + + void _showErrorAlert() { + showModalBottomSheet( + backgroundColor: StreamChatTheme.of(context).colorTheme.white, + context: context, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(16.0), + topRight: Radius.circular(16.0), + )), + builder: (context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: 26.0, + ), + StreamSvgIcon.error( + color: StreamChatTheme.of(context).colorTheme.accentRed, + size: 24.0, + ), + SizedBox( + height: 26.0, + ), + Text( + 'Something went wrong', + style: StreamChatTheme.of(context).textTheme.headlineBold, + ), + SizedBox( + height: 7.0, + ), + Text('The operation couldn\'t be completed.'), + SizedBox( + height: 36.0, + ), + Container( + color: + StreamChatTheme.of(context).colorTheme.black.withOpacity(.08), + height: 1.0, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FlatButton( + child: Text( + 'OK', + style: TextStyle( + color: + StreamChatTheme.of(context).colorTheme.accentBlue, + fontWeight: FontWeight.w400), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ), + ], + ); + }, + ); } Widget _buildReplyButton(BuildContext context) { @@ -586,9 +432,7 @@ class _MessageActionsModalState extends State { ], ), onTap: () { - setState(() { - state = ActionsModalState.confirmDialog; - }); + _showFlagDialog(); }, ); } diff --git a/lib/src/utils.dart b/lib/src/utils.dart index f573b77b..21ca8114 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -75,7 +75,7 @@ Future showConfirmationDialog( fontWeight: FontWeight.w400), ), onPressed: () { - Navigator.of(context).pop(); + Navigator.of(context).pop(false); }, ), FlatButton( @@ -95,6 +95,77 @@ Future showConfirmationDialog( }); } +Future showInfoDialog( + BuildContext context, { + String title, + Widget icon, + String question, + String okText, + StreamChatThemeData theme, +}) { + return showModalBottomSheet( + backgroundColor: + theme.colorTheme.white ?? StreamChatTheme.of(context).colorTheme.white, + context: context, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(16.0), + topRight: Radius.circular(16.0), + )), + builder: (context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: 26.0, + ), + if (icon != null) icon, + SizedBox( + height: 26.0, + ), + Text( + title, + style: theme.textTheme.headlineBold ?? + StreamChatTheme.of(context).textTheme.headlineBold, + ), + SizedBox( + height: 7.0, + ), + Text(question), + SizedBox( + height: 36.0, + ), + Container( + color: theme.colorTheme.black.withOpacity(.08) ?? + StreamChatTheme.of(context).colorTheme.black.withOpacity(.08), + height: 1.0, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + FlatButton( + child: Text( + okText, + style: TextStyle( + color: theme.colorTheme.black.withOpacity(0.5) ?? + StreamChatTheme.of(context) + .colorTheme + .black + .withOpacity(0.5), + fontWeight: FontWeight.w400), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ), + ], + ); + }, + ); +} + /// Get random png with initials String getRandomPicUrl(User user) => 'https://getstream.io/random_png/?id=${user.id}&name=${user.name}'; From 6274f1437e9d48bdca37ed49ed2929201f5002ff Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 11 Jan 2021 21:29:31 +0530 Subject: [PATCH 09/19] feat: corrected text styles --- lib/src/message_actions_modal.dart | 22 ++++++++++++++-------- lib/src/utils.dart | 19 +++++++++++-------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index e4db71f5..4db2dd73 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -313,10 +313,13 @@ class _MessageActionsModalState extends State { FlatButton( child: Text( 'OK', - style: TextStyle( - color: - StreamChatTheme.of(context).colorTheme.accentBlue, - fontWeight: FontWeight.w400), + style: StreamChatTheme.of(context) + .textTheme + .bodyBold + .copyWith( + color: StreamChatTheme.of(context) + .colorTheme + .accentBlue), ), onPressed: () { Navigator.of(context).pop(); @@ -375,10 +378,13 @@ class _MessageActionsModalState extends State { FlatButton( child: Text( 'OK', - style: TextStyle( - color: - StreamChatTheme.of(context).colorTheme.accentBlue, - fontWeight: FontWeight.w400), + style: StreamChatTheme.of(context) + .textTheme + .bodyBold + .copyWith( + color: StreamChatTheme.of(context) + .colorTheme + .accentBlue), ), onPressed: () { Navigator.of(context).pop(); diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 21ca8114..4e55111f 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -67,12 +67,10 @@ Future showConfirmationDialog( FlatButton( child: Text( cancelText, - style: TextStyle( - color: StreamChatTheme.of(context) - .colorTheme - .black - .withOpacity(0.5), - fontWeight: FontWeight.w400), + style: StreamChatTheme.of(context) + .textTheme + .bodyBold + .copyWith(color: Colors.black.withOpacity(0.5)), ), onPressed: () { Navigator.of(context).pop(false); @@ -81,8 +79,13 @@ Future showConfirmationDialog( FlatButton( child: Text( okText, - style: TextStyle( - color: Colors.red, fontWeight: FontWeight.w400), + style: StreamChatTheme.of(context) + .textTheme + .bodyBold + .copyWith( + color: StreamChatTheme.of(context) + .colorTheme + .accentRed), ), onPressed: () { Navigator.pop(context, true); From 746688e04d9d5fcab23bef7619891e836ef0bfec Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 11 Jan 2021 21:43:47 +0530 Subject: [PATCH 10/19] fix: merge --- lib/src/message_actions_modal.dart | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index fa7b8da1..d1f6e0ff 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -1,6 +1,5 @@ import 'dart:ui'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -15,12 +14,6 @@ import 'message_widget.dart'; import 'stream_chat.dart'; import 'stream_chat_theme.dart'; -enum ActionsModalState { - modal, - confirmDialog, - dismissMessage, -} - class MessageActionsModal extends StatefulWidget { final Widget Function(BuildContext, Message) editMessageInputBuilder; final void Function(Message) onThreadReplyTap; @@ -149,7 +142,7 @@ class _MessageActionsModalState extends State { showUsername: false, showThreadReplyIndicator: false, showReplyMessage: false, - showUserAvatar: showUserAvatar, + showUserAvatar: widget.showUserAvatar, showTimestamp: false, translateUserAvatar: false, showReactionPickerIndicator: @@ -158,7 +151,7 @@ class _MessageActionsModalState extends State { MessageSendingStatus.SENT || widget.message.status == null), showInChannelIndicator: false, - showSendingIndicator: DisplayWidget.gone, + showSendingIndicator: false, shape: widget.messageShape, ), ), From 4f41f6be7f46b2266f96a4443128fb581a4f08c5 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 11 Jan 2021 21:45:31 +0530 Subject: [PATCH 11/19] fix: color and fmt --- lib/src/utils.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 27559f7a..0b59e54c 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -70,7 +70,11 @@ Future showConfirmationDialog( style: StreamChatTheme.of(context) .textTheme .bodyBold - .copyWith(color: Colors.black.withOpacity(0.5)), + .copyWith( + color: StreamChatTheme.of(context) + .colorTheme + .black + .withOpacity(0.5)), ), onPressed: () { Navigator.of(context).pop(false); From 1556564ea7ebafa5ccc5a2b2f1b788c089f15d0b Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 11 Jan 2021 21:49:34 +0530 Subject: [PATCH 12/19] fix: Cancel behaviour --- lib/src/message_actions_modal.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index d1f6e0ff..f288fa7a 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -256,8 +256,6 @@ class _MessageActionsModalState extends State { } catch (err) { _showErrorAlert(); } - } else { - Navigator.pop(context); } } From 53b73ef8defcf0a0290ae333c82be821cbc8d8a8 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 12 Jan 2021 10:10:39 +0100 Subject: [PATCH 13/19] bump version --- example/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 16bc8108..4719a6e5 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,6 +1,6 @@ name: example description: A new Flutter project. -version: 1.0.113+116 +version: 1.1.0 environment: sdk: ">=2.2.2 <3.0.0" From 81589778fae7d4c3c990765f0fb65c5dac457e70 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 12 Jan 2021 10:33:18 +0100 Subject: [PATCH 14/19] fix gihpy dark mode --- example/pubspec.yaml | 2 +- lib/src/giphy_attachment.dart | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 4719a6e5..cdc07de7 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,6 +1,6 @@ name: example description: A new Flutter project. -version: 1.1.0 +version: 1.1.0+1 environment: sdk: ">=2.2.2 <3.0.0" diff --git a/lib/src/giphy_attachment.dart b/lib/src/giphy_attachment.dart index c879cd5b..df5a5705 100644 --- a/lib/src/giphy_attachment.dart +++ b/lib/src/giphy_attachment.dart @@ -45,6 +45,7 @@ class GiphyAttachment extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ Card( + color: StreamChatTheme.of(context).colorTheme.white, elevation: 2, clipBehavior: Clip.antiAlias, shape: RoundedRectangleBorder( @@ -160,6 +161,7 @@ class GiphyAttachment extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Card( + color: Colors.white, elevation: 2, child: IconButton( padding: const EdgeInsets.all(0), @@ -187,6 +189,7 @@ class GiphyAttachment extends StatelessWidget { ), ), Card( + color: Colors.white, elevation: 2, child: IconButton( padding: const EdgeInsets.all(0), From b402add586ed080fc5e2e1f5339b79fc21594270 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Tue, 12 Jan 2021 15:11:52 +0530 Subject: [PATCH 15/19] feat: Added delete dialog and added dismiss dialog in case of message flagged --- lib/src/message_actions_modal.dart | 35 +++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index f288fa7a..95ecdea5 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -1,3 +1,4 @@ +import 'dart:convert'; import 'dart:ui'; import 'package:flutter/foundation.dart'; @@ -253,6 +254,34 @@ class _MessageActionsModalState extends State { try { await client.flagMessage(widget.message.id); _showDismissAlert(); + } catch (err) { + if (json.decode(err?.body ?? {})['code'] == 4) { + _showDismissAlert(); + } else { + _showErrorAlert(); + } + } + } + } + + void _showDeleteDialog() async { + var answer = await showConfirmationDialog(context, + title: 'Delete message', + icon: StreamSvgIcon.flag( + color: StreamChatTheme.of(context).colorTheme.accentRed, + size: 24.0, + ), + question: 'Are you sure you want to permanently delete this\nmessage?', + okText: 'DELETE', + cancelText: 'CANCEL'); + + if (answer) { + try { + Navigator.pop(context); + StreamChat.of(context).client.deleteMessage( + widget.message, + StreamChannel.of(context).channel.cid, + ); } catch (err) { _showErrorAlert(); } @@ -455,11 +484,7 @@ class _MessageActionsModalState extends State { ], ), onTap: () { - Navigator.pop(context); - StreamChat.of(context).client.deleteMessage( - widget.message, - StreamChannel.of(context).channel.cid, - ); + _showDeleteDialog(); }, ); } From 317de62925d79a8dd735db9507e77d05f82bd94c Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 12 Jan 2021 11:23:31 +0100 Subject: [PATCH 16/19] fix deleted message preview --- lib/src/channel_preview.dart | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/src/channel_preview.dart b/lib/src/channel_preview.dart index eac6e3c2..3564be7a 100644 --- a/lib/src/channel_preview.dart +++ b/lib/src/channel_preview.dart @@ -96,12 +96,16 @@ class ChannelPreview extends StatelessWidget { Flexible(child: _buildSubtitle(context)), Builder( builder: (context) { - if (channel.state.lastMessage?.user?.id == + final lastMessage = channel.state.messages.lastWhere( + (m) => !m.isDeleted && m.shadowed != true, + orElse: () => null, + ); + if (lastMessage?.user?.id == StreamChat.of(context).user.id) { return Padding( padding: const EdgeInsets.only(right: 4.0), child: SendingIndicator( - message: channel.state.lastMessage, + message: lastMessage, size: StreamChatTheme.of(context) .channelPreviewTheme .indicatorIconSize, @@ -110,8 +114,7 @@ class ChannelPreview extends StatelessWidget { element.user.id != channel.client.state.user.id) ?.where((element) => element.lastRead - .isAfter(channel - .state.lastMessage.createdAt)) + .isAfter(lastMessage.createdAt)) ?.isNotEmpty == true, ), @@ -201,16 +204,15 @@ class ChannelPreview extends StatelessWidget { stream: channel.state.messagesStream, initialData: channel.state.messages, builder: (context, snapshot) { - final lastMessage = snapshot.data - ?.lastWhere((m) => m.shadowed != true, orElse: () => null); + final lastMessage = snapshot.data?.lastWhere( + (m) => m.shadowed != true && !m.isDeleted, + orElse: () => null); if (lastMessage == null) { return SizedBox(); } var text = lastMessage.text; - if (lastMessage.isDeleted) { - text = 'This message was deleted.'; - } else if (lastMessage.attachments != null) { + if (lastMessage.attachments != null) { final parts = [ ...lastMessage.attachments.map((e) { if (e.type == 'image') { From d9a50ae42eee303eae2bdbfbb21559fa18ff8e09 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 12 Jan 2021 12:05:01 +0100 Subject: [PATCH 17/19] fix animations --- example/pubspec.yaml | 2 +- lib/src/message_actions_modal.dart | 283 +++++++++++++-------------- lib/src/message_reactions_modal.dart | 265 ++++++++++++------------- lib/src/reaction_picker.dart | 30 +-- 4 files changed, 284 insertions(+), 296 deletions(-) diff --git a/example/pubspec.yaml b/example/pubspec.yaml index cdc07de7..46dc8820 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,6 +1,6 @@ name: example description: A new Flutter project. -version: 1.1.0+1 +version: 1.1.0+2 environment: sdk: ">=2.2.2 <3.0.0" diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index 95ecdea5..7f9c5e85 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -84,155 +84,146 @@ class _MessageActionsModalState extends State { ? 1 : (roughSentenceSize == 0 ? 1 : (roughSentenceSize / roughMaxSize)); - return GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () => Navigator.maybePop(context), - child: Stack( - children: [ - Positioned.fill( - child: BackdropFilter( - filter: ImageFilter.blur( - sigmaX: 10, - sigmaY: 10, - ), - child: Container( - color: StreamChatTheme.of(context).colorTheme.overlay, - ), - ), - ), - Center( - child: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Column( - crossAxisAlignment: widget.reverse - ? CrossAxisAlignment.end - : CrossAxisAlignment.start, - children: [ - if (widget.showReactions && - (widget.message.status == MessageSendingStatus.SENT || - widget.message.status == null)) - Align( - alignment: Alignment( - user.id == widget.message.user.id - ? (divFactor > 1.0 ? 0.0 : (1.0 - divFactor)) - : (divFactor > 1.0 ? 0.0 : -(1.0 - divFactor)), - 0.0), - child: ReactionPicker( - message: widget.message, - messageTheme: widget.messageTheme, - ), - ), - TweenAnimationBuilder( - tween: Tween(begin: 0.0, end: 1.0), - duration: Duration(milliseconds: 300), - builder: (context, val, snapshot) { - return Transform.scale( - scale: val, - child: IgnorePointer( - child: MessageWidget( - key: Key('MessageWidget'), - reverse: widget.reverse, - message: widget.message.copyWith( - text: widget.message.text.length > 200 - ? '${widget.message.text.substring(0, 200)}...' - : widget.message.text, - ), - messageTheme: widget.messageTheme, - showReactions: false, - showUsername: false, - showThreadReplyIndicator: false, - showReplyMessage: false, - showUserAvatar: widget.showUserAvatar, - showTimestamp: false, - translateUserAvatar: false, - showReactionPickerIndicator: - widget.showReactions && - (widget.message.status == - MessageSendingStatus.SENT || - widget.message.status == null), - showInChannelIndicator: false, - showSendingIndicator: false, - shape: widget.messageShape, - ), - ), - ); - }), - TweenAnimationBuilder( - tween: Tween(begin: 0.0, end: 1.0), - duration: Duration(milliseconds: 300), - curve: Curves.easeInOut, - builder: (context, val, wid) { - return Transform( - transform: Matrix4.identity() - ..scale(val) - ..rotateZ(-1.0 + val), - alignment: widget.reverse - ? Alignment.topRight - : Alignment.topLeft, - child: Padding( - padding: EdgeInsets.only( - right: widget.reverse ? 16 : 0, - left: widget.reverse ? 0 : 48, - ), - child: SizedBox( - width: MediaQuery.of(context).size.width * 0.75, - child: Material( - color: StreamChatTheme.of(context) - .colorTheme - .whiteSnow, - clipBehavior: Clip.hardEdge, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - child: Column( - crossAxisAlignment: - CrossAxisAlignment.stretch, - children: ListTile.divideTiles( - color: StreamChatTheme.of(context) - .colorTheme - .greyWhisper, - context: context, - tiles: [ - if (widget.showReplyMessage && - (widget.message.status == - MessageSendingStatus.SENT || - widget.message.status == - null) && - widget.message.parentId == null) - _buildReplyButton(context), - if (widget.showThreadReplyMessage && - (widget.message.status == - MessageSendingStatus.SENT || - widget.message.status == - null) && - widget.message.parentId == null) - _buildThreadReplyButton(context), - if (widget.showResendMessage) - _buildResendMessage(context), - if (widget.showEditMessage) - _buildEditMessage(context), - if (widget.showCopyMessage) - _buildCopyButton(context), - if (widget.showFlagButton) - _buildFlagButton(context), - if (widget.showDeleteMessage) - _buildDeleteButton(context), - ], - ).toList(), - ), - ), - ), - ), - ); - }) - ], + return TweenAnimationBuilder( + tween: Tween(begin: 0.0, end: 1.0), + duration: Duration(milliseconds: 300), + curve: Curves.easeInOutBack, + builder: (context, val, snapshot) { + return GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () => Navigator.maybePop(context), + child: Stack( + children: [ + Positioned.fill( + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaX: 10, + sigmaY: 10, + ), + child: Container( + color: StreamChatTheme.of(context).colorTheme.overlay, + ), ), ), - ), + Transform.scale( + scale: val, + child: Center( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Column( + crossAxisAlignment: widget.reverse + ? CrossAxisAlignment.end + : CrossAxisAlignment.start, + children: [ + if (widget.showReactions && + (widget.message.status == + MessageSendingStatus.SENT || + widget.message.status == null)) + Align( + alignment: Alignment( + user.id == widget.message.user.id + ? (divFactor > 1.0 + ? 0.0 + : (1.0 - divFactor)) + : (divFactor > 1.0 + ? 0.0 + : -(1.0 - divFactor)), + 0.0), + child: ReactionPicker( + message: widget.message, + messageTheme: widget.messageTheme, + ), + ), + IgnorePointer( + child: MessageWidget( + key: Key('MessageWidget'), + reverse: widget.reverse, + message: widget.message.copyWith( + text: widget.message.text.length > 200 + ? '${widget.message.text.substring(0, 200)}...' + : widget.message.text, + ), + messageTheme: widget.messageTheme, + showReactions: false, + showUsername: false, + showThreadReplyIndicator: false, + showReplyMessage: false, + showUserAvatar: widget.showUserAvatar, + showTimestamp: false, + translateUserAvatar: false, + showReactionPickerIndicator: + widget.showReactions && + (widget.message.status == + MessageSendingStatus.SENT || + widget.message.status == null), + showInChannelIndicator: false, + showSendingIndicator: false, + shape: widget.messageShape, + ), + ), + Padding( + padding: EdgeInsets.only( + right: widget.reverse ? 16 : 0, + left: widget.reverse ? 0 : 48, + ), + child: SizedBox( + width: MediaQuery.of(context).size.width * 0.75, + child: Material( + color: StreamChatTheme.of(context) + .colorTheme + .whiteSnow, + clipBehavior: Clip.hardEdge, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.stretch, + children: ListTile.divideTiles( + color: StreamChatTheme.of(context) + .colorTheme + .greyWhisper, + context: context, + tiles: [ + if (widget.showReplyMessage && + (widget.message.status == + MessageSendingStatus.SENT || + widget.message.status == null) && + widget.message.parentId == null) + _buildReplyButton(context), + if (widget.showThreadReplyMessage && + (widget.message.status == + MessageSendingStatus.SENT || + widget.message.status == null) && + widget.message.parentId == null) + _buildThreadReplyButton(context), + if (widget.showResendMessage) + _buildResendMessage(context), + if (widget.showEditMessage) + _buildEditMessage(context), + if (widget.showCopyMessage) + _buildCopyButton(context), + if (widget.showFlagButton) + _buildFlagButton(context), + if (widget.showDeleteMessage) + _buildDeleteButton(context), + ], + ).toList(), + ), + ), + ), + ), + ], + ), + ), + ), + ), + ), + ], ), - ], - ), + ); + }, ); } diff --git a/lib/src/message_reactions_modal.dart b/lib/src/message_reactions_modal.dart index 5c24e047..15a301cc 100644 --- a/lib/src/message_reactions_modal.dart +++ b/lib/src/message_reactions_modal.dart @@ -56,86 +56,92 @@ class MessageReactionsModal extends StatelessWidget { ? 1 : (roughSentenceSize == 0 ? 1 : (roughSentenceSize / roughMaxSize)); - return GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () => Navigator.maybePop(context), - child: Stack( - children: [ - Positioned.fill( - child: BackdropFilter( - filter: ImageFilter.blur( - sigmaX: 10, - sigmaY: 10, - ), - child: Container( - color: StreamChatTheme.of(context).colorTheme.overlay, - ), - ), - ), - Center( - child: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (showReactions && - (message.status == MessageSendingStatus.SENT || - message.status == null)) - Align( - alignment: Alignment( - user.id == message.user.id - ? (divFactor > 1.0 ? 0.0 : (1.0 - divFactor)) - : (divFactor > 1.0 ? 0.0 : -(1.0 - divFactor)), - 0.0), - child: ReactionPicker( - message: message, - messageTheme: messageTheme, - ), - ), - TweenAnimationBuilder( - tween: Tween(begin: 0.0, end: 1.0), - duration: Duration(milliseconds: 300), - builder: (context, val, snapshot) { - return Transform.scale( - scale: val, - child: IgnorePointer( - child: MessageWidget( - key: Key('MessageWidget'), - reverse: reverse, - message: message.copyWith( - text: message.text.length > 200 - ? '${message.text.substring(0, 200)}...' - : message.text, - ), - messageTheme: messageTheme, - showReactions: false, - showUsername: false, - showUserAvatar: showUserAvatar, - showThreadReplyIndicator: false, - showTimestamp: false, - translateUserAvatar: false, - showSendingIndicator: false, - shape: messageShape, - showInChannelIndicator: false, - showReactionPickerIndicator: showReactions && - (message.status == - MessageSendingStatus.SENT || - message.status == null), - ), - ), - ); - }), - if (message.latestReactions?.isNotEmpty == true) - _buildReactionCard(context), - ], + return TweenAnimationBuilder( + tween: Tween(begin: 0.0, end: 1.0), + duration: Duration(milliseconds: 300), + curve: Curves.easeInOutBack, + builder: (context, val, snapshot) { + return GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () => Navigator.maybePop(context), + child: Stack( + children: [ + Positioned.fill( + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaX: 10, + sigmaY: 10, + ), + child: Container( + color: StreamChatTheme.of(context).colorTheme.overlay, + ), ), ), - ), + Transform.scale( + scale: val, + child: Center( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + if (showReactions && + (message.status == MessageSendingStatus.SENT || + message.status == null)) + Align( + alignment: Alignment( + user.id == message.user.id + ? (divFactor > 1.0 + ? 0.0 + : (1.0 - divFactor)) + : (divFactor > 1.0 + ? 0.0 + : -(1.0 - divFactor)), + 0.0), + child: ReactionPicker( + message: message, + messageTheme: messageTheme, + ), + ), + IgnorePointer( + child: MessageWidget( + key: Key('MessageWidget'), + reverse: reverse, + message: message.copyWith( + text: message.text.length > 200 + ? '${message.text.substring(0, 200)}...' + : message.text, + ), + messageTheme: messageTheme, + showReactions: false, + showUsername: false, + showUserAvatar: showUserAvatar, + showThreadReplyIndicator: false, + showTimestamp: false, + translateUserAvatar: false, + showSendingIndicator: false, + shape: messageShape, + showInChannelIndicator: false, + showReactionPickerIndicator: showReactions && + (message.status == + MessageSendingStatus.SENT || + message.status == null), + ), + ), + if (message.latestReactions?.isNotEmpty == true) + _buildReactionCard(context), + ], + ), + ), + ), + ), + ), + ], ), - ], - ), + ); + }, ); } @@ -191,64 +197,53 @@ class MessageReactionsModal extends StatelessWidget { BuildContext context, ) { final isCurrentUser = reaction.user.id == currentUser.id; - return TweenAnimationBuilder( - tween: Tween(begin: 0.0, end: 1.0), - duration: Duration(milliseconds: 300), - curve: Curves.easeInOut, - builder: (context, val, snapshot) { - return Transform.scale( - scale: val, - child: ConstrainedBox( - constraints: BoxConstraints.loose(Size( - 64, - 98, - )), - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Stack( - children: [ - UserAvatar( - onTap: onUserAvatarTap, - user: reaction.user, - constraints: BoxConstraints.tightFor( - height: 64, - width: 64, - ), - borderRadius: BorderRadius.circular(32), - ), - Positioned( - child: Align( - alignment: reverse - ? Alignment.centerRight - : Alignment.centerLeft, - child: ReactionBubble( - reactions: [reaction], - flipTail: !reverse, - borderColor: messageTheme.reactionsBorderColor, - backgroundColor: - messageTheme.reactionsBackgroundColor, - highlightOwnReactions: false, - ), - ), - bottom: 6, - left: isCurrentUser ? 0 : null, - right: isCurrentUser ? 0 : null, - ), - ], - ), - const SizedBox(height: 8), - Text( - reaction.user.name, - style: StreamChatTheme.of(context).textTheme.footnoteBold, - textAlign: TextAlign.center, - ), - ], + return ConstrainedBox( + constraints: BoxConstraints.loose(Size( + 64, + 98, + )), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Stack( + children: [ + UserAvatar( + onTap: onUserAvatarTap, + user: reaction.user, + constraints: BoxConstraints.tightFor( + height: 64, + width: 64, + ), + borderRadius: BorderRadius.circular(32), ), - ), - ); - }); + Positioned( + child: Align( + alignment: + reverse ? Alignment.centerRight : Alignment.centerLeft, + child: ReactionBubble( + reactions: [reaction], + flipTail: !reverse, + borderColor: messageTheme.reactionsBorderColor, + backgroundColor: messageTheme.reactionsBackgroundColor, + highlightOwnReactions: false, + ), + ), + bottom: 6, + left: isCurrentUser ? 0 : null, + right: isCurrentUser ? 0 : null, + ), + ], + ), + const SizedBox(height: 8), + Text( + reaction.user.name, + style: StreamChatTheme.of(context).textTheme.footnoteBold, + textAlign: TextAlign.center, + ), + ], + ), + ); } } diff --git a/lib/src/reaction_picker.dart b/lib/src/reaction_picker.dart index c5c42bd1..bca74a99 100644 --- a/lib/src/reaction_picker.dart +++ b/lib/src/reaction_picker.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:ezanimation/ezanimation.dart'; import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; @@ -36,13 +38,10 @@ class _ReactionPickerState extends State if (animations.isEmpty && reactionIcons.isNotEmpty) { reactionIcons.forEach((element) { animations.add( - EzAnimation.sequence( - [ - SequenceItem(0.0, 1.4), - SequenceItem(1.4, 1.0), - ], + EzAnimation.tween( + Tween(begin: 0.0, end: 1.0), Duration(milliseconds: 500), - vsync: this, + curve: Curves.easeInOutBack, ), ); }); @@ -52,7 +51,7 @@ class _ReactionPickerState extends State return TweenAnimationBuilder( tween: Tween(begin: 0.0, end: 1.0), - curve: Curves.easeInOutExpo, + curve: Curves.easeInOutBack, duration: Duration(milliseconds: 500), builder: (context, val, wid) { return Transform.scale( @@ -81,15 +80,18 @@ class _ReactionPickerState extends State icon: AnimatedBuilder( animation: animations[index], builder: (context, val) { - return Transform( - transform: Matrix4.identity() - ..scale(animations[index].value, - animations[index].value) - ..rotateZ(1.0 - animations[index].value), + return Transform.scale( + scale: animations[index].value, child: StreamSvgIcon( assetName: reactionIcon.assetName, - height: animations[index].value * 24.0, - width: animations[index].value * 24.0, + height: max( + 0, + animations[index].value * 24.0, + ), + width: max( + 0, + animations[index].value * 24.0, + ), color: ownReactionIndex != -1 ? StreamChatTheme.of(context) .colorTheme From 40910f93f1e82b49291e44e0868d1a62b8fbccca Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 12 Jan 2021 13:49:44 +0100 Subject: [PATCH 18/19] fix reaction picker spacing and size --- example/pubspec.yaml | 2 +- lib/src/reaction_picker.dart | 123 ++++++++++++++++++++--------------- 2 files changed, 71 insertions(+), 54 deletions(-) diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 46dc8820..6c4ae4dc 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,6 +1,6 @@ name: example description: A new Flutter project. -version: 1.1.0+2 +version: 1.1.0+3 environment: sdk: ">=2.2.2 <3.0.0" diff --git a/lib/src/reaction_picker.dart b/lib/src/reaction_picker.dart index bca74a99..804b1559 100644 --- a/lib/src/reaction_picker.dart +++ b/lib/src/reaction_picker.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; import '../stream_chat_flutter.dart'; +import 'extension.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) @@ -57,67 +58,83 @@ class _ReactionPickerState extends State return Transform.scale( scale: val, child: Material( + borderRadius: BorderRadius.circular(24), color: StreamChatTheme.of(context).colorTheme.white, clipBehavior: Clip.hardEdge, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(24), - ), child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.min, - children: reactionIcons.map((reactionIcon) { - final ownReactionIndex = widget.message.ownReactions - ?.indexWhere((reaction) => - reaction.type == reactionIcon.type) ?? - -1; - var index = reactionIcons.indexOf(reactionIcon); + children: reactionIcons + .map((reactionIcon) { + final ownReactionIndex = widget.message.ownReactions + ?.indexWhere((reaction) => + reaction.type == reactionIcon.type) ?? + -1; + var index = reactionIcons.indexOf(reactionIcon); - return IconButton( - iconSize: 24, - icon: AnimatedBuilder( - animation: animations[index], - builder: (context, val) { - return Transform.scale( - scale: animations[index].value, - child: StreamSvgIcon( - assetName: reactionIcon.assetName, - height: max( - 0, - animations[index].value * 24.0, - ), - width: max( - 0, - animations[index].value * 24.0, - ), - color: ownReactionIndex != -1 - ? StreamChatTheme.of(context) - .colorTheme - .accentBlue - : Theme.of(context) - .iconTheme - .color - .withOpacity(.5), - ), - ); - }), - onPressed: () { - if (ownReactionIndex != -1) { - removeReaction( - context, - widget.message.ownReactions[ownReactionIndex], - ); - } else { - sendReaction( - context, - reactionIcon.type, - ); - } - }, - ); - }).toList(), + return RawMaterialButton( + elevation: 0, + padding: const EdgeInsets.all(0), + clipBehavior: Clip.none, + shape: ContinuousRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + constraints: BoxConstraints.tightFor( + height: 24, + width: 24, + ), + child: AnimatedBuilder( + animation: animations[index], + builder: (context, val) { + return Transform.scale( + alignment: Alignment.center, + scale: animations[index].value, + child: StreamSvgIcon( + assetName: reactionIcon.assetName, + height: max( + 0, + animations[index].value * 24.0, + ), + width: max( + 0, + animations[index].value * 24.0, + ), + color: ownReactionIndex != -1 + ? StreamChatTheme.of(context) + .colorTheme + .accentBlue + : Theme.of(context) + .iconTheme + .color + .withOpacity(.5), + ), + ); + }), + onPressed: () { + if (ownReactionIndex != -1) { + removeReaction( + context, + widget.message.ownReactions[ownReactionIndex], + ); + } else { + sendReaction( + context, + reactionIcon.type, + ); + } + }, + ); + }) + .insertBetween(SizedBox( + width: 16, + )) + .toList(), ), ), ), From 294c9edafb455813ceda4cb74cb9ed13091c199e Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 12 Jan 2021 14:35:29 +0100 Subject: [PATCH 19/19] fix reaction picker spacing and size --- lib/src/reaction_picker.dart | 96 +++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 45 deletions(-) diff --git a/lib/src/reaction_picker.dart b/lib/src/reaction_picker.dart index 804b1559..b0aa51ce 100644 --- a/lib/src/reaction_picker.dart +++ b/lib/src/reaction_picker.dart @@ -78,57 +78,63 @@ class _ReactionPickerState extends State -1; var index = reactionIcons.indexOf(reactionIcon); - return RawMaterialButton( - elevation: 0, - padding: const EdgeInsets.all(0), - clipBehavior: Clip.none, - shape: ContinuousRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), + return ConstrainedBox( constraints: BoxConstraints.tightFor( height: 24, width: 24, ), - child: AnimatedBuilder( - animation: animations[index], - builder: (context, val) { - return Transform.scale( - alignment: Alignment.center, - scale: animations[index].value, - child: StreamSvgIcon( - assetName: reactionIcon.assetName, - height: max( - 0, - animations[index].value * 24.0, + child: RawMaterialButton( + elevation: 0, + padding: const EdgeInsets.all(0), + clipBehavior: Clip.none, + shape: ContinuousRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + constraints: BoxConstraints.tightFor( + height: 24, + width: 24, + ), + child: AnimatedBuilder( + animation: animations[index], + builder: (context, val) { + return Transform.scale( + alignment: Alignment.center, + scale: animations[index].value, + child: StreamSvgIcon( + assetName: reactionIcon.assetName, + height: max( + 0, + animations[index].value * 24.0, + ), + width: max( + 0, + animations[index].value * 24.0, + ), + color: ownReactionIndex != -1 + ? StreamChatTheme.of(context) + .colorTheme + .accentBlue + : Theme.of(context) + .iconTheme + .color + .withOpacity(.5), ), - width: max( - 0, - animations[index].value * 24.0, - ), - color: ownReactionIndex != -1 - ? StreamChatTheme.of(context) - .colorTheme - .accentBlue - : Theme.of(context) - .iconTheme - .color - .withOpacity(.5), - ), + ); + }), + onPressed: () { + if (ownReactionIndex != -1) { + removeReaction( + context, + widget.message.ownReactions[ownReactionIndex], ); - }), - onPressed: () { - if (ownReactionIndex != -1) { - removeReaction( - context, - widget.message.ownReactions[ownReactionIndex], - ); - } else { - sendReaction( - context, - reactionIcon.type, - ); - } - }, + } else { + sendReaction( + context, + reactionIcon.type, + ); + } + }, + ), ); }) .insertBetween(SizedBox(