mirror of
https://github.com/Stone-Red-Code/Decho.git
synced 2026-09-08 07:46:12 +02:00
Add clickable mention and channel links to message content display
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Documents;
|
using Avalonia.Controls.Documents;
|
||||||
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
@@ -10,6 +12,8 @@ using Decho.ViewModels;
|
|||||||
using EchoHub.Core.DTOs;
|
using EchoHub.Core.DTOs;
|
||||||
using EchoHub.Core.Models;
|
using EchoHub.Core.Models;
|
||||||
|
|
||||||
|
using Romzetron.Avalonia;
|
||||||
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace Decho.Views;
|
namespace Decho.Views;
|
||||||
@@ -17,6 +21,7 @@ namespace Decho.Views;
|
|||||||
public partial class MessageItemView : UserControl
|
public partial class MessageItemView : UserControl
|
||||||
{
|
{
|
||||||
private static readonly Regex MentionRegex = new(@"@(\w+)", RegexOptions.Compiled);
|
private static readonly Regex MentionRegex = new(@"@(\w+)", RegexOptions.Compiled);
|
||||||
|
private static readonly Regex ChannelRegex = new(@"(?<!\w)#(\w+)", RegexOptions.Compiled);
|
||||||
private CancellationTokenSource? _loadCts;
|
private CancellationTokenSource? _loadCts;
|
||||||
private string? _loadedMessageId;
|
private string? _loadedMessageId;
|
||||||
|
|
||||||
@@ -54,19 +59,51 @@ public partial class MessageItemView : UserControl
|
|||||||
|
|
||||||
tb.Inlines?.Clear();
|
tb.Inlines?.Clear();
|
||||||
|
|
||||||
|
var mentionMatches = MentionRegex.Matches(content).Cast<Match>();
|
||||||
|
var channelMatches = ChannelRegex.Matches(content).Cast<Match>();
|
||||||
|
var allMatches = mentionMatches.Concat(channelMatches)
|
||||||
|
.OrderBy(m => m.Index)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
int lastIndex = 0;
|
int lastIndex = 0;
|
||||||
foreach (Match match in MentionRegex.Matches(content))
|
foreach (Match match in allMatches)
|
||||||
{
|
{
|
||||||
if (match.Index > lastIndex)
|
if (match.Index > lastIndex)
|
||||||
{
|
{
|
||||||
tb.Inlines?.Add(new Run(content[lastIndex..match.Index]));
|
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,
|
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;
|
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)
|
private async void OnAttachmentImageLoaded(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is not Image image)
|
if (sender is not Image image)
|
||||||
|
|||||||
Reference in New Issue
Block a user