namespace ytLive.Models; /// What the top bar's primary action button will do. public enum SessionStartKind { Record, Live, Test, } /// Creator ruling (2026-10-30): the primary button is context-sensitive, so the separate /// TEST button is gone. /// /// /// EDIT off + ON-AIR armed → Go Live (the livestream message) /// EDIT on + ON-AIR armed → Test Stream (the test-livestream message) — a dry run you /// cannot leak, because every broadcast is private pre-v1 anyway /// REC armed → Start Recording, whatever EDIT says (there is no test recording) /// /// /// Pure so the whole matrix is testable without a window. public static class SessionStartDecision { /// /// Which kind of session the armed output starts. Since 2026-11-12 there is no inference from /// editing: the output stage offers "Private Test Stream" and "Review & Go Live" as separate /// buttons, so ON-AIR always means the real livestream and Test is something the creator asks for. /// public static SessionStartKind For(bool recordArmed, bool onAirArmed) { // Neither output armed still starts something: local recording needs no account, and the // no-dead-ends rule forbids an inert primary button. if (recordArmed || !onAirArmed) return SessionStartKind.Record; return SessionStartKind.Live; } /// What pressing the button actually does, given whether YouTube is connected. /// A signed-out ON-AIR press must start the SIGN-IN flow, not sit there disabled — a dead /// primary button is the dead end the design principle forbids (creator, 2026-10-30). public enum Route { SignIn, Record, Live, Test, } public static Route RouteFor(SessionStartKind kind, bool connected) => kind switch { SessionStartKind.Record => Route.Record, SessionStartKind.Test => connected ? Route.Test : Route.SignIn, _ => connected ? Route.Live : Route.SignIn, }; public static string LabelFor(SessionStartKind kind) => kind switch { SessionStartKind.Record => "Start Recording", SessionStartKind.Test => "Test Stream", _ => "Go Live", }; }