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 &self.subscriptions_page.entries,
96 self.selected_index,
97 self.scroll_offset,
98 &self.active_pane,
99 &self.thread_message_blocks(),
100 self.message_scroll_offset,
101 theme,
102 );
103 } else {
104 let mail_title = self.mail_list_title();
105 ui::mail_list::draw_view(
106 frame,
107 chunks[1],
108 &ui::mail_list::MailListView {
109 rows: &self.mail_list_rows(),
110 selected_index: self.selected_index,
111 scroll_offset: self.scroll_offset,
112 active_pane: &self.active_pane,
113 title: &mail_title,
114 selected_set: &self.selected_set,
115 mode: self.mail_list_mode,
116 },
117 theme,
118 );
119 }
120 }
121 LayoutMode::ThreePane => {
122 let chunks = Layout::default()
123 .direction(Direction::Horizontal)
124 .constraints([Constraint::Percentage(15), Constraint::Percentage(85)])
125 .split(content_area);
126
127 ui::sidebar::draw(
128 frame,
129 chunks[0],
130 &ui::sidebar::SidebarView {
131 labels: &self.labels,
132 active_pane: &self.active_pane,
133 saved_searches: &self.saved_searches,
134 sidebar_selected: self.sidebar_selected,
135 all_mail_active: !self.search_active
136 && self.mailbox_view == MailboxView::Messages
137 && self.active_label.is_none()
138 && self.pending_active_label.is_none(),
139 subscriptions_active: self.mailbox_view == MailboxView::Subscriptions,
140 subscription_count: self.subscriptions_page.entries.len(),
141 system_expanded: self.sidebar_system_expanded,
142 user_expanded: self.sidebar_user_expanded,
143 saved_searches_expanded: self.sidebar_saved_searches_expanded,
144 active_label: self
145 .pending_active_label
146 .as_ref()
147 .or(self.active_label.as_ref()),
148 },
149 theme,
150 );
151
152 if self.mailbox_view == MailboxView::Subscriptions {
153 ui::subscriptions_page::draw(
154 frame,
155 chunks[1],
156 &self.subscriptions_page.entries,
157 self.selected_index,
158 self.scroll_offset,
159 &self.active_pane,
160 &self.thread_message_blocks(),
161 self.message_scroll_offset,
162 theme,
163 );
164 } else {
165 let inner = Layout::default()
166 .direction(Direction::Horizontal)
167 .constraints([Constraint::Percentage(41), Constraint::Percentage(59)])
168 .split(chunks[1]);
169 let mail_title = self.mail_list_title();
170 ui::mail_list::draw_view(
171 frame,
172 inner[0],
173 &ui::mail_list::MailListView {
174 rows: &self.mail_list_rows(),
175 selected_index: self.selected_index,
176 scroll_offset: self.scroll_offset,
177 active_pane: &self.active_pane,
178 title: &mail_title,
179 selected_set: &self.selected_set,
180 mode: self.mail_list_mode,
181 },
182 theme,
183 );
184 ui::message_view::draw(
185 frame,
186 inner[1],
187 &self.thread_message_blocks(),
188 self.message_scroll_offset,
189 &self.active_pane,
190 theme,
191 );
192 }
193 }
194 LayoutMode::FullScreen => {
195 if self.mailbox_view == MailboxView::Subscriptions {
196 ui::subscriptions_page::draw(
197 frame,
198 content_area,
199 &self.subscriptions_page.entries,
200 self.selected_index,
201 self.scroll_offset,
202 &self.active_pane,
203 &self.thread_message_blocks(),
204 self.message_scroll_offset,
205 theme,
206 );
207 } else {
208 ui::message_view::draw(
209 frame,
210 content_area,
211 &self.thread_message_blocks(),
212 self.message_scroll_offset,
213 &self.active_pane,
214 theme,
215 );
216 }
217 }
218 },
219 Screen::Search => {
220 let rows = self.search_mail_list_rows();
221 ui::search_page::draw(
222 frame,
223 content_area,
224 &self.search_page,
225 &rows,
226 self.mail_list_mode,
227 &self.thread_message_blocks(),
228 self.message_scroll_offset,
229 theme,
230 );
231 }
232 Screen::Rules => {
233 ui::rules_page::draw(frame, content_area, &self.rules_page, theme);
234 }
235 Screen::Diagnostics => {
236 ui::diagnostics_page::draw(frame, content_area, &self.diagnostics_page, theme);
237 }
238 Screen::Accounts => {
239 ui::accounts_page::draw(frame, content_area, &self.accounts_page, theme);
240 }
241 }
242
243 ui::status_bar::draw(
244 frame,
245 bottom_bar_area,
246 &self.envelopes,
247 self.last_sync_status.as_deref(),
248 self.status_message.as_deref(),
249 theme,
250 );
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}