//! The app's own furniture: where you can go, and what stays on screen. //! //! //! //! [`Chrome`] is where it goes: a nav is places rather than content, and a //! panel says what it is for. //! //! # The places //! //! Eleven views over three tabs, and which view a tab opens. A nav offering a //! different set would be a second answer to what the app contains. //! //! Three views are not places, and each for a different reason: //! //! - `project-dashboard`, `task-overview` and `contact-dashboard` are details //! reached from a row, not tabs. Which tab stays lit while one is open is //! [`Screen::at_place`](quasi_router::Screen::at_place). //! - `task-graph` is bespoke. A place pointing at a route that does not exist //! would be a `NotFound` the first time it was pressed rather than a gap //! anybody could see, so it is left out and said so here. //! - The board is a mode of the Tasks place, so [`super::board`] marks Tasks //! while it is showing. //! //! # Settings is a place, drawn wherever the renderer draws places //! //! Where it sits is a stylesheet's opinion about a place, not a different kind //! of thing, so a webview is free to push it right. Describing it as anything //! else would be this module answering the placement question the vocabulary //! declines. //! //! # What the chrome does not carry //! //! - **A sync indicator.** A [`Role::Status`] panel is its shape, and there is //! nothing describable to put in one while every sync command awaits a //! network client and a route handler is synchronous. A `Status` panel //! drawing an empty box would be worse than none. //! - **The `?` shortcut overlay.** [`Chrome::bind`] is the member for it and //! this app binds nothing, because there is no described help screen to bind //! to and an overlay needs a container quasi-webview emits only alongside a //! binding. Binding a key to a route that does not exist to get the container //! is a trick, not a fix. //! //! # One chrome, assembled here //! //! [`super::protocol`] takes [`chrome`], because more than one module //! contributes to it and somewhere has to put them together. The timer band is //! [`super::time_tracking`]'s to describe; this only asks for it. use quasi_router::{Action, Chrome, Place}; #[cfg(test)] mod tests; /// The key a screen names to say it is the task list. /// /// The keys are spelled as consts because two places name each one: the [`Place`] /// in the nav, and the screen that says it is there. A typo in either is a tab /// that never lights, which is the quietest possible failure, so neither end /// spells it as a literal. pub const TASKS: &str = "tasks"; /// The projects screen, and the project dashboard behind it. pub const PROJECTS: &str = "projects"; /// The problems queue. pub const PROBLEMS: &str = "problems"; /// The day plan. pub const DAY: &str = "day-plan"; /// The weekly review. pub const WEEK: &str = "weekly-review"; /// The monthly review. pub const MONTH: &str = "monthly-review"; /// The Timer screen. pub const TIMER: &str = "timer"; /// The calendar. pub const EVENTS: &str = "events"; /// Mail, and the Out box and compose screens reached from it. pub const EMAILS: &str = "emails"; /// The Out box. pub const OUTBOX: &str = "outbox"; /// Contacts, and the contact dashboard behind it. pub const CONTACTS: &str = "contacts"; /// Search, across all five kinds the index holds. pub const SEARCH: &str = "search"; /// Settings, and Import & Export, which its sidebar navigates to. pub const SETTINGS: &str = "settings"; /// The places the app has, in the order the header offers them. /// /// A group's own action is the view its tab opens, which is `TAB_DEFAULTS` in /// `navigation.js`. A group with nowhere to go would be a tab that does /// nothing, and pressing Work in the shipped app opens Tasks. fn nav() -> [Place; 5] { [ Place::new("work", "Work", Action::get("/tasks")).within([ Place::new(TASKS, "Tasks", Action::get("/tasks")), Place::new(PROJECTS, "Projects", Action::get("/projects")), Place::new(PROBLEMS, "Problems", Action::get("/problems")), ]), Place::new("time", "Time", Action::get("/day")).within([ Place::new(DAY, "Day", Action::get("/day")), Place::new(WEEK, "Week", Action::get("/weekly-review")), Place::new(MONTH, "Month", Action::get("/monthly-review")), Place::new(TIMER, "Timer", Action::get("/timer")), Place::new(EVENTS, "Events", Action::get("/events")), ]), Place::new("messages", "Messages", Action::get("/emails")).within([ Place::new(EMAILS, "Email", Action::get("/emails")), // Eudora put Out beside In, and this app's outbox is a real place // rather than a state a message is in: goingson `a3c76a24`. Place::new(OUTBOX, "Out box", Action::get("/outbox")), Place::new(CONTACTS, "Contacts", Action::get("/contacts")), ]), // Search reaches every kind, so it belongs to no group and is a place // of its own. It is a nav entry rather than a binding because the app // binds no keys and cannot until quasicoherent `858be2a6` gives the // overlay a container -- and because a nav entry is the one entry point // that serves pointer, touch and keyboard alike, which is what goingson // `6b3aa22b` was actually complaining about. Place::new(SEARCH, "Search", Action::get("/search")), // No sub-places, so it draws as a place of its own. The shipped header // puts it on the right; where it lands here is the stylesheet's. Place::new(SETTINGS, "Settings", Action::get("/settings")), ] } /// Everything the app keeps offered, whatever screen is showing. /// /// Each contributor takes the chrome and gives it back, which is the chain /// [`super::router`] already is. The band is described by the module that owns /// it, so there is one answer to what it says. #[must_use] pub fn chrome() -> Chrome { let offered = nav().into_iter().fold(Chrome::new(), Chrome::offering); super::time_tracking::chrome(offered) }