Fix Chat Resize Layout
This commit is contained in:
parent
9b65048bfd
commit
52d0a6cf3f
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<Settings>(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();
|
||||
}
|
||||
|
|
|
@ -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<Settings>(bcontext).isExperimentEnabled(FormattingExperiment);
|
||||
return compileMessageContentWidget(context, false, content, FocusNode(), formatMessages, false);
|
||||
return compileMessageContentWidget(context, constraints ?? BoxConstraints.expand(), false, content, FocusNode(), formatMessages, false);
|
||||
;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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<Linkifier> 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<InlineSpan>(
|
||||
(element) {
|
||||
if (element is LinkableElement) {
|
||||
|
|
|
@ -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<MessageView> {
|
|||
backgroundColor: Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor),
|
||||
textAlign: TextAlign.left,
|
||||
textWidthBasis: TextWidthBasis.longestLine,
|
||||
constraints: BoxConstraints.expand(),
|
||||
));
|
||||
|
||||
var showMessageFormattingPreview = Provider.of<Settings>(context).isExperimentEnabled(FormattingExperiment);
|
||||
|
@ -601,22 +597,6 @@ class _MessageViewState extends State<MessageView> {
|
|||
});
|
||||
});
|
||||
|
||||
// var subscript = IconButton(
|
||||
// icon: Icon(Icons.subscript),
|
||||
// tooltip: AppLocalizations.of(context)!.tooltipSubscript,
|
||||
// onPressed: () {
|
||||
// setState(() {
|
||||
// var ctrlrCompose = Provider.of<ContactInfoState>(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<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose = ctrlrCompose;
|
||||
// });
|
||||
// });
|
||||
|
||||
var strikethrough = IconButton(
|
||||
icon: Icon(Icons.format_strikethrough),
|
||||
tooltip: AppLocalizations.of(context)!.tooltipStrikethrough,
|
||||
|
|
|
@ -150,7 +150,10 @@ class FileBubbleState extends State<FileBubble> {
|
|||
var wdgSender = Visibility(
|
||||
visible: widget.interactive,
|
||||
child: Container(
|
||||
height: 14 * Provider.of<Settings>(context).fontScaling, clipBehavior: Clip.hardEdge, decoration: BoxDecoration(), child: compileSenderWidget(context, fromMe, senderDisplayStr)));
|
||||
height: 14 * Provider.of<Settings>(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<Settings>(context).scaleFonts(defaultTextStyle))
|
||||
|
|
|
@ -56,47 +56,46 @@ class InvitationBubbleState extends State<InvitationBubble> {
|
|||
senderDisplayStr = Provider.of<MessageMetadata>(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<ProfileInfoState>(context).onion;
|
||||
if (selfInvite) {
|
||||
return MalformedBubble();
|
||||
}
|
||||
|
||||
var wdgMessage = isGroup && !showGroupInvite
|
||||
? Text(AppLocalizations.of(context)!.groupInviteSettingsWarning, style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle))
|
||||
: fromMe
|
||||
? senderInviteChrome(
|
||||
AppLocalizations.of(context)!.sendAnInvitation, isGroup ? Provider.of<ProfileInfoState>(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<MessageMetadata>(context).ackd, errored: Provider.of<MessageMetadata>(context).error, fromMe: fromMe, messageDate: messageDate);
|
||||
} else if (isAccepted) {
|
||||
wdgDecorations = Text(AppLocalizations.of(context)!.accepted + '\u202F', style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle));
|
||||
} else if (this.rejected) {
|
||||
wdgDecorations = Text(AppLocalizations.of(context)!.rejected + '\u202F', style: Provider.of<Settings>(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<Settings>(context).scaleFonts(defaultTextButtonStyle)), onPressed: _btnReject)),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(5),
|
||||
child: ElevatedButton(
|
||||
child: Text(AppLocalizations.of(context)!.acceptGroupBtn + '\u202F', style: Provider.of<Settings>(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<ProfileInfoState>(context).onion;
|
||||
if (selfInvite) {
|
||||
return MalformedBubble();
|
||||
}
|
||||
|
||||
var wdgMessage = isGroup && !showGroupInvite
|
||||
? Text(AppLocalizations.of(context)!.groupInviteSettingsWarning, style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle))
|
||||
: fromMe
|
||||
? senderInviteChrome(
|
||||
AppLocalizations.of(context)!.sendAnInvitation, isGroup ? Provider.of<ProfileInfoState>(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<MessageMetadata>(context).ackd, errored: Provider.of<MessageMetadata>(context).error, fromMe: fromMe, messageDate: messageDate);
|
||||
} else if (isAccepted) {
|
||||
wdgDecorations = Text(AppLocalizations.of(context)!.accepted + '\u202F', style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle));
|
||||
} else if (this.rejected) {
|
||||
wdgDecorations = Text(AppLocalizations.of(context)!.rejected + '\u202F', style: Provider.of<Settings>(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<Settings>(context).scaleFonts(defaultTextButtonStyle)), onPressed: _btnReject)),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(5),
|
||||
child: ElevatedButton(
|
||||
child: Text(AppLocalizations.of(context)!.acceptGroupBtn + '\u202F', style: Provider.of<Settings>(context).scaleFonts(defaultTextButtonStyle)), onPressed: _btnAccept)),
|
||||
]));
|
||||
}
|
||||
|
||||
//print(constraints.toString()+", "+constraints.maxWidth.toString());
|
||||
return Center(
|
||||
widthFactor: 1.0,
|
||||
|
|
|
@ -46,7 +46,12 @@ class MalformedBubbleState extends State<MalformedBubble> {
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [Text(AppLocalizations.of(context)!.malformedMessage)],
|
||||
children: [
|
||||
Text(
|
||||
AppLocalizations.of(context)!.malformedMessage,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
)
|
||||
],
|
||||
))
|
||||
])))));
|
||||
});
|
||||
|
|
|
@ -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<Settings>(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<Settings>(context)
|
||||
.scaleFonts(defaultMessageTextStyle.copyWith(color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor)),
|
||||
|
@ -43,6 +42,7 @@ Widget compileMessageContentWidget(BuildContext context, bool fromMe, String con
|
|||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromOtherTextColor : Provider.of<Settings>(context).theme.messageFromMeTextColor,
|
||||
backgroundColor: fromMe ? Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor : Provider.of<Settings>(context).theme.messageFromMeBackgroundColor)),
|
||||
textAlign: TextAlign.left,
|
||||
constraints: constraints,
|
||||
textWidthBasis: TextWidthBasis.longestLine,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -45,49 +45,45 @@ class MessageBubbleState extends State<MessageBubble> {
|
|||
senderDisplayStr = Provider.of<MessageMetadata>(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<MessageMetadata>(context).ackd, errored: Provider.of<MessageMetadata>(context).error, fromMe: fromMe, messageDate: messageDate);
|
||||
var error = Provider.of<MessageMetadata>(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<Settings>(context).theme.messageFromMeBackgroundColor : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor),
|
||||
border: Border.all(
|
||||
color: error
|
||||
? malformedColor
|
||||
: (fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor : Provider.of<Settings>(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<Settings>(context).theme.messageSelectionColor,
|
||||
selectionColor: Provider.of<Settings>(context).theme.messageSelectionColor,
|
||||
selectionHandleColor: Provider.of<Settings>(context).theme.messageSelectionColor),
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: error ? malformedColor : (fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor),
|
||||
border: Border.all(
|
||||
color: error ? malformedColor : (fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor : Provider.of<Settings>(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<Settings>(context).theme.messageSelectionColor,
|
||||
selectionColor: Provider.of<Settings>(context).theme.messageSelectionColor,
|
||||
selectionHandleColor: Provider.of<Settings>(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<Settings>(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<Settings>(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]))));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ class _MessageBubbleDecoration extends State<MessageBubbleDecoration> {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(prettyDate,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 9.0 * Provider.of<Settings>(context).fontScaling,
|
||||
fontWeight: FontWeight.w200,
|
||||
|
|
|
@ -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<MessageList> {
|
|||
bool isP2P = !Provider.of<ContactInfoState>(context).isGroup;
|
||||
bool isGroupAndSyncing = Provider.of<ContactInfoState>(context).isGroup == true && Provider.of<ContactInfoState>(context).status == "Authenticated";
|
||||
|
||||
// Older checks, no longer used, kept for reference.
|
||||
//bool isGroupAndSynced = Provider.of<ContactInfoState>(context).isGroup && Provider.of<ContactInfoState>(context).status == "Synced";
|
||||
//bool isGroupAndNotAuthenticated = Provider.of<ContactInfoState>(context).isGroup && Provider.of<ContactInfoState>(context).status != "Authenticated";
|
||||
|
||||
bool preserveHistoryByDefault = Provider.of<Settings>(context, listen: false).preserveHistoryByDefault;
|
||||
bool showEphemeralWarning = (isP2P && (!preserveHistoryByDefault && Provider.of<ContactInfoState>(context).savePeerHistory != "SaveHistory"));
|
||||
bool showOfflineWarning = Provider.of<ContactInfoState>(context).isOnline() == false;
|
||||
|
@ -143,14 +137,14 @@ class _MessageListState extends State<MessageList> {
|
|||
|
||||
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<ContactInfoState>(itemBuilderContext, listen: false).getMessageKey(contactHandle, messageIndex);
|
||||
return message.getWidget(context, key, messageIndex);
|
||||
return message.getWidget(fbcontext, key, messageIndex);
|
||||
} else {
|
||||
return MessageLoadingBubble();
|
||||
}
|
||||
|
|
|
@ -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<MessageRow> with SingleTickerProviderStateMi
|
|||
var isContact = Provider.of<ProfileInfoState>(context).contactList.findContact(Provider.of<MessageMetadata>(context).senderHandle) != null;
|
||||
var isGroup = Provider.of<ProfileInfoState>(context).contactList.getContact(Provider.of<MessageMetadata>(context, listen: false).conversationIdentifier)!.isGroup;
|
||||
var isBlocked = isContact ? Provider.of<ProfileInfoState>(context).contactList.findContact(Provider.of<MessageMetadata>(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;
|
||||
|
|
|
@ -45,12 +45,10 @@ class QuotedMessageBubbleState extends State<QuotedMessageBubble> {
|
|||
}
|
||||
}
|
||||
|
||||
var wdgSender = compileSenderWidget(context, fromMe, senderDisplayStr);
|
||||
|
||||
var showClickableLinks = Provider.of<Settings>(context).isExperimentEnabled(ClickableLinksExperiment);
|
||||
var formatMessages = Provider.of<Settings>(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<QuotedMessageBubble> {
|
|||
|
||||
var error = Provider.of<MessageMetadata>(context).error;
|
||||
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
return RepaintBoundary(
|
||||
child: Container(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: error ? malformedColor : (fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor),
|
||||
border: Border.all(
|
||||
color: error
|
||||
? malformedColor
|
||||
: (fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor : Provider.of<Settings>(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<Settings>(context).theme.messageSelectionColor,
|
||||
selectionColor: Provider.of<Settings>(context).theme.messageSelectionColor,
|
||||
selectionHandleColor: Provider.of<Settings>(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<Settings>(context).theme.messageFromMeBackgroundColor : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor),
|
||||
border: Border.all(
|
||||
color: error ? malformedColor : (fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor : Provider.of<Settings>(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<Settings>(context).theme.messageSelectionColor,
|
||||
selectionColor: Provider.of<Settings>(context).theme.messageSelectionColor,
|
||||
selectionHandleColor: Provider.of<Settings>(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<Settings>(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<Settings>(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]))));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,13 +42,10 @@ class StaticMessageBubbleState extends State<StaticMessageBubble> {
|
|||
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(
|
||||
|
|
Loading…
Reference in New Issue