From 814e6df6f6211db8b9c2a89968c98254e21f51cb Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Thu, 7 Jul 2022 12:34:31 -0700 Subject: [PATCH 1/4] Implement View Replies --- lib/l10n/intl_cy.arb | 4 +- lib/l10n/intl_da.arb | 4 +- lib/l10n/intl_de.arb | 4 +- lib/l10n/intl_el.arb | 4 +- lib/l10n/intl_en.arb | 4 +- lib/l10n/intl_es.arb | 4 +- lib/l10n/intl_fr.arb | 4 +- lib/l10n/intl_it.arb | 4 +- lib/l10n/intl_lb.arb | 4 +- lib/l10n/intl_no.arb | 4 +- lib/l10n/intl_pl.arb | 4 +- lib/l10n/intl_pt.arb | 4 +- lib/l10n/intl_ro.arb | 4 +- lib/l10n/intl_ru.arb | 4 +- lib/models/message.dart | 36 ++++++++ lib/models/messages/quotedmessage.dart | 1 - lib/widgets/messagerow.dart | 113 ++++++++++++++++++++++++- lib/widgets/staticmessagebubble.dart | 72 ++++++++++++++++ 18 files changed, 259 insertions(+), 19 deletions(-) create mode 100644 lib/widgets/staticmessagebubble.dart diff --git a/lib/l10n/intl_cy.arb b/lib/l10n/intl_cy.arb index 80fcdb37..4e58d39c 100644 --- a/lib/l10n/intl_cy.arb +++ b/lib/l10n/intl_cy.arb @@ -1,6 +1,8 @@ { "@@locale": "cy", - "@@last_modified": "2022-07-06T20:42:11+02:00", + "@@last_modified": "2022-07-07T21:07:20+02:00", + "headingReplies": "Replies", + "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", "stopSharingFile": "Stop Sharing File", "manageSharedFiles": "Manage Shared Files", diff --git a/lib/l10n/intl_da.arb b/lib/l10n/intl_da.arb index d2c9e9af..a3200930 100644 --- a/lib/l10n/intl_da.arb +++ b/lib/l10n/intl_da.arb @@ -1,6 +1,8 @@ { "@@locale": "da", - "@@last_modified": "2022-07-06T20:42:11+02:00", + "@@last_modified": "2022-07-07T21:07:20+02:00", + "headingReplies": "Replies", + "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", "stopSharingFile": "Stop Sharing File", "manageSharedFiles": "Manage Shared Files", diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index 17090263..6a51c81f 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,8 @@ { "@@locale": "de", - "@@last_modified": "2022-07-06T20:42:11+02:00", + "@@last_modified": "2022-07-07T21:07:20+02:00", + "headingReplies": "Replies", + "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", "stopSharingFile": "Stop Sharing File", "manageSharedFiles": "Manage Shared Files", diff --git a/lib/l10n/intl_el.arb b/lib/l10n/intl_el.arb index 27a108e8..842c0254 100644 --- a/lib/l10n/intl_el.arb +++ b/lib/l10n/intl_el.arb @@ -1,6 +1,8 @@ { "@@locale": "el", - "@@last_modified": "2022-07-06T20:42:11+02:00", + "@@last_modified": "2022-07-07T21:07:20+02:00", + "headingReplies": "Replies", + "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", "stopSharingFile": "Stop Sharing File", "manageSharedFiles": "Manage Shared Files", diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 1f3e4da7..9c44e787 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1,6 +1,8 @@ { "@@locale": "en", - "@@last_modified": "2022-07-06T20:42:11+02:00", + "@@last_modified": "2022-07-07T21:07:20+02:00", + "headingReplies": "Replies", + "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", "stopSharingFile": "Stop Sharing File", "manageSharedFiles": "Manage Shared Files", diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index 35227f35..fd08919b 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,8 @@ { "@@locale": "es", - "@@last_modified": "2022-07-06T20:42:11+02:00", + "@@last_modified": "2022-07-07T21:07:20+02:00", + "headingReplies": "Replies", + "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", "stopSharingFile": "Stop Sharing File", "manageSharedFiles": "Manage Shared Files", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index e77e678d..14d62509 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,6 +1,8 @@ { "@@locale": "fr", - "@@last_modified": "2022-07-06T20:42:11+02:00", + "@@last_modified": "2022-07-07T21:07:20+02:00", + "headingReplies": "Replies", + "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", "stopSharingFile": "Stop Sharing File", "manageSharedFiles": "Manage Shared Files", diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 65630157..c8250f9b 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,6 +1,8 @@ { "@@locale": "it", - "@@last_modified": "2022-07-06T20:42:11+02:00", + "@@last_modified": "2022-07-07T21:07:20+02:00", + "headingReplies": "Replies", + "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", "stopSharingFile": "Stop Sharing File", "manageSharedFiles": "Manage Shared Files", diff --git a/lib/l10n/intl_lb.arb b/lib/l10n/intl_lb.arb index 27eb26c0..bee2b7e6 100644 --- a/lib/l10n/intl_lb.arb +++ b/lib/l10n/intl_lb.arb @@ -1,6 +1,8 @@ { "@@locale": "lb", - "@@last_modified": "2022-07-06T20:42:11+02:00", + "@@last_modified": "2022-07-07T21:07:20+02:00", + "headingReplies": "Replies", + "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", "stopSharingFile": "Stop Sharing File", "manageSharedFiles": "Manage Shared Files", diff --git a/lib/l10n/intl_no.arb b/lib/l10n/intl_no.arb index 3dde1ad9..d13492a7 100644 --- a/lib/l10n/intl_no.arb +++ b/lib/l10n/intl_no.arb @@ -1,6 +1,8 @@ { "@@locale": "no", - "@@last_modified": "2022-07-06T20:42:11+02:00", + "@@last_modified": "2022-07-07T21:07:20+02:00", + "headingReplies": "Replies", + "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", "stopSharingFile": "Stop Sharing File", "manageSharedFiles": "Manage Shared Files", diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index 4d2d8ec8..b85854fe 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,6 +1,8 @@ { "@@locale": "pl", - "@@last_modified": "2022-07-06T20:42:11+02:00", + "@@last_modified": "2022-07-07T21:07:20+02:00", + "headingReplies": "Replies", + "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", "stopSharingFile": "Stop Sharing File", "manageSharedFiles": "Manage Shared Files", diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index 9c6857b9..c5e363b3 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,6 +1,8 @@ { "@@locale": "pt", - "@@last_modified": "2022-07-06T20:42:11+02:00", + "@@last_modified": "2022-07-07T21:07:20+02:00", + "headingReplies": "Replies", + "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", "stopSharingFile": "Stop Sharing File", "manageSharedFiles": "Manage Shared Files", diff --git a/lib/l10n/intl_ro.arb b/lib/l10n/intl_ro.arb index 6126bf74..fb756dfa 100644 --- a/lib/l10n/intl_ro.arb +++ b/lib/l10n/intl_ro.arb @@ -1,6 +1,8 @@ { "@@locale": "ro", - "@@last_modified": "2022-07-06T20:42:11+02:00", + "@@last_modified": "2022-07-07T21:07:20+02:00", + "headingReplies": "Replies", + "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", "stopSharingFile": "Stop Sharing File", "manageSharedFiles": "Manage Shared Files", diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index 6d72fe2e..d0a225d1 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -1,6 +1,8 @@ { "@@locale": "ru", - "@@last_modified": "2022-07-06T20:42:11+02:00", + "@@last_modified": "2022-07-07T21:07:20+02:00", + "headingReplies": "Replies", + "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", "stopSharingFile": "Stop Sharing File", "manageSharedFiles": "Manage Shared Files", diff --git a/lib/models/message.dart b/lib/models/message.dart index 7d169698..68721bc7 100644 --- a/lib/models/message.dart +++ b/lib/models/message.dart @@ -201,6 +201,42 @@ class ByContentHash implements CacheHandler { } } +List getReplies(MessageCache cache, int messageIdentifier) { + List replies = List.empty(growable: true); + + try { + MessageInfo original = cache.cache[messageIdentifier]!; + String hash = original.metadata.contenthash; + + cache.cache.forEach((key, messageInfo) { + // only bother searching for identifiers that came *after* + if (key > messageIdentifier) { + try { + dynamic message = jsonDecode(messageInfo.wrapper); + var content = message['d'] as dynamic; + dynamic qmessage = jsonDecode(content); + if (qmessage["body"] == null || qmessage["quotedHash"] == null) { + return; + } + if (qmessage["quotedHash"] == hash) { + replies.add(compileOverlay(messageInfo)); + } + } catch (e) { + // ignore + } + } + }); + } catch (e) { + EnvironmentConfig.debugLog("message handler exception on get from cache: $e"); + } + + replies.sort((a, b) { + return a.getMetadata().messageID.compareTo(b.getMetadata().messageID); + }); + + return replies; +} + Future messageHandler(BuildContext context, String profileOnion, int conversationIdentifier, CacheHandler cacheHandler) async { var malformedMetadata = MessageMetadata(profileOnion, conversationIdentifier, 0, DateTime.now(), "", "", "", {}, false, true, false, ""); var cwtch = Provider.of(context, listen: false).cwtch; diff --git a/lib/models/messages/quotedmessage.dart b/lib/models/messages/quotedmessage.dart index 471ed536..9f819618 100644 --- a/lib/models/messages/quotedmessage.dart +++ b/lib/models/messages/quotedmessage.dart @@ -36,7 +36,6 @@ class QuotedMessage extends Message { var content = message["body"]; return Text( content, - overflow: TextOverflow.ellipsis, ); } catch (e) { return MalformedBubble(); diff --git a/lib/widgets/messagerow.dart b/lib/widgets/messagerow.dart index 3bbb8b87..d34f26bf 100644 --- a/lib/widgets/messagerow.dart +++ b/lib/widgets/messagerow.dart @@ -7,6 +7,7 @@ import 'package:cwtch/models/contact.dart'; import 'package:cwtch/models/message.dart'; import 'package:cwtch/models/profile.dart'; import 'package:cwtch/views/contactsview.dart'; +import 'package:cwtch/widgets/staticmessagebubble.dart'; import 'package:flutter/material.dart'; import 'package:cwtch/widgets/profileimage.dart'; import 'package:flutter/physics.dart'; @@ -15,6 +16,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import '../main.dart'; +import '../models/messagecache.dart'; import '../settings.dart'; class MessageRow extends StatefulWidget { @@ -74,7 +76,7 @@ class MessageRowState extends State with SingleTickerProviderStateMi } } - Widget wdgIcons = Platform.isAndroid + Widget wdgReply = Platform.isAndroid ? SizedBox.shrink() : Visibility( visible: EnvironmentConfig.TEST_MODE || Provider.of(context).hoveredIndex == Provider.of(context).messageID, @@ -90,13 +92,37 @@ class MessageRowState extends State with SingleTickerProviderStateMi }, icon: Icon(Icons.reply, color: Provider.of(context).theme.dropShadowColor))); + var settings = Provider.of(context); + var pis = Provider.of(context); + var cis = Provider.of(context); + var borderColor = Provider.of(context).theme.portraitOnlineBorderColor; + var messageID = Provider.of(context).messageID; + var cache = Provider.of(context).messageCache; + + Widget wdgSeeReplies = Platform.isAndroid + ? SizedBox.shrink() + : Visibility( + visible: EnvironmentConfig.TEST_MODE || Provider.of(context).hoveredIndex == Provider.of(context).messageID, + maintainSize: true, + maintainAnimation: true, + maintainState: true, + maintainInteractivity: false, + child: IconButton( + tooltip: AppLocalizations.of(context)!.viewReplies, + splashRadius: Material.defaultSplashRadius / 2, + onPressed: () { + modalShowReplies(context, AppLocalizations.of(context)!.headingReplies, settings, pis, cis, borderColor, cache, messageID); + }, + icon: Icon(Icons.message_rounded, color: Provider.of(context).theme.dropShadowColor))); + Widget wdgSpacer = Flexible(flex: 1, child: SizedBox(width: Platform.isAndroid ? 20 : 60, height: 10)); var widgetRow = []; if (fromMe) { widgetRow = [ wdgSpacer, - wdgIcons, + wdgSeeReplies, + wdgReply, actualMessage, ]; } else if (isBlocked && !showBlockedMessage) { @@ -143,7 +169,8 @@ class MessageRowState extends State with SingleTickerProviderStateMi }); })), ]))), - wdgIcons, + wdgReply, + wdgSeeReplies, wdgSpacer, ]; } else { @@ -179,7 +206,8 @@ class MessageRowState extends State with SingleTickerProviderStateMi widgetRow = [ wdgPortrait, actualMessage, - wdgIcons, + wdgReply, + wdgSeeReplies, wdgSpacer, ]; } @@ -332,3 +360,80 @@ class MessageRowState extends State with SingleTickerProviderStateMi ); } } + +void modalShowReplies(BuildContext ctx, String replyHeader, Settings settings, ProfileInfoState profile, ContactInfoState cis, Color borderColor, MessageCache cache, int messageID, + {bool showImage = true}) { + showModalBottomSheet( + context: ctx, + builder: (BuildContext bcontext) { + List replies = getReplies(cache, messageID); + + return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) { + var replyWidgets = replies.map((e) { + var fromMe = e.getMetadata().senderHandle == profile.onion; + + var bubble = StaticMessageBubble(profile, settings, e.getMetadata(), Row(children: [Flexible(child: e.getPreviewWidget(context))])); + + String imagePath = e.getMetadata().senderImage!; + var sender = profile.contactList.findContact(e.getMetadata().senderHandle); + if (sender != null) { + imagePath = showImage ? sender.imagePath : sender.defaultImagePath; + } + + if (fromMe) { + imagePath = profile.imagePath; + } + + var image = Padding( + padding: EdgeInsets.all(4.0), + child: ProfileImage( + imagePath: imagePath, + diameter: 48.0, + border: borderColor, + badgeTextColor: Colors.red, + badgeColor: Colors.red, + )); + + return Padding( + padding: EdgeInsets.all(10.0), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [image, Flexible(child: bubble)], + )); + }).toList(); + + var withHeader = replyWidgets; + + var original = StaticMessageBubble(profile, settings, cache.cache[messageID]!.metadata, Row(children: [Flexible(child: compileOverlay(cache.cache[messageID]!).getPreviewWidget(context))])); + + withHeader.insert(0, + Padding(padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Center(child: original))); + + + withHeader.insert(1, + Padding(padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), + child: Divider( + color: settings.theme.mainTextColor, + )) + ); + + withHeader.insert(2, + Padding(padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Text(replyHeader, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)))); + + + + return Scrollbar( + isAlwaysShown: true, + child: SingleChildScrollView( + clipBehavior: Clip.antiAlias, + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: viewportConstraints.maxHeight, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: withHeader, + )))); + }); + }); +} diff --git a/lib/widgets/staticmessagebubble.dart b/lib/widgets/staticmessagebubble.dart new file mode 100644 index 00000000..d5e64c8c --- /dev/null +++ b/lib/widgets/staticmessagebubble.dart @@ -0,0 +1,72 @@ +import 'package:cwtch/models/contact.dart'; +import 'package:cwtch/models/message.dart'; +import 'package:cwtch/models/profile.dart'; +import 'package:cwtch/widgets/malformedbubble.dart'; +import 'package:flutter/material.dart'; + +import '../settings.dart'; +import 'messagebubbledecorations.dart'; + +class StaticMessageBubble extends StatefulWidget { + final ProfileInfoState profile; + final Settings settings; + final MessageMetadata metadata; + final Widget child; + + StaticMessageBubble(this.profile, this.settings, this.metadata, this.child); + + @override + StaticMessageBubbleState createState() => StaticMessageBubbleState(); +} + +class StaticMessageBubbleState extends State { + @override + Widget build(BuildContext context) { + var fromMe = widget.metadata.senderHandle == widget.profile.onion; + var borderRadiousEh = 15.0; + DateTime messageDate = widget.metadata.timestamp; + + // If the sender is not us, then we want to give them a nickname... + var senderDisplayStr = ""; + if (!fromMe) { + ContactInfoState? contact = widget.profile.contactList.findContact(widget.metadata.senderHandle); + if (contact != null) { + senderDisplayStr = contact.nickname; + } else { + senderDisplayStr = widget.metadata.senderHandle; + } + } else { + senderDisplayStr = widget.profile.nickname; + } + + var wdgSender = SelectableText(senderDisplayStr, style: TextStyle(fontSize: 9.0, color: fromMe ? widget.settings.theme.messageFromMeTextColor : widget.settings.theme.messageFromOtherTextColor)); + + var wdgDecorations = MessageBubbleDecoration(ackd: widget.metadata.ackd, errored: widget.metadata.error, fromMe: fromMe, messageDate: messageDate); + + var error = widget.metadata.error; + + return LayoutBuilder(builder: (context, constraints) { + //print(constraints.toString()+", "+constraints.maxWidth.toString()); + return RepaintBoundary( + child: Container( + child: Container( + decoration: BoxDecoration( + color: error ? malformedColor : (fromMe ? widget.settings.theme.messageFromMeBackgroundColor : widget.settings.theme.messageFromOtherBackgroundColor), + border: Border.all(color: error ? malformedColor : (fromMe ? widget.settings.theme.messageFromMeBackgroundColor : widget.settings.theme.messageFromOtherBackgroundColor), width: 1), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(borderRadiousEh), + topRight: Radius.circular(borderRadiousEh), + bottomLeft: Radius.zero, + bottomRight: Radius.circular(borderRadiousEh), + ), + ), + child: Padding( + padding: EdgeInsets.all(9.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [wdgSender, widget.child, wdgDecorations]))))); + }); + } +} From 337f6dc5d9b6c49b143e0d911fd08672e14dda6d Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Thu, 7 Jul 2022 12:58:28 -0700 Subject: [PATCH 2/4] Use Long Press on Android to access replies --- lib/widgets/messagerow.dart | 23 +++++++++++------------ lib/widgets/staticmessagebubble.dart | 4 ++-- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/lib/widgets/messagerow.dart b/lib/widgets/messagerow.dart index d34f26bf..540cf54d 100644 --- a/lib/widgets/messagerow.dart +++ b/lib/widgets/messagerow.dart @@ -240,6 +240,9 @@ class MessageRowState extends State with SingleTickerProviderStateMi _runAnimation(details.velocity.pixelsPerSecond, size); Provider.of(context, listen: false).selectedIndex = Provider.of(context, listen: false).messageID; }, + onLongPress: () async { + modalShowReplies(context, AppLocalizations.of(context)!.headingReplies, settings, pis, cis, borderColor, cache, messageID); + }, child: Padding( padding: EdgeInsets.all(2), child: Align( @@ -404,23 +407,19 @@ void modalShowReplies(BuildContext ctx, String replyHeader, Settings settings, P var withHeader = replyWidgets; - var original = StaticMessageBubble(profile, settings, cache.cache[messageID]!.metadata, Row(children: [Flexible(child: compileOverlay(cache.cache[messageID]!).getPreviewWidget(context))])); + var original = StaticMessageBubble(profile, settings, cache.cache[messageID]!.metadata, Row(children: [Flexible(child: compileOverlay(cache.cache[messageID]!).getPreviewWidget(context))])); - withHeader.insert(0, - Padding(padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Center(child: original))); + withHeader.insert(0, Padding(padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Center(child: original))); - - withHeader.insert(1, - Padding(padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), + withHeader.insert( + 1, + Padding( + padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Divider( color: settings.theme.mainTextColor, - )) - ); - - withHeader.insert(2, - Padding(padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Text(replyHeader, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)))); - + ))); + withHeader.insert(2, Padding(padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Text(replyHeader, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)))); return Scrollbar( isAlwaysShown: true, diff --git a/lib/widgets/staticmessagebubble.dart b/lib/widgets/staticmessagebubble.dart index d5e64c8c..3f27489c 100644 --- a/lib/widgets/staticmessagebubble.dart +++ b/lib/widgets/staticmessagebubble.dart @@ -56,14 +56,14 @@ class StaticMessageBubbleState extends State { borderRadius: BorderRadius.only( topLeft: Radius.circular(borderRadiousEh), topRight: Radius.circular(borderRadiousEh), - bottomLeft: Radius.zero, + bottomLeft: Radius.zero, bottomRight: Radius.circular(borderRadiousEh), ), ), child: Padding( padding: EdgeInsets.all(9.0), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [wdgSender, widget.child, wdgDecorations]))))); From ad52f2e0c8b2ed4328b93f1d67519c0277693abe Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Thu, 7 Jul 2022 13:40:23 -0700 Subject: [PATCH 3/4] Fixup Padding and add message about no replies --- lib/l10n/intl_cy.arb | 4 +++- lib/l10n/intl_da.arb | 4 +++- lib/l10n/intl_de.arb | 4 +++- lib/l10n/intl_el.arb | 4 +++- lib/l10n/intl_en.arb | 4 +++- lib/l10n/intl_es.arb | 4 +++- lib/l10n/intl_fr.arb | 4 +++- lib/l10n/intl_it.arb | 4 +++- lib/l10n/intl_lb.arb | 4 +++- lib/l10n/intl_no.arb | 4 +++- lib/l10n/intl_pl.arb | 4 +++- lib/l10n/intl_pt.arb | 4 +++- lib/l10n/intl_ro.arb | 4 +++- lib/l10n/intl_ru.arb | 4 +++- lib/widgets/messagerow.dart | 23 +++++++++++++++-------- 15 files changed, 57 insertions(+), 22 deletions(-) diff --git a/lib/l10n/intl_cy.arb b/lib/l10n/intl_cy.arb index 4e58d39c..0a8fd661 100644 --- a/lib/l10n/intl_cy.arb +++ b/lib/l10n/intl_cy.arb @@ -1,6 +1,8 @@ { "@@locale": "cy", - "@@last_modified": "2022-07-07T21:07:20+02:00", + "@@last_modified": "2022-07-07T22:32:20+02:00", + "fileDownloadUnavailable": "This file appears unavailable for download. The sender may have disabled downloads for this file.", + "messageNoReplies": "There are no replies to this message.", "headingReplies": "Replies", "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", diff --git a/lib/l10n/intl_da.arb b/lib/l10n/intl_da.arb index a3200930..46d534be 100644 --- a/lib/l10n/intl_da.arb +++ b/lib/l10n/intl_da.arb @@ -1,6 +1,8 @@ { "@@locale": "da", - "@@last_modified": "2022-07-07T21:07:20+02:00", + "@@last_modified": "2022-07-07T22:32:20+02:00", + "fileDownloadUnavailable": "This file appears unavailable for download. The sender may have disabled downloads for this file.", + "messageNoReplies": "There are no replies to this message.", "headingReplies": "Replies", "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index 6a51c81f..f9b8b4de 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,8 @@ { "@@locale": "de", - "@@last_modified": "2022-07-07T21:07:20+02:00", + "@@last_modified": "2022-07-07T22:32:20+02:00", + "fileDownloadUnavailable": "This file appears unavailable for download. The sender may have disabled downloads for this file.", + "messageNoReplies": "There are no replies to this message.", "headingReplies": "Replies", "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", diff --git a/lib/l10n/intl_el.arb b/lib/l10n/intl_el.arb index 842c0254..a27e7fda 100644 --- a/lib/l10n/intl_el.arb +++ b/lib/l10n/intl_el.arb @@ -1,6 +1,8 @@ { "@@locale": "el", - "@@last_modified": "2022-07-07T21:07:20+02:00", + "@@last_modified": "2022-07-07T22:32:20+02:00", + "fileDownloadUnavailable": "This file appears unavailable for download. The sender may have disabled downloads for this file.", + "messageNoReplies": "There are no replies to this message.", "headingReplies": "Replies", "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 9c44e787..70281e8d 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1,6 +1,8 @@ { "@@locale": "en", - "@@last_modified": "2022-07-07T21:07:20+02:00", + "@@last_modified": "2022-07-07T22:32:20+02:00", + "fileDownloadUnavailable": "This file appears unavailable for download. The sender may have disabled downloads for this file.", + "messageNoReplies": "There are no replies to this message.", "headingReplies": "Replies", "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index fd08919b..55886a19 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,8 @@ { "@@locale": "es", - "@@last_modified": "2022-07-07T21:07:20+02:00", + "@@last_modified": "2022-07-07T22:32:20+02:00", + "fileDownloadUnavailable": "This file appears unavailable for download. The sender may have disabled downloads for this file.", + "messageNoReplies": "There are no replies to this message.", "headingReplies": "Replies", "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 14d62509..fd6aee80 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,6 +1,8 @@ { "@@locale": "fr", - "@@last_modified": "2022-07-07T21:07:20+02:00", + "@@last_modified": "2022-07-07T22:32:20+02:00", + "fileDownloadUnavailable": "This file appears unavailable for download. The sender may have disabled downloads for this file.", + "messageNoReplies": "There are no replies to this message.", "headingReplies": "Replies", "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index c8250f9b..ccef6c6c 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,6 +1,8 @@ { "@@locale": "it", - "@@last_modified": "2022-07-07T21:07:20+02:00", + "@@last_modified": "2022-07-07T22:32:20+02:00", + "fileDownloadUnavailable": "This file appears unavailable for download. The sender may have disabled downloads for this file.", + "messageNoReplies": "There are no replies to this message.", "headingReplies": "Replies", "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", diff --git a/lib/l10n/intl_lb.arb b/lib/l10n/intl_lb.arb index bee2b7e6..d296b51a 100644 --- a/lib/l10n/intl_lb.arb +++ b/lib/l10n/intl_lb.arb @@ -1,6 +1,8 @@ { "@@locale": "lb", - "@@last_modified": "2022-07-07T21:07:20+02:00", + "@@last_modified": "2022-07-07T22:32:20+02:00", + "fileDownloadUnavailable": "This file appears unavailable for download. The sender may have disabled downloads for this file.", + "messageNoReplies": "There are no replies to this message.", "headingReplies": "Replies", "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", diff --git a/lib/l10n/intl_no.arb b/lib/l10n/intl_no.arb index d13492a7..2849c2f9 100644 --- a/lib/l10n/intl_no.arb +++ b/lib/l10n/intl_no.arb @@ -1,6 +1,8 @@ { "@@locale": "no", - "@@last_modified": "2022-07-07T21:07:20+02:00", + "@@last_modified": "2022-07-07T22:32:20+02:00", + "fileDownloadUnavailable": "This file appears unavailable for download. The sender may have disabled downloads for this file.", + "messageNoReplies": "There are no replies to this message.", "headingReplies": "Replies", "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index b85854fe..6d599fdc 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,6 +1,8 @@ { "@@locale": "pl", - "@@last_modified": "2022-07-07T21:07:20+02:00", + "@@last_modified": "2022-07-07T22:32:20+02:00", + "fileDownloadUnavailable": "This file appears unavailable for download. The sender may have disabled downloads for this file.", + "messageNoReplies": "There are no replies to this message.", "headingReplies": "Replies", "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index c5e363b3..2abeecf1 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,6 +1,8 @@ { "@@locale": "pt", - "@@last_modified": "2022-07-07T21:07:20+02:00", + "@@last_modified": "2022-07-07T22:32:20+02:00", + "fileDownloadUnavailable": "This file appears unavailable for download. The sender may have disabled downloads for this file.", + "messageNoReplies": "There are no replies to this message.", "headingReplies": "Replies", "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", diff --git a/lib/l10n/intl_ro.arb b/lib/l10n/intl_ro.arb index fb756dfa..322bf13c 100644 --- a/lib/l10n/intl_ro.arb +++ b/lib/l10n/intl_ro.arb @@ -1,6 +1,8 @@ { "@@locale": "ro", - "@@last_modified": "2022-07-07T21:07:20+02:00", + "@@last_modified": "2022-07-07T22:32:20+02:00", + "fileDownloadUnavailable": "This file appears unavailable for download. The sender may have disabled downloads for this file.", + "messageNoReplies": "There are no replies to this message.", "headingReplies": "Replies", "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index d0a225d1..ad92d5c3 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -1,6 +1,8 @@ { "@@locale": "ru", - "@@last_modified": "2022-07-07T21:07:20+02:00", + "@@last_modified": "2022-07-07T22:32:20+02:00", + "fileDownloadUnavailable": "This file appears unavailable for download. The sender may have disabled downloads for this file.", + "messageNoReplies": "There are no replies to this message.", "headingReplies": "Replies", "viewReplies": "View replies to this message", "restartFileShare": "Start Sharing File", diff --git a/lib/widgets/messagerow.dart b/lib/widgets/messagerow.dart index 540cf54d..6ff631cb 100644 --- a/lib/widgets/messagerow.dart +++ b/lib/widgets/messagerow.dart @@ -111,7 +111,7 @@ class MessageRowState extends State with SingleTickerProviderStateMi tooltip: AppLocalizations.of(context)!.viewReplies, splashRadius: Material.defaultSplashRadius / 2, onPressed: () { - modalShowReplies(context, AppLocalizations.of(context)!.headingReplies, settings, pis, cis, borderColor, cache, messageID); + modalShowReplies(context, AppLocalizations.of(context)!.headingReplies, AppLocalizations.of(context)!.messageNoReplies, settings, pis, cis, borderColor, cache, messageID); }, icon: Icon(Icons.message_rounded, color: Provider.of(context).theme.dropShadowColor))); @@ -241,7 +241,7 @@ class MessageRowState extends State with SingleTickerProviderStateMi Provider.of(context, listen: false).selectedIndex = Provider.of(context, listen: false).messageID; }, onLongPress: () async { - modalShowReplies(context, AppLocalizations.of(context)!.headingReplies, settings, pis, cis, borderColor, cache, messageID); + modalShowReplies(context, AppLocalizations.of(context)!.headingReplies, AppLocalizations.of(context)!.messageNoReplies, settings, pis, cis, borderColor, cache, messageID); }, child: Padding( padding: EdgeInsets.all(2), @@ -364,7 +364,8 @@ class MessageRowState extends State with SingleTickerProviderStateMi } } -void modalShowReplies(BuildContext ctx, String replyHeader, Settings settings, ProfileInfoState profile, ContactInfoState cis, Color borderColor, MessageCache cache, int messageID, +void modalShowReplies( + BuildContext ctx, String replyHeader, String noRepliesText, Settings settings, ProfileInfoState profile, ContactInfoState cis, Color borderColor, MessageCache cache, int messageID, {bool showImage = true}) { showModalBottomSheet( context: ctx, @@ -419,7 +420,11 @@ void modalShowReplies(BuildContext ctx, String replyHeader, Settings settings, P color: settings.theme.mainTextColor, ))); - withHeader.insert(2, Padding(padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Text(replyHeader, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)))); + if (replies.isNotEmpty) { + withHeader.insert(2, Padding(padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Text(replyHeader, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)))); + } else { + withHeader.insert(2, Padding(padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Center(child: Text(noRepliesText, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold))))); + } return Scrollbar( isAlwaysShown: true, @@ -429,10 +434,12 @@ void modalShowReplies(BuildContext ctx, String replyHeader, Settings settings, P constraints: BoxConstraints( minHeight: viewportConstraints.maxHeight, ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: withHeader, - )))); + child: Padding( + padding: EdgeInsets.symmetric(vertical: 0.0, horizontal: 20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: withHeader, + ))))); }); }); } From 7540aed7016536b06512d88896afa32ac7249e43 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Thu, 7 Jul 2022 13:51:59 -0700 Subject: [PATCH 4/4] Handle viewing replies to images correctly --- lib/widgets/messagerow.dart | 122 +++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 58 deletions(-) diff --git a/lib/widgets/messagerow.dart b/lib/widgets/messagerow.dart index 6ff631cb..be02b494 100644 --- a/lib/widgets/messagerow.dart +++ b/lib/widgets/messagerow.dart @@ -372,74 +372,80 @@ void modalShowReplies( builder: (BuildContext bcontext) { List replies = getReplies(cache, messageID); - return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) { - var replyWidgets = replies.map((e) { - var fromMe = e.getMetadata().senderHandle == profile.onion; + return ChangeNotifierProvider.value( + value: profile, + builder: (bcontext, child) { + return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) { + var replyWidgets = replies.map((e) { + var fromMe = e.getMetadata().senderHandle == profile.onion; - var bubble = StaticMessageBubble(profile, settings, e.getMetadata(), Row(children: [Flexible(child: e.getPreviewWidget(context))])); + var bubble = StaticMessageBubble(profile, settings, e.getMetadata(), Row(children: [Flexible(child: e.getPreviewWidget(context))])); - String imagePath = e.getMetadata().senderImage!; - var sender = profile.contactList.findContact(e.getMetadata().senderHandle); - if (sender != null) { - imagePath = showImage ? sender.imagePath : sender.defaultImagePath; - } + String imagePath = e.getMetadata().senderImage!; + var sender = profile.contactList.findContact(e.getMetadata().senderHandle); + if (sender != null) { + imagePath = showImage ? sender.imagePath : sender.defaultImagePath; + } - if (fromMe) { - imagePath = profile.imagePath; - } + if (fromMe) { + imagePath = profile.imagePath; + } - var image = Padding( - padding: EdgeInsets.all(4.0), - child: ProfileImage( - imagePath: imagePath, - diameter: 48.0, - border: borderColor, - badgeTextColor: Colors.red, - badgeColor: Colors.red, - )); + var image = Padding( + padding: EdgeInsets.all(4.0), + child: ProfileImage( + imagePath: imagePath, + diameter: 48.0, + border: borderColor, + badgeTextColor: Colors.red, + badgeColor: Colors.red, + )); - return Padding( - padding: EdgeInsets.all(10.0), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [image, Flexible(child: bubble)], - )); - }).toList(); + return Padding( + padding: EdgeInsets.all(10.0), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [image, Flexible(child: bubble)], + )); + }).toList(); - var withHeader = replyWidgets; + var withHeader = replyWidgets; - var original = StaticMessageBubble(profile, settings, cache.cache[messageID]!.metadata, Row(children: [Flexible(child: compileOverlay(cache.cache[messageID]!).getPreviewWidget(context))])); + var original = + StaticMessageBubble(profile, settings, cache.cache[messageID]!.metadata, Row(children: [Flexible(child: compileOverlay(cache.cache[messageID]!).getPreviewWidget(context))])); - withHeader.insert(0, Padding(padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Center(child: original))); + withHeader.insert(0, Padding(padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Center(child: original))); - withHeader.insert( - 1, - Padding( - padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), - child: Divider( - color: settings.theme.mainTextColor, - ))); + withHeader.insert( + 1, + Padding( + padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), + child: Divider( + color: settings.theme.mainTextColor, + ))); - if (replies.isNotEmpty) { - withHeader.insert(2, Padding(padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Text(replyHeader, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)))); - } else { - withHeader.insert(2, Padding(padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Center(child: Text(noRepliesText, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold))))); - } + if (replies.isNotEmpty) { + withHeader.insert(2, Padding(padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Text(replyHeader, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)))); + } else { + withHeader.insert( + 2, Padding(padding: EdgeInsets.fromLTRB(10.0, 10.0, 2.0, 15.0), child: Center(child: Text(noRepliesText, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold))))); + } - return Scrollbar( - isAlwaysShown: true, - child: SingleChildScrollView( - clipBehavior: Clip.antiAlias, - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: viewportConstraints.maxHeight, - ), - child: Padding( - padding: EdgeInsets.symmetric(vertical: 0.0, horizontal: 20.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: withHeader, - ))))); - }); + return Scrollbar( + isAlwaysShown: true, + child: SingleChildScrollView( + clipBehavior: Clip.antiAlias, + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: viewportConstraints.maxHeight, + ), + child: Padding( + padding: EdgeInsets.symmetric(vertical: 0.0, horizontal: 20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: withHeader, + ))))); + }); + }); }); }