forked from cwtch.im/cwtch-ui
Merge pull request 'reply_links' (#470) from reply_links into marcia_fixes
Reviewed-on: cwtch.im/cwtch-ui#470
This commit is contained in:
commit
a5b253f185
|
@ -0,0 +1,61 @@
|
||||||
|
import 'package:cwtch/third_party/linkify/linkify.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
void modalOpenLink(BuildContext ctx, LinkableElement link) {
|
||||||
|
showModalBottomSheet<void>(
|
||||||
|
context: ctx,
|
||||||
|
builder: (BuildContext bcontext) {
|
||||||
|
return Container(
|
||||||
|
height: 200, // bespoke value courtesy of the [TextField] docs
|
||||||
|
child: Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(30.0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(AppLocalizations.of(bcontext)!.clickableLinksWarning),
|
||||||
|
Flex(direction: Axis.horizontal, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
|
||||||
|
child: ElevatedButton(
|
||||||
|
child: Text(AppLocalizations.of(bcontext)!.clickableLinksCopy, semanticsLabel: AppLocalizations.of(bcontext)!.clickableLinksCopy),
|
||||||
|
onPressed: () {
|
||||||
|
Clipboard.setData(new ClipboardData(text: link.url));
|
||||||
|
|
||||||
|
final snackBar = SnackBar(
|
||||||
|
content: Text(AppLocalizations.of(bcontext)!.copiedToClipboardNotification),
|
||||||
|
);
|
||||||
|
|
||||||
|
Navigator.pop(bcontext);
|
||||||
|
ScaffoldMessenger.of(bcontext).showSnackBar(snackBar);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
|
||||||
|
child: ElevatedButton(
|
||||||
|
child: Text(AppLocalizations.of(bcontext)!.clickableLinkOpen, semanticsLabel: AppLocalizations.of(bcontext)!.clickableLinkOpen),
|
||||||
|
onPressed: () async {
|
||||||
|
if (await canLaunch(link.url)) {
|
||||||
|
await launch(link.url);
|
||||||
|
Navigator.pop(bcontext);
|
||||||
|
} else {
|
||||||
|
final snackBar = SnackBar(
|
||||||
|
content: Text(AppLocalizations.of(bcontext)!.clickableLinkError),
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(bcontext).showSnackBar(snackBar);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,16 +1,13 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:cwtch/controllers/open_link_modal.dart';
|
||||||
import 'package:cwtch/models/contact.dart';
|
import 'package:cwtch/models/contact.dart';
|
||||||
import 'package:cwtch/models/message.dart';
|
import 'package:cwtch/models/message.dart';
|
||||||
import 'package:cwtch/third_party/linkify/flutter_linkify.dart';
|
import 'package:cwtch/third_party/linkify/flutter_linkify.dart';
|
||||||
import 'package:cwtch/models/profile.dart';
|
import 'package:cwtch/models/profile.dart';
|
||||||
import 'package:cwtch/widgets/malformedbubble.dart';
|
import 'package:cwtch/widgets/malformedbubble.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
|
|
||||||
import '../settings.dart';
|
import '../settings.dart';
|
||||||
import 'messagebubbledecorations.dart';
|
import 'messagebubbledecorations.dart';
|
||||||
|
@ -55,7 +52,7 @@ class MessageBubbleState extends State<MessageBubble> {
|
||||||
linkifiers: [UrlLinkifier()],
|
linkifiers: [UrlLinkifier()],
|
||||||
onOpen: showClickableLinks
|
onOpen: showClickableLinks
|
||||||
? (link) {
|
? (link) {
|
||||||
_modalOpenLink(context, link);
|
modalOpenLink(context, link);
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
//key: Key(myKey),
|
//key: Key(myKey),
|
||||||
|
@ -104,59 +101,4 @@ class MessageBubbleState extends State<MessageBubble> {
|
||||||
children: fromMe ? [wdgMessage, wdgDecorations] : [wdgSender, wdgMessage, wdgDecorations])))));
|
children: fromMe ? [wdgMessage, wdgDecorations] : [wdgSender, wdgMessage, wdgDecorations])))));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _modalOpenLink(BuildContext ctx, LinkableElement link) {
|
|
||||||
showModalBottomSheet<void>(
|
|
||||||
context: ctx,
|
|
||||||
builder: (BuildContext bcontext) {
|
|
||||||
return Container(
|
|
||||||
height: 200, // bespoke value courtesy of the [TextField] docs
|
|
||||||
child: Center(
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.all(30.0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: <Widget>[
|
|
||||||
Text(AppLocalizations.of(bcontext)!.clickableLinksWarning),
|
|
||||||
Flex(direction: Axis.horizontal, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
|
|
||||||
Container(
|
|
||||||
margin: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
|
|
||||||
child: ElevatedButton(
|
|
||||||
child: Text(AppLocalizations.of(bcontext)!.clickableLinksCopy, semanticsLabel: AppLocalizations.of(bcontext)!.clickableLinksCopy),
|
|
||||||
onPressed: () {
|
|
||||||
Clipboard.setData(new ClipboardData(text: link.url));
|
|
||||||
|
|
||||||
final snackBar = SnackBar(
|
|
||||||
content: Text(AppLocalizations.of(bcontext)!.copiedToClipboardNotification),
|
|
||||||
);
|
|
||||||
|
|
||||||
Navigator.pop(bcontext);
|
|
||||||
ScaffoldMessenger.of(bcontext).showSnackBar(snackBar);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
margin: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
|
|
||||||
child: ElevatedButton(
|
|
||||||
child: Text(AppLocalizations.of(bcontext)!.clickableLinkOpen, semanticsLabel: AppLocalizations.of(bcontext)!.clickableLinkOpen),
|
|
||||||
onPressed: () async {
|
|
||||||
if (await canLaunch(link.url)) {
|
|
||||||
await launch(link.url);
|
|
||||||
Navigator.pop(bcontext);
|
|
||||||
} else {
|
|
||||||
final snackBar = SnackBar(
|
|
||||||
content: Text(AppLocalizations.of(bcontext)!.clickableLinkError),
|
|
||||||
);
|
|
||||||
ScaffoldMessenger.of(bcontext).showSnackBar(snackBar);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
)),
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
import 'package:cwtch/controllers/open_link_modal.dart';
|
||||||
import 'package:cwtch/models/appstate.dart';
|
import 'package:cwtch/models/appstate.dart';
|
||||||
import 'package:cwtch/models/contact.dart';
|
import 'package:cwtch/models/contact.dart';
|
||||||
import 'package:cwtch/models/message.dart';
|
import 'package:cwtch/models/message.dart';
|
||||||
import 'package:cwtch/models/profile.dart';
|
import 'package:cwtch/models/profile.dart';
|
||||||
|
import 'package:cwtch/third_party/linkify/flutter_linkify.dart';
|
||||||
import 'package:cwtch/views/contactsview.dart';
|
import 'package:cwtch/views/contactsview.dart';
|
||||||
import 'package:cwtch/widgets/malformedbubble.dart';
|
import 'package:cwtch/widgets/malformedbubble.dart';
|
||||||
import 'package:cwtch/widgets/messageloadingbubble.dart';
|
import 'package:cwtch/widgets/messageloadingbubble.dart';
|
||||||
|
@ -45,12 +47,29 @@ class QuotedMessageBubbleState extends State<QuotedMessageBubble> {
|
||||||
var wdgSender = SelectableText(senderDisplayStr,
|
var wdgSender = SelectableText(senderDisplayStr,
|
||||||
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor));
|
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor));
|
||||||
|
|
||||||
var wdgMessage = SelectableText(
|
var showClickableLinks = Provider.of<Settings>(context).isExperimentEnabled(ClickableLinksExperiment);
|
||||||
widget.body + '\u202F',
|
var formatMessages = Provider.of<Settings>(context).isExperimentEnabled(FormattingExperiment);
|
||||||
|
|
||||||
|
var wdgMessage = SelectableLinkify(
|
||||||
|
text: widget.body + '\u202F',
|
||||||
|
// TODO: onOpen breaks the "selectable" functionality. Maybe something to do with gesture handler?
|
||||||
|
options: LinkifyOptions(messageFormatting: formatMessages, parseLinks: showClickableLinks, looseUrl: true, defaultToHttps: true),
|
||||||
|
linkifiers: [UrlLinkifier()],
|
||||||
|
onOpen: showClickableLinks
|
||||||
|
? (link) {
|
||||||
|
modalOpenLink(context, link);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
//key: Key(myKey),
|
||||||
focusNode: _focus,
|
focusNode: _focus,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor,
|
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor,
|
||||||
),
|
),
|
||||||
|
linkStyle: TextStyle(color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor),
|
||||||
|
codeStyle: TextStyle(
|
||||||
|
// note: these colors are flipped
|
||||||
|
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,
|
textAlign: TextAlign.left,
|
||||||
textWidthBasis: TextWidthBasis.longestLine,
|
textWidthBasis: TextWidthBasis.longestLine,
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue