diff --git a/lib/widgets/room_chat.dart b/lib/widgets/room_chat.dart index 3ad104c..f4ad046 100644 --- a/lib/widgets/room_chat.dart +++ b/lib/widgets/room_chat.dart @@ -1,3 +1,4 @@ +import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/foundation.dart"; import "package:flutter/material.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; @@ -8,6 +9,7 @@ import "package:flyer_chat_file_message/flyer_chat_file_message.dart"; import "package:flyer_chat_image_message/flyer_chat_image_message.dart"; import "package:flyer_chat_system_message/flyer_chat_system_message.dart"; import "package:flyer_chat_text_message/flyer_chat_text_message.dart"; +import "package:gpt_markdown/custom_widgets/code_field.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:nexus/controllers/current_room_controller.dart"; import "package:nexus/controllers/room_chat_controller.dart"; @@ -16,6 +18,7 @@ import "package:nexus/helpers/launch_helper.dart"; import "package:nexus/widgets/chat_box.dart"; import "package:nexus/widgets/member_list.dart"; import "package:nexus/widgets/room_appbar.dart"; +import "package:nexus/widgets/spoiler_text.dart"; import "package:nexus/widgets/top_widget.dart"; import "package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart"; @@ -125,10 +128,67 @@ class RoomChat extends HookConsumerWidget { }) => FlyerChatTextMessage( customWidget: HtmlWidget( message.metadata?["formatted"], - customWidgetBuilder: (element) => - element.localName == "mx-reply" - ? SizedBox.shrink() - : null, + customWidgetBuilder: (element) { + if (element.localName == "mx-reply") { + return SizedBox.shrink(); + } + if (element.localName == "code") { + return SizedBox( + width: 400, + child: CodeField( + name: element.className + .replaceAll("language-", ""), + codes: element.text, + ), + ); + } + if (element.localName == "img") { + final src = Uri.tryParse( + element.attributes["src"] ?? "", + ); + if (src?.scheme != "mxc") { + return SizedBox.shrink(); + } + + // TODO: Should do something like: + // return Image.network( + // src!.getThumbnailUri( + // room.roomData.client, + // ), + // ); + + return SizedBox.shrink(); + } + if (element.attributes.keys.contains( + "data-mx-spoiler", + )) { + return SpoilerText( + text: element.text, + ); + } + return null; + }, + customStylesBuilder: (element) => { + "width": "auto", + ...Map.fromEntries( + element.attributes + .mapTo?>( + (key, value) => switch (key) { + "data-mx-color" => MapEntry( + "color", + value, + ), + "data-mx-bg-color" => + MapEntry( + "background-color", + value, + ), + _ => null, + }, + ) + .nonNulls, + ), + }, onTapUrl: (url) => ref .watch(LaunchHelper.provider) .launchUrl(Uri.parse(url)), diff --git a/lib/widgets/spoiler_text.dart b/lib/widgets/spoiler_text.dart new file mode 100644 index 0000000..9a42bff --- /dev/null +++ b/lib/widgets/spoiler_text.dart @@ -0,0 +1,29 @@ +import "package:flutter/material.dart"; +import "package:flutter_hooks/flutter_hooks.dart"; + +class SpoilerText extends HookWidget { + final String text; + + const SpoilerText({super.key, required this.text}); + + @override + Widget build(BuildContext context) { + final revealed = useState(false); + + return InkWell( + onTap: () => revealed.value = !revealed.value, + child: AnimatedContainer( + duration: const Duration(milliseconds: 100), + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), + decoration: BoxDecoration( + color: revealed.value ? Colors.transparent : Colors.blueGrey, + borderRadius: BorderRadius.circular(4), + ), + child: Text( + text, + style: TextStyle(color: revealed.value ? null : Colors.transparent), + ), + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 2b6d548..a1b17b0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -664,7 +664,7 @@ packages: source: hosted version: "2.1.3" gpt_markdown: - dependency: transitive + dependency: "direct main" description: name: gpt_markdown sha256: "8174983f2ed7d8576d25810913e3afe3f8ffdaa3172c0c823b7cfc289b67f380" diff --git a/pubspec.yaml b/pubspec.yaml index 703708c..44bfd0c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -58,6 +58,7 @@ dependencies: scaled_app: ^2.3.0 flutter_vodozemac: ^0.4.1 flutter_widget_from_html_core: ^0.17.0 + gpt_markdown: ^1.1.4 dev_dependencies: build_runner: ^2.4.11