mirror of
https://github.com/Stone-Red-Code/Decho.git
synced 2026-09-04 08:56:14 +02:00
Add unread and mention badges with notification sounds for channels
This commit is contained in:
@@ -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" />
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -25,12 +25,12 @@ public sealed class MessageViewModel(MessageModel model) : ViewModelBase
|
||||
return Brushes.White;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
return new SolidColorBrush(Color.Parse(color));
|
||||
}
|
||||
catch
|
||||
{
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user