feat: add system tray UI with status icon, tooltip, and context menu
This commit is contained in:
110
src/SpamGuard/Tray/TrayApplicationContext.cs
Normal file
110
src/SpamGuard/Tray/TrayApplicationContext.cs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
// src/SpamGuard/Tray/TrayApplicationContext.cs
|
||||||
|
namespace SpamGuard.Tray;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using SpamGuard.Services;
|
||||||
|
|
||||||
|
public sealed class TrayApplicationContext : ApplicationContext
|
||||||
|
{
|
||||||
|
private readonly NotifyIcon _notifyIcon;
|
||||||
|
private readonly System.Windows.Forms.Timer _refreshTimer;
|
||||||
|
private readonly ActivityLog _activityLog;
|
||||||
|
private readonly InboxMonitorService _monitor;
|
||||||
|
private readonly IHost _host;
|
||||||
|
private ActivityLogForm? _logForm;
|
||||||
|
private readonly ToolStripMenuItem _pauseMenuItem;
|
||||||
|
|
||||||
|
public TrayApplicationContext(IHost host)
|
||||||
|
{
|
||||||
|
_host = host;
|
||||||
|
_activityLog = host.Services.GetRequiredService<ActivityLog>();
|
||||||
|
_monitor = host.Services.GetRequiredService<InboxMonitorService>();
|
||||||
|
|
||||||
|
_pauseMenuItem = new ToolStripMenuItem("Pause", null, OnPauseResume);
|
||||||
|
|
||||||
|
var contextMenu = new ContextMenuStrip();
|
||||||
|
contextMenu.Items.Add("View Activity Log", null, OnViewLog);
|
||||||
|
contextMenu.Items.Add(_pauseMenuItem);
|
||||||
|
contextMenu.Items.Add(new ToolStripSeparator());
|
||||||
|
contextMenu.Items.Add("Quit", null, OnQuit);
|
||||||
|
|
||||||
|
_notifyIcon = new NotifyIcon
|
||||||
|
{
|
||||||
|
Icon = IconGenerator.Green,
|
||||||
|
Text = "SpamGuard - Starting...",
|
||||||
|
Visible = true,
|
||||||
|
ContextMenuStrip = contextMenu
|
||||||
|
};
|
||||||
|
|
||||||
|
_notifyIcon.DoubleClick += OnViewLog;
|
||||||
|
|
||||||
|
_refreshTimer = new System.Windows.Forms.Timer { Interval = 5000 };
|
||||||
|
_refreshTimer.Tick += OnRefreshTick;
|
||||||
|
_refreshTimer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRefreshTick(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var checked_ = _activityLog.TodayChecked;
|
||||||
|
var spam = _activityLog.TodaySpam;
|
||||||
|
_notifyIcon.Text = $"SpamGuard - {checked_} checked, {spam} spam caught today";
|
||||||
|
|
||||||
|
// Update icon based on state
|
||||||
|
if (!_monitor.IsPaused)
|
||||||
|
{
|
||||||
|
var recent = _activityLog.GetRecent(1);
|
||||||
|
var hasRecentError = recent.Count > 0
|
||||||
|
&& recent[0].Verdict == Models.Verdict.Error
|
||||||
|
&& recent[0].Timestamp > DateTime.UtcNow.AddMinutes(-5);
|
||||||
|
_notifyIcon.Icon = hasRecentError ? IconGenerator.Red : IconGenerator.Green;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logForm?.RefreshData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnViewLog(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (_logForm == null || _logForm.IsDisposed)
|
||||||
|
{
|
||||||
|
_logForm = new ActivityLogForm(_activityLog);
|
||||||
|
}
|
||||||
|
_logForm.Show();
|
||||||
|
_logForm.BringToFront();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPauseResume(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (_monitor.IsPaused)
|
||||||
|
{
|
||||||
|
_monitor.Resume();
|
||||||
|
_pauseMenuItem.Text = "Pause";
|
||||||
|
_notifyIcon.Icon = IconGenerator.Green;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_monitor.Pause();
|
||||||
|
_pauseMenuItem.Text = "Resume";
|
||||||
|
_notifyIcon.Icon = IconGenerator.Yellow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnQuit(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
_notifyIcon.Visible = false;
|
||||||
|
_refreshTimer.Stop();
|
||||||
|
await _host.StopAsync();
|
||||||
|
Application.Exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_refreshTimer.Dispose();
|
||||||
|
_notifyIcon.Dispose();
|
||||||
|
_logForm?.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user