1use super::*;
2
3impl App {
4 pub fn draw(&mut self, frame: &mut Frame) {
5 let theme = &self.theme;
6 let area = frame.area();
7
8 let outer_chunks = Layout::default()
10 .direction(Direction::Vertical)
11 .constraints([
12 Constraint::Length(1), Constraint::Length(2), Constraint::Min(0), Constraint::Length(1), ])
17 .split(area);
18
19 let tab_bar_area = outer_chunks[0];
20 let hint_bar_area = outer_chunks[1];
21 let content_area = outer_chunks[2];
22 self.visible_height = content_area.height.saturating_sub(2) as usize;
24 let bottom_bar_area = outer_chunks[3];
25
26 let tab_titles = vec!["Mailbox", "Search", "Rules", "Accounts", "Diagnostics"];
28 let selected_tab = match self.screen {
29 Screen::Mailbox => 0,
30 Screen::Search => 1,
31 Screen::Rules => 2,
32 Screen::Accounts => 3,
33 Screen::Diagnostics => 4,
34 };
35 let tabs = ratatui::widgets::Tabs::new(tab_titles)
36 .select(selected_tab)
37 .style(Style::default().fg(theme.text_muted))
38 .highlight_style(Style::default().fg(theme.accent).bold())
39 .divider(Span::styled(" | ", Style::default().fg(theme.text_muted)));
40 frame.render_widget(tabs, tab_bar_area);
41
42 ui::hint_bar::draw(
44 frame,
45 hint_bar_area,
46 ui::hint_bar::HintBarState {
47 screen: self.screen,
48 active_pane: &self.active_pane,
49 search_active: self.search_bar.active,
50 help_modal_open: self.help_modal_open,
51 selected_count: self.selected_set.len(),
52 bulk_confirm_open: self.pending_bulk_confirm.is_some(),
53 sync_status: self.last_sync_status.clone(),
54 },
55 theme,
56 );
57
58 match self.screen {
59 Screen::Mailbox => match self.layout_mode {
60 LayoutMode::TwoPane => {
61 let chunks = Layout::default()
62 .direction(Direction::Horizontal)
63 .constraints([Constraint::Percentage(20), Constraint::Percentage(80)])
64 .split(content_area);
65
66 ui::sidebar::draw(
67 frame,
68 chunks[0],
69 &ui::sidebar::SidebarView {
70 labels: &self.labels,
71 active_pane: &self.active_pane,
72 saved_searches: &self.saved_searches,
73 sidebar_selected: self.sidebar_selected,
74 all_mail_active: !self.search_active
75 && self.mailbox_view == MailboxView::Messages
76 && self.active_label.is_none()
77 && self.pending_active_label.is_none(),
78 subscriptions_active: self.mailbox_view == MailboxView::Subscriptions,
79 subscription_count: self.subscriptions_page.entries.len(),
80 system_expanded: self.sidebar_system_expanded,
81 user_expanded: self.sidebar_user_expanded,
82 saved_searches_expanded: self.sidebar_saved_searches_expanded,
83 active_label: self
84 .pending_active_label
85 .as_ref()
86 .or(self.active_label.as_ref()),
87 },
88 theme,
89 );
90
91 if self.mailbox_view == MailboxView::Subscriptions {
92 ui::subscriptions_page::draw(
93 frame,
94 chunks[1],
95 &ui::subscriptions_page::SubscriptionsPageView {
96 entries: &self.subscriptions_page.entries,
97 selected_index: self.selected_index,
98 scroll_offset: self.scroll_offset,
99 active_pane: &self.active_pane,
100 preview_blocks: &self.thread_message_blocks(),
101 message_scroll_offset: self.message_scroll_offset,
102 },
103 theme,
104 );
105 } else {
106 let mail_title = self.mail_list_title();
107 ui::mail_list::draw_view(
108 frame,
109 chunks[1],
110 &ui::mail_list::MailListView {
111 rows: &self.mail_list_rows(),
112 selected_index: self.selected_index,
113 scroll_offset: self.scroll_offset,
114 active_pane: &self.active_pane,
115 title: &mail_title,
116 selected_set: &self.selected_set,
117 mode: self.mail_list_mode,
118 },
119 theme,
120 );
121 }
122 }
123 LayoutMode::ThreePane => {
124 let chunks = Layout::default()
125 .direction(Direction::Horizontal)
126 .constraints([Constraint::Percentage(15), Constraint::Percentage(85)])
127 .split(content_area);
128
129 ui::sidebar::draw(
130 frame,
131 chunks[0],
132 &ui::sidebar::SidebarView {
133 labels: &self.labels,
134 active_pane: &self.active_pane,
135 saved_searches: &self.saved_searches,
136 sidebar_selected: self.sidebar_selected,
137 all_mail_active: !self.search_active
138 && self.mailbox_view == MailboxView::Messages
139 && self.active_label.is_none()
140 && self.pending_active_label.is_none(),
141 subscriptions_active: self.mailbox_view == MailboxView::Subscriptions,
142 subscription_count: self.subscriptions_page.entries.len(),
143 system_expanded: self.sidebar_system_expanded,
144 user_expanded: self.sidebar_user_expanded,
145 saved_searches_expanded: self.sidebar_saved_searches_expanded,
146 active_label: self
147 .pending_active_label
148 .as_ref()
149 .or(self.active_label.as_ref()),
150 },
151 theme,
152 );
153
154 if self.mailbox_view == MailboxView::Subscriptions {
155 ui::subscriptions_page::draw(
156 frame,
157 chunks[1],
158 &ui::subscriptions_page::SubscriptionsPageView {
159 entries: &self.subscriptions_page.entries,
160 selected_index: self.selected_index,
161 scroll_offset: self.scroll_offset,
162 active_pane: &self.active_pane,
163 preview_blocks: &self.thread_message_blocks(),
164 message_scroll_offset: self.message_scroll_offset,
165 },
166 theme,
167 );
168 } else {
169 let inner = Layout::default()
170 .direction(Direction::Horizontal)
171 .constraints([Constraint::Percentage(41), Constraint::Percentage(59)])
172 .split(chunks[1]);
173 let mail_title = self.mail_list_title();
174 ui::mail_list::draw_view(
175 frame,
176 inner[0],
177 &ui::mail_list::MailListView {
178 rows: &self.mail_list_rows(),
179 selected_index: self.selected_index,
180 scroll_offset: self.scroll_offset,
181 active_pane: &self.active_pane,
182 title: &mail_title,
183 selected_set: &self.selected_set,
184 mode: self.mail_list_mode,
185 },
186 theme,
187 );
188 ui::message_view::draw(
189 frame,
190 inner[1],
191 &self.thread_message_blocks(),
192 self.message_scroll_offset,
193 &self.active_pane,
194 theme,
195 );
196 }
197 }
198 LayoutMode::FullScreen => {
199 if self.mailbox_view == MailboxView::Subscriptions {
200 ui::subscriptions_page::draw(
201 frame,
202 content_area,
203 &ui::subscriptions_page::SubscriptionsPageView {
204 entries: &self.subscriptions_page.entries,
205 selected_index: self.selected_index,
206 scroll_offset: self.scroll_offset,
207 active_pane: &self.active_pane,
208 preview_blocks: &self.thread_message_blocks(),
209 message_scroll_offset: self.message_scroll_offset,
210 },
211 theme,
212 );
213 } else {
214 ui::message_view::draw(
215 frame,
216 content_area,
217 &self.thread_message_blocks(),
218 self.message_scroll_offset,
219 &self.active_pane,
220 theme,
221 );
222 }
223 }
224 },
225 Screen::Search => {
226 let rows = self.search_mail_list_rows();
227 ui::search_page::draw(
228 frame,
229 content_area,
230 &self.search_page,
231 &rows,
232 self.mail_list_mode,
233 &self.thread_message_blocks(),
234 self.message_scroll_offset,
235 theme,
236 );
237 }
238 Screen::Rules => {
239 ui::rules_page::draw(frame, content_area, &self.rules_page, theme);
240 }
241 Screen::Diagnostics => {
242 ui::diagnostics_page::draw(frame, content_area, &self.diagnostics_page, theme);
243 }
244 Screen::Accounts => {
245 ui::accounts_page::draw(frame, content_area, &self.accounts_page, theme);
246 }
247 }
248
249 let status_bar = self.status_bar_state();
250 ui::status_bar::draw(frame, bottom_bar_area, &status_bar, theme);
251
252 if self.search_bar.active {
253 ui::search_bar::draw(frame, area, &self.search_bar, theme);
254 }
255
256 ui::command_palette::draw(frame, area, &self.command_palette, theme);
258
259 ui::label_picker::draw(frame, area, &self.label_picker, theme);
261
262 ui::compose_picker::draw(frame, area, &self.compose_picker, theme);
264
265 ui::attachment_modal::draw(frame, area, &self.attachment_panel, theme);
267
268 ui::url_modal::draw(frame, area, self.url_modal.as_ref(), theme);
270
271 ui::snooze_modal::draw(frame, area, &self.snooze_panel, &self.snooze_config, theme);
273
274 ui::send_confirm_modal::draw(frame, area, self.pending_send_confirm.as_ref(), theme);
276
277 ui::bulk_confirm_modal::draw(frame, area, self.pending_bulk_confirm.as_ref(), theme);
279
280 ui::unsubscribe_modal::draw(
282 frame,
283 area,
284 self.pending_unsubscribe_confirm.as_ref(),
285 theme,
286 );
287
288 ui::help_modal::draw(
290 frame,
291 area,
292 ui::help_modal::HelpModalState {
293 open: self.help_modal_open,
294 screen: self.screen,
295 active_pane: &self.active_pane,
296 selected_count: self.selected_set.len(),
297 scroll_offset: self.help_scroll_offset,
298 },
299 theme,
300 );
301 }
302}