mirror of
https://github.com/krille-chan/fluffychat
synced 2024-09-10 13:05:11 +00:00
chore: Follow up html messages mxc images
This commit is contained in:
parent
d154e2a194
commit
a8538d7488
1 changed files with 134 additions and 2 deletions
|
@ -10,6 +10,7 @@ import 'package:matrix/matrix.dart';
|
|||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/mxc_image.dart';
|
||||
import '../../../utils/url_launcher.dart';
|
||||
|
||||
class HtmlMessage extends StatelessWidget {
|
||||
|
@ -105,6 +106,12 @@ class HtmlMessage extends StatelessWidget {
|
|||
'th': Style(
|
||||
border: Border.all(color: textColor, width: 0.5),
|
||||
),
|
||||
'li': Style(
|
||||
// https://github.com/Sub6Resources/flutter_html/issues/1280
|
||||
// Workaround for list items printed in the same line. This will
|
||||
// remove the dots/numbers. Hours wasted: 4
|
||||
display: Display.block,
|
||||
),
|
||||
},
|
||||
extensions: [
|
||||
UserPillExtension(context, room),
|
||||
|
@ -114,6 +121,9 @@ class HtmlMessage extends StatelessWidget {
|
|||
style: TextStyle(fontSize: fontSize, color: textColor),
|
||||
),
|
||||
const TableHtmlExtension(),
|
||||
SpoilerExtension(textColor: textColor),
|
||||
const ImageExtension(),
|
||||
FontColorExtension(),
|
||||
],
|
||||
onLinkTap: (url, _, __) => UrlLauncher(context, url).launchUrl(),
|
||||
onlyRenderTheseTags: const {
|
||||
|
@ -163,13 +173,135 @@ class HtmlMessage extends StatelessWidget {
|
|||
'caption',
|
||||
'pre',
|
||||
'span',
|
||||
// TODO: Implement image extension for Mxc URIs
|
||||
//'img',
|
||||
'img',
|
||||
'details',
|
||||
'summary'
|
||||
};
|
||||
}
|
||||
|
||||
class FontColorExtension extends HtmlExtension {
|
||||
static const String colorAttribute = 'color';
|
||||
static const String mxColorAttribute = 'data-mx-color';
|
||||
static const String bgColorAttribute = 'data-mx-bg-color';
|
||||
|
||||
@override
|
||||
Set<String> get supportedTags => {'font', 'span'};
|
||||
|
||||
@override
|
||||
bool matches(ExtensionContext context) {
|
||||
if (!supportedTags.contains(context.elementName)) return false;
|
||||
return context.element?.attributes.keys.any(
|
||||
{
|
||||
colorAttribute,
|
||||
mxColorAttribute,
|
||||
bgColorAttribute,
|
||||
}.contains,
|
||||
) ??
|
||||
false;
|
||||
}
|
||||
|
||||
Color? hexToColor(String? hexCode) {
|
||||
if (hexCode == null) return null;
|
||||
if (hexCode.startsWith('#')) hexCode = hexCode.substring(1);
|
||||
if (hexCode.length == 6) hexCode = 'FF$hexCode';
|
||||
final colorValue = int.tryParse(hexCode, radix: 16);
|
||||
return colorValue == null ? null : Color(colorValue);
|
||||
}
|
||||
|
||||
@override
|
||||
InlineSpan build(
|
||||
ExtensionContext context,
|
||||
Map<StyledElement, InlineSpan> Function() parseChildren,
|
||||
) {
|
||||
final colorText = context.element?.attributes[colorAttribute] ??
|
||||
context.element?.attributes[mxColorAttribute];
|
||||
final bgColor = context.element?.attributes[bgColorAttribute];
|
||||
return TextSpan(
|
||||
style: TextStyle(
|
||||
color: hexToColor(colorText),
|
||||
backgroundColor: hexToColor(bgColor),
|
||||
),
|
||||
text: context.innerHtml,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ImageExtension extends HtmlExtension {
|
||||
final double defaultDimension;
|
||||
|
||||
const ImageExtension({this.defaultDimension = 64});
|
||||
|
||||
@override
|
||||
Set<String> get supportedTags => {'img'};
|
||||
|
||||
@override
|
||||
InlineSpan build(
|
||||
ExtensionContext context,
|
||||
Map<StyledElement, InlineSpan> Function() parseChildren,
|
||||
) {
|
||||
final mxcUrl = Uri.tryParse(context.attributes['href'] ?? '');
|
||||
if (mxcUrl == null || mxcUrl.scheme != 'mxc') {
|
||||
return TextSpan(text: context.attributes['alt']);
|
||||
}
|
||||
|
||||
final width =
|
||||
double.tryParse(context.attributes['width'] ?? '') ?? defaultDimension;
|
||||
final height =
|
||||
double.tryParse(context.attributes['height'] ?? '') ?? defaultDimension;
|
||||
|
||||
return WidgetSpan(
|
||||
child: MxcImage(
|
||||
uri: mxcUrl,
|
||||
width: width,
|
||||
height: height,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SpoilerExtension extends HtmlExtension {
|
||||
final Color textColor;
|
||||
|
||||
const SpoilerExtension({required this.textColor});
|
||||
|
||||
@override
|
||||
Set<String> get supportedTags => {'span'};
|
||||
|
||||
static const String customDataAttribute = 'data-mx-spoiler';
|
||||
|
||||
@override
|
||||
bool matches(ExtensionContext context) {
|
||||
if (context.elementName != 'span') return false;
|
||||
return context.element?.attributes.containsKey(customDataAttribute) ??
|
||||
false;
|
||||
}
|
||||
|
||||
@override
|
||||
InlineSpan build(
|
||||
ExtensionContext context,
|
||||
Map<StyledElement, InlineSpan> Function() parseChildren,
|
||||
) {
|
||||
var obscure = true;
|
||||
return WidgetSpan(
|
||||
child: StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return InkWell(
|
||||
onTap: () => setState(() {
|
||||
obscure = !obscure;
|
||||
}),
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
style: obscure ? TextStyle(backgroundColor: textColor) : null,
|
||||
children: parseChildren().values.toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MatrixMathExtension extends HtmlExtension {
|
||||
final TextStyle? style;
|
||||
|
||||
|
|
Loading…
Reference in a new issue