Add unread and mention badges with notification sounds for channels

This commit is contained in:
Stone_Red
2026-07-16 14:46:05 +02:00
parent 4b0a06db4e
commit 9ceff54de6
5 changed files with 106 additions and 11 deletions
+1
View File
@@ -24,6 +24,7 @@
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
<PackageReference Include="MessageBox.Avalonia" Version="3.2.0" />
<PackageReference Include="NetCoreAudio" Version="2.0.1" />
<PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.6.2" />
<PackageReference Include="ReactiveUI.Avalonia" Version="11.3.8" />
<PackageReference Include="Romzetron.Avalonia" Version="11.3.4" />
+59
View File
@@ -36,9 +36,68 @@ public sealed class ChannelViewModel(ChannelModel model) : ViewModelBase
set => this.RaiseAndSetIfChanged(ref field, value);
}
public int UnreadCount
{
get => field;
set
{
if (field == value)
{
return;
}
field = value;
this.RaisePropertyChanged();
this.RaisePropertyChanged(nameof(HasUnread));
this.RaisePropertyChanged(nameof(BadgeText));
this.RaisePropertyChanged(nameof(BadgeColor));
}
}
public bool HasUnread => UnreadCount > 0;
public int MentionCount
{
get => field;
set
{
if (field == value)
{
return;
}
field = value;
this.RaisePropertyChanged();
this.RaisePropertyChanged(nameof(HasMentions));
this.RaisePropertyChanged(nameof(BadgeText));
this.RaisePropertyChanged(nameof(BadgeColor));
}
}
public bool HasMentions => MentionCount > 0;
public string BadgeColor => HasMentions ? "#E53935" : "#78909C";
public string BadgeText => HasMentions ? MentionCount.ToString() : UnreadCount.ToString();
public ObservableCollection<MessageViewModel> Messages { get; } = new ObservableCollection<MessageViewModel>(
model.Messages.Select(message => new MessageViewModel(message)));
public void IncrementUnread(bool isMention = false)
{
UnreadCount++;
if (isMention)
{
MentionCount++;
}
}
public void ClearUnread()
{
UnreadCount = 0;
MentionCount = 0;
}
public void ClearMessages()
{
Model.Messages.Clear();
+25 -2
View File
@@ -6,6 +6,7 @@ using Decho.Views;
using EchoHub.Client.Commands;
using EchoHub.Client.Config;
using EchoHub.Client.Services;
using EchoHub.Core.Constants;
using EchoHub.Core.DTOs;
using EchoHub.Core.Models;
@@ -22,6 +23,7 @@ namespace Decho.ViewModels;
public sealed class MainWindowViewModel : ViewModelBase
{
private readonly CommandHandler _commandHandler;
private readonly NotificationSoundService _notificationService;
private Window? _mainWindow;
public string Title { get; } = "Decho";
@@ -44,6 +46,7 @@ public sealed class MainWindowViewModel : ViewModelBase
{
ConnectionService = new ConnectionService();
_commandHandler = new CommandHandler();
_notificationService = new NotificationSoundService(ConfigManager.Load().Notifications);
Sidebar = new SidebarViewModel();
Chat = new ChatViewModel();
@@ -360,7 +363,7 @@ public sealed class MainWindowViewModel : ViewModelBase
await ConnectionService.NukeChannelAsync(serverUrl, channel);
};
_commandHandler.OnTestSound += () => Task.CompletedTask;
_commandHandler.OnTestSound += _notificationService.PlayTestAsync;
_commandHandler.OnQuit += () =>
{
@@ -554,11 +557,29 @@ public sealed class MainWindowViewModel : ViewModelBase
ConnectionService.MessageReceived += (serverUrl, message) =>
{
string? username = ConnectionService.GetCurrentUsername(serverUrl);
bool isMention = !string.IsNullOrEmpty(username)
&& message.Content.Contains($"@{username}", StringComparison.OrdinalIgnoreCase);
Avalonia.Threading.Dispatcher.UIThread.Post(() =>
{
ChannelViewModel? channelVm = FindChannelViewModel(serverUrl, message.ChannelName);
channelVm?.AddMessage(message);
if (channelVm is not null)
{
channelVm.AddMessage(message);
bool isSelected = string.Equals(Chat.CurrentChannelName, message.ChannelName, StringComparison.OrdinalIgnoreCase)
&& string.Equals(Chat.CurrentServerUrl, serverUrl, StringComparison.OrdinalIgnoreCase);
if (!isSelected)
{
channelVm.IncrementUnread(isMention);
}
}
});
if (isMention)
{
_ = _notificationService.PlayAsync();
}
};
ConnectionService.ErrorOccurred += (serverUrl, error) =>
@@ -646,6 +667,8 @@ public sealed class MainWindowViewModel : ViewModelBase
return;
}
channel.ClearUnread();
string serverUrl = FindServerUrlForChannel(channel);
bool isServerConnected = Sidebar.GetServer(serverUrl)?.IsConnected ?? false;
Chat.SetChannel(channel, serverUrl, isServerConnected);
+18 -6
View File
@@ -12,12 +12,24 @@
SelectedItem="{Binding SelectedChannel, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="vm:ChannelViewModel">
<TextBlock>
<InlineUIContainer BaselineAlignment="Bottom">
<i:Icon Value="fa-hashtag" FontWeight="Bold" Margin="0 0 0 1" />
</InlineUIContainer>
<Run Text="{Binding Name}" />
</TextBlock>
<Grid ColumnDefinitions="Auto, *, Auto" Margin="2 0">
<i:Icon Grid.Column="0" Value="fa-hashtag" FontWeight="Bold" Margin="0 0 4 1" VerticalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding Name}" TextTrimming="CharacterEllipsis" VerticalAlignment="Center" />
<Border Grid.Column="2"
IsVisible="{Binding HasUnread}"
Background="{Binding BadgeColor}"
CornerRadius="8"
MinWidth="16"
Height="16"
Padding="4 0"
Margin="4 0 0 0">
<TextBlock Text="{Binding BadgeText}"
FontSize="11"
Foreground="White"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>