diff --git a/lib/widgets/account/account_profile_popup.dart b/lib/widgets/account/account_profile_popup.dart index 2fd5759..f1cf350 100644 --- a/lib/widgets/account/account_profile_popup.dart +++ b/lib/widgets/account/account_profile_popup.dart @@ -8,9 +8,9 @@ import 'package:solian/services.dart'; import 'package:solian/widgets/account/account_heading.dart'; class AccountProfilePopup extends StatefulWidget { - final Account account; + final String name; - const AccountProfilePopup({super.key, required this.account}); + const AccountProfilePopup({super.key, required this.name}); @override State createState() => _AccountProfilePopupState(); @@ -21,11 +21,11 @@ class _AccountProfilePopupState extends State { Account? _userinfo; - void getUserinfo() async { + void _getUserinfo() async { setState(() => _isBusy = true); final client = ServiceFinder.configureClient('auth'); - final resp = await client.get('/users/${widget.account.name}'); + final resp = await client.get('/users/${widget.name}'); if (resp.statusCode == 200) { _userinfo = Account.fromJson(resp.body); setState(() => _isBusy = false); @@ -38,7 +38,7 @@ class _AccountProfilePopupState extends State { @override void initState() { super.initState(); - getUserinfo(); + _getUserinfo(); } @override diff --git a/lib/widgets/account/relative_list.dart b/lib/widgets/account/relative_list.dart index d992157..de2e7a2 100644 --- a/lib/widgets/account/relative_list.dart +++ b/lib/widgets/account/relative_list.dart @@ -35,7 +35,7 @@ class SilverRelativeList extends StatelessWidget { context: context, builder: (context) => AccountProfilePopup( - account: element.related, + name: element.related.name, ), ); }, diff --git a/lib/widgets/channel/channel_member.dart b/lib/widgets/channel/channel_member.dart index 9f8ef7c..c73dd18 100644 --- a/lib/widgets/channel/channel_member.dart +++ b/lib/widgets/channel/channel_member.dart @@ -160,7 +160,7 @@ class _ChannelMemberListPopupState extends State { backgroundColor: Theme.of(context).colorScheme.surface, context: context, builder: (context) => AccountProfilePopup( - account: element.account, + name: element.account.name, ), ); }, diff --git a/lib/widgets/chat/chat_event.dart b/lib/widgets/chat/chat_event.dart index 2ddd1df..2f127b3 100644 --- a/lib/widgets/chat/chat_event.dart +++ b/lib/widgets/chat/chat_event.dart @@ -243,7 +243,7 @@ class ChatEvent extends StatelessWidget { backgroundColor: Theme.of(context).colorScheme.surface, context: context, builder: (context) => AccountProfilePopup( - account: item.sender.account, + name: item.sender.account.name, ), ); }, diff --git a/lib/widgets/markdown_text_content.dart b/lib/widgets/markdown_text_content.dart index 3fe9635..fe38d98 100644 --- a/lib/widgets/markdown_text_content.dart +++ b/lib/widgets/markdown_text_content.dart @@ -1,8 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_markdown_selectionarea/flutter_markdown.dart'; import 'package:markdown/markdown.dart' as markdown; +import 'package:markdown/markdown.dart'; import 'package:url_launcher/url_launcher_string.dart'; +import 'account/account_profile_popup.dart'; + class MarkdownTextContent extends StatelessWidget { final String content; final bool isSelectable; @@ -34,6 +37,8 @@ class MarkdownTextContent extends StatelessWidget { extensionSet: markdown.ExtensionSet( markdown.ExtensionSet.gitHubFlavored.blockSyntaxes, [ + _UserNameCardInlineSyntax(), + _CustomEmoteInlineSyntax(), markdown.EmojiSyntax(), markdown.AutolinkExtensionSyntax(), ...markdown.ExtensionSet.gitHubFlavored.inlineSyntaxes @@ -41,6 +46,23 @@ class MarkdownTextContent extends StatelessWidget { ), onTapLink: (text, href, title) async { if (href == null) return; + if (href.startsWith('solink://')) { + final segments = href.replaceFirst('solink://', '').split('/'); + switch (segments[0]) { + case 'users': + showModalBottomSheet( + useRootNavigator: true, + isScrollControlled: true, + backgroundColor: Theme.of(context).colorScheme.surface, + context: context, + builder: (context) => AccountProfilePopup( + name: segments[1], + ), + ); + } + return; + } + await launchUrlString( href, mode: LaunchMode.externalApplication, @@ -57,3 +79,34 @@ class MarkdownTextContent extends StatelessWidget { return _buildContent(context); } } + +class _UserNameCardInlineSyntax extends InlineSyntax { + _UserNameCardInlineSyntax() : super(r'@[a-zA-Z0-9_]+'); + + @override + bool onMatch(markdown.InlineParser parser, Match match) { + final alias = match[0]!; + final anchor = markdown.Element.text('a', alias) + ..attributes['href'] = Uri.encodeFull( + 'solink://users/${alias.substring(1)}', + ); + parser.addNode(anchor); + + return true; + } +} + +class _CustomEmoteInlineSyntax extends InlineSyntax { + _CustomEmoteInlineSyntax() : super(r':([a-z0-9_+-]+):'); + + @override + bool onMatch(markdown.InlineParser parser, Match match) { + // final alias = match[1]!; + // TODO mapping things... + final element = markdown.Element.empty('img'); + element.attributes['src'] = 'https://www.twitch.tv/creatorcamp/assets/uploads/lul.png'; + parser.addNode(element); + + return true; + } +} diff --git a/lib/widgets/posts/post_item.dart b/lib/widgets/posts/post_item.dart index 0b3dd34..d4434ed 100644 --- a/lib/widgets/posts/post_item.dart +++ b/lib/widgets/posts/post_item.dart @@ -310,7 +310,7 @@ class _PostItemState extends State { backgroundColor: Theme.of(context).colorScheme.surface, context: context, builder: (context) => AccountProfilePopup( - account: item.author, + name: item.author.name, ), ); }, diff --git a/lib/widgets/realms/realm_member.dart b/lib/widgets/realms/realm_member.dart index 864b469..fe407c1 100644 --- a/lib/widgets/realms/realm_member.dart +++ b/lib/widgets/realms/realm_member.dart @@ -157,7 +157,7 @@ class _RealmMemberListPopupState extends State { backgroundColor: Theme.of(context).colorScheme.surface, context: context, builder: (context) => AccountProfilePopup( - account: element.account, + name: element.account.name, ), ); }, diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 8479c14..e547997 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -79,7 +79,7 @@ PODS: - GoogleUtilities/UserDefaults (7.13.3): - GoogleUtilities/Logger - GoogleUtilities/Privacy - - livekit_client (2.2.2): + - livekit_client (2.2.3): - FlutterMacOS - WebRTC-SDK (= 125.6422.04) - macos_window_utils (1.0.0): @@ -108,7 +108,7 @@ PODS: - screen_brightness_macos (0.1.0): - FlutterMacOS - Sentry/HybridSDK (8.32.0) - - sentry_flutter (8.5.0): + - sentry_flutter (8.6.0): - Flutter - FlutterMacOS - Sentry/HybridSDK (= 8.32.0) @@ -240,7 +240,7 @@ SPEC CHECKSUMS: gal: 61e868295d28fe67ffa297fae6dacebf56fd53e1 GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 - livekit_client: c24af2b8474a39325596e714118e05551ec5eacc + livekit_client: a59d8778582019242d96fe9da69d4ec48833b5ca macos_window_utils: 933f91f64805e2eb91a5bd057cf97cd097276663 media_kit_libs_macos_video: b3e2bbec2eef97c285f2b1baa7963c67c753fb82 media_kit_native_event_loop: 81fd5b45192b72f8b5b69eaf5b540f45777eb8d5 @@ -253,7 +253,7 @@ SPEC CHECKSUMS: protocol_handler_macos: d10a6c01d6373389ffd2278013ab4c47ed6d6daa screen_brightness_macos: 2d6d3af2165592d9a55ffcd95b7550970e41ebda Sentry: 96ae1dcdf01a644bc3a3b1dc279cecaf48a833fb - sentry_flutter: f1d86adcb93a959bc47a40d8d55059bdf7569bc5 + sentry_flutter: 090351ce1ff5f96a4b33ef9455b7e3b28185387d share_plus: 36537c04ce0c3e3f5bd297ce4318b6d5ee5fd6cf shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec