using System.Collections.ObjectModel; using Avalonia.Controls; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using LanMountainDesktop.PluginSdk; using VoiceHubLanDesktop.Models; using VoiceHubLanDesktop.Services; namespace VoiceHubLanDesktop.Views; /// /// 广播站排期显示组件 /// public sealed partial class VoiceHubScheduleControl : UserControl { private readonly VoiceHubScheduleService _scheduleService; private readonly VoiceHubSettingsService _settingsService; private readonly DispatcherTimer? _refreshTimer; private CancellationTokenSource? _loadCts; public ObservableCollection Songs { get; } = []; [ObservableProperty] private string _titleText = "广播站排期"; [ObservableProperty] private string _dateText = ""; [ObservableProperty] private string _emptyMessage = "暂无排期数据"; [ObservableProperty] private string _errorMessage = ""; [ObservableProperty] private bool _isLoading = true; [ObservableProperty] private bool _isNormal = false; [ObservableProperty] private bool _isEmpty = false; [ObservableProperty] private bool _isError = false; public VoiceHubScheduleControl( VoiceHubScheduleService scheduleService, VoiceHubSettingsService settingsService, IPluginRuntimeContext runtimeContext) { InitializeComponent(); DataContext = this; _scheduleService = scheduleService; _settingsService = settingsService; // 设置刷新定时器 var settings = _settingsService.GetSettings(); _refreshTimer = new DispatcherTimer { Interval = TimeSpan.FromMinutes(settings.RefreshIntervalMinutes) }; _refreshTimer.Tick += async (_, _) => await RefreshAsync(); _refreshTimer.Start(); // 监听设置变化 _settingsService.SettingsChanged += OnSettingsChanged; // 初始加载 _ = LoadAsync(); } private void OnSettingsChanged(object? sender, PluginSettings settings) { if (_refreshTimer != null) { _refreshTimer.Interval = TimeSpan.FromMinutes(settings.RefreshIntervalMinutes); } _scheduleService.ClearCache(); _ = RefreshAsync(); } private async Task LoadAsync() { SetState(ComponentState.Loading); try { _loadCts?.Cancel(); _loadCts = new CancellationTokenSource(); var displayData = await _scheduleService.GetTodayScheduleAsync(_loadCts.Token); await Dispatcher.UIThread.InvokeAsync(() => { ApplyDisplayData(displayData); }); } catch (OperationCanceledException) { // 忽略取消 } catch (Exception ex) { await Dispatcher.UIThread.InvokeAsync(() => { SetState(ComponentState.NetworkError, $"加载失败: {ex.Message}"); }); } } private void ApplyDisplayData(DisplayData data) { switch (data.State) { case ComponentState.Normal: Songs.Clear(); foreach (var song in data.Songs) { Songs.Add(song); } DateText = data.DisplayDate?.ToString("MM月dd日") ?? ""; SetState(ComponentState.Normal); break; case ComponentState.NoSchedule: EmptyMessage = data.ErrorMessage ?? "暂无排期数据"; SetState(ComponentState.NoSchedule); break; case ComponentState.NetworkError: SetState(ComponentState.NetworkError, data.ErrorMessage ?? "网络错误"); break; default: SetState(ComponentState.Loading); break; } } private void SetState(ComponentState state, string? message = null) { IsLoading = state == ComponentState.Loading; IsNormal = state == ComponentState.Normal; IsEmpty = state == ComponentState.NoSchedule; IsError = state == ComponentState.NetworkError; if (!string.IsNullOrWhiteSpace(message)) { if (state == ComponentState.NetworkError) { ErrorMessage = message; } else if (state == ComponentState.NoSchedule) { EmptyMessage = message; } } } [RelayCommand] private async Task RetryAsync() { _scheduleService.ClearCache(); await LoadAsync(); } public async Task RefreshAsync() { await LoadAsync(); } protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) { base.OnDetachedFromVisualTree(e); _refreshTimer?.Stop(); _loadCts?.Cancel(); _settingsService.SettingsChanged -= OnSettingsChanged; } }