[Message Widget] Add message error state
Signed-off-by: Sahil Kumar <[email protected]>
This commit is contained in:
@@ -22,6 +22,7 @@ class MessageActionsModal extends StatelessWidget {
|
|||||||
final bool showDeleteMessage;
|
final bool showDeleteMessage;
|
||||||
final bool showCopyMessage;
|
final bool showCopyMessage;
|
||||||
final bool showEditMessage;
|
final bool showEditMessage;
|
||||||
|
final bool showResendMessage;
|
||||||
final bool showReply;
|
final bool showReply;
|
||||||
final bool reverse;
|
final bool reverse;
|
||||||
final ShapeBorder messageShape;
|
final ShapeBorder messageShape;
|
||||||
@@ -37,6 +38,7 @@ class MessageActionsModal extends StatelessWidget {
|
|||||||
this.onThreadTap,
|
this.onThreadTap,
|
||||||
this.showCopyMessage = true,
|
this.showCopyMessage = true,
|
||||||
this.showReply = true,
|
this.showReply = true,
|
||||||
|
this.showResendMessage = true,
|
||||||
this.showUserAvatar = DisplayWidget.show,
|
this.showUserAvatar = DisplayWidget.show,
|
||||||
this.editMessageInputBuilder,
|
this.editMessageInputBuilder,
|
||||||
this.messageShape,
|
this.messageShape,
|
||||||
@@ -159,6 +161,8 @@ class MessageActionsModal extends StatelessWidget {
|
|||||||
message.status == null) &&
|
message.status == null) &&
|
||||||
message.parentId == null)
|
message.parentId == null)
|
||||||
_buildReplyButton(context),
|
_buildReplyButton(context),
|
||||||
|
if (showResendMessage)
|
||||||
|
_buildResendMessage(context),
|
||||||
if (showEditMessage)
|
if (showEditMessage)
|
||||||
_buildEditMessage(context),
|
_buildEditMessage(context),
|
||||||
if (showDeleteMessage)
|
if (showDeleteMessage)
|
||||||
@@ -183,9 +187,10 @@ class MessageActionsModal extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDeleteButton(BuildContext context) {
|
Widget _buildDeleteButton(BuildContext context) {
|
||||||
|
final isDeleteFailed = message.status == MessageSendingStatus.FAILED_DELETE;
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
'Delete message',
|
isDeleteFailed ? 'Retry deleting message' : 'Delete message',
|
||||||
style:
|
style:
|
||||||
Theme.of(context).textTheme.headline6.copyWith(color: Colors.red),
|
Theme.of(context).textTheme.headline6.copyWith(color: Colors.red),
|
||||||
),
|
),
|
||||||
@@ -234,6 +239,29 @@ class MessageActionsModal extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildResendMessage(BuildContext context) {
|
||||||
|
final isUpdateFailed = message.status == MessageSendingStatus.FAILED_UPDATE;
|
||||||
|
return ListTile(
|
||||||
|
title: Text(
|
||||||
|
isUpdateFailed ? 'Resend edited message' : 'Resend',
|
||||||
|
style: Theme.of(context).textTheme.headline6,
|
||||||
|
),
|
||||||
|
leading: StreamSvgIcon.circle_up(
|
||||||
|
color: StreamChatTheme.of(context).accentColor,
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
final client = StreamChat.of(context).client;
|
||||||
|
final channel = StreamChannel.of(context).channel;
|
||||||
|
if (isUpdateFailed) {
|
||||||
|
client.updateMessage(message, channel.cid);
|
||||||
|
} else {
|
||||||
|
channel.sendMessage(message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _showEditBottomSheet(BuildContext context) {
|
void _showEditBottomSheet(BuildContext context) {
|
||||||
final channel = StreamChannel.of(context).channel;
|
final channel = StreamChannel.of(context).channel;
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
|
|||||||
+53
-94
@@ -114,6 +114,9 @@ class MessageWidget extends StatefulWidget {
|
|||||||
/// Used in [MessageReactionsModal] and [MessageActionsModal]
|
/// Used in [MessageReactionsModal] and [MessageActionsModal]
|
||||||
final bool showReactionPickerIndicator;
|
final bool showReactionPickerIndicator;
|
||||||
|
|
||||||
|
/// If true the widget will show the resendMessage indicator
|
||||||
|
final bool showResendMessage;
|
||||||
|
|
||||||
final List<Read> readList;
|
final List<Read> readList;
|
||||||
|
|
||||||
/// If true show the users username next to the timestamp of the message
|
/// If true show the users username next to the timestamp of the message
|
||||||
@@ -157,6 +160,7 @@ class MessageWidget extends StatefulWidget {
|
|||||||
this.editMessageInputBuilder,
|
this.editMessageInputBuilder,
|
||||||
this.textBuilder,
|
this.textBuilder,
|
||||||
Map<String, AttachmentBuilder> customAttachmentBuilders,
|
Map<String, AttachmentBuilder> customAttachmentBuilders,
|
||||||
|
this.showResendMessage = true,
|
||||||
this.readList,
|
this.readList,
|
||||||
this.padding,
|
this.padding,
|
||||||
this.textPadding = const EdgeInsets.symmetric(
|
this.textPadding = const EdgeInsets.symmetric(
|
||||||
@@ -229,6 +233,16 @@ class _MessageWidgetState extends State<MessageWidget> {
|
|||||||
bool get showInChannel =>
|
bool get showInChannel =>
|
||||||
widget.showInChannelIndicator && widget.message?.showInChannel == true;
|
widget.showInChannelIndicator && widget.message?.showInChannel == true;
|
||||||
|
|
||||||
|
bool get isSendFailed => widget.message.status == MessageSendingStatus.FAILED;
|
||||||
|
|
||||||
|
bool get isUpdateFailed =>
|
||||||
|
widget.message.status == MessageSendingStatus.FAILED_UPDATE;
|
||||||
|
|
||||||
|
bool get isDeleteFailed =>
|
||||||
|
widget.message.status == MessageSendingStatus.FAILED_DELETE;
|
||||||
|
|
||||||
|
bool get isFailedState => isSendFailed || isUpdateFailed || isDeleteFailed;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var leftPadding = widget.showUserAvatar != DisplayWidget.gone
|
var leftPadding = widget.showUserAvatar != DisplayWidget.gone
|
||||||
@@ -298,9 +312,7 @@ class _MessageWidgetState extends State<MessageWidget> {
|
|||||||
)
|
)
|
||||||
: EdgeInsets.zero,
|
: EdgeInsets.zero,
|
||||||
child: (widget.message.isDeleted &&
|
child: (widget.message.isDeleted &&
|
||||||
widget.message.status !=
|
!isFailedState)
|
||||||
MessageSendingStatus
|
|
||||||
.FAILED_DELETE)
|
|
||||||
? Transform(
|
? Transform(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
transform: Matrix4.rotationY(
|
transform: Matrix4.rotationY(
|
||||||
@@ -377,6 +389,21 @@ class _MessageWidgetState extends State<MessageWidget> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (isFailedState)
|
||||||
|
Positioned(
|
||||||
|
left: widget.reverse ? -6 : null,
|
||||||
|
right: widget.reverse ? null : -6,
|
||||||
|
bottom: 0,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: StreamSvgIcon.error(
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -619,14 +646,19 @@ class _MessageWidgetState extends State<MessageWidget> {
|
|||||||
message: widget.message,
|
message: widget.message,
|
||||||
editMessageInputBuilder: widget.editMessageInputBuilder,
|
editMessageInputBuilder: widget.editMessageInputBuilder,
|
||||||
onThreadTap: widget.onThreadTap,
|
onThreadTap: widget.onThreadTap,
|
||||||
showCopyMessage: widget.message.text?.trim()?.isNotEmpty == true,
|
showResendMessage:
|
||||||
|
widget.showResendMessage && (isSendFailed || isUpdateFailed),
|
||||||
|
showCopyMessage: !isFailedState &&
|
||||||
|
widget.message.text?.trim()?.isNotEmpty == true,
|
||||||
showEditMessage: widget.showEditMessage &&
|
showEditMessage: widget.showEditMessage &&
|
||||||
|
!isDeleteFailed &&
|
||||||
widget.message.attachments
|
widget.message.attachments
|
||||||
?.any((element) => element.type == 'giphy') !=
|
?.any((element) => element.type == 'giphy') !=
|
||||||
true,
|
true,
|
||||||
showReactions: widget.showReactions,
|
showReactions: widget.showReactions,
|
||||||
showReply:
|
showReply: widget.showThreadReplyIndicator &&
|
||||||
widget.showThreadReplyIndicator && widget.onThreadTap != null,
|
!isFailedState &&
|
||||||
|
widget.onThreadTap != null,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -729,7 +761,6 @@ class _MessageWidgetState extends State<MessageWidget> {
|
|||||||
final attachmentShape =
|
final attachmentShape =
|
||||||
widget.attachmentShape ?? widget.shape ?? _getDefaultShape(context);
|
widget.attachmentShape ?? widget.shape ?? _getDefaultShape(context);
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => retryMessage(context),
|
|
||||||
onLongPress: () => onLongPress(context),
|
onLongPress: () => onLongPress(context),
|
||||||
child: Material(
|
child: Material(
|
||||||
color: _getBackgroundColor(),
|
color: _getBackgroundColor(),
|
||||||
@@ -744,17 +775,7 @@ class _MessageWidgetState extends State<MessageWidget> {
|
|||||||
child: Transform(
|
child: Transform(
|
||||||
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
|
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Column(
|
child: attachmentWidget,
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
|
||||||
children: <Widget>[
|
|
||||||
getFailedMessageWidget(
|
|
||||||
context,
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
),
|
|
||||||
attachmentWidget,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -809,55 +830,6 @@ class _MessageWidgetState extends State<MessageWidget> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget getFailedMessageWidget(
|
|
||||||
BuildContext context, {
|
|
||||||
EdgeInsetsGeometry padding,
|
|
||||||
}) {
|
|
||||||
Widget failedWidget;
|
|
||||||
if (widget.message.status == MessageSendingStatus.FAILED) {
|
|
||||||
failedWidget = Text(
|
|
||||||
'MESSAGE FAILED · CLICK TO TRY AGAIN',
|
|
||||||
style: widget.messageTheme.messageText.copyWith(
|
|
||||||
color: Theme.of(context).brightness == Brightness.dark
|
|
||||||
? Colors.white.withOpacity(.5)
|
|
||||||
: Colors.black.withOpacity(.5),
|
|
||||||
fontSize: 11,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (widget.message.status == MessageSendingStatus.FAILED_UPDATE) {
|
|
||||||
failedWidget = Text(
|
|
||||||
'MESSAGE UPDATE FAILED · CLICK TO TRY AGAIN',
|
|
||||||
style: widget.messageTheme.messageText.copyWith(
|
|
||||||
color: Theme.of(context).brightness == Brightness.dark
|
|
||||||
? Colors.white.withOpacity(.5)
|
|
||||||
: Colors.black.withOpacity(.5),
|
|
||||||
fontSize: 11,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (widget.message.status == MessageSendingStatus.FAILED_DELETE) {
|
|
||||||
failedWidget = Text(
|
|
||||||
'MESSAGE DELETE FAILED · CLICK TO TRY AGAIN',
|
|
||||||
style: widget.messageTheme.messageText.copyWith(
|
|
||||||
color: Theme.of(context).brightness == Brightness.dark
|
|
||||||
? Colors.white.withOpacity(.5)
|
|
||||||
: Colors.black.withOpacity(.5),
|
|
||||||
fontSize: 11,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (failedWidget != null) {
|
|
||||||
return Padding(
|
|
||||||
padding: padding ?? EdgeInsets.zero,
|
|
||||||
child: failedWidget,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SizedBox();
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildTextBubble(BuildContext context) {
|
Widget _buildTextBubble(BuildContext context) {
|
||||||
Widget child = Transform(
|
Widget child = Transform(
|
||||||
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
|
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
|
||||||
@@ -867,26 +839,20 @@ class _MessageWidgetState extends State<MessageWidget> {
|
|||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: widget.textPadding,
|
padding: widget.textPadding,
|
||||||
child: Column(
|
child: widget.textBuilder != null
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
? widget.textBuilder(context, widget.message)
|
||||||
children: [
|
: MessageText(
|
||||||
getFailedMessageWidget(context),
|
onLinkTap: widget.onLinkTap,
|
||||||
widget.textBuilder != null
|
message: widget.message,
|
||||||
? widget.textBuilder(context, widget.message)
|
onMentionTap: widget.onMentionTap,
|
||||||
: MessageText(
|
messageTheme: isOnlyEmoji
|
||||||
onLinkTap: widget.onLinkTap,
|
? widget.messageTheme.copyWith(
|
||||||
message: widget.message,
|
messageText:
|
||||||
onMentionTap: widget.onMentionTap,
|
widget.messageTheme.messageText.copyWith(
|
||||||
messageTheme: isOnlyEmoji
|
fontSize: 40,
|
||||||
? widget.messageTheme.copyWith(
|
))
|
||||||
messageText:
|
: widget.messageTheme,
|
||||||
widget.messageTheme.messageText.copyWith(
|
),
|
||||||
fontSize: 40,
|
|
||||||
))
|
|
||||||
: widget.messageTheme,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (widget.message.attachments
|
if (widget.message.attachments
|
||||||
?.any((element) => element.ogScrapeUrl != null) ==
|
?.any((element) => element.ogScrapeUrl != null) ==
|
||||||
@@ -896,7 +862,6 @@ class _MessageWidgetState extends State<MessageWidget> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => retryMessage(context),
|
|
||||||
onLongPress: () => onLongPress(context),
|
onLongPress: () => onLongPress(context),
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
@@ -907,12 +872,6 @@ class _MessageWidgetState extends State<MessageWidget> {
|
|||||||
widget.message.text.characters.every((c) => Emoji.byChar(c) != null);
|
widget.message.text.characters.every((c) => Emoji.byChar(c) != null);
|
||||||
|
|
||||||
Color _getBackgroundColor() {
|
Color _getBackgroundColor() {
|
||||||
if ((widget.message.status == MessageSendingStatus.FAILED ||
|
|
||||||
widget.message.status == MessageSendingStatus.FAILED_UPDATE ||
|
|
||||||
widget.message.status == MessageSendingStatus.FAILED_DELETE)) {
|
|
||||||
return Color(0xffd0021B).withOpacity(.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (widget.message.attachments
|
if (widget.message.attachments
|
||||||
?.any((element) => element.ogScrapeUrl != null) ==
|
?.any((element) => element.ogScrapeUrl != null) ==
|
||||||
true) {
|
true) {
|
||||||
|
|||||||
@@ -35,15 +35,6 @@ class SendingIndicator extends StatelessWidget {
|
|||||||
size: 8,
|
size: 8,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (message.status == MessageSendingStatus.FAILED ||
|
|
||||||
message.status == MessageSendingStatus.FAILED_UPDATE ||
|
|
||||||
message.status == MessageSendingStatus.FAILED_DELETE) {
|
|
||||||
return Icon(
|
|
||||||
Icons.error_outline,
|
|
||||||
size: 8,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SizedBox();
|
return SizedBox();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -757,4 +757,29 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
height: size,
|
height: size,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
factory StreamSvgIcon.error({
|
||||||
|
double size,
|
||||||
|
Color color,
|
||||||
|
}) {
|
||||||
|
return StreamSvgIcon(
|
||||||
|
assetName: 'Icon_error.svg',
|
||||||
|
color: color,
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
factory StreamSvgIcon.circle_up({
|
||||||
|
double size,
|
||||||
|
Color color,
|
||||||
|
}) {
|
||||||
|
return StreamSvgIcon(
|
||||||
|
assetName: 'Icon_circle_up.svg',
|
||||||
|
color: color,
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user