diff --git a/lib/models/message.dart b/lib/models/message.dart index decdf51b..391286c8 100644 --- a/lib/models/message.dart +++ b/lib/models/message.dart @@ -34,7 +34,9 @@ abstract class Message { Widget getWidget(BuildContext context, Key key, int index); - Widget getPreviewWidget(BuildContext context); + Widget getPreviewWidget(BuildContext context, {BoxConstraints? constraints}); + + //double requestedWidth(BuildContext context, BoxConstraints constraints); } Message compileOverlay(MessageInfo messageInfo) { diff --git a/lib/models/messages/filemessage.dart b/lib/models/messages/filemessage.dart index d8ebc6cf..26cf60a4 100644 --- a/lib/models/messages/filemessage.dart +++ b/lib/models/messages/filemessage.dart @@ -49,7 +49,7 @@ class FileMessage extends Message { } @override - Widget getPreviewWidget(BuildContext context) { + Widget getPreviewWidget(BuildContext context, {BoxConstraints? constraints}) { return ChangeNotifierProvider.value( value: this.metadata, builder: (bcontext, child) { diff --git a/lib/models/messages/invitemessage.dart b/lib/models/messages/invitemessage.dart index 63c9236f..e2f339f6 100644 --- a/lib/models/messages/invitemessage.dart +++ b/lib/models/messages/invitemessage.dart @@ -48,7 +48,7 @@ class InviteMessage extends Message { } @override - Widget getPreviewWidget(BuildContext context) { + Widget getPreviewWidget(BuildContext context, {BoxConstraints? constraints}) { return ChangeNotifierProvider.value( value: this.metadata, builder: (bcontext, child) { diff --git a/lib/models/messages/malformedmessage.dart b/lib/models/messages/malformedmessage.dart index 033663f2..ece8338c 100644 --- a/lib/models/messages/malformedmessage.dart +++ b/lib/models/messages/malformedmessage.dart @@ -18,7 +18,7 @@ class MalformedMessage extends Message { } @override - Widget getPreviewWidget(BuildContext context) { + Widget getPreviewWidget(BuildContext context, {BoxConstraints? constraints}) { return ChangeNotifierProvider.value( value: this.metadata, builder: (bcontext, child) { diff --git a/lib/models/messages/quotedmessage.dart b/lib/models/messages/quotedmessage.dart index 81e4ef0c..0564dcad 100644 --- a/lib/models/messages/quotedmessage.dart +++ b/lib/models/messages/quotedmessage.dart @@ -30,7 +30,7 @@ class QuotedMessage extends Message { QuotedMessage(this.metadata, this.content); @override - Widget getPreviewWidget(BuildContext context) { + Widget getPreviewWidget(BuildContext context, {BoxConstraints? constraints}) { return ChangeNotifierProvider.value( value: this.metadata, builder: (bcontext, child) { @@ -40,7 +40,7 @@ class QuotedMessage extends Message { ); var content = message["body"]; var formatMessages = Provider.of(bcontext).isExperimentEnabled(FormattingExperiment); - return compileMessageContentWidget(context, false, content, FocusNode(), formatMessages, false); + return compileMessageContentWidget(context, constraints ?? BoxConstraints.expand(), false, content, FocusNode(), formatMessages, false); } catch (e) { return MalformedBubble(); } diff --git a/lib/models/messages/textmessage.dart b/lib/models/messages/textmessage.dart index bbbb7f23..c2914a0b 100644 --- a/lib/models/messages/textmessage.dart +++ b/lib/models/messages/textmessage.dart @@ -21,12 +21,12 @@ class TextMessage extends Message { TextMessage(this.metadata, this.content); @override - Widget getPreviewWidget(BuildContext context) { + Widget getPreviewWidget(BuildContext context, {BoxConstraints? constraints}) { return ChangeNotifierProvider.value( value: this.metadata, builder: (bcontext, child) { var formatMessages = Provider.of(bcontext).isExperimentEnabled(FormattingExperiment); - return compileMessageContentWidget(context, false, content, FocusNode(), formatMessages, false); + return compileMessageContentWidget(context, constraints ?? BoxConstraints.expand(), false, content, FocusNode(), formatMessages, false); ; }); } diff --git a/lib/third_party/linkify/flutter_linkify.dart b/lib/third_party/linkify/flutter_linkify.dart index 9b61eca9..e6595dbc 100644 --- a/lib/third_party/linkify/flutter_linkify.dart +++ b/lib/third_party/linkify/flutter_linkify.dart @@ -37,124 +37,6 @@ export 'linkify.dart' show LinkifyElement, LinkifyOptions, LinkableElement, Text /// Callback clicked link typedef LinkCallback = void Function(LinkableElement link); -/// Turns URLs into links -class Linkify extends StatelessWidget { - /// Text to be linkified - final String text; - - /// Linkifiers to be used for linkify - final List linkifiers; - - /// Callback for tapping a link - final LinkCallback? onOpen; - - /// linkify's options. - final LinkifyOptions options; - - // TextSpan - - /// Style for non-link text - final TextStyle? style; - - /// Style of link text - final TextStyle? linkStyle; - - // Text.rich - - /// How the text should be aligned horizontally. - final TextAlign textAlign; - - /// Text direction of the text - final TextDirection? textDirection; - - /// The maximum number of lines for the text to span, wrapping if necessary - final int? maxLines; - - /// How visual overflow should be handled. - final TextOverflow overflow; - - /// The number of font pixels for each logical pixel - final double textScaleFactor; - - /// Whether the text should break at soft line breaks. - final bool softWrap; - - /// The strut style used for the vertical layout - final StrutStyle? strutStyle; - - /// Used to select a font when the same Unicode character can - /// be rendered differently, depending on the locale - final Locale? locale; - - /// Defines how to measure the width of the rendered text. - final TextWidthBasis textWidthBasis; - - /// Defines how the paragraph will apply TextStyle.height to the ascent of the first line and descent of the last line. - final TextHeightBehavior? textHeightBehavior; - - const Linkify({ - Key? key, - required this.text, - this.linkifiers = defaultLinkifiers, - this.onOpen, - this.options = const LinkifyOptions(), - // TextSpan - this.style, - this.linkStyle, - // RichText - this.textAlign = TextAlign.start, - this.textDirection, - this.maxLines, - this.overflow = TextOverflow.clip, - this.textScaleFactor = 1.0, - this.softWrap = true, - this.strutStyle, - this.locale, - this.textWidthBasis = TextWidthBasis.parent, - this.textHeightBehavior, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - final elements = linkify( - text, - options: options, - linkifiers: linkifiers, - ); - - return SelectionArea( - child: RichText( - selectionRegistrar: SelectionContainer.maybeOf(context), - text: buildTextSpan( - elements, - style: Theme.of(context).textTheme.bodyText2?.merge(style), - onOpen: onOpen, - useMouseRegion: true, - context: context, - linkStyle: Theme.of(context) - .textTheme - .bodyText2 - ?.merge(style) - .copyWith( - color: Colors.blueAccent, - decoration: TextDecoration.underline, - ) - .merge(linkStyle), - ), - textAlign: textAlign, - textDirection: textDirection, - maxLines: maxLines, - overflow: overflow, - textScaleFactor: textScaleFactor, - softWrap: softWrap, - strutStyle: strutStyle, - locale: locale, - textWidthBasis: textWidthBasis, - textHeightBehavior: textHeightBehavior, - )); - } -} - /// Turns URLs into links class SelectableLinkify extends StatelessWidget { /// Text to be linkified @@ -175,13 +57,13 @@ class SelectableLinkify extends StatelessWidget { // TextSpan /// Style for code text - final TextStyle? codeStyle; + final TextStyle codeStyle; /// Style for non-link text - final TextStyle? style; + final TextStyle style; /// Style of link text - final TextStyle? linkStyle; + final TextStyle linkStyle; // Text.rich @@ -214,9 +96,6 @@ class SelectableLinkify extends StatelessWidget { /// Whether this text field should focus itself if nothing else is already focused. final bool autofocus; - /// Configuration of toolbar options - final ToolbarOptions? toolbarOptions; - /// How thick the cursor will be final double cursorWidth; @@ -250,6 +129,8 @@ class SelectableLinkify extends StatelessWidget { /// Called when the user changes the selection of text (including the cursor location). final SelectionChangedCallback? onSelectionChanged; + final BoxConstraints constraints; + const SelectableLinkify({ Key? key, required this.text, @@ -257,11 +138,11 @@ class SelectableLinkify extends StatelessWidget { this.onOpen, this.options = const LinkifyOptions(), // TextSpan - this.style, - this.linkStyle, + required this.style, + required this.linkStyle, // RichText this.textAlign, - this.codeStyle, + required this.codeStyle, this.textDirection, this.minLines, this.maxLines, @@ -271,7 +152,6 @@ class SelectableLinkify extends StatelessWidget { this.strutStyle, this.showCursor = false, this.autofocus = false, - this.toolbarOptions, this.cursorWidth = 2.0, this.cursorRadius, this.cursorColor, @@ -284,6 +164,7 @@ class SelectableLinkify extends StatelessWidget { this.cursorHeight, this.selectionControls, this.onSelectionChanged, + required this.constraints, }) : super(key: key); @override @@ -295,25 +176,17 @@ class SelectableLinkify extends StatelessWidget { ); return Container( - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration(), + constraints: constraints, child: SelectableText.rich( - buildTextSpan( - elements, - style: Theme.of(context).textTheme.bodyText2?.merge(style), - codeStyle: Theme.of(context).textTheme.bodyText2?.merge(codeStyle), - onOpen: onOpen, - context: context, - linkStyle: Theme.of(context) - .textTheme - .bodyText2 - ?.merge(style) - .copyWith( - color: Colors.blueAccent, - decoration: TextDecoration.underline, - ) - .merge(linkStyle), - ), + buildTextSpan(elements, + style: style, + codeStyle: codeStyle, + onOpen: onOpen, + context: context, + linkStyle: linkStyle.copyWith( + decoration: TextDecoration.underline, + ), + constraints: constraints), textAlign: textAlign, textDirection: textDirection, minLines: minLines, @@ -323,7 +196,6 @@ class SelectableLinkify extends StatelessWidget { showCursor: showCursor, textScaleFactor: textScaleFactor, autofocus: autofocus, - toolbarOptions: toolbarOptions, cursorWidth: cursorWidth, cursorRadius: cursorRadius, cursorColor: cursorColor, @@ -366,8 +238,24 @@ TextSpan buildTextSpan( LinkCallback? onOpen, required BuildContext context, bool useMouseRegion = false, + required BoxConstraints constraints, }) { + // Ok, so the problem here is that Flutter really wants to optimize this function + // out of the rebuild process. This is fine when the screen gets smaller because + // Flutter forces TextSpan to rebuild with the new constraints automatically. + // HOWEVER, when the screen gets larger, Flutter seems to think that it doesn't + // need to bother rebuilding this TextSpan because it already fits in the provided constraints. + // To force a rebuild here we append a constraint-determined space character to the end of the + // text element. + // (I tried a few other things, including the docs-sanctioned MediaQuery.sizeOf(context) - which promises a rebuild + // but Flutter is pretty good at optimizing "useless" checks out) + String inlineText = "\u0020"; + if (constraints.maxWidth % 2 == 0) { + inlineText = "\u00A0"; + } + elements.add(TextElement(inlineText)); return TextSpan( + style: style, children: elements.map( (element) { if (element is LinkableElement) { diff --git a/lib/views/messageview.dart b/lib/views/messageview.dart index 5136ce8f..4de4508d 100644 --- a/lib/views/messageview.dart +++ b/lib/views/messageview.dart @@ -10,21 +10,16 @@ import 'package:cwtch/models/appstate.dart'; import 'package:cwtch/models/chatmessage.dart'; import 'package:cwtch/models/contact.dart'; import 'package:cwtch/models/message.dart'; -import 'package:cwtch/models/message_draft.dart'; import 'package:cwtch/models/messagecache.dart'; -import 'package:cwtch/models/messages/invitemessage.dart'; import 'package:cwtch/models/messages/quotedmessage.dart'; import 'package:cwtch/models/profile.dart'; import 'package:cwtch/themes/opaque.dart'; import 'package:cwtch/third_party/linkify/flutter_linkify.dart'; -import 'package:cwtch/widgets/invitationbubble.dart'; import 'package:cwtch/widgets/malformedbubble.dart'; import 'package:cwtch/widgets/messageloadingbubble.dart'; import 'package:cwtch/widgets/profileimage.dart'; import 'package:cwtch/controllers/filesharing.dart' as filesharing; import 'package:cwtch/widgets/staticmessagebubble.dart'; - -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:cwtch/views/peersettingsview.dart'; import 'package:cwtch/widgets/DropdownContacts.dart'; @@ -493,6 +488,7 @@ class _MessageViewState extends State { backgroundColor: Provider.of(context).theme.messageFromOtherBackgroundColor), textAlign: TextAlign.left, textWidthBasis: TextWidthBasis.longestLine, + constraints: BoxConstraints.expand(), )); var showMessageFormattingPreview = Provider.of(context).isExperimentEnabled(FormattingExperiment); @@ -601,22 +597,6 @@ class _MessageViewState extends State { }); }); - // var subscript = IconButton( - // icon: Icon(Icons.subscript), - // tooltip: AppLocalizations.of(context)!.tooltipSubscript, - // onPressed: () { - // setState(() { - // var ctrlrCompose = Provider.of(context, listen: false).messageDraft.ctrlCompose; - // var selected = ctrlrCompose.selection.textInside(ctrlrCompose.text); - // var selection = ctrlrCompose.selection; - // var start = ctrlrCompose.selection.start; - // var end = ctrlrCompose.selection.end; - // ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "_" + selected + "_"); - // ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 1, extentOffset: selection.start + 1); - // Provider.of(context, listen: false).messageDraft.ctrlCompose = ctrlrCompose; - // }); - // }); - var strikethrough = IconButton( icon: Icon(Icons.format_strikethrough), tooltip: AppLocalizations.of(context)!.tooltipStrikethrough, diff --git a/lib/widgets/filebubble.dart b/lib/widgets/filebubble.dart index ac151618..e0369688 100644 --- a/lib/widgets/filebubble.dart +++ b/lib/widgets/filebubble.dart @@ -150,7 +150,10 @@ class FileBubbleState extends State { var wdgSender = Visibility( visible: widget.interactive, child: Container( - height: 14 * Provider.of(context).fontScaling, clipBehavior: Clip.hardEdge, decoration: BoxDecoration(), child: compileSenderWidget(context, fromMe, senderDisplayStr))); + height: 14 * Provider.of(context).fontScaling, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration(), + child: compileSenderWidget(context, constraints, fromMe, senderDisplayStr))); var isPreview = false; var wdgMessage = !showFileSharing ? Text(AppLocalizations.of(context)!.messageEnableFileSharing, style: Provider.of(context).scaleFonts(defaultTextStyle)) diff --git a/lib/widgets/invitationbubble.dart b/lib/widgets/invitationbubble.dart index 97489f86..5af90875 100644 --- a/lib/widgets/invitationbubble.dart +++ b/lib/widgets/invitationbubble.dart @@ -56,47 +56,46 @@ class InvitationBubbleState extends State { senderDisplayStr = Provider.of(context).senderHandle; } } - - var wdgSender = compileSenderWidget(context, fromMe, senderDisplayStr); - // If we receive an invite for ourselves, treat it as a bug. The UI no longer allows this so it could have only come from - // some kind of malfeasance. - var selfInvite = widget.inviteNick == Provider.of(context).onion; - if (selfInvite) { - return MalformedBubble(); - } - - var wdgMessage = isGroup && !showGroupInvite - ? Text(AppLocalizations.of(context)!.groupInviteSettingsWarning, style: Provider.of(context).scaleFonts(defaultTextStyle)) - : fromMe - ? senderInviteChrome( - AppLocalizations.of(context)!.sendAnInvitation, isGroup ? Provider.of(context).contactList.findContact(widget.inviteTarget)!.nickname : widget.inviteTarget) - : (inviteChrome(isGroup ? AppLocalizations.of(context)!.inviteToGroup : AppLocalizations.of(context)!.contactSuggestion, widget.inviteNick, widget.inviteTarget)); - - Widget wdgDecorations; - if (isGroup && !showGroupInvite) { - wdgDecorations = Text('\u202F'); - } else if (fromMe) { - wdgDecorations = MessageBubbleDecoration(ackd: Provider.of(context).ackd, errored: Provider.of(context).error, fromMe: fromMe, messageDate: messageDate); - } else if (isAccepted) { - wdgDecorations = Text(AppLocalizations.of(context)!.accepted + '\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle)); - } else if (this.rejected) { - wdgDecorations = Text(AppLocalizations.of(context)!.rejected + '\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle)); - } else { - wdgDecorations = Center( - widthFactor: 1, - child: Wrap(children: [ - Padding( - padding: EdgeInsets.all(5), - child: ElevatedButton( - child: Text(AppLocalizations.of(context)!.rejectGroupBtn + '\u202F', style: Provider.of(context).scaleFonts(defaultTextButtonStyle)), onPressed: _btnReject)), - Padding( - padding: EdgeInsets.all(5), - child: ElevatedButton( - child: Text(AppLocalizations.of(context)!.acceptGroupBtn + '\u202F', style: Provider.of(context).scaleFonts(defaultTextButtonStyle)), onPressed: _btnAccept)), - ])); - } - return LayoutBuilder(builder: (context, constraints) { + var wdgSender = compileSenderWidget(context, constraints, fromMe, senderDisplayStr); + // If we receive an invite for ourselves, treat it as a bug. The UI no longer allows this so it could have only come from + // some kind of malfeasance. + var selfInvite = widget.inviteNick == Provider.of(context).onion; + if (selfInvite) { + return MalformedBubble(); + } + + var wdgMessage = isGroup && !showGroupInvite + ? Text(AppLocalizations.of(context)!.groupInviteSettingsWarning, style: Provider.of(context).scaleFonts(defaultTextStyle)) + : fromMe + ? senderInviteChrome( + AppLocalizations.of(context)!.sendAnInvitation, isGroup ? Provider.of(context).contactList.findContact(widget.inviteTarget)!.nickname : widget.inviteTarget) + : (inviteChrome(isGroup ? AppLocalizations.of(context)!.inviteToGroup : AppLocalizations.of(context)!.contactSuggestion, widget.inviteNick, widget.inviteTarget)); + + Widget wdgDecorations; + if (isGroup && !showGroupInvite) { + wdgDecorations = Text('\u202F'); + } else if (fromMe) { + wdgDecorations = MessageBubbleDecoration(ackd: Provider.of(context).ackd, errored: Provider.of(context).error, fromMe: fromMe, messageDate: messageDate); + } else if (isAccepted) { + wdgDecorations = Text(AppLocalizations.of(context)!.accepted + '\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle)); + } else if (this.rejected) { + wdgDecorations = Text(AppLocalizations.of(context)!.rejected + '\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle)); + } else { + wdgDecorations = Center( + widthFactor: 1, + child: Wrap(children: [ + Padding( + padding: EdgeInsets.all(5), + child: ElevatedButton( + child: Text(AppLocalizations.of(context)!.rejectGroupBtn + '\u202F', style: Provider.of(context).scaleFonts(defaultTextButtonStyle)), onPressed: _btnReject)), + Padding( + padding: EdgeInsets.all(5), + child: ElevatedButton( + child: Text(AppLocalizations.of(context)!.acceptGroupBtn + '\u202F', style: Provider.of(context).scaleFonts(defaultTextButtonStyle)), onPressed: _btnAccept)), + ])); + } + //print(constraints.toString()+", "+constraints.maxWidth.toString()); return Center( widthFactor: 1.0, diff --git a/lib/widgets/malformedbubble.dart b/lib/widgets/malformedbubble.dart index c56c48c7..7a9ad6e3 100644 --- a/lib/widgets/malformedbubble.dart +++ b/lib/widgets/malformedbubble.dart @@ -46,7 +46,12 @@ class MalformedBubbleState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, - children: [Text(AppLocalizations.of(context)!.malformedMessage)], + children: [ + Text( + AppLocalizations.of(context)!.malformedMessage, + overflow: TextOverflow.ellipsis, + ) + ], )) ]))))); }); diff --git a/lib/widgets/messageBubbleWidgetHelpers.dart b/lib/widgets/messageBubbleWidgetHelpers.dart index 118c22b4..8771ffcd 100644 --- a/lib/widgets/messageBubbleWidgetHelpers.dart +++ b/lib/widgets/messageBubbleWidgetHelpers.dart @@ -5,7 +5,7 @@ import '../settings.dart'; import '../themes/opaque.dart'; import '../third_party/linkify/flutter_linkify.dart'; -Widget compileSenderWidget(BuildContext context, bool fromMe, String senderDisplayStr) { +Widget compileSenderWidget(BuildContext context, BoxConstraints? constraints, bool fromMe, String senderDisplayStr) { return Container( height: 14 * Provider.of(context).fontScaling, clipBehavior: Clip.hardEdge, @@ -21,7 +21,7 @@ Widget compileSenderWidget(BuildContext context, bool fromMe, String senderDispl ))); } -Widget compileMessageContentWidget(BuildContext context, bool fromMe, String content, FocusNode focus, bool formatMessages, bool showClickableLinks) { +Widget compileMessageContentWidget(BuildContext context, BoxConstraints constraints, fromMe, String content, FocusNode focus, bool formatMessages, bool showClickableLinks) { return SelectableLinkify( text: content + '\u202F', // TODO: onOpen breaks the "selectable" functionality. Maybe something to do with gesture handler? @@ -32,7 +32,6 @@ Widget compileMessageContentWidget(BuildContext context, bool fromMe, String con modalOpenLink(context, link); } : null, - //key: Key(myKey), focusNode: focus, style: Provider.of(context) .scaleFonts(defaultMessageTextStyle.copyWith(color: fromMe ? Provider.of(context).theme.messageFromMeTextColor : Provider.of(context).theme.messageFromOtherTextColor)), @@ -43,6 +42,7 @@ Widget compileMessageContentWidget(BuildContext context, bool fromMe, String con color: fromMe ? Provider.of(context).theme.messageFromOtherTextColor : Provider.of(context).theme.messageFromMeTextColor, backgroundColor: fromMe ? Provider.of(context).theme.messageFromOtherBackgroundColor : Provider.of(context).theme.messageFromMeBackgroundColor)), textAlign: TextAlign.left, + constraints: constraints, textWidthBasis: TextWidthBasis.longestLine, ); } diff --git a/lib/widgets/messagebubble.dart b/lib/widgets/messagebubble.dart index 5f36f6c6..b8747371 100644 --- a/lib/widgets/messagebubble.dart +++ b/lib/widgets/messagebubble.dart @@ -45,49 +45,45 @@ class MessageBubbleState extends State { senderDisplayStr = Provider.of(context).senderHandle; } } - var wdgSender = compileSenderWidget(context, fromMe, senderDisplayStr); - var wdgMessage = compileMessageContentWidget(context, fromMe, widget.content, _focus, formatMessages, showClickableLinks); + + Size size = MediaQuery.sizeOf(context); + + var wdgSender = compileSenderWidget(context, null, fromMe, senderDisplayStr); + var wdgMessage = compileMessageContentWidget(context, BoxConstraints.loose(size), fromMe, widget.content, _focus, formatMessages, showClickableLinks); var wdgDecorations = MessageBubbleDecoration(ackd: Provider.of(context).ackd, errored: Provider.of(context).error, fromMe: fromMe, messageDate: messageDate); var error = Provider.of(context).error; - return LayoutBuilder(builder: (context, constraints) { - //print(constraints.toString()+", "+constraints.maxWidth.toString()); - return RepaintBoundary( - child: Container( - child: Container( - decoration: BoxDecoration( - color: error ? malformedColor : (fromMe ? Provider.of(context).theme.messageFromMeBackgroundColor : Provider.of(context).theme.messageFromOtherBackgroundColor), - border: Border.all( - color: error - ? malformedColor - : (fromMe ? Provider.of(context).theme.messageFromMeBackgroundColor : Provider.of(context).theme.messageFromOtherBackgroundColor), - width: 1), - borderRadius: BorderRadius.only( - topLeft: Radius.circular(borderRadiousEh), - topRight: Radius.circular(borderRadiousEh), - bottomLeft: fromMe ? Radius.circular(borderRadiousEh) : Radius.zero, - bottomRight: fromMe ? Radius.zero : Radius.circular(borderRadiousEh), - ), - ), - child: Padding( - padding: EdgeInsets.all(9.0), - child: Theme( - data: Theme.of(context).copyWith( - textSelectionTheme: TextSelectionThemeData( - cursorColor: Provider.of(context).theme.messageSelectionColor, - selectionColor: Provider.of(context).theme.messageSelectionColor, - selectionHandleColor: Provider.of(context).theme.messageSelectionColor), + return Container( + decoration: BoxDecoration( + color: error ? malformedColor : (fromMe ? Provider.of(context).theme.messageFromMeBackgroundColor : Provider.of(context).theme.messageFromOtherBackgroundColor), + border: Border.all( + color: error ? malformedColor : (fromMe ? Provider.of(context).theme.messageFromMeBackgroundColor : Provider.of(context).theme.messageFromOtherBackgroundColor), + width: 1), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(borderRadiousEh), + topRight: Radius.circular(borderRadiousEh), + bottomLeft: fromMe ? Radius.circular(borderRadiousEh) : Radius.zero, + bottomRight: fromMe ? Radius.zero : Radius.circular(borderRadiousEh), + ), + ), + child: Padding( + padding: EdgeInsets.all(9.0), + child: Theme( + data: Theme.of(context).copyWith( + textSelectionTheme: TextSelectionThemeData( + cursorColor: Provider.of(context).theme.messageSelectionColor, + selectionColor: Provider.of(context).theme.messageSelectionColor, + selectionHandleColor: Provider.of(context).theme.messageSelectionColor), - // Horrifying Hack: Flutter doesn't give us direct control over system menus but instead picks BG color from TextButtonThemeData ¯\_(ツ)_/¯ - textButtonTheme: TextButtonThemeData( - style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Provider.of(context).theme.menuBackgroundColor)), - ), - ), - child: Column( - crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, - mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: fromMe ? [wdgMessage, wdgDecorations] : [wdgSender, wdgMessage, wdgDecorations])))))); - }); + // Horrifying Hack: Flutter doesn't give us direct control over system menus but instead picks BG color from TextButtonThemeData ¯\_(ツ)_/¯ + textButtonTheme: TextButtonThemeData( + style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Provider.of(context).theme.menuBackgroundColor)), + ), + ), + child: Column( + crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, + mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: fromMe ? [wdgMessage, wdgDecorations] : [wdgSender, wdgMessage, wdgDecorations])))); } } diff --git a/lib/widgets/messagebubbledecorations.dart b/lib/widgets/messagebubbledecorations.dart index 80b04651..55bbc3eb 100644 --- a/lib/widgets/messagebubbledecorations.dart +++ b/lib/widgets/messagebubbledecorations.dart @@ -29,6 +29,7 @@ class _MessageBubbleDecoration extends State { mainAxisSize: MainAxisSize.min, children: [ Text(prettyDate, + overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 9.0 * Provider.of(context).fontScaling, fontWeight: FontWeight.w200, diff --git a/lib/widgets/messagelist.dart b/lib/widgets/messagelist.dart index 2774bf0d..1c2a987d 100644 --- a/lib/widgets/messagelist.dart +++ b/lib/widgets/messagelist.dart @@ -1,10 +1,8 @@ -import 'package:cwtch/config.dart'; import 'package:cwtch/models/appstate.dart'; import 'package:cwtch/models/contact.dart'; import 'package:cwtch/models/message.dart'; import 'package:cwtch/models/messagecache.dart'; import 'package:cwtch/models/profile.dart'; -import 'package:cwtch/themes/opaque.dart'; import 'package:cwtch/widgets/messageloadingbubble.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; @@ -35,10 +33,6 @@ class _MessageListState extends State { bool isP2P = !Provider.of(context).isGroup; bool isGroupAndSyncing = Provider.of(context).isGroup == true && Provider.of(context).status == "Authenticated"; - // Older checks, no longer used, kept for reference. - //bool isGroupAndSynced = Provider.of(context).isGroup && Provider.of(context).status == "Synced"; - //bool isGroupAndNotAuthenticated = Provider.of(context).isGroup && Provider.of(context).status != "Authenticated"; - bool preserveHistoryByDefault = Provider.of(context, listen: false).preserveHistoryByDefault; bool showEphemeralWarning = (isP2P && (!preserveHistoryByDefault && Provider.of(context).savePeerHistory != "SaveHistory")); bool showOfflineWarning = Provider.of(context).isOnline() == false; @@ -143,14 +137,14 @@ class _MessageListState extends State { return FutureBuilder( future: messageHandler(itemBuilderContext, profileOnion, contactHandle, ByIndex(messageIndex)), - builder: (context, snapshot) { + builder: (fbcontext, snapshot) { if (snapshot.hasData) { var message = snapshot.data as Message; // here we create an index key for the contact and assign it to the row. Indexes are unique so we can // reliably use this without running into duplicate keys...it isn't ideal as it means keys need to be re-built // when new messages are added...however it is better than the alternative of not having widget keys at all. var key = Provider.of(itemBuilderContext, listen: false).getMessageKey(contactHandle, messageIndex); - return message.getWidget(context, key, messageIndex); + return message.getWidget(fbcontext, key, messageIndex); } else { return MessageLoadingBubble(); } diff --git a/lib/widgets/messagerow.dart b/lib/widgets/messagerow.dart index 3de2d4cb..0230a986 100644 --- a/lib/widgets/messagerow.dart +++ b/lib/widgets/messagerow.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'package:cwtch/config.dart'; import 'package:cwtch/cwtch_icons_icons.dart'; -import 'package:cwtch/models/appstate.dart'; import 'package:cwtch/models/contact.dart'; import 'package:cwtch/models/message.dart'; import 'package:cwtch/models/profile.dart'; @@ -16,7 +15,6 @@ import 'package:cwtch/widgets/profileimage.dart'; import 'package:flutter/physics.dart'; import 'package:provider/provider.dart'; 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'; @@ -65,7 +63,7 @@ class MessageRowState extends State with SingleTickerProviderStateMi var isContact = Provider.of(context).contactList.findContact(Provider.of(context).senderHandle) != null; var isGroup = Provider.of(context).contactList.getContact(Provider.of(context, listen: false).conversationIdentifier)!.isGroup; var isBlocked = isContact ? Provider.of(context).contactList.findContact(Provider.of(context).senderHandle)!.isBlocked : false; - var actualMessage = Flexible(flex: Platform.isAndroid ? 10 : 3, fit: FlexFit.loose, child: widget.child); + var actualMessage = Flexible(flex: 1, fit: FlexFit.loose, child: widget.child); _dragAffinity = fromMe ? Alignment.centerRight : Alignment.centerLeft; _dragAlignment = fromMe ? Alignment.centerRight : Alignment.centerLeft; diff --git a/lib/widgets/quotedmessage.dart b/lib/widgets/quotedmessage.dart index afcc07bf..781a8ede 100644 --- a/lib/widgets/quotedmessage.dart +++ b/lib/widgets/quotedmessage.dart @@ -45,12 +45,10 @@ class QuotedMessageBubbleState extends State { } } - var wdgSender = compileSenderWidget(context, fromMe, senderDisplayStr); - var showClickableLinks = Provider.of(context).isExperimentEnabled(ClickableLinksExperiment); var formatMessages = Provider.of(context).isExperimentEnabled(FormattingExperiment); - var wdgMessage = compileMessageContentWidget(context, fromMe, widget.body, _focus, formatMessages, showClickableLinks); - + Size size = MediaQuery.of(context).size; + BoxConstraints constraints = BoxConstraints(maxWidth: size.width, minWidth: size.width); var wdgQuote = FutureBuilder( future: widget.quotedMessage, builder: (context, snapshot) { @@ -123,43 +121,39 @@ class QuotedMessageBubbleState extends State { var error = Provider.of(context).error; - return LayoutBuilder(builder: (context, constraints) { - return RepaintBoundary( - child: Container( - child: Container( - decoration: BoxDecoration( - color: error ? malformedColor : (fromMe ? Provider.of(context).theme.messageFromMeBackgroundColor : Provider.of(context).theme.messageFromOtherBackgroundColor), - border: Border.all( - color: error - ? malformedColor - : (fromMe ? Provider.of(context).theme.messageFromMeBackgroundColor : Provider.of(context).theme.messageFromOtherBackgroundColor), - width: 1), - borderRadius: BorderRadius.only( - topLeft: Radius.circular(borderRadiousEh), - topRight: Radius.circular(borderRadiousEh), - bottomLeft: fromMe ? Radius.circular(borderRadiousEh) : Radius.zero, - bottomRight: fromMe ? Radius.zero : Radius.circular(borderRadiousEh), - ), - ), - child: Padding( - padding: EdgeInsets.all(9.0), - child: Theme( - data: Theme.of(context).copyWith( - textSelectionTheme: TextSelectionThemeData( - cursorColor: Provider.of(context).theme.messageSelectionColor, - selectionColor: Provider.of(context).theme.messageSelectionColor, - selectionHandleColor: Provider.of(context).theme.messageSelectionColor), + var wdgMessage = compileMessageContentWidget(context, constraints, fromMe, widget.body, _focus, formatMessages, showClickableLinks); + var wdgSender = compileSenderWidget(context, constraints, fromMe, senderDisplayStr); + return Container( + decoration: BoxDecoration( + color: error ? malformedColor : (fromMe ? Provider.of(context).theme.messageFromMeBackgroundColor : Provider.of(context).theme.messageFromOtherBackgroundColor), + border: Border.all( + color: error ? malformedColor : (fromMe ? Provider.of(context).theme.messageFromMeBackgroundColor : Provider.of(context).theme.messageFromOtherBackgroundColor), + width: 1), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(borderRadiousEh), + topRight: Radius.circular(borderRadiousEh), + bottomLeft: fromMe ? Radius.circular(borderRadiousEh) : Radius.zero, + bottomRight: fromMe ? Radius.zero : Radius.circular(borderRadiousEh), + ), + ), + child: Padding( + padding: EdgeInsets.all(9.0), + child: Theme( + data: Theme.of(context).copyWith( + textSelectionTheme: TextSelectionThemeData( + cursorColor: Provider.of(context).theme.messageSelectionColor, + selectionColor: Provider.of(context).theme.messageSelectionColor, + selectionHandleColor: Provider.of(context).theme.messageSelectionColor), - // Horrifying Hack: Flutter doesn't give us direct control over system menus but instead picks BG color from TextButtonThemeData ¯\_(ツ)_/¯ - textButtonTheme: TextButtonThemeData( - style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Provider.of(context).theme.menuBackgroundColor)), - ), - ), - child: Column( - crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, - mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: fromMe ? [wdgQuote, wdgMessage, wdgDecorations] : [wdgSender, wdgQuote, wdgMessage, wdgDecorations])))))); - }); + // Horrifying Hack: Flutter doesn't give us direct control over system menus but instead picks BG color from TextButtonThemeData ¯\_(ツ)_/¯ + textButtonTheme: TextButtonThemeData( + style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Provider.of(context).theme.menuBackgroundColor)), + ), + ), + child: Column( + crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, + mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: fromMe ? [wdgQuote, wdgMessage, wdgDecorations] : [wdgSender, wdgQuote, wdgMessage, wdgDecorations])))); } } diff --git a/lib/widgets/staticmessagebubble.dart b/lib/widgets/staticmessagebubble.dart index f146c98c..5bb22b92 100644 --- a/lib/widgets/staticmessagebubble.dart +++ b/lib/widgets/staticmessagebubble.dart @@ -42,13 +42,10 @@ class StaticMessageBubbleState extends State { senderDisplayStr = widget.profile.nickname; } - var wdgSender = compileSenderWidget(context, fromMe, senderDisplayStr); - - 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) { + var wdgSender = compileSenderWidget(context, constraints, fromMe, senderDisplayStr); + var wdgDecorations = MessageBubbleDecoration(ackd: widget.metadata.ackd, errored: widget.metadata.error, fromMe: fromMe, messageDate: messageDate); + var error = widget.metadata.error; //print(constraints.toString()+", "+constraints.maxWidth.toString()); return RepaintBoundary( child: Container(