Skip to main content

max / goingson

6.1 KB · 135 lines History Blame Raw
1 //! The app's own furniture: where you can go, and what stays on screen.
2 //!
3 //! <!-- wiki: quasi-overview -->
4 //!
5 //! [`Chrome`] is where it goes: a nav is places rather than content, and a
6 //! panel says what it is for.
7 //!
8 //! # The places
9 //!
10 //! Eleven views over three tabs, and which view a tab opens. A nav offering a
11 //! different set would be a second answer to what the app contains.
12 //!
13 //! Three views are not places, and each for a different reason:
14 //!
15 //! - `project-dashboard`, `task-overview` and `contact-dashboard` are details
16 //! reached from a row, not tabs. Which tab stays lit while one is open is
17 //! [`Screen::at_place`](quasi_router::Screen::at_place).
18 //! - `task-graph` is bespoke. A place pointing at a route that does not exist
19 //! would be a `NotFound` the first time it was pressed rather than a gap
20 //! anybody could see, so it is left out and said so here.
21 //! - The board is a mode of the Tasks place, so [`super::board`] marks Tasks
22 //! while it is showing.
23 //!
24 //! # Settings is a place, drawn wherever the renderer draws places
25 //!
26 //! Where it sits is a stylesheet's opinion about a place, not a different kind
27 //! of thing, so a webview is free to push it right. Describing it as anything
28 //! else would be this module answering the placement question the vocabulary
29 //! declines.
30 //!
31 //! # What the chrome does not carry
32 //!
33 //! - **A sync indicator.** A [`Role::Status`] panel is its shape, and there is
34 //! nothing describable to put in one while every sync command awaits a
35 //! network client and a route handler is synchronous. A `Status` panel
36 //! drawing an empty box would be worse than none.
37 //! - **The `?` shortcut overlay.** [`Chrome::bind`] is the member for it and
38 //! this app binds nothing, because there is no described help screen to bind
39 //! to and an overlay needs a container quasi-webview emits only alongside a
40 //! binding. Binding a key to a route that does not exist to get the container
41 //! is a trick, not a fix.
42 //!
43 //! # One chrome, assembled here
44 //!
45 //! [`super::protocol`] takes [`chrome`], because more than one module
46 //! contributes to it and somewhere has to put them together. The timer band is
47 //! [`super::time_tracking`]'s to describe; this only asks for it.
48
49 use quasi_router::{Action, Chrome, Place};
50
51 #[cfg(test)]
52 mod tests;
53
54 /// The key a screen names to say it is the task list.
55 ///
56 /// The keys are spelled as consts because two places name each one: the [`Place`]
57 /// in the nav, and the screen that says it is there. A typo in either is a tab
58 /// that never lights, which is the quietest possible failure, so neither end
59 /// spells it as a literal.
60 pub const TASKS: &str = "tasks";
61 /// The projects screen, and the project dashboard behind it.
62 pub const PROJECTS: &str = "projects";
63 /// The problems queue.
64 pub const PROBLEMS: &str = "problems";
65 /// The day plan.
66 pub const DAY: &str = "day-plan";
67 /// The weekly review.
68 pub const WEEK: &str = "weekly-review";
69 /// The monthly review.
70 pub const MONTH: &str = "monthly-review";
71 /// The Timer screen.
72 pub const TIMER: &str = "timer";
73 /// The calendar.
74 pub const EVENTS: &str = "events";
75 /// Mail, and the Out box and compose screens reached from it.
76 pub const EMAILS: &str = "emails";
77 /// The Out box.
78 pub const OUTBOX: &str = "outbox";
79 /// Contacts, and the contact dashboard behind it.
80 pub const CONTACTS: &str = "contacts";
81 /// Search, across all five kinds the index holds.
82 pub const SEARCH: &str = "search";
83 /// Settings, and Import & Export, which its sidebar navigates to.
84 pub const SETTINGS: &str = "settings";
85
86 /// The places the app has, in the order the header offers them.
87 ///
88 /// A group's own action is the view its tab opens, which is `TAB_DEFAULTS` in
89 /// `navigation.js`. A group with nowhere to go would be a tab that does
90 /// nothing, and pressing Work in the shipped app opens Tasks.
91 fn nav() -> [Place; 5] {
92 [
93 Place::new("work", "Work", Action::get("/tasks")).within([
94 Place::new(TASKS, "Tasks", Action::get("/tasks")),
95 Place::new(PROJECTS, "Projects", Action::get("/projects")),
96 Place::new(PROBLEMS, "Problems", Action::get("/problems")),
97 ]),
98 Place::new("time", "Time", Action::get("/day")).within([
99 Place::new(DAY, "Day", Action::get("/day")),
100 Place::new(WEEK, "Week", Action::get("/weekly-review")),
101 Place::new(MONTH, "Month", Action::get("/monthly-review")),
102 Place::new(TIMER, "Timer", Action::get("/timer")),
103 Place::new(EVENTS, "Events", Action::get("/events")),
104 ]),
105 Place::new("messages", "Messages", Action::get("/emails")).within([
106 Place::new(EMAILS, "Email", Action::get("/emails")),
107 // Eudora put Out beside In, and this app's outbox is a real place
108 // rather than a state a message is in: goingson `a3c76a24`.
109 Place::new(OUTBOX, "Out box", Action::get("/outbox")),
110 Place::new(CONTACTS, "Contacts", Action::get("/contacts")),
111 ]),
112 // Search reaches every kind, so it belongs to no group and is a place
113 // of its own. It is a nav entry rather than a binding because the app
114 // binds no keys and cannot until quasicoherent `858be2a6` gives the
115 // overlay a container -- and because a nav entry is the one entry point
116 // that serves pointer, touch and keyboard alike, which is what goingson
117 // `6b3aa22b` was actually complaining about.
118 Place::new(SEARCH, "Search", Action::get("/search")),
119 // No sub-places, so it draws as a place of its own. The shipped header
120 // puts it on the right; where it lands here is the stylesheet's.
121 Place::new(SETTINGS, "Settings", Action::get("/settings")),
122 ]
123 }
124
125 /// Everything the app keeps offered, whatever screen is showing.
126 ///
127 /// Each contributor takes the chrome and gives it back, which is the chain
128 /// [`super::router`] already is. The band is described by the module that owns
129 /// it, so there is one answer to what it says.
130 #[must_use]
131 pub fn chrome() -> Chrome {
132 let offered = nav().into_iter().fold(Chrome::new(), Chrome::offering);
133 super::time_tracking::chrome(offered)
134 }
135