Add HTML render

This commit is contained in:
Sorunome 2020-05-09 11:36:41 +00:00 committed by Christian Pauly
parent eb72198048
commit 3ee1018eb5
12 changed files with 167 additions and 21 deletions

View File

@ -0,0 +1,41 @@
import 'package:famedlysdk/famedlysdk.dart';
import 'package:flutter_matrix_html/flutter_html.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'matrix.dart';
class HtmlMessage extends StatelessWidget {
final String html;
final Color textColor;
final int maxLines;
const HtmlMessage({this.html, this.textColor, this.maxLines});
@override
Widget build(BuildContext context) {
// there is no need to pre-validate the html, as we validate it while rendering
return Html(
data: html,
defaultTextStyle: TextStyle(color: textColor),
shrinkToFit: true,
maxLines: maxLines,
onLinkTap: (String url) {
if (url == null || url.isEmpty) {
return;
}
launch(url);
},
getMxcUrl: (String mxc, double width, double height) {
final ratio = MediaQuery.of(context).devicePixelRatio;
return Uri.parse(mxc)?.getThumbnail(
Matrix.of(context).client,
width: (width ?? 800) * ratio,
height: (height ?? 800) * ratio,
method: ThumbnailMethod.scale,
);
},
);
}
}

View File

@ -55,6 +55,7 @@ class MatrixState extends State<Matrix> {
String activeRoomId; String activeRoomId;
File wallpaper; File wallpaper;
bool renderHtml = false;
String jitsiInstance = 'https://meet.jit.si/'; String jitsiInstance = 'https://meet.jit.si/';
@ -189,6 +190,9 @@ class MatrixState extends State<Matrix> {
wallpaper = file; wallpaper = file;
} }
}); });
client.storeAPI.getItem("chat.fluffy.renderHtml").then((final render) async {
renderHtml = render == "1";
});
} }
super.initState(); super.initState();
} }

View File

@ -8,6 +8,7 @@ import 'package:link_text/link_text.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'matrix.dart'; import 'matrix.dart';
import 'message_download_content.dart'; import 'message_download_content.dart';
import 'html_message.dart';
class MessageContent extends StatelessWidget { class MessageContent extends StatelessWidget {
final Event event; final Event event;
@ -36,13 +37,30 @@ class MessageContent extends StatelessWidget {
case MessageTypes.Video: case MessageTypes.Video:
case MessageTypes.File: case MessageTypes.File:
return MessageDownloadContent(event, textColor); return MessageDownloadContent(event, textColor);
case MessageTypes.BadEncrypted:
case MessageTypes.Text: case MessageTypes.Text:
case MessageTypes.Notice:
case MessageTypes.Emote:
if (
Matrix.of(context).renderHtml && !event.redacted &&
event.content['format'] == 'org.matrix.custom.html' &&
event.content['formatted_body'] is String
) {
String html = event.content['formatted_body'];
if (event.messageType == MessageTypes.Emote) {
html = "* $html";
}
return HtmlMessage(
html: html,
textColor: textColor,
);
}
// else we fall through to the normal message rendering
continue textmessage;
case MessageTypes.BadEncrypted:
case MessageTypes.Reply: case MessageTypes.Reply:
case MessageTypes.Location: case MessageTypes.Location:
case MessageTypes.None: case MessageTypes.None:
case MessageTypes.Notice: textmessage:
case MessageTypes.Emote:
default: default:
if (event.content['msgtype'] == Matrix.callNamespace) { if (event.content['msgtype'] == Matrix.callNamespace) {
return RaisedButton( return RaisedButton(
@ -76,5 +94,6 @@ class MessageContent extends StatelessWidget {
), ),
); );
} }
return Container(); // else flutter analyze complains
} }
} }

View File

@ -2,6 +2,9 @@ import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'html_message.dart';
import 'matrix.dart';
class ReplyContent extends StatelessWidget { class ReplyContent extends StatelessWidget {
final Event replyEvent; final Event replyEvent;
final bool lightText; final bool lightText;
@ -11,6 +14,40 @@ class ReplyContent extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget replyBody;
if (
replyEvent != null && Matrix.of(context).renderHtml &&
[EventTypes.Message, EventTypes.Encrypted].contains(replyEvent.type) &&
[MessageTypes.Text, MessageTypes.Notice, MessageTypes.Emote].contains(replyEvent.messageType) &&
!replyEvent.redacted && replyEvent.content['format'] == 'org.matrix.custom.html' && replyEvent.content['formatted_body'] is String
) {
String html = replyEvent.content['formatted_body'];
if (replyEvent.messageType == MessageTypes.Emote) {
html = "* $html";
}
replyBody = HtmlMessage(
html: html,
textColor: lightText
? Colors.white
: Theme.of(context).textTheme.bodyText2.color,
maxLines: 1,
);
} else {
replyBody = Text(
replyEvent?.getLocalizedBody(
L10n.of(context),
withSenderNamePrefix: false,
hideReply: true,
) ??
"",
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(
color: lightText
? Colors.white
: Theme.of(context).textTheme.bodyText2.color),
);
}
return Row( return Row(
children: <Widget>[ children: <Widget>[
Container( Container(
@ -34,20 +71,7 @@ class ReplyContent extends StatelessWidget {
lightText ? Colors.white : Theme.of(context).primaryColor, lightText ? Colors.white : Theme.of(context).primaryColor,
), ),
), ),
Text( replyBody,
replyEvent?.getLocalizedBody(
L10n.of(context),
withSenderNamePrefix: false,
hideReply: true,
) ??
"",
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(
color: lightText
? Colors.white
: Theme.of(context).textTheme.bodyText2.color),
),
], ],
), ),
), ),

View File

@ -248,6 +248,11 @@
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
}, },
"Chat": "Chat",
"@Chat": {
"type": "text",
"placeholders": {}
},
"Chat details": "Gruppeninfo", "Chat details": "Gruppeninfo",
"@Chat details": { "@Chat details": {
"type": "text", "type": "text",
@ -847,6 +852,11 @@
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
}, },
"Render rich message content": "Zeige Nachrichtenformatierungen an",
"@Render rich message content": {
"type": "text",
"placeholders": {}
},
"redactedAnEvent": "{username} hat ein Event enternt", "redactedAnEvent": "{username} hat ein Event enternt",
"@redactedAnEvent": { "@redactedAnEvent": {
"type": "text", "type": "text",
@ -1317,4 +1327,4 @@
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
} }
} }

View File

@ -1,5 +1,5 @@
{ {
"@@last_modified": "2020-05-09T12:56:54.540935", "@@last_modified": "2020-05-09T13:02:58.452942",
"About": "About", "About": "About",
"@About": { "@About": {
"type": "text", "type": "text",
@ -248,6 +248,11 @@
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
}, },
"Chat": "Chat",
"@Chat": {
"type": "text",
"placeholders": {}
},
"Chat details": "Chat details", "Chat details": "Chat details",
"@Chat details": { "@Chat details": {
"type": "text", "type": "text",
@ -852,6 +857,11 @@
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
}, },
"Render rich message content": "Render rich message content",
"@Render rich message content": {
"type": "text",
"placeholders": {}
},
"Recording": "Recording", "Recording": "Recording",
"@Recording": { "@Recording": {
"type": "text", "type": "text",
@ -1327,4 +1337,4 @@
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
} }
} }

View File

@ -206,6 +206,8 @@ class L10n extends MatrixLocalizations {
String get channelCorruptedDecryptError => String get channelCorruptedDecryptError =>
Intl.message("The encryption has been corrupted"); Intl.message("The encryption has been corrupted");
String get chat => Intl.message('Chat');
String get chatDetails => Intl.message('Chat details'); String get chatDetails => Intl.message('Chat details');
String get chooseAStrongPassword => Intl.message("Choose a strong password"); String get chooseAStrongPassword => Intl.message("Choose a strong password");
@ -524,6 +526,8 @@ class L10n extends MatrixLocalizations {
String get rejoin => Intl.message("Rejoin"); String get rejoin => Intl.message("Rejoin");
String get renderRichContent => Intl.message("Render rich message content");
String get recording => Intl.message("Recording"); String get recording => Intl.message("Recording");
String redactedAnEvent(String username) => Intl.message( String redactedAnEvent(String username) => Intl.message(

View File

@ -164,6 +164,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Change wallpaper" : MessageLookupByLibrary.simpleMessage("Hintergrund ändern"), "Change wallpaper" : MessageLookupByLibrary.simpleMessage("Hintergrund ändern"),
"Change your style" : MessageLookupByLibrary.simpleMessage("Ändere Deinen Style"), "Change your style" : MessageLookupByLibrary.simpleMessage("Ändere Deinen Style"),
"Changelog" : MessageLookupByLibrary.simpleMessage("Changelog"), "Changelog" : MessageLookupByLibrary.simpleMessage("Changelog"),
"Chat" : MessageLookupByLibrary.simpleMessage("Chat"),
"Chat details" : MessageLookupByLibrary.simpleMessage("Gruppeninfo"), "Chat details" : MessageLookupByLibrary.simpleMessage("Gruppeninfo"),
"Choose a strong password" : MessageLookupByLibrary.simpleMessage("Wähle ein sicheres Passwort"), "Choose a strong password" : MessageLookupByLibrary.simpleMessage("Wähle ein sicheres Passwort"),
"Choose a username" : MessageLookupByLibrary.simpleMessage("Wähle einen Benutzernamen"), "Choose a username" : MessageLookupByLibrary.simpleMessage("Wähle einen Benutzernamen"),
@ -261,6 +262,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Remove device" : MessageLookupByLibrary.simpleMessage("Gerät entfernen"), "Remove device" : MessageLookupByLibrary.simpleMessage("Gerät entfernen"),
"Remove exile" : MessageLookupByLibrary.simpleMessage("Verbannung aufheben"), "Remove exile" : MessageLookupByLibrary.simpleMessage("Verbannung aufheben"),
"Remove message" : MessageLookupByLibrary.simpleMessage("Nachricht entfernen"), "Remove message" : MessageLookupByLibrary.simpleMessage("Nachricht entfernen"),
"Render rich message content" : MessageLookupByLibrary.simpleMessage("Zeige Nachrichtenformatierungen an"),
"Reply" : MessageLookupByLibrary.simpleMessage("Antworten"), "Reply" : MessageLookupByLibrary.simpleMessage("Antworten"),
"Request permission" : MessageLookupByLibrary.simpleMessage("Berechtigung anfragen"), "Request permission" : MessageLookupByLibrary.simpleMessage("Berechtigung anfragen"),
"Request to read older messages" : MessageLookupByLibrary.simpleMessage("Anfrage um ältere Nachrichten zu lesen"), "Request to read older messages" : MessageLookupByLibrary.simpleMessage("Anfrage um ältere Nachrichten zu lesen"),

View File

@ -164,6 +164,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Change wallpaper" : MessageLookupByLibrary.simpleMessage("Change wallpaper"), "Change wallpaper" : MessageLookupByLibrary.simpleMessage("Change wallpaper"),
"Change your style" : MessageLookupByLibrary.simpleMessage("Change your style"), "Change your style" : MessageLookupByLibrary.simpleMessage("Change your style"),
"Changelog" : MessageLookupByLibrary.simpleMessage("Changelog"), "Changelog" : MessageLookupByLibrary.simpleMessage("Changelog"),
"Chat" : MessageLookupByLibrary.simpleMessage("Chat"),
"Chat details" : MessageLookupByLibrary.simpleMessage("Chat details"), "Chat details" : MessageLookupByLibrary.simpleMessage("Chat details"),
"Choose a strong password" : MessageLookupByLibrary.simpleMessage("Choose a strong password"), "Choose a strong password" : MessageLookupByLibrary.simpleMessage("Choose a strong password"),
"Choose a username" : MessageLookupByLibrary.simpleMessage("Choose a username"), "Choose a username" : MessageLookupByLibrary.simpleMessage("Choose a username"),
@ -263,6 +264,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Remove device" : MessageLookupByLibrary.simpleMessage("Remove device"), "Remove device" : MessageLookupByLibrary.simpleMessage("Remove device"),
"Remove exile" : MessageLookupByLibrary.simpleMessage("Remove exile"), "Remove exile" : MessageLookupByLibrary.simpleMessage("Remove exile"),
"Remove message" : MessageLookupByLibrary.simpleMessage("Remove message"), "Remove message" : MessageLookupByLibrary.simpleMessage("Remove message"),
"Render rich message content" : MessageLookupByLibrary.simpleMessage("Render rich message content"),
"Reply" : MessageLookupByLibrary.simpleMessage("Reply"), "Reply" : MessageLookupByLibrary.simpleMessage("Reply"),
"Request permission" : MessageLookupByLibrary.simpleMessage("Request permission"), "Request permission" : MessageLookupByLibrary.simpleMessage("Request permission"),
"Request to read older messages" : MessageLookupByLibrary.simpleMessage("Request to read older messages"), "Request to read older messages" : MessageLookupByLibrary.simpleMessage("Request to read older messages"),

View File

@ -210,6 +210,28 @@ class _SettingsState extends State<Settings> {
); );
}), }),
Divider(thickness: 1), Divider(thickness: 1),
ListTile(
title: Text(
L10n.of(context).chat,
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold,
),
),
),
ListTile(
title: Text(L10n.of(context).renderRichContent),
trailing: Switch(
value: Matrix.of(context).renderHtml,
activeColor: Theme.of(context).primaryColor,
onChanged: (bool newValue) async {
Matrix.of(context).renderHtml = newValue;
await client.storeAPI.setItem("chat.fluffy.renderHtml", newValue ? "1" : "0");
setState(() => null);
},
),
),
Divider(thickness: 1),
ListTile( ListTile(
title: Text( title: Text(
L10n.of(context).account, L10n.of(context).account,

View File

@ -181,6 +181,13 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_matrix_html:
dependency: "direct main"
description:
name: flutter_matrix_html
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.5"
flutter_secure_storage: flutter_secure_storage:
dependency: "direct main" dependency: "direct main"
description: description:
@ -738,7 +745,7 @@ packages:
name: webkit_inspection_protocol name: webkit_inspection_protocol
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.5.2" version: "0.5.3"
webview_flutter: webview_flutter:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -54,6 +54,7 @@ dependencies:
open_file: ^3.0.1 open_file: ^3.0.1
mime_type: ^0.3.0 mime_type: ^0.3.0
flutter_styled_toast: ^1.2.1 flutter_styled_toast: ^1.2.1
flutter_matrix_html: ^0.0.5
intl: ^0.16.0 intl: ^0.16.0
intl_translation: ^0.17.9 intl_translation: ^0.17.9