diff --git a/src/Decho/Views/MessageItemView.axaml.cs b/src/Decho/Views/MessageItemView.axaml.cs index aa239ae..273884c 100644 --- a/src/Decho/Views/MessageItemView.axaml.cs +++ b/src/Decho/Views/MessageItemView.axaml.cs @@ -1,5 +1,7 @@ +using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Documents; +using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Media; using Avalonia.Media.Imaging; @@ -10,6 +12,8 @@ using Decho.ViewModels; using EchoHub.Core.DTOs; using EchoHub.Core.Models; +using Romzetron.Avalonia; + using System.Text.RegularExpressions; namespace Decho.Views; @@ -17,6 +21,7 @@ namespace Decho.Views; public partial class MessageItemView : UserControl { private static readonly Regex MentionRegex = new(@"@(\w+)", RegexOptions.Compiled); + private static readonly Regex ChannelRegex = new(@"(?(); + var channelMatches = ChannelRegex.Matches(content).Cast(); + var allMatches = mentionMatches.Concat(channelMatches) + .OrderBy(m => m.Index) + .ToList(); + int lastIndex = 0; - foreach (Match match in MentionRegex.Matches(content)) + foreach (Match match in allMatches) { if (match.Index > lastIndex) { tb.Inlines?.Add(new Run(content[lastIndex..match.Index])); } - tb.Inlines?.Add(new Run(match.Value) + bool isMention = match.Value[0] == '@'; + var container = new InlineUIContainer { - Foreground = new SolidColorBrush(Color.Parse("#FEE75C")), + BaselineAlignment = BaselineAlignment.Center, + }; + var label = new TextBlock + { + Text = match.Value, FontWeight = FontWeight.Bold, - }); + Cursor = new Cursor(StandardCursorType.Hand), + Padding = new Thickness(0), + LineHeight = double.NaN, + }; + + if (isMention) + { + string username = match.Groups[1].Value; + label.Margin = new Thickness(0, 0, 0, -2); + label.Foreground = ColorPalette.Yellow05; + label.PointerPressed += (_, args) => OnMentionPointerPressed(username, args); + } + else + { + string channelName = match.Groups[1].Value; + label.Margin = new Thickness(0, 0, 0, -2); + label.Foreground = ColorPalette.Blue05; + label.PointerPressed += (_, args) => OnChannelPointerPressed(channelName, args); + } + + container.Child = label; + tb.Inlines?.Add(container); lastIndex = match.Index + match.Length; } @@ -77,6 +114,64 @@ public partial class MessageItemView : UserControl } } + private async void OnMentionPointerPressed(string username, PointerPressedEventArgs e) + { + if (DataContext is not MessageViewModel msg) + { + return; + } + + MainWindowViewModel? mainVm = this.GetMainWindowViewModel(); + if (mainVm is null) + { + return; + } + + string serverUrl = msg.ServerUrl ?? mainVm.Chat.CurrentServerUrl; + UserProfileDto? profile = await mainVm.ConnectionService.GetUserProfileAsync(serverUrl, username); + if (profile is null) + { + return; + } + + ProfileWindow dialog = new ProfileWindow(profile); + + if (TopLevel.GetTopLevel(this) is Window parent) + { + await dialog.ShowDialog(parent); + } + } + + private void OnChannelPointerPressed(string channelName, PointerPressedEventArgs e) + { + if (DataContext is not MessageViewModel msg) + { + return; + } + + MainWindowViewModel? mainVm = this.GetMainWindowViewModel(); + if (mainVm is null) + { + return; + } + + string serverUrl = msg.ServerUrl ?? mainVm.Chat.CurrentServerUrl; + ServerViewModel? serverVm = mainVm.Sidebar.GetServer(serverUrl); + if (serverVm is null) + { + return; + } + + ChannelViewModel? channel = serverVm.Channels.FirstOrDefault(c => + string.Equals(c.Name, channelName, StringComparison.OrdinalIgnoreCase)); + if (channel is null) + { + return; + } + + mainVm.Sidebar.SelectedChannel = channel; + } + private async void OnAttachmentImageLoaded(object? sender, RoutedEventArgs e) { if (sender is not Image image)