forked from cwtch.im/cwtch-ui
Message Formatting Experiment Initial Commit
This commit is contained in:
parent
471a729d46
commit
a4a2af08b4
Binary file not shown.
Binary file not shown.
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"@@locale": "cy",
|
||||
"@@last_modified": "2022-03-21T17:12:38+01:00",
|
||||
"@@last_modified": "2022-04-06T22:31:33+02:00",
|
||||
"messageFormattingDescription": "Enable rich text formatting in displayed messages e.g. **bold** and *italic*",
|
||||
"formattingExperiment": "Message Formatting",
|
||||
"clickableLinkError": "Error encountered while attempting to open URL",
|
||||
"clickableLinksCopy": "Copy URL",
|
||||
"clickableLinkOpen": "Open URL",
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"@@locale": "da",
|
||||
"@@last_modified": "2022-03-21T17:12:38+01:00",
|
||||
"@@last_modified": "2022-04-06T22:31:33+02:00",
|
||||
"messageFormattingDescription": "Enable rich text formatting in displayed messages e.g. **bold** and *italic*",
|
||||
"formattingExperiment": "Message Formatting",
|
||||
"clickableLinkError": "Error encountered while attempting to open URL",
|
||||
"clickableLinksCopy": "Copy URL",
|
||||
"clickableLinkOpen": "Open URL",
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"@@locale": "de",
|
||||
"@@last_modified": "2022-03-21T17:12:38+01:00",
|
||||
"@@last_modified": "2022-04-06T22:31:33+02:00",
|
||||
"messageFormattingDescription": "Enable rich text formatting in displayed messages e.g. **bold** and *italic*",
|
||||
"formattingExperiment": "Message Formatting",
|
||||
"clickableLinkError": "Error encountered while attempting to open URL",
|
||||
"clickableLinksCopy": "Copy URL",
|
||||
"clickableLinkOpen": "Open URL",
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"@@locale": "el",
|
||||
"@@last_modified": "2022-03-21T17:12:38+01:00",
|
||||
"@@last_modified": "2022-04-06T22:31:33+02:00",
|
||||
"messageFormattingDescription": "Enable rich text formatting in displayed messages e.g. **bold** and *italic*",
|
||||
"formattingExperiment": "Message Formatting",
|
||||
"clickableLinkError": "Error encountered while attempting to open URL",
|
||||
"clickableLinksCopy": "Copy URL",
|
||||
"clickableLinkOpen": "Open URL",
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"@@locale": "en",
|
||||
"@@last_modified": "2022-03-21T17:12:38+01:00",
|
||||
"@@last_modified": "2022-04-06T22:31:33+02:00",
|
||||
"messageFormattingDescription": "Enable rich text formatting in displayed messages e.g. **bold** and *italic*",
|
||||
"formattingExperiment": "Message Formatting",
|
||||
"clickableLinkError": "Error encountered while attempting to open URL",
|
||||
"clickableLinksWarning": "Opening this URL will launch an application outside of Cwtch and may reveal metadata or otherwise compromise the security of Cwtch. Only open URLs from people you trust. Are you sure you want to continue?",
|
||||
"clickableLinksCopy": "Copy URL",
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"@@locale": "es",
|
||||
"@@last_modified": "2022-03-21T17:12:38+01:00",
|
||||
"@@last_modified": "2022-04-06T22:31:33+02:00",
|
||||
"messageFormattingDescription": "Enable rich text formatting in displayed messages e.g. **bold** and *italic*",
|
||||
"formattingExperiment": "Message Formatting",
|
||||
"clickableLinkError": "Error encountered while attempting to open URL",
|
||||
"clickableLinksCopy": "Copy URL",
|
||||
"clickableLinkOpen": "Open URL",
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
{
|
||||
"@@locale": "fr",
|
||||
"@@last_modified": "2022-03-21T17:12:38+01:00",
|
||||
"clickableLinkError": "Error encountered while attempting to open URL",
|
||||
"clickableLinksCopy": "Copy URL",
|
||||
"clickableLinkOpen": "Open URL",
|
||||
"clickableLinksWarning": "Opening this URL will launch an application outside of Cwtch and may reveal metadata or otherwise compromise the security of Cwtch. Only open URLs from people you trust. Are you sure you want to continue?",
|
||||
"@@last_modified": "2022-04-06T22:31:33+02:00",
|
||||
"messageFormattingDescription": "Enable rich text formatting in displayed messages e.g. **bold** and *italic*",
|
||||
"formattingExperiment": "Message Formatting",
|
||||
"clickableLinksWarning": "L'ouverture de cette URL lancera une application en dehors de Cwtch et peut révéler des métadonnées ou compromettre la sécurité de Cwtch. N'ouvrez que les URLs de personnes en qui vous avez confiance. Êtes-vous sûr de vouloir continuer ?",
|
||||
"clickableLinksCopy": "Copier l'URL",
|
||||
"clickableLinkOpen": "Ouvrir l'URL",
|
||||
"clickableLinkError": "Erreur rencontrée lors de la tentative d'ouverture de l'URL",
|
||||
"acceptGroupBtn": "Accepter",
|
||||
"successfullyImportedProfile": "Profil importé avec succès : %profile",
|
||||
"shuttingDownApp": "Fermeture...",
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"@@locale": "it",
|
||||
"@@last_modified": "2022-03-21T17:12:38+01:00",
|
||||
"@@last_modified": "2022-04-06T22:31:33+02:00",
|
||||
"messageFormattingDescription": "Enable rich text formatting in displayed messages e.g. **bold** and *italic*",
|
||||
"formattingExperiment": "Message Formatting",
|
||||
"clickableLinkError": "Error encountered while attempting to open URL",
|
||||
"clickableLinksCopy": "Copy URL",
|
||||
"clickableLinkOpen": "Open URL",
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"@@locale": "lb",
|
||||
"@@last_modified": "2022-03-21T17:12:38+01:00",
|
||||
"@@last_modified": "2022-04-06T22:31:33+02:00",
|
||||
"messageFormattingDescription": "Enable rich text formatting in displayed messages e.g. **bold** and *italic*",
|
||||
"formattingExperiment": "Message Formatting",
|
||||
"clickableLinkError": "Error encountered while attempting to open URL",
|
||||
"clickableLinksCopy": "Copy URL",
|
||||
"clickableLinkOpen": "Open URL",
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"@@locale": "no",
|
||||
"@@last_modified": "2022-03-21T17:12:38+01:00",
|
||||
"@@last_modified": "2022-04-06T22:31:33+02:00",
|
||||
"messageFormattingDescription": "Enable rich text formatting in displayed messages e.g. **bold** and *italic*",
|
||||
"formattingExperiment": "Message Formatting",
|
||||
"clickableLinkError": "Error encountered while attempting to open URL",
|
||||
"clickableLinksCopy": "Copy URL",
|
||||
"clickableLinkOpen": "Open URL",
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"@@locale": "pl",
|
||||
"@@last_modified": "2022-03-21T17:12:38+01:00",
|
||||
"@@last_modified": "2022-04-06T22:31:33+02:00",
|
||||
"messageFormattingDescription": "Enable rich text formatting in displayed messages e.g. **bold** and *italic*",
|
||||
"formattingExperiment": "Message Formatting",
|
||||
"clickableLinkError": "Error encountered while attempting to open URL",
|
||||
"clickableLinksCopy": "Copy URL",
|
||||
"clickableLinkOpen": "Open URL",
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"@@locale": "pt",
|
||||
"@@last_modified": "2022-03-21T17:12:38+01:00",
|
||||
"@@last_modified": "2022-04-06T22:31:33+02:00",
|
||||
"messageFormattingDescription": "Enable rich text formatting in displayed messages e.g. **bold** and *italic*",
|
||||
"formattingExperiment": "Message Formatting",
|
||||
"clickableLinkError": "Error encountered while attempting to open URL",
|
||||
"clickableLinksCopy": "Copy URL",
|
||||
"clickableLinkOpen": "Open URL",
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"@@locale": "ro",
|
||||
"@@last_modified": "2022-03-21T17:12:38+01:00",
|
||||
"@@last_modified": "2022-04-06T22:31:33+02:00",
|
||||
"messageFormattingDescription": "Enable rich text formatting in displayed messages e.g. **bold** and *italic*",
|
||||
"formattingExperiment": "Message Formatting",
|
||||
"clickableLinkError": "Error encountered while attempting to open URL",
|
||||
"clickableLinksCopy": "Copy URL",
|
||||
"clickableLinkOpen": "Open URL",
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"@@locale": "ru",
|
||||
"@@last_modified": "2022-03-21T17:12:38+01:00",
|
||||
"@@last_modified": "2022-04-06T22:31:33+02:00",
|
||||
"messageFormattingDescription": "Enable rich text formatting in displayed messages e.g. **bold** and *italic*",
|
||||
"formattingExperiment": "Message Formatting",
|
||||
"clickableLinkError": "Error encountered while attempting to open URL",
|
||||
"clickableLinksCopy": "Copy URL",
|
||||
"clickableLinkOpen": "Open URL",
|
||||
|
|
|
@ -145,7 +145,6 @@ class ById implements CacheHandler {
|
|||
}
|
||||
return fetch(cwtch, profileOnion, conversationIdentifier, cache);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ByContentHash implements CacheHandler {
|
||||
|
@ -182,11 +181,7 @@ Future<Message> messageHandler(BuildContext context, String profileOnion, int co
|
|||
|
||||
MessageCache? cache;
|
||||
try {
|
||||
cache = Provider
|
||||
.of<ProfileInfoState>(context, listen: false)
|
||||
.contactList
|
||||
.getContact(conversationIdentifier)
|
||||
?.messageCache;
|
||||
cache = Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(conversationIdentifier)?.messageCache;
|
||||
if (cache == null) {
|
||||
EnvironmentConfig.debugLog("error: cannot get message cache for profile: $profileOnion conversation: $conversationIdentifier");
|
||||
return MalformedMessage(malformedMetadata);
|
||||
|
@ -271,6 +266,6 @@ class MessageMetadata extends ChangeNotifier {
|
|||
notifyListeners();
|
||||
}
|
||||
|
||||
MessageMetadata(
|
||||
this.profileOnion, this.conversationIdentifier, this.messageID, this.timestamp, this.senderHandle, this.senderImage, this.signature, this._attributes, this._ackd, this._error, this.isAuto, this.contenthash);
|
||||
MessageMetadata(this.profileOnion, this.conversationIdentifier, this.messageID, this.timestamp, this.senderHandle, this.senderImage, this.signature, this._attributes, this._ackd, this._error,
|
||||
this.isAuto, this.contenthash);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ class LocalIndexMessage {
|
|||
|
||||
late int? messageId;
|
||||
|
||||
|
||||
LocalIndexMessage(int? messageId, {cacheOnly = false, isLoading = false}) {
|
||||
this.messageId = messageId;
|
||||
this.cacheOnly = cacheOnly;
|
||||
|
|
|
@ -14,6 +14,7 @@ const ServerManagementExperiment = "servers-experiment";
|
|||
const FileSharingExperiment = "filesharing";
|
||||
const ImagePreviewsExperiment = "filesharing-images";
|
||||
const ClickableLinksExperiment = "clickable-links";
|
||||
const FormattingExperiment = "message-formatting";
|
||||
|
||||
enum DualpaneMode {
|
||||
Single,
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
|
@ -361,6 +363,7 @@ TextSpan buildTextSpan(
|
|||
text: element.text,
|
||||
style: linkStyle,
|
||||
recognizer: onOpen != null ? (TapGestureRecognizer()..onTap = () => onOpen(element)) : null,
|
||||
semanticsLabel: element.text
|
||||
),
|
||||
));
|
||||
} else {
|
||||
|
@ -372,6 +375,57 @@ TextSpan buildTextSpan(
|
|||
recognizer: onOpen != null ? (TapGestureRecognizer()..onTap = () => onOpen(element)) : null,
|
||||
));
|
||||
}
|
||||
} else if (element is BoldElement) {
|
||||
return TextSpan(
|
||||
text: element.text.replaceAll("*", ""),
|
||||
style: style?.copyWith(fontWeight: FontWeight.bold),
|
||||
semanticsLabel: element.text
|
||||
|
||||
);
|
||||
} else if (element is ItalicElement) {
|
||||
return TextSpan(
|
||||
text: element.text.replaceAll("*", ""),
|
||||
style: style?.copyWith(fontStyle: FontStyle.italic),
|
||||
semanticsLabel: element.text
|
||||
);
|
||||
} else if (element is SuperElement) {
|
||||
return WidgetSpan(
|
||||
child: Transform.translate(
|
||||
offset: const Offset(2, -6),
|
||||
child: Text(
|
||||
element.text.replaceAll("^", ""),
|
||||
//superscript is usually smaller in size
|
||||
textScaleFactor: 0.7,
|
||||
style: style,
|
||||
semanticsLabel: element.text
|
||||
),
|
||||
));
|
||||
} else if (element is SubElement) {
|
||||
return WidgetSpan(
|
||||
child: Transform.translate(
|
||||
offset: const Offset(2, 4),
|
||||
child: Text(
|
||||
element.text.replaceAll("_", ""),
|
||||
//superscript is usually smaller in size
|
||||
textScaleFactor: 0.7,
|
||||
style: style,
|
||||
semanticsLabel: element.text
|
||||
),
|
||||
));
|
||||
} else if (element is StrikeElement) {
|
||||
return TextSpan(
|
||||
text: element.text.replaceAll("~~", ""),
|
||||
style: style?.copyWith(decoration: TextDecoration.lineThrough, decorationColor: style.color, decorationStyle: TextDecorationStyle.solid),
|
||||
semanticsLabel: element.text
|
||||
);
|
||||
} else if (element is CodeElement) {
|
||||
return TextSpan(
|
||||
text: element.text.replaceAll("\`", ""),
|
||||
// monospace fonts at the same size as regular text makes them appear
|
||||
// slightly larger, so we compensate by making them slightly smaller...
|
||||
style: style?.copyWith(fontFamily: "RobotoMono", fontSize: style.fontSize!-1.0),
|
||||
semanticsLabel: element.text
|
||||
);
|
||||
} else {
|
||||
return TextSpan(
|
||||
text: element.text,
|
||||
|
|
|
@ -37,6 +37,30 @@ abstract class LinkifyElement {
|
|||
bool equals(other) => other is LinkifyElement && other.text == text;
|
||||
}
|
||||
|
||||
class BoldElement extends LinkifyElement {
|
||||
BoldElement(String text) : super(text);
|
||||
}
|
||||
|
||||
class ItalicElement extends LinkifyElement {
|
||||
ItalicElement(String text) : super(text);
|
||||
}
|
||||
|
||||
class SuperElement extends LinkifyElement {
|
||||
SuperElement(String text) : super(text);
|
||||
}
|
||||
|
||||
class SubElement extends LinkifyElement {
|
||||
SubElement(String text) : super(text);
|
||||
}
|
||||
|
||||
class StrikeElement extends LinkifyElement {
|
||||
StrikeElement(String text) : super(text);
|
||||
}
|
||||
|
||||
class CodeElement extends LinkifyElement {
|
||||
CodeElement(String text) : super(text);
|
||||
}
|
||||
|
||||
class LinkableElement extends LinkifyElement {
|
||||
final String url;
|
||||
|
||||
|
@ -81,11 +105,10 @@ class LinkifyOptions {
|
|||
/// Excludes `.` at end of URLs.
|
||||
final bool excludeLastPeriod;
|
||||
|
||||
const LinkifyOptions({
|
||||
this.looseUrl = false,
|
||||
this.defaultToHttps = false,
|
||||
this.excludeLastPeriod = true,
|
||||
});
|
||||
final bool messageFormatting;
|
||||
final bool parseLinks;
|
||||
|
||||
const LinkifyOptions({this.looseUrl = false, this.defaultToHttps = false, this.excludeLastPeriod = true, this.messageFormatting = false, this.parseLinks = false});
|
||||
}
|
||||
|
||||
const _urlLinkifier = UrlLinkifier();
|
||||
|
|
|
@ -44,24 +44,137 @@ final _protocolIdentifierRegex = RegExp(
|
|||
caseSensitive: false,
|
||||
);
|
||||
|
||||
class Formatter {
|
||||
final RegExp expression;
|
||||
final LinkifyElement Function(String) element;
|
||||
|
||||
Formatter(this.expression, this.element);
|
||||
}
|
||||
|
||||
// regex to match **bold**
|
||||
final _boldRegex = RegExp(
|
||||
r'^(.*?)(\*\*([^*]*)\*\*)',
|
||||
caseSensitive: false,
|
||||
dotAll: true,
|
||||
);
|
||||
|
||||
// regex to match *italic*
|
||||
final _italicRegex = RegExp(
|
||||
r'^(.*?)(\*([^*]*)\*)',
|
||||
caseSensitive: false,
|
||||
dotAll: true,
|
||||
);
|
||||
|
||||
// regex to match ^superscript^
|
||||
final _superRegex = RegExp(
|
||||
r'^(.*?)(\^([^\^]*)\^)',
|
||||
caseSensitive: false,
|
||||
dotAll: true,
|
||||
);
|
||||
|
||||
// regex to match ^subscript^
|
||||
final _subRegex = RegExp(
|
||||
r'^(.*?)(\_([^\_]*)\_)',
|
||||
caseSensitive: false,
|
||||
dotAll: true,
|
||||
);
|
||||
|
||||
// regex to match ~~strikethrough~~
|
||||
final _strikeRegex = RegExp(
|
||||
r'^(.*?)(\~\~([^\~]*)\~\~)',
|
||||
caseSensitive: false,
|
||||
dotAll: true,
|
||||
);
|
||||
|
||||
// regex to match `code`
|
||||
final _codeRegex = RegExp(
|
||||
r'^(.*?)(\`([^\`]*)\`)',
|
||||
caseSensitive: false,
|
||||
dotAll: true,
|
||||
);
|
||||
|
||||
class UrlLinkifier extends Linkifier {
|
||||
const UrlLinkifier();
|
||||
|
||||
List<LinkifyElement> replaceAndParse(tle, TextElement element, RegExpMatch match, List<LinkifyElement> list, options) {
|
||||
final text = element.text.replaceFirst(match.group(0)!, '');
|
||||
|
||||
if (match.group(1)?.isNotEmpty == true) {
|
||||
list.addAll(parse([TextElement(match.group(1)!)], options));
|
||||
}
|
||||
|
||||
if (match.group(2)?.isNotEmpty == true) {
|
||||
list.add(tle(match.group(2)!));
|
||||
}
|
||||
|
||||
if (text.isNotEmpty) {
|
||||
list.addAll(parse([TextElement(text)], options));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
List<LinkifyElement> parseFormatting(element, options) {
|
||||
var list = <LinkifyElement>[];
|
||||
|
||||
// code -> bold -> italic -> super -> sub -> strike
|
||||
// not we don't currently allow combinations of these elements the first
|
||||
// one to match a given set will be the only style applied - this will be fixed
|
||||
final formattingPrecedence = [
|
||||
Formatter(_codeRegex, CodeElement.new),
|
||||
Formatter(_boldRegex, BoldElement.new),
|
||||
Formatter(_italicRegex, ItalicElement.new),
|
||||
Formatter(_superRegex, SuperElement.new),
|
||||
Formatter(_subRegex, SubElement.new),
|
||||
Formatter(_strikeRegex, StrikeElement.new)
|
||||
];
|
||||
|
||||
// Loop through the formatters in with precedence and break when something is found...
|
||||
for (var formatter in formattingPrecedence) {
|
||||
var formattingMatch = formatter.expression.firstMatch(element.text);
|
||||
if (formattingMatch != null) {
|
||||
list = replaceAndParse(formatter.element, element, formattingMatch, list, options);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// catch all case where we didn't match anything and so need to return back
|
||||
// the unformatted text
|
||||
// conceptually this is Formatter((.*), TextElement.new)
|
||||
if (list.isEmpty) {
|
||||
list.add(element);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@override
|
||||
List<LinkifyElement> parse(elements, options) {
|
||||
final list = <LinkifyElement>[];
|
||||
var list = <LinkifyElement>[];
|
||||
|
||||
elements.forEach((element) {
|
||||
if (element is TextElement) {
|
||||
if (options.parseLinks == false && options.messageFormatting == false) {
|
||||
list.add(element);
|
||||
} else if (options.parseLinks == true) {
|
||||
// check if there is a link...
|
||||
var match = options.looseUrl ? _looseUrlRegex.firstMatch(element.text) : _urlRegex.firstMatch(element.text);
|
||||
|
||||
// if not then we only have to consider formatting...
|
||||
if (match == null) {
|
||||
// only do formatting if message formatting is enabled
|
||||
if (options.messageFormatting == false) {
|
||||
list.add(element);
|
||||
} else {
|
||||
// add all the formatting elements contained in this text
|
||||
list.addAll(parseFormatting(element, options));
|
||||
}
|
||||
} else {
|
||||
final text = element.text.replaceFirst(match.group(0)!, '');
|
||||
|
||||
if (match.group(1)?.isNotEmpty == true) {
|
||||
list.add(TextElement(match.group(1)!));
|
||||
// we match links first and the feed everything before the link
|
||||
// back through the parser
|
||||
list.addAll(parse([TextElement(match.group(1)!)], options));
|
||||
}
|
||||
|
||||
if (match.group(2)?.isNotEmpty == true) {
|
||||
|
@ -92,8 +205,12 @@ class UrlLinkifier extends Linkifier {
|
|||
list.addAll(parse([TextElement(text)], options));
|
||||
}
|
||||
}
|
||||
} else if (options.messageFormatting == true) {
|
||||
// we can jump straight to message formatting...
|
||||
list.addAll(parseFormatting(element, options));
|
||||
} else {
|
||||
list.add(element);
|
||||
// unreachable
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -368,6 +368,24 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
|
|||
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.link, color: settings.current().mainTextColor),
|
||||
)),
|
||||
Visibility(
|
||||
visible: settings.experimentsEnabled,
|
||||
child: SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!.formattingExperiment, style: TextStyle(color: settings.current().mainTextColor)),
|
||||
subtitle: Text(AppLocalizations.of(context)!.messageFormattingDescription),
|
||||
value: settings.isExperimentEnabled(FormattingExperiment),
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.enableExperiment(FormattingExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(FormattingExperiment);
|
||||
}
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonActiveColor,
|
||||
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.link, color: settings.current().mainTextColor),
|
||||
)),
|
||||
AboutListTile(
|
||||
icon: appIcon,
|
||||
applicationIcon: Padding(padding: EdgeInsets.all(5), child: Icon(CwtchIcons.cwtch_knott)),
|
||||
|
|
|
@ -230,7 +230,8 @@ class _MessageViewState extends State<MessageView> {
|
|||
ChatMessage cm = new ChatMessage(o: QuotedMessageOverlay, d: quotedMessage);
|
||||
Provider.of<FlwtchState>(context, listen: false)
|
||||
.cwtch
|
||||
.SendMessage(Provider.of<ContactInfoState>(context, listen: false).profileOnion, Provider.of<ContactInfoState>(context, listen: false).identifier, jsonEncode(cm)).then(_sendMessageHandler);
|
||||
.SendMessage(Provider.of<ContactInfoState>(context, listen: false).profileOnion, Provider.of<ContactInfoState>(context, listen: false).identifier, jsonEncode(cm))
|
||||
.then(_sendMessageHandler);
|
||||
} catch (e) {}
|
||||
Provider.of<AppState>(context, listen: false).selectedIndex = null;
|
||||
});
|
||||
|
@ -238,7 +239,8 @@ class _MessageViewState extends State<MessageView> {
|
|||
ChatMessage cm = new ChatMessage(o: TextMessageOverlay, d: ctrlrCompose.value.text);
|
||||
Provider.of<FlwtchState>(context, listen: false)
|
||||
.cwtch
|
||||
.SendMessage(Provider.of<ContactInfoState>(context, listen: false).profileOnion, Provider.of<ContactInfoState>(context, listen: false).identifier, jsonEncode(cm)).then(_sendMessageHandler);
|
||||
.SendMessage(Provider.of<ContactInfoState>(context, listen: false).profileOnion, Provider.of<ContactInfoState>(context, listen: false).identifier, jsonEncode(cm))
|
||||
.then(_sendMessageHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -246,14 +248,15 @@ class _MessageViewState extends State<MessageView> {
|
|||
void _sendInvitation([String? ignoredParam]) {
|
||||
Provider.of<FlwtchState>(context, listen: false)
|
||||
.cwtch
|
||||
.SendInvitation(Provider.of<ContactInfoState>(context, listen: false).profileOnion, Provider.of<ContactInfoState>(context, listen: false).identifier, this.selectedContact).then(_sendMessageHandler);
|
||||
.SendInvitation(Provider.of<ContactInfoState>(context, listen: false).profileOnion, Provider.of<ContactInfoState>(context, listen: false).identifier, this.selectedContact)
|
||||
.then(_sendMessageHandler);
|
||||
}
|
||||
|
||||
void _sendFile(String filePath) {
|
||||
|
||||
Provider.of<FlwtchState>(context, listen: false)
|
||||
.cwtch
|
||||
.ShareFile(Provider.of<ContactInfoState>(context, listen: false).profileOnion, Provider.of<ContactInfoState>(context, listen: false).identifier, filePath).then(_sendMessageHandler);
|
||||
.ShareFile(Provider.of<ContactInfoState>(context, listen: false).profileOnion, Provider.of<ContactInfoState>(context, listen: false).identifier, filePath)
|
||||
.then(_sendMessageHandler);
|
||||
}
|
||||
|
||||
void _sendMessageHandler(dynamic messageJson) {
|
||||
|
|
|
@ -32,7 +32,7 @@ class MessageBubbleState extends State<MessageBubble> {
|
|||
var fromMe = Provider.of<MessageMetadata>(context).senderHandle == Provider.of<ProfileInfoState>(context).onion;
|
||||
var borderRadiousEh = 15.0;
|
||||
var showClickableLinks = Provider.of<Settings>(context).isExperimentEnabled(ClickableLinksExperiment);
|
||||
|
||||
var formatMessages = Provider.of<Settings>(context).isExperimentEnabled(FormattingExperiment);
|
||||
DateTime messageDate = Provider.of<MessageMetadata>(context).timestamp;
|
||||
|
||||
// If the sender is not us, then we want to give them a nickname...
|
||||
|
@ -48,28 +48,16 @@ class MessageBubbleState extends State<MessageBubble> {
|
|||
var wdgSender = SelectableText(senderDisplayStr,
|
||||
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor));
|
||||
|
||||
var wdgMessage;
|
||||
|
||||
if (!showClickableLinks) {
|
||||
wdgMessage = SelectableText(
|
||||
widget.content + '\u202F',
|
||||
//key: Key(myKey),
|
||||
focusNode: _focus,
|
||||
style: TextStyle(
|
||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
textWidthBasis: TextWidthBasis.longestLine,
|
||||
);
|
||||
} else {
|
||||
wdgMessage = SelectableLinkify(
|
||||
var wdgMessage = SelectableLinkify(
|
||||
text: widget.content + '\u202F',
|
||||
// TODO: onOpen breaks the "selectable" functionality. Maybe something to do with gesture handler?
|
||||
options: LinkifyOptions(looseUrl: true, defaultToHttps: true),
|
||||
options: LinkifyOptions(messageFormatting: formatMessages, parseLinks: showClickableLinks, looseUrl: true, defaultToHttps: true),
|
||||
linkifiers: [UrlLinkifier()],
|
||||
onOpen: (link) {
|
||||
onOpen: showClickableLinks
|
||||
? (link) {
|
||||
_modalOpenLink(context, link);
|
||||
},
|
||||
}
|
||||
: null,
|
||||
//key: Key(myKey),
|
||||
focusNode: _focus,
|
||||
style: TextStyle(
|
||||
|
@ -81,7 +69,6 @@ class MessageBubbleState extends State<MessageBubble> {
|
|||
textAlign: TextAlign.left,
|
||||
textWidthBasis: TextWidthBasis.longestLine,
|
||||
);
|
||||
}
|
||||
|
||||
var wdgDecorations = MessageBubbleDecoration(ackd: Provider.of<MessageMetadata>(context).ackd, errored: Provider.of<MessageMetadata>(context).error, fromMe: fromMe, messageDate: messageDate);
|
||||
|
||||
|
|
|
@ -95,6 +95,11 @@ flutter:
|
|||
- family: CwtchIcons
|
||||
fonts:
|
||||
- asset: assets/fonts/CwtchIcons.ttf
|
||||
- family: RobotoMono
|
||||
fonts:
|
||||
- asset: assets/fonts/RobotoMono-Regular.ttf
|
||||
- asset: assets/fonts/RobotoMono-Bold.ttf
|
||||
weight: 700
|
||||
|
||||
# To add custom fonts to your application, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
|
|
Loading…
Reference in New Issue