using AvParser.Core.Collecting; using AvParser.Core.Collecting.Sources; using AvParser.UI.Localization; using ReactiveUI; namespace AvParser.UI.ViewModels; /// A media source paired with its translated name and description. /// /// The domain knows nothing about languages, so carries English text. /// This looks the id up as Source.{id}.Name and falls back to what the source itself says — /// which is what user-added sources always do, since their name is data the user typed, not a resx /// key. Disposable because the catalog rebuilds these on every edit, and each one holds a /// language-changed subscription that would otherwise leak. /// public sealed class MediaSourceViewModel : ReactiveObject, IDisposable { private bool _isSelected; /// Wraps a source. public MediaSourceViewModel(IMediaSource source) { Source = source ?? throw new ArgumentNullException(nameof(source)); Localizer.Instance.LanguageChanged += OnLanguageChanged; } /// Whether the next run includes this source. /// /// Ticking is what a run acts on; the list's highlight only says which source the editor, /// the delete button and the purge button are aimed at. Keeping the two apart is what lets a /// run cover five sources while the user edits a sixth. /// public bool IsSelected { get => _isSelected; set => this.RaiseAndSetIfChanged(ref _isSelected, value); } /// The source itself. public IMediaSource Source { get; } /// The config behind the source, when it is a pattern source (they all are today). public PatternSourceConfig? Config => (Source as PatternMediaSource)?.Config; /// Stable identifier. public string Id => Source.Id; /// Translated name, or the source's own when untranslated. public string Name => Localizer.Instance.GetOrDefault($"Source.{Id}.Name", Source.DisplayName); /// Translated description, or the source's own when untranslated. public string Description => Localizer.Instance.GetOrDefault($"Source.{Id}.Description", Source.Description); /// Whether this source goes to the network at all. public bool RequiresNetwork => Source.RequiresNetwork; /// /// Whether this source may run from the user's own address when no proxy is live. /// /// /// Anything that is not a configured pattern source answers : the safe /// end, since a source with nowhere to say otherwise has not said otherwise. /// public bool AllowsDirectConnection => Config?.AllowDirectConnection ?? false; /// Whether a run including this source is gated on a live proxy. public bool NeedsProxy => RequiresNetwork && !AllowsDirectConnection; /// public override string ToString() => Name; /// public void Dispose() => Localizer.Instance.LanguageChanged -= OnLanguageChanged; private void OnLanguageChanged(object? sender, EventArgs e) { this.RaisePropertyChanged(nameof(Name)); this.RaisePropertyChanged(nameof(Description)); } }