feat(top bar): EDIT decides live vs test on the primary button — the TEST button is gone
Creator ruling (2026-10-30): - EDIT off + ON-AIR armed → the primary button reads "Go Live" and runs the livestream. - EDIT on + ON-AIR armed → the same button reads "Test Stream" and runs the test broadcast. - REC armed (or nothing armed) → "Start Recording", whatever EDIT says; there is no test recording, and an unarmed start records locally rather than doing nothing. So the fifth button was redundant: `ShowTestButton` no longer feeds any control. The decision is a pure function (`Models/SessionStartDecision`) so the whole matrix is testable without a window; `PrimaryStartCommand` follows the same kind, so label and action cannot disagree. Guard: SessionStartDecisionTests pins the four cases, including the no-dead-ends fallback that my first version got wrong (unarmed returned Live instead of Record — the test caught it). Note for the v1 gate: the "hide TEST at v1" item is now satisfied by construction — the test path is a mode of the primary button, not a separate control.
This commit is contained in:
@@ -72,6 +72,9 @@ public partial class MainViewModel : ViewModelBase
|
||||
OnPropertyChanged(nameof(OnAirBrush));
|
||||
OnPropertyChanged(nameof(IsLivePrivate));
|
||||
OnPropertyChanged(nameof(ShowPrimaryStartButton));
|
||||
OnPropertyChanged(nameof(PrimaryStartKind));
|
||||
OnPropertyChanged(nameof(PrimaryStartButtonLabel));
|
||||
OnPropertyChanged(nameof(PrimaryStartCommand));
|
||||
OnPropertyChanged(nameof(ShowEndStreamButton));
|
||||
OnPropertyChanged(nameof(EndButtonText));
|
||||
OnPropertyChanged(nameof(ShowTestButton));
|
||||
@@ -109,6 +112,9 @@ public partial class MainViewModel : ViewModelBase
|
||||
if (!SetProperty(ref _recordPillOn, value)) return;
|
||||
if (value && _onAirPillOn) OnAirPillOn = false; // record-OR-live — arming REC drops ON-AIR
|
||||
OnPropertyChanged(nameof(ShowPrimaryStartButton));
|
||||
OnPropertyChanged(nameof(PrimaryStartKind));
|
||||
OnPropertyChanged(nameof(PrimaryStartButtonLabel));
|
||||
OnPropertyChanged(nameof(PrimaryStartCommand));
|
||||
OnPropertyChanged(nameof(CanStartSession));
|
||||
RaiseTopBarModes();
|
||||
}
|
||||
@@ -125,6 +131,9 @@ public partial class MainViewModel : ViewModelBase
|
||||
if (!SetProperty(ref _onAirPillOn, value)) return;
|
||||
if (value && _recordPillOn) RecordPillOn = false; // record-OR-live — arming ON-AIR drops REC
|
||||
OnPropertyChanged(nameof(ShowPrimaryStartButton));
|
||||
OnPropertyChanged(nameof(PrimaryStartKind));
|
||||
OnPropertyChanged(nameof(PrimaryStartButtonLabel));
|
||||
OnPropertyChanged(nameof(PrimaryStartCommand));
|
||||
OnPropertyChanged(nameof(CanStartSession));
|
||||
RaiseTopBarModes();
|
||||
}
|
||||
@@ -146,6 +155,9 @@ public partial class MainViewModel : ViewModelBase
|
||||
OnPropertyChanged(nameof(IsEditMode));
|
||||
OnPropertyChanged(nameof(ShowEndStreamButton));
|
||||
OnPropertyChanged(nameof(ShowPrimaryStartButton));
|
||||
OnPropertyChanged(nameof(PrimaryStartKind));
|
||||
OnPropertyChanged(nameof(PrimaryStartButtonLabel));
|
||||
OnPropertyChanged(nameof(PrimaryStartCommand));
|
||||
OnPropertyChanged(nameof(EndButtonText));
|
||||
OnPropertyChanged(nameof(ShowTestButton));
|
||||
RaiseTopBarModes();
|
||||
@@ -169,8 +181,18 @@ public partial class MainViewModel : ViewModelBase
|
||||
/// routes to the sign-in inside StartSession (Start — the account step shows up
|
||||
/// beside it as the Sign In button); a signed-out go-live stays the dead-end the
|
||||
/// creator ruling 2026-09-20-02 forbids.</summary>
|
||||
public string PrimaryStartButtonLabel =>
|
||||
OnAirPillOn ? "Go Live" : "Start Recording";
|
||||
/// <summary>Creator ruling 2026-10-30: EDIT decides whether the ON-AIR start is the real
|
||||
/// livestream or the test one, which is why the separate TEST button is gone.</summary>
|
||||
public SessionStartKind PrimaryStartKind =>
|
||||
SessionStartDecision.For(RecordPillOn, OnAirPillOn, EditingActive);
|
||||
|
||||
public string PrimaryStartButtonLabel => SessionStartDecision.LabelFor(PrimaryStartKind);
|
||||
|
||||
/// <summary>The button's action follows its label: Test runs the same go-live pipeline with
|
||||
/// `IsTestStream` set, Go Live runs it for real.</summary>
|
||||
public ICommand PrimaryStartCommand => PrimaryStartKind == SessionStartKind.Test
|
||||
? StartTestCommand
|
||||
: StartStreamCommand;
|
||||
|
||||
/// <summary>The End button shows while recording OR live.</summary>
|
||||
public bool ShowEndStreamButton => IsLive || IsRecording;
|
||||
|
||||
Reference in New Issue
Block a user