mirror of
https://github.com/RedWizardsLab/EchoHub.git
synced 2026-09-04 08:36:11 +02:00
feat: implement notification sound functionality with user customization options
This commit is contained in:
@@ -21,6 +21,7 @@ public sealed class AppOrchestrator : IDisposable
|
||||
private readonly IApplication _app;
|
||||
private readonly MainWindow _mainWindow;
|
||||
private readonly CommandHandler _commandHandler;
|
||||
private readonly NotificationSoundService _notificationSound;
|
||||
|
||||
private EchoHubConnection? _connection;
|
||||
private ApiClient? _apiClient;
|
||||
@@ -41,6 +42,7 @@ public sealed class AppOrchestrator : IDisposable
|
||||
_config = config;
|
||||
_mainWindow = new MainWindow(app);
|
||||
_commandHandler = new CommandHandler();
|
||||
_notificationSound = new NotificationSoundService(config.Notifications);
|
||||
|
||||
WireMainWindowEvents();
|
||||
WireCommandHandlerEvents();
|
||||
@@ -572,7 +574,9 @@ public sealed class AppOrchestrator : IDisposable
|
||||
var editResult = ProfileEditDialog.Show(_app,
|
||||
currentProfile?.DisplayName,
|
||||
currentProfile?.Bio,
|
||||
currentProfile?.NicknameColor);
|
||||
currentProfile?.NicknameColor,
|
||||
_config.Notifications.Enabled,
|
||||
_config.Notifications.Volume);
|
||||
|
||||
if (editResult is null) return;
|
||||
|
||||
@@ -633,6 +637,18 @@ public sealed class AppOrchestrator : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
if (editResult.NotificationSoundEnabled.HasValue)
|
||||
{
|
||||
_config.Notifications.Enabled = editResult.NotificationSoundEnabled.Value;
|
||||
_notificationSound.SetEnabled(editResult.NotificationSoundEnabled.Value);
|
||||
}
|
||||
|
||||
if (editResult.NotificationVolume.HasValue)
|
||||
{
|
||||
_config.Notifications.Volume = editResult.NotificationVolume.Value;
|
||||
_notificationSound.SetVolume(editResult.NotificationVolume.Value);
|
||||
}
|
||||
|
||||
_config.DefaultPreset = new AccountPreset
|
||||
{
|
||||
DisplayName = editResult.DisplayName,
|
||||
@@ -768,8 +784,17 @@ public sealed class AppOrchestrator : IDisposable
|
||||
private void WireConnectionEvents(EchoHubConnection connection)
|
||||
{
|
||||
connection.OnMessageReceived += message =>
|
||||
{
|
||||
InvokeUI(() => _mainWindow.AddMessage(message));
|
||||
|
||||
if (!string.IsNullOrEmpty(_currentUsername)
|
||||
&& !message.SenderUsername.Equals(_currentUsername, StringComparison.OrdinalIgnoreCase)
|
||||
&& message.Content.Contains($"@{_currentUsername}", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_ = _notificationSound.PlayAsync();
|
||||
}
|
||||
};
|
||||
|
||||
connection.OnUserJoined += (channelName, username) =>
|
||||
{
|
||||
InvokeUI(() => _mainWindow.AddSystemMessage(channelName, $"{username} joined the channel"));
|
||||
|
||||
@@ -10,7 +10,8 @@ public class ClientConfig
|
||||
|
||||
public class NotificationConfig
|
||||
{
|
||||
public bool Enabled { get; set; } = true;
|
||||
public bool Enabled { get; set; } = false;
|
||||
public byte Volume { get; set; } = 30;
|
||||
public string? SoundFile { get; set; }
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,10 @@ public class NotificationSoundService
|
||||
ResolveSoundPath();
|
||||
}
|
||||
|
||||
public void SetEnabled(bool enabled) => _config.Enabled = enabled;
|
||||
|
||||
public void SetVolume(byte volume) => _config.Volume = Math.Min(volume, (byte)100);
|
||||
|
||||
public async Task PlayAsync()
|
||||
{
|
||||
if (!_config.Enabled || _resolvedSoundPath is null)
|
||||
@@ -26,6 +30,7 @@ public class NotificationSoundService
|
||||
if (_player.Playing)
|
||||
await _player.Stop();
|
||||
|
||||
await _player.SetVolume(_config.Volume);
|
||||
await _player.Play(_resolvedSoundPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace EchoHub.Client.UI;
|
||||
/// <summary>
|
||||
/// Result returned from the profile edit dialog.
|
||||
/// </summary>
|
||||
public record ProfileEditResult(string? DisplayName, string? Bio, string? NicknameColor, string? AvatarPath);
|
||||
public record ProfileEditResult(string? DisplayName, string? Bio, string? NicknameColor, string? AvatarPath, bool? NotificationSoundEnabled, byte? NotificationVolume);
|
||||
|
||||
/// <summary>
|
||||
/// A Terminal.Gui dialog for editing the user's profile (display name, bio, nickname color).
|
||||
@@ -19,11 +19,11 @@ public sealed class ProfileEditDialog
|
||||
/// <summary>
|
||||
/// Shows the profile edit dialog and returns the result, or null if cancelled.
|
||||
/// </summary>
|
||||
public static ProfileEditResult? Show(IApplication app, string? currentDisplayName, string? currentBio, string? currentColor)
|
||||
public static ProfileEditResult? Show(IApplication app, string? currentDisplayName, string? currentBio, string? currentColor, bool notificationSoundEnabled = false, byte notificationVolume = 30)
|
||||
{
|
||||
ProfileEditResult? result = null;
|
||||
|
||||
var dialog = new Dialog { Title = "Edit Profile", Width = 60, Height = 22 };
|
||||
var dialog = new Dialog { Title = "Edit Profile", Width = 60, Height = 26 };
|
||||
|
||||
// Display Name
|
||||
var nameLabel = new Label
|
||||
@@ -148,20 +148,53 @@ public sealed class ProfileEditDialog
|
||||
}
|
||||
};
|
||||
|
||||
// Notification Sound
|
||||
var notifCheckbox = new CheckBox
|
||||
{
|
||||
Text = "Notification sound on @mention",
|
||||
X = 1,
|
||||
Y = 13,
|
||||
Value = notificationSoundEnabled ? CheckState.Checked : CheckState.UnChecked
|
||||
};
|
||||
|
||||
var volumeLabel = new Label
|
||||
{
|
||||
Text = "Volume:",
|
||||
X = 1,
|
||||
Y = 15
|
||||
};
|
||||
var volumeField = new TextField
|
||||
{
|
||||
Text = notificationVolume.ToString(),
|
||||
X = 17,
|
||||
Y = 15,
|
||||
Width = 6
|
||||
};
|
||||
var volumeHintLabel = new Label
|
||||
{
|
||||
Text = "(0-100)",
|
||||
X = 24,
|
||||
Y = 15
|
||||
};
|
||||
volumeHintLabel.SetScheme(new Scheme
|
||||
{
|
||||
Normal = new Attribute(Color.DarkGray, Color.Blue)
|
||||
});
|
||||
|
||||
// Buttons
|
||||
var saveButton = new Button
|
||||
{
|
||||
Text = "Save",
|
||||
IsDefault = true,
|
||||
X = Pos.Center() - 10,
|
||||
Y = 14
|
||||
Y = 18
|
||||
};
|
||||
|
||||
var cancelButton = new Button
|
||||
{
|
||||
Text = "Cancel",
|
||||
X = Pos.Center() + 5,
|
||||
Y = 14
|
||||
Y = 18
|
||||
};
|
||||
|
||||
saveButton.Accepting += (s, e) =>
|
||||
@@ -171,7 +204,8 @@ public sealed class ProfileEditDialog
|
||||
var nicknameColor = NullIfEmpty(colorField.Text?.Trim());
|
||||
var avatarPath = NullIfEmpty(avatarField.Text?.Trim());
|
||||
|
||||
result = new ProfileEditResult(displayName, bio, nicknameColor, avatarPath);
|
||||
byte? volume = byte.TryParse(volumeField.Text, out var v) ? Math.Min(v, (byte)100) : null;
|
||||
result = new ProfileEditResult(displayName, bio, nicknameColor, avatarPath, notifCheckbox.Value == CheckState.Checked, volume);
|
||||
e.Handled = true;
|
||||
app.RequestStop();
|
||||
};
|
||||
@@ -186,6 +220,7 @@ public sealed class ProfileEditDialog
|
||||
dialog.Add(nameLabel, nameField, bioLabel, bioField, colorLabel, colorField,
|
||||
colorHintLabel, previewLabel, colorPreview,
|
||||
avatarLabel, avatarField, browseButton, avatarHintLabel,
|
||||
notifCheckbox, volumeLabel, volumeField, volumeHintLabel,
|
||||
saveButton, cancelButton);
|
||||
|
||||
nameField.SetFocus();
|
||||
|
||||
Reference in New Issue
Block a user