using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; namespace XFEToolBox.Client.Models; public enum ActivityKind { General, SoftwareDownload, ToolPackage, Build, Publish, Update } public enum ActivityState { Pending, Running, Succeeded, Failed, Cancelled } public sealed class ActivityItem : ObservableObject { private readonly CancellationTokenSource? cancellationSource; private ActivityState state = ActivityState.Pending; private double? progress; private string message = string.Empty; private bool canCancel; internal ActivityItem(string id, ActivityKind kind, string title, bool canCancel) { Id = id; Kind = kind; Title = title; StartedAt = DateTimeOffset.Now; this.canCancel = canCancel; cancellationSource = canCancel ? new CancellationTokenSource() : null; CancelCommand = new RelayCommand(RequestCancel, () => CanCancel); } public string Id { get; } public ActivityKind Kind { get; } public string Title { get; } public DateTimeOffset StartedAt { get; } public DateTimeOffset? CompletedAt { get; internal set; } public ActivityState State { get => state; internal set { if (!SetProperty(ref state, value)) return; OnPropertyChanged(nameof(StateText)); OnPropertyChanged(nameof(CanCancel)); CancelCommand.NotifyCanExecuteChanged(); } } public double? Progress { get => progress; internal set { if (!SetProperty(ref progress, value is null ? null : Math.Clamp(value.Value, 0, 100))) return; OnPropertyChanged(nameof(ProgressText)); } } public string Message { get => message; internal set => SetProperty(ref message, value); } public bool CanCancel { get => canCancel && State is ActivityState.Pending or ActivityState.Running; internal set { if (!SetProperty(ref canCancel, value)) return; CancelCommand.NotifyCanExecuteChanged(); } } public string StateText => State switch { ActivityState.Pending => "等待中", ActivityState.Running => "进行中", ActivityState.Succeeded => "已完成", ActivityState.Failed => "失败", ActivityState.Cancelled => "已取消", _ => State.ToString() }; public string ProgressText => Progress is { } value ? $"{value:0}%" : StateText; public RelayCommand CancelCommand { get; } internal CancellationToken Token => cancellationSource?.Token ?? CancellationToken.None; private void RequestCancel() { if (!CanCancel) return; Message = "正在取消…"; cancellationSource?.Cancel(); CanCancel = false; } }