using System.Collections.ObjectModel; using System.Diagnostics; using System.Windows; using System.Windows.Controls; using WindowsGotify.Models; using WindowsGotify.Services; using WindowsGotify.Tray; // 双开 WPF + WinForms 时,MessageBox 在两个命名空间都有,用别名强制走 WPF 的。 using MessageBox = System.Windows.MessageBox; namespace WindowsGotify; public partial class MainWindow : Window { private const int MaxHistoryInUi = 200; private AppConfig? _config; private ConfigService? _configService; private Logger? _logger; private TrayIconService? _tray; private GotifyWebSocketService? _webSocket; private GotifyApiService? _apiService; private readonly ObservableCollection _history = new(); public MainWindow() { InitializeComponent(); HistoryList.ItemsSource = _history; Closing += (_, e) => { e.Cancel = true; Hide(); }; } public void Initialize( AppConfig config, ConfigService configService, Logger logger, TrayIconService tray, GotifyWebSocketService webSocket, GotifyApiService apiService) { _config = config; _configService = configService; _logger = logger; _tray = tray; _webSocket = webSocket; _apiService = apiService; UrlTextBox.Text = config.Gotify.Url; TokenBox.Password = config.Gotify.ClientToken; StatusText.Text = "状态:等待连接"; UpdateHistoryCount(); // 实时消息追加到历史 _webSocket.MessageReceived += OnLiveMessage; } public void ShowFromTray() { Show(); WindowState = WindowState.Normal; Activate(); // 每次打开都拉一次最新历史,确保和远端同步 _ = LoadHistoryAsync(); } private void OnLiveMessage(GotifyMessage msg) { // WebSocket 回调可能在非 UI 线程,切回 UI 线程 Dispatcher.Invoke(() => { _history.Insert(0, msg); TrimHistory(); UpdateHistoryCount(); }); } private void TrimHistory() { while (_history.Count > MaxHistoryInUi) { _history.RemoveAt(_history.Count - 1); } } private void UpdateHistoryCount() { HistoryCountText.Text = $"共 {_history.Count} 条(本地缓存上限 {MaxHistoryInUi})"; } private async Task LoadHistoryAsync() { if (_apiService is null || _config is null) return; if (string.IsNullOrWhiteSpace(_config.Gotify.ClientToken)) { StatusText.Text = "状态:未配置 Client Token,无法拉取历史"; return; } try { StatusText.Text = "状态:正在拉取历史..."; var limit = Math.Clamp(_config.History.ViewerLimit, 1, 100); var messages = await _apiService.GetRecentMessagesAsync(limit, CancellationToken.None); // 用清空 + 批量 Insert 让 UI 一次性刷新,避免多次 CollectionChanged _history.Clear(); foreach (var m in messages) { _history.Add(m); } UpdateHistoryCount(); StatusText.Text = $"状态:已加载 {messages.Count} 条历史消息"; _logger?.Info($"History loaded. count={messages.Count}"); } catch (Exception ex) { _logger?.Error("Load history failed.", ex); StatusText.Text = $"状态:拉取历史失败 - {ex.Message}"; } } private async void RefreshButton_Click(object sender, RoutedEventArgs e) { await LoadHistoryAsync(); } private void HistoryList_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e) { if (HistoryList.SelectedItem is not GotifyMessage msg) return; var url = msg.ClickUrl; if (string.IsNullOrWhiteSpace(url)) return; try { Process.Start(new ProcessStartInfo { FileName = url, UseShellExecute = true }); _logger?.Info($"Opened click url. id={msg.Id}, url={url}"); } catch (Exception ex) { _logger?.Error("Failed to open click url.", ex); MessageBox.Show($"无法打开链接:{ex.Message}", "Windows Gotify", MessageBoxButton.OK, MessageBoxImage.Warning); } } private async void SaveButton_Click(object sender, RoutedEventArgs e) { if (_config is null || _configService is null) return; var url = UrlTextBox.Text.Trim(); var token = TokenBox.Password.Trim(); if (!Uri.TryCreate(url, UriKind.Absolute, out var uri) || (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps)) { MessageBox.Show("Gotify URL 无效。", "Windows Gotify", MessageBoxButton.OK, MessageBoxImage.Warning); return; } if (string.IsNullOrWhiteSpace(token)) { MessageBox.Show("Client Token 不能为空。", "Windows Gotify", MessageBoxButton.OK, MessageBoxImage.Warning); return; } _config.Gotify.Url = url.TrimEnd('/'); _config.Gotify.ClientToken = token; await _configService.SaveAsync(_config); _webSocket?.RequestReconnect(); MessageBox.Show("配置已保存,正在重新连接。", "Windows Gotify", MessageBoxButton.OK, MessageBoxImage.Information); } private async void TestButton_Click(object sender, RoutedEventArgs e) { try { var notification = new NotificationService(_config!, _logger!); await notification.ShowAsync(new GotifyMessage { Id = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), Message = "Windows Gotify Agent 测试成功。", Title = "Gotify 测试", Priority = 5, Date = DateTimeOffset.UtcNow }); } catch (Exception ex) { MessageBox.Show(ex.Message, "测试通知失败", MessageBoxButton.OK, MessageBoxImage.Error); } } }