From 1497996becbc12001fea78e96999dc64d76709bf Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Wed, 12 May 2021 15:39:10 -0700 Subject: [PATCH] Malformed Message Warning --- assets/core/broken_heart_24.png | Bin 0 -> 20262 bytes assets/core/broken_heart_24.svg | 71 ++++++++++++++++++++++ lib/model.dart | 82 ++++++++++++++------------ lib/views/messageview.dart | 64 +++++++++++--------- lib/widgets/DropdownContacts.dart | 27 +++++---- lib/widgets/invitationbubble.dart | 80 ++++++++++++++----------- lib/widgets/malformedbubble.dart | 63 ++++++++++++++++++++ lib/widgets/messageloadingbubble.dart | 2 +- lib/widgets/messagerow.dart | 32 +++++++--- 9 files changed, 299 insertions(+), 122 deletions(-) create mode 100644 assets/core/broken_heart_24.png create mode 100644 assets/core/broken_heart_24.svg create mode 100644 lib/widgets/malformedbubble.dart diff --git a/assets/core/broken_heart_24.png b/assets/core/broken_heart_24.png new file mode 100644 index 0000000000000000000000000000000000000000..9ad024fc572f7016d84924210b79bb5ed1daeec6 GIT binary patch literal 20262 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 literal 0 HcmV?d00001 diff --git a/assets/core/broken_heart_24.svg b/assets/core/broken_heart_24.svg new file mode 100644 index 0000000..73f47f7 --- /dev/null +++ b/assets/core/broken_heart_24.svg @@ -0,0 +1,71 @@ + + + +image/svg+xml + + + + + + + \ No newline at end of file diff --git a/lib/model.dart b/lib/model.dart index bead765..cb4fd9e 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -363,6 +363,7 @@ class MessageState extends ChangeNotifier { bool _ackd = false; bool _error = false; bool _loaded = false; + bool _malformed = false; MessageState({ BuildContext context, @@ -379,6 +380,7 @@ class MessageState extends ChangeNotifier { get timestamp => this._timestamp; get ackd => this._ackd; get error => this._error; + get malformed => this._malformed; get senderOnion => this._senderOnion; get senderImage => this._senderImage; get loaded => this._loaded; @@ -399,50 +401,54 @@ class MessageState extends ChangeNotifier { void tryLoad(BuildContext context) { Provider.of(context, listen: false).cwtch.GetMessage(profileOnion, contactHandle, messageIndex).then((jsonMessage) { - dynamic messageWrapper = jsonDecode(jsonMessage); - if (messageWrapper['Message'] == null || messageWrapper['Message'] == '' || messageWrapper['Message'] == '{}') { - //todo: remove once sent group messages are prestored - Future.delayed(const Duration(milliseconds: 2), () { - tryLoad(context); - }); - return; - } + try { + dynamic messageWrapper = jsonDecode(jsonMessage); + if (messageWrapper['Message'] == null || messageWrapper['Message'] == '' || messageWrapper['Message'] == '{}') { + this._senderOnion = profileOnion; + //todo: remove once sent group messages are prestored + Future.delayed(const Duration(milliseconds: 2), () { + tryLoad(context); + }); + return; + } + dynamic message = jsonDecode(messageWrapper['Message']); + this._message = message['d']; + this._overlay = int.parse(message['o'].toString()); + this._timestamp = DateTime.tryParse(messageWrapper['Timestamp']); + this._senderOnion = messageWrapper['PeerID']; + this._senderImage = messageWrapper['ContactImage']; - dynamic message = jsonDecode(messageWrapper['Message']); - this._message = message['d']; - this._overlay = int.parse(message['o'].toString()); - this._timestamp = DateTime.tryParse(messageWrapper['Timestamp']); - this._senderOnion = messageWrapper['PeerID']; - this._senderImage = messageWrapper['ContactImage']; + // If this is a group, store the signature + if (contactHandle.length == 32) { + this._signature = messageWrapper['Signature']; + } - // If this is a group, store the signature - if (contactHandle.length == 32) { - this._signature = messageWrapper['Signature']; - } - - // if this is an invite, get the contact handle - if (this.isInvite) { - if (message['d'].toString().length == 56) { - this._inviteTarget = message['d']; - var targetContact = Provider.of(context).contactList.getContact(this._inviteTarget); - this._inviteNick = targetContact == null ? message['d'] : targetContact.nickname; - } else { - var parts = message['d'].toString().split("||"); - if (parts.length == 2) { - print("jsondecoding: "+utf8.fuse(base64).decode(parts[1].substring(5))); - var jsonObj = jsonDecode(utf8.fuse(base64).decode(parts[1].substring(5))); - this._inviteTarget = jsonObj['GroupID']; - this._inviteNick = jsonObj['GroupName']; + // if this is an invite, get the contact handle + if (this.isInvite) { + if (message['d'].toString().length == 56) { + this._inviteTarget = message['d']; + var targetContact = Provider.of(context).contactList.getContact(this._inviteTarget); + this._inviteNick = targetContact == null ? message['d'] : targetContact.nickname; + } else { + var parts = message['d'].toString().split("||"); + if (parts.length == 2) { + print("jsondecoding: " + utf8.fuse(base64).decode(parts[1].substring(5))); + var jsonObj = jsonDecode(utf8.fuse(base64).decode(parts[1].substring(5))); + this._inviteTarget = jsonObj['GroupID']; + this._inviteNick = jsonObj['GroupName']; + } } } - } - this._loaded = true; + this._loaded = true; - //update ackd and error last as they are changenotified - this.ackd = messageWrapper['Acknowledged']; - if (messageWrapper['Error'] != null) { - this.error = true; + //update ackd and error last as they are changenotified + this.ackd = messageWrapper['Acknowledged']; + if (messageWrapper['Error'] != null) { + this.error = true; + } + } catch (e) { + this._malformed = true; } }); } diff --git a/lib/views/messageview.dart b/lib/views/messageview.dart index f163348..090a52d 100644 --- a/lib/views/messageview.dart +++ b/lib/views/messageview.dart @@ -81,18 +81,16 @@ class _MessageViewState extends State { void _sendMessage([String ignoredParam]) { ChatMessage cm = new ChatMessage(o: 1, d: ctrlrCompose.value.text); - Provider.of(context, listen: false).cwtch.SendMessage( - Provider.of(context, listen: false).profileOnion, - Provider.of(context, listen: false).onion, - jsonEncode(cm)); + Provider.of(context, listen: false) + .cwtch + .SendMessage(Provider.of(context, listen: false).profileOnion, Provider.of(context, listen: false).onion, jsonEncode(cm)); _sendMessageHelper(); } void _sendInvitation([String ignoredParam]) { - Provider.of(context, listen: false).cwtch.SendInvitation( - Provider.of(context, listen: false).profileOnion, - Provider.of(context, listen: false).onion, - this.selectedContact); + Provider.of(context, listen: false) + .cwtch + .SendInvitation(Provider.of(context, listen: false).profileOnion, Provider.of(context, listen: false).onion, this.selectedContact); _sendMessageHelper(); } @@ -121,23 +119,25 @@ class _MessageViewState extends State { textInputAction: TextInputAction.send, onSubmitted: _sendMessage, )), - Column(children:[SizedBox( - width: 100, - height: 50, - child: Padding( - padding: EdgeInsets.fromLTRB(2, 2, 2, 2), - child: ElevatedButton( - child: Icon(Icons.send, size: 24, color: Provider.of(context).theme.mainTextColor()), - style: ButtonStyle( - fixedSize: MaterialStateProperty.all(Size(86, 50)), - backgroundColor: MaterialStateProperty.all(Provider.of(context).theme.defaultButtonColor()), - ), - onPressed: _sendMessage, - ))), - SizedBox( - width: 86, height: 40, - child: IconButton(icon: Icon(Icons.insert_invitation, size: 12, color: Provider.of(context).theme.mainTextColor()), onPressed: () => _modalSendInvitation(context)) - ),]) + Column(children: [ + SizedBox( + width: 100, + height: 50, + child: Padding( + padding: EdgeInsets.fromLTRB(2, 2, 2, 2), + child: ElevatedButton( + child: Icon(Icons.send, size: 24, color: Provider.of(context).theme.mainTextColor()), + style: ButtonStyle( + fixedSize: MaterialStateProperty.all(Size(86, 50)), + backgroundColor: MaterialStateProperty.all(Provider.of(context).theme.defaultButtonColor()), + ), + onPressed: _sendMessage, + ))), + SizedBox( + width: 86, + height: 40, + child: IconButton(icon: Icon(Icons.insert_invitation, size: 12, color: Provider.of(context).theme.mainTextColor()), onPressed: () => _modalSendInvitation(context))), + ]) ], ), ); @@ -165,10 +165,16 @@ class _MessageViewState extends State { SizedBox( height: 20, ), - ChangeNotifierProvider.value(value: Provider.of(ctx, listen: false), child: DropdownContacts(onChanged: (newVal) { - setState((){ this.selectedContact = newVal; }); - })), - SizedBox(height: 20,), + ChangeNotifierProvider.value( + value: Provider.of(ctx, listen: false), + child: DropdownContacts(onChanged: (newVal) { + setState(() { + this.selectedContact = newVal; + }); + })), + SizedBox( + height: 20, + ), ElevatedButton( child: Text(AppLocalizations.of(bcontext).inviteBtn, semanticsLabel: AppLocalizations.of(bcontext).inviteBtn), onPressed: () { diff --git a/lib/widgets/DropdownContacts.dart b/lib/widgets/DropdownContacts.dart index 9ea288e..5a8d80a 100644 --- a/lib/widgets/DropdownContacts.dart +++ b/lib/widgets/DropdownContacts.dart @@ -8,7 +8,9 @@ import '../model.dart'; // Displays nicknames to UI but uses handles as values // Pass an onChanged handler to access value class DropdownContacts extends StatefulWidget { - DropdownContacts({this.onChanged,}); + DropdownContacts({ + this.onChanged, + }); final Function(dynamic) onChanged; @override @@ -20,15 +22,18 @@ class _DropdownContactsState extends State { @override Widget build(BuildContext context) { - return DropdownButton(value: this.selected, items: Provider.of(context, listen: false).contactList.contacts.map>((ContactInfoState contact) { - return DropdownMenuItem(value: contact.onion, child: Text(contact.nickname??contact.onion)); - }).toList(), onChanged: (newVal) { - setState(() { - this.selected = newVal; - }); - if (widget.onChanged != null) { - widget.onChanged(newVal); - } - }); + return DropdownButton( + value: this.selected, + items: Provider.of(context, listen: false).contactList.contacts.map>((ContactInfoState contact) { + return DropdownMenuItem(value: contact.onion, child: Text(contact.nickname ?? contact.onion)); + }).toList(), + onChanged: (newVal) { + setState(() { + this.selected = newVal; + }); + if (widget.onChanged != null) { + widget.onChanged(newVal); + } + }); } } diff --git a/lib/widgets/invitationbubble.dart b/lib/widgets/invitationbubble.dart index 32fcc41..8f52d0b 100644 --- a/lib/widgets/invitationbubble.dart +++ b/lib/widgets/invitationbubble.dart @@ -43,27 +43,33 @@ class InvitationBubbleState extends State { senderDisplayStr = contact.nickname ?? contact.onion; } } - var wdgSender = Center(widthFactor:1, child: SelectableText(senderDisplayStr + '\u202F', - style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of(context).theme.messageFromMeTextColor() : Provider.of(context).theme.messageFromOtherTextColor()))); + var wdgSender = Center( + widthFactor: 1, + child: SelectableText(senderDisplayStr + '\u202F', + style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of(context).theme.messageFromMeTextColor() : Provider.of(context).theme.messageFromOtherTextColor()))); // todo: translations var messageStr = ""; if (fromMe) { //todo: get group name? - messageStr = "You sent an invitation for "+(isGroup ? "a group" : Provider.of(context).message ?? ""); + messageStr = "You sent an invitation for " + (isGroup ? "a group" : Provider.of(context).message ?? ""); } else { - messageStr = (isGroup ? "You have been invited to join "+(Provider.of(context).inviteNick??"") : "This is a contact suggestion for:") + "\n" + (Provider.of(context).inviteTarget ?? ""); + messageStr = (isGroup ? "You have been invited to join " + (Provider.of(context).inviteNick ?? "") : "This is a contact suggestion for:") + + "\n" + + (Provider.of(context).inviteTarget ?? ""); } - var wdgMessage = Center(widthFactor:1, child: SelectableText( - messageStr + '\u202F', - key: Key(myKey), - focusNode: _focus, - style: TextStyle( - color: fromMe ? Provider.of(context).theme.messageFromMeTextColor() : Provider.of(context).theme.messageFromOtherTextColor(), - ), - textAlign: TextAlign.left, - textWidthBasis: TextWidthBasis.longestLine, - )); + var wdgMessage = Center( + widthFactor: 1, + child: SelectableText( + messageStr + '\u202F', + key: Key(myKey), + focusNode: _focus, + style: TextStyle( + color: fromMe ? Provider.of(context).theme.messageFromMeTextColor() : Provider.of(context).theme.messageFromOtherTextColor(), + ), + textAlign: TextAlign.left, + textWidthBasis: TextWidthBasis.longestLine, + )); Widget wdgDecorations; if (fromMe) { @@ -72,15 +78,14 @@ class InvitationBubbleState extends State { child: Row( mainAxisSize: MainAxisSize.min, children: [ - Text(prettyDate, style: TextStyle( - fontSize: 9.0, - color: fromMe ? Provider.of(context).theme.messageFromMeTextColor() : Provider.of(context).theme.messageFromOtherTextColor() - ), textAlign: fromMe ? TextAlign.right : TextAlign.left), + Text(prettyDate, + style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of(context).theme.messageFromMeTextColor() : Provider.of(context).theme.messageFromOtherTextColor()), + textAlign: fromMe ? TextAlign.right : TextAlign.left), !fromMe ? SizedBox(width: 1, height: 1) : Provider.of(context).ackd - ? Icon(Icons.check_circle_outline, color: Provider.of(context).theme.messageFromMeTextColor(), size: 12) - : Icon(Icons.hourglass_bottom_outlined, color: Provider.of(context).theme.messageFromMeTextColor(), size: 12) + ? Icon(Icons.check_circle_outline, color: Provider.of(context).theme.messageFromMeTextColor(), size: 12) + : Icon(Icons.hourglass_bottom_outlined, color: Provider.of(context).theme.messageFromMeTextColor(), size: 12) ], )); } else if (isAccepted) { @@ -88,10 +93,12 @@ class InvitationBubbleState extends State { } else if (this.rejected) { wdgDecorations = Text("Rejected."); } else { - wdgDecorations = Center(widthFactor:1,child:Row(children: [ - Padding(padding: EdgeInsets.all(5), child: TextButton(child: Text("Reject"), onPressed: _btnReject)), - Padding(padding: EdgeInsets.all(5), child: TextButton(child: Text("Accept"), onPressed: _btnAccept)), - ])); + wdgDecorations = Center( + widthFactor: 1, + child: Row(children: [ + Padding(padding: EdgeInsets.all(5), child: TextButton(child: Text("Reject"), onPressed: _btnReject)), + Padding(padding: EdgeInsets.all(5), child: TextButton(child: Text("Accept"), onPressed: _btnAccept)), + ])); } return LayoutBuilder(builder: (context, constraints) { @@ -110,21 +117,26 @@ class InvitationBubbleState extends State { bottomRight: fromMe ? Radius.zero : Radius.circular(borderRadiousEh), ), ), - child: Center(widthFactor: 1.0,child:Padding( - padding: EdgeInsets.all(9.0), - child: Row(mainAxisSize: MainAxisSize.min, children: [Center(widthFactor: 1,child: Padding(padding:EdgeInsets.all(4), child:Icon(Icons.group_add, size: 32))), - Center(widthFactor: 1.0,child: Column( - crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, - mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: fromMe ? [wdgMessage, wdgDecorations] : [wdgSender, wdgMessage, wdgDecorations])), - ]))))); + child: Center( + widthFactor: 1.0, + child: Padding( + padding: EdgeInsets.all(9.0), + child: Row(mainAxisSize: MainAxisSize.min, children: [ + Center(widthFactor: 1, child: Padding(padding: EdgeInsets.all(4), child: Icon(Icons.group_add, size: 32))), + Center( + widthFactor: 1.0, + child: Column( + crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, + mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: fromMe ? [wdgMessage, wdgDecorations] : [wdgSender, wdgMessage, wdgDecorations])), + ]))))); }); } void _btnReject() { //todo: how should we track inline invite rejections? - setState(()=>this.rejected = true); + setState(() => this.rejected = true); } void _btnAccept() { diff --git a/lib/widgets/malformedbubble.dart b/lib/widgets/malformedbubble.dart new file mode 100644 index 0000000..cc8bfcd --- /dev/null +++ b/lib/widgets/malformedbubble.dart @@ -0,0 +1,63 @@ +import 'dart:convert'; +import 'dart:ffi'; + +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../settings.dart'; + +final Color malformedColor = Color(0xFFE85DA1); + +// MalformedBubble is displayed in the case of a malformed message +class MalformedBubble extends StatefulWidget { + @override + MalformedBubbleState createState() => MalformedBubbleState(); +} + +class MalformedBubbleState extends State { + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (context, constraints) { + return Center( + widthFactor: 1.0, + child: Container( + decoration: BoxDecoration( + color: malformedColor, + border: Border.all(color: malformedColor, width: 1), + borderRadius: BorderRadius.only( + topLeft: Radius.zero, + topRight: Radius.zero, + bottomLeft: Radius.zero, + bottomRight: Radius.zero, + ), + ), + child: Center( + widthFactor: 1.0, + child: Padding( + padding: EdgeInsets.all(9.0), + child: Row(mainAxisSize: MainAxisSize.min, children: [ + Center( + widthFactor: 1, + child: Padding( + padding: EdgeInsets.all(4), + child: Image( + image: AssetImage("assets/core/broken_heart_24.png"), + filterQuality: FilterQuality.medium, + // We need some theme specific blending here...we might want to consider making this a theme level attribute + colorBlendMode: BlendMode.srcIn, + color: Provider.of(context).theme.mainTextColor(), + isAntiAlias: false, + width: 32, + height: 32))), + Center( + widthFactor: 1.0, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [Text("Malformed Message")], + )) + ]))))); + }); + } +} diff --git a/lib/widgets/messageloadingbubble.dart b/lib/widgets/messageloadingbubble.dart index 237c770..f9605d1 100644 --- a/lib/widgets/messageloadingbubble.dart +++ b/lib/widgets/messageloadingbubble.dart @@ -13,6 +13,6 @@ class MessageLoadingBubble extends StatefulWidget { class MessageLoadingBubbleState extends State { @override Widget build(BuildContext context) { - return Center(child:Row(children:[SizedBox(width:40, height:100, child: Text(""))])); + return Center(child: Row(children: [SizedBox(width: 40, height: 100, child: Text(""))])); } } diff --git a/lib/widgets/messagerow.dart b/lib/widgets/messagerow.dart index 25385ee..035b068 100644 --- a/lib/widgets/messagerow.dart +++ b/lib/widgets/messagerow.dart @@ -9,6 +9,7 @@ import '../main.dart'; import '../model.dart'; import '../settings.dart'; import 'invitationbubble.dart'; +import 'malformedbubble.dart'; import 'messagebubble.dart'; import 'messageloadingbubble.dart'; @@ -23,8 +24,17 @@ class _MessageRowState extends State { @override Widget build(BuildContext context) { var fromMe = Provider.of(context).senderOnion == Provider.of(context).onion; + var malformed = Provider.of(context).malformed; - Widget wdgBubble = Flexible(flex: 3, fit: FlexFit.loose, child: Provider.of(context).loaded == true ? widgetForOverlay(Provider.of(context).overlay) : MessageLoadingBubble()); + // If the message is malformed then override fromme as we can't trust it + if (malformed) { + fromMe = false; + } + + Widget wdgBubble = Flexible( + flex: 3, + fit: FlexFit.loose, + child: malformed ? MalformedBubble() : (Provider.of(context).loaded == true ? widgetForOverlay(Provider.of(context).overlay) : MessageLoadingBubble())); Widget wdgIcons = Icon(Icons.delete_forever_outlined, color: Provider.of(context).theme.dropShadowColor()); Widget wdgSpacer = Expanded(child: SizedBox(width: 60, height: 10)); var widgetRow = []; @@ -38,12 +48,14 @@ class _MessageRowState extends State { } else { var contact = Provider.of(context); Widget wdgPortrait = GestureDetector( - onTap: _btnAdd, - child: ProfileImage( - diameter: 48.0, - imagePath: Provider.of(context).senderImage ?? contact.imagePath, - //maskOut: contact.status != "Authenticated", - border: contact.status == "Authenticated" ? Provider.of(context).theme.portraitOnlineBorderColor() : Provider.of(context).theme.portraitOfflineBorderColor())); + onTap: _btnAdd, + child: Padding( + padding: EdgeInsets.all(4.0), + child: ProfileImage( + diameter: 48.0, + imagePath: Provider.of(context).senderImage ?? contact.imagePath, + //maskOut: contact.status != "Authenticated", + border: contact.status == "Authenticated" ? Provider.of(context).theme.portraitOnlineBorderColor() : Provider.of(context).theme.portraitOfflineBorderColor()))); widgetRow = [ wdgPortrait, @@ -58,9 +70,11 @@ class _MessageRowState extends State { Widget widgetForOverlay(int o) { switch (o) { - case 1: return MessageBubble(); + case 1: + return MessageBubble(); case 100: - case 101: return InvitationBubble(); + case 101: + return InvitationBubble(); } return null; }