|
|
|
@ -8,7 +8,9 @@ import 'package:html/dom.dart' as dom;
|
|
|
|
|
import 'package:html/parser.dart' as parser;
|
|
|
|
|
import 'package:matrix/matrix.dart';
|
|
|
|
|
|
|
|
|
|
import 'package:fluffychat/utils/event_checkbox_extension.dart';
|
|
|
|
|
import 'package:fluffychat/widgets/avatar.dart';
|
|
|
|
|
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
|
|
|
|
import 'package:fluffychat/widgets/mxc_image.dart';
|
|
|
|
|
import '../../../utils/url_launcher.dart';
|
|
|
|
|
|
|
|
|
@ -19,6 +21,8 @@ class HtmlMessage extends StatelessWidget {
|
|
|
|
|
final double fontSize;
|
|
|
|
|
final TextStyle linkStyle;
|
|
|
|
|
final void Function(LinkableElement) onOpen;
|
|
|
|
|
final String? eventId;
|
|
|
|
|
final Set<Event>? checkboxCheckedEvents;
|
|
|
|
|
|
|
|
|
|
const HtmlMessage({
|
|
|
|
|
super.key,
|
|
|
|
@ -28,6 +32,8 @@ class HtmlMessage extends StatelessWidget {
|
|
|
|
|
required this.linkStyle,
|
|
|
|
|
this.textColor = Colors.black,
|
|
|
|
|
required this.onOpen,
|
|
|
|
|
this.eventId,
|
|
|
|
|
this.checkboxCheckedEvents,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/// Keep in sync with: https://spec.matrix.org/latest/client-server-api/#mroommessage-msgtypes
|
|
|
|
@ -218,6 +224,24 @@ class HtmlMessage extends StatelessWidget {
|
|
|
|
|
if (!{'ol', 'ul'}.contains(node.parent?.localName)) {
|
|
|
|
|
continue block;
|
|
|
|
|
}
|
|
|
|
|
final eventId = this.eventId;
|
|
|
|
|
|
|
|
|
|
final isCheckbox = node.className == 'task-list-item';
|
|
|
|
|
final checkboxIndex = isCheckbox
|
|
|
|
|
? node.rootElement
|
|
|
|
|
.getElementsByClassName('task-list-item')
|
|
|
|
|
.indexOf(node) +
|
|
|
|
|
1
|
|
|
|
|
: null;
|
|
|
|
|
final checkedByReaction = !isCheckbox
|
|
|
|
|
? null
|
|
|
|
|
: checkboxCheckedEvents?.firstWhereOrNull(
|
|
|
|
|
(event) => event.checkedCheckboxId == checkboxIndex,
|
|
|
|
|
);
|
|
|
|
|
final staticallyChecked = !isCheckbox
|
|
|
|
|
? false
|
|
|
|
|
: node.children.first.attributes['checked'] == 'true';
|
|
|
|
|
|
|
|
|
|
return WidgetSpan(
|
|
|
|
|
child: Padding(
|
|
|
|
|
padding: EdgeInsets.only(left: fontSize),
|
|
|
|
@ -231,6 +255,42 @@ class HtmlMessage extends StatelessWidget {
|
|
|
|
|
text:
|
|
|
|
|
'${(node.parent?.nodes.whereType<dom.Element>().toList().indexOf(node) ?? 0) + (int.tryParse(node.parent?.attributes['start'] ?? '1') ?? 1)}. ',
|
|
|
|
|
),
|
|
|
|
|
if (node.className == 'task-list-item')
|
|
|
|
|
WidgetSpan(
|
|
|
|
|
child: Padding(
|
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
|
|
|
|
child: SizedBox.square(
|
|
|
|
|
dimension: fontSize,
|
|
|
|
|
child: Checkbox.adaptive(
|
|
|
|
|
checkColor: textColor,
|
|
|
|
|
side: BorderSide(color: textColor),
|
|
|
|
|
activeColor: textColor.withAlpha(64),
|
|
|
|
|
visualDensity: VisualDensity.compact,
|
|
|
|
|
value:
|
|
|
|
|
staticallyChecked || checkedByReaction != null,
|
|
|
|
|
onChanged: eventId == null ||
|
|
|
|
|
checkboxIndex == null ||
|
|
|
|
|
staticallyChecked ||
|
|
|
|
|
!room.canSendDefaultMessages ||
|
|
|
|
|
(checkedByReaction != null &&
|
|
|
|
|
checkedByReaction.senderId !=
|
|
|
|
|
room.client.userID)
|
|
|
|
|
? null
|
|
|
|
|
: (_) => showFutureLoadingDialog(
|
|
|
|
|
context: context,
|
|
|
|
|
future: () => checkedByReaction != null
|
|
|
|
|
? room.redactEvent(
|
|
|
|
|
checkedByReaction.eventId,
|
|
|
|
|
)
|
|
|
|
|
: room.checkCheckbox(
|
|
|
|
|
eventId,
|
|
|
|
|
checkboxIndex,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
..._renderWithLineBreaks(
|
|
|
|
|
node.nodes,
|
|
|
|
|
context,
|
|
|
|
@ -446,11 +506,9 @@ class HtmlMessage extends StatelessWidget {
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
final element = parser.parse(html).body ?? dom.Element.html('');
|
|
|
|
|
return Text.rich(
|
|
|
|
|
_renderHtml(
|
|
|
|
|
parser.parse(html).body ?? dom.Element.html(''),
|
|
|
|
|
context,
|
|
|
|
|
),
|
|
|
|
|
_renderHtml(element, context),
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
fontSize: fontSize,
|
|
|
|
|
color: textColor,
|
|
|
|
@ -516,3 +574,7 @@ extension on String {
|
|
|
|
|
return colorValue == null ? null : Color(colorValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extension on dom.Element {
|
|
|
|
|
dom.Element get rootElement => parent?.rootElement ?? this;
|
|
|
|
|
}
|
|
|
|
|