using System.Windows.Input; using ytLive.Helpers; namespace ytLive.ViewModels; /// Where the YouTube slot is in its own five-step flow. public enum YouTubeSlotState { /// [ ▶ YouTube ] Idle, /// [ ▶ Live | 🔒 Private | ✕ ] ChoosePrivacy, /// [ ● Start Live | ✕ ] or [ ● Start Private | ✕ ] Armed, /// [ ■ Stop Live ] or [ ■ Stop Private ] Streaming, } /// Where the Record slot is in its own three-step flow. public enum RecordSlotState { /// [ ◉ Record ] Idle, /// [ ● Start Recording | ✕ ] Armed, /// [ ■ Stop Recording ] Recording, } /// /// The top bar is THREE FIXED SLOTS (creator, 2026-11-13): Build Scenes, YouTube, Record. A slot never /// changes the bar's shape — every state transition happens INSIDE its own slot, and the neighbouring /// slots never move. Earlier attempts added completion states as siblings in the bar, which read as five /// ungrouped toolbar items and pushed Record around. /// public partial class MainViewModel { private YouTubeSlotState _youTubeStep = YouTubeSlotState.Idle; private RecordSlotState _recordStep = RecordSlotState.Idle; private bool _podcastLive; // true = Private chosen (the private-visibility test stream) // ─── Build Scenes slot ─── // Enabled only while BOTH other slots are idle: editing a scene under a live stream or a running // recording is what the lock is for, and the tooltip says so out loud. public bool IsBuildScenesEnabled => _youTubeStep == YouTubeSlotState.Idle && _recordStep == RecordSlotState.Idle; public string BuildScenesTooltip => IsBuildScenesEnabled ? "Edit layers, scenes and webcam placement" : "Scene editing is locked while streaming or recording"; // ─── YouTube slot ─── public YouTubeSlotState YouTubeStep => _youTubeStep; public bool IsYouTubeIdle => _youTubeStep == YouTubeSlotState.Idle; public bool IsYouTubeChoosingPrivacy => _youTubeStep == YouTubeSlotState.ChoosePrivacy; public bool IsYouTubeArmed => _youTubeStep == YouTubeSlotState.Armed; public bool IsYouTubeStreaming => _youTubeStep == YouTubeSlotState.Streaming; public bool IsLiveChosen => !_podcastLive; public bool IsPrivateChosen => _podcastLive; public string YouTubeArmLabel => _podcastLive ? "Start Private" : "Start Live"; public string YouTubeStopLabel => _podcastLive ? "Stop Private" : "Stop Live"; // ─── Record slot ─── public RecordSlotState RecordStep => _recordStep; public bool IsRecordIdle => _recordStep == RecordSlotState.Idle; public bool IsRecordArmed => _recordStep == RecordSlotState.Armed; public bool IsRecordRecording => _recordStep == RecordSlotState.Recording; public string RecordElapsedLabel => $"REC: {LiveElapsedText}"; public ICommand SelectBuildCommand { get; private set; } = null!; public ICommand OpenYouTubeCommand { get; private set; } = null!; public ICommand ChooseLiveCommand { get; private set; } = null!; public ICommand ChoosePrivateCommand { get; private set; } = null!; public ICommand CancelYouTubeCommand { get; private set; } = null!; public ICommand StartYouTubeCommand { get; private set; } = null!; public ICommand StopYouTubeCommand { get; private set; } = null!; public ICommand OpenRecordCommand { get; private set; } = null!; public ICommand StartRecordingCommand { get; private set; } = null!; public ICommand CancelRecordCommand { get; private set; } = null!; public ICommand StopRecordingCommand { get; private set; } = null!; private void InitializeActivityCommands() { SelectBuildCommand = new RelayCommand(_ => RaiseSlotStates(), _ => IsBuildScenesEnabled); OpenYouTubeCommand = new RelayCommand(_ => SetYouTubeStep(YouTubeSlotState.ChoosePrivacy)); ChooseLiveCommand = new RelayCommand(_ => { _podcastLive = false; SetYouTubeStep(YouTubeSlotState.Armed); }); ChoosePrivateCommand = new RelayCommand(_ => { _podcastLive = true; SetYouTubeStep(YouTubeSlotState.Armed); }); CancelYouTubeCommand = new RelayCommand(_ => SetYouTubeStep(YouTubeSlotState.Idle)); // Signed out still routes to sign-in rather than sitting dead (no-dead-ends rule). StartYouTubeCommand = new RelayCommand(_ => { if (!IsConnected) { _ = SignInAsync(); return; } if (_podcastLive) BeginTestStream(); else StartSession(); SetYouTubeStep(YouTubeSlotState.Streaming); }); StopYouTubeCommand = new RelayCommand(_ => { StopStream(); SetYouTubeStep(YouTubeSlotState.Idle); }); OpenRecordCommand = new RelayCommand(_ => SetRecordStep(RecordSlotState.Armed)); StartRecordingCommand = new RelayCommand(_ => { BeginRecordOnly(); SetRecordStep(RecordSlotState.Recording); }); CancelRecordCommand = new RelayCommand(_ => SetRecordStep(RecordSlotState.Idle)); StopRecordingCommand = new RelayCommand(_ => { StopStream(); SetRecordStep(RecordSlotState.Idle); }); } private void SetYouTubeStep(YouTubeSlotState step) { _youTubeStep = step; RaiseSlotStates(); } private void SetRecordStep(RecordSlotState step) { _recordStep = step; RaiseSlotStates(); } /// One place that announces everything the three slots show, so no caller can forget a flag. /// internal void RaiseSlotStates() { OnPropertyChanged(nameof(IsBuildScenesEnabled)); OnPropertyChanged(nameof(BuildScenesTooltip)); OnPropertyChanged(nameof(YouTubeStep)); OnPropertyChanged(nameof(IsYouTubeIdle)); OnPropertyChanged(nameof(IsYouTubeChoosingPrivacy)); OnPropertyChanged(nameof(IsYouTubeArmed)); OnPropertyChanged(nameof(IsYouTubeStreaming)); OnPropertyChanged(nameof(IsLiveChosen)); OnPropertyChanged(nameof(IsPrivateChosen)); OnPropertyChanged(nameof(YouTubeArmLabel)); OnPropertyChanged(nameof(YouTubeStopLabel)); OnPropertyChanged(nameof(RecordStep)); OnPropertyChanged(nameof(IsRecordIdle)); OnPropertyChanged(nameof(IsRecordArmed)); OnPropertyChanged(nameof(IsRecordRecording)); OnPropertyChanged(nameof(RecordElapsedLabel)); // Build Scenes is the edit surface, so the lock has to reach editing too. EditingActive = IsBuildScenesEnabled; } }