using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows.Input; using ytLive.Helpers; using ytLive.Models; namespace ytLive.Services; /// /// Backing VM for the broadcast metadata pull-out (the "Text" tab on the Live /// screen). Owns the creator-editable liveBroadcast fields: every edit is /// persisted to the Settings store immediately (pre-load on next run), and the /// Update command pushes the whole snippet/status set to YouTube via /// liveBroadcasts.update. The drawer open/close state lives here too so the /// XAML binds one object. /// public sealed class LiveBroadcastFormViewModel : ViewModelBase { private readonly Func _store; private readonly Func _streamService; private readonly Func _broadcastId; private string _title = string.Empty; private string _description = string.Empty; private string _tagsCsv = string.Empty; private string _visibility = "private"; private bool _madeForKids; private DateTime? _scheduledStartTime; private bool _isDrawerOpen; private bool _isUpdating; private string? _updateError; public LiveBroadcastFormViewModel( Func store, Func streamService, Func broadcastId) { _store = store; _streamService = streamService; _broadcastId = broadcastId; UpdateBroadcastCommand = new RelayCommand(_ => _ = UpdateRemoteAsync(), _ => CanUpdateRemote); ToggleDrawerCommand = new RelayCommand(_ => IsDrawerOpen = !IsDrawerOpen); CloseDrawerCommand = new RelayCommand(_ => IsDrawerOpen = false); } public ICommand UpdateBroadcastCommand { get; } public RelayCommand ToggleDrawerCommand { get; } public RelayCommand CloseDrawerCommand { get; } public string Title { get => _title; set { if (SetProperty(ref _title, value)) Persist(); } } public string Description { get => _description; set { if (SetProperty(ref _description, value)) Persist(); } } public string TagsCsv { get => _tagsCsv; set { if (SetProperty(ref _tagsCsv, value)) Persist(); } } public bool MadeForKids { get => _madeForKids; set { if (SetProperty(ref _madeForKids, value)) Persist(); } } public string Visibility { get => _visibility; set { if (SetProperty(ref _visibility, value)) Persist(); } } public IReadOnlyList VisibilityOptions { get; } = new[] { "private", "unlisted", "public" }; public DateTime? ScheduledStartTime { get => _scheduledStartTime; private set => SetProperty(ref _scheduledStartTime, value); } public string ScheduledStartText => ScheduledStartTime?.ToLocalTime().ToString("g") ?? "set at Go Live"; public bool IsDrawerOpen { get => _isDrawerOpen; set => SetProperty(ref _isDrawerOpen, value); } public bool IsUpdating { get => _isUpdating; private set { if (SetProperty(ref _isUpdating, value)) OnPropertyChanged(nameof(CanUpdateRemote)); } } public string? UpdateError { get => _updateError; private set => SetProperty(ref _updateError, value); } public bool CanUpdateRemote => !string.IsNullOrEmpty(_broadcastId()) && !IsUpdating; /// Pull everything from the store (startup / Go Live refresh). public void Load() { var meta = _store().LoadBroadcastMetadata(); _title = meta.Title; _description = meta.Description; _tagsCsv = meta.TagsCsv; _visibility = meta.Visibility; _madeForKids = meta.MadeForKids; ScheduledStartTime = meta.ScheduledStartTime; OnPropertyChanged(nameof(Title)); OnPropertyChanged(nameof(Description)); OnPropertyChanged(nameof(TagsCsv)); OnPropertyChanged(nameof(Visibility)); OnPropertyChanged(nameof(MadeForKids)); OnPropertyChanged(nameof(ScheduledStartText)); } /// Called at Go Live with what was actually inserted remotely. public void CaptureGoLive(string title, string description, DateTime scheduledStart) { Title = title; Description = description; ScheduledStartTime = scheduledStart; UpdateError = null; } public BroadcastMetadata ToMetadata() => new() { Title = Title, Description = Description, TagsCsv = TagsCsv, Visibility = Visibility, MadeForKids = MadeForKids, ScheduledStartTime = ScheduledStartTime, }; private void Persist() { try { _store().SaveBroadcastMetadata(ToMetadata()); } catch (Exception ex) { AppLog.Write($"Broadcast metadata save failed: {ex.Message}"); } } private async Task UpdateRemoteAsync() { var id = _broadcastId(); var service = _streamService(); if (id == null || service == null || IsUpdating) return; IsUpdating = true; UpdateError = null; try { var error = await service.UpdateBroadcast(id, ToMetadata()); UpdateError = error; } catch (Exception ex) { AppLog.Write($"Broadcast update failed: {ex.Message}"); UpdateError = ex.Message; } finally { IsUpdating = false; } } }