Skip to content

Commit fe09806

Browse files
committed
refactor: organize ui components
1 parent a509450 commit fe09806

File tree

9 files changed

+118
-74
lines changed

9 files changed

+118
-74
lines changed

syndterm/src/application/mod.rs

+42-28
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@ use crate::{
1616
terminal::Terminal,
1717
ui::{
1818
self,
19-
entries::Entries,
20-
login::LoginMethods,
21-
prompt::Prompt,
22-
subscription::Subscription,
23-
tabs::{Tab, Tabs},
19+
components::{
20+
entries::Entries,
21+
login::Login,
22+
prompt::Prompt,
23+
subscription::Subscription,
24+
tabs::{Tab, Tabs},
25+
Components,
26+
},
2427
theme::Theme,
2528
},
2629
};
@@ -43,17 +46,14 @@ pub enum AuthenticateState {
4346
}
4447

4548
pub struct LoginState {
46-
pub login_methods: LoginMethods,
49+
pub login_methods: Login,
4750
pub auth_state: AuthenticateState,
4851
}
4952

5053
pub struct State {
5154
pub screen: Screen,
5255
pub login: LoginState,
53-
pub tabs: Tabs,
54-
pub prompt: Prompt,
55-
pub subscription: Subscription,
56-
pub entries: Entries,
56+
pub components: Components,
5757
}
5858

5959
pub struct Application {
@@ -78,13 +78,15 @@ impl Application {
7878
let state = State {
7979
screen: Screen::Login,
8080
login: LoginState {
81-
login_methods: LoginMethods::new(),
81+
login_methods: Login::new(),
8282
auth_state: AuthenticateState::NotAuthenticated,
8383
},
84-
tabs: Tabs::new(),
85-
subscription: Subscription::new(),
86-
entries: Entries::new(),
87-
prompt: Prompt::new(),
84+
components: Components {
85+
tabs: Tabs::new(),
86+
prompt: Prompt::new(),
87+
subscription: Subscription::new(),
88+
entries: Entries::new(),
89+
},
8890
};
8991

9092
Self {
@@ -170,7 +172,7 @@ impl Application {
170172
if self.should_render {
171173
self.render();
172174
self.should_render = false;
173-
self.state.prompt.clear_error_message();
175+
self.state.components.prompt.clear_error_message();
174176
}
175177

176178
if self.should_quit {
@@ -204,8 +206,10 @@ impl Application {
204206
self.complete_device_authroize_flow(device_access_token)
205207
}
206208
Command::MoveTabSelection(direction) => {
207-
match self.state.tabs.move_selection(direction) {
208-
Tab::Subscription if !self.state.subscription.has_subscription() => {
209+
match self.state.components.tabs.move_selection(direction) {
210+
Tab::Subscription
211+
if !self.state.components.subscription.has_subscription() =>
212+
{
209213
next = Some(Command::FetchSubscription {
210214
after: None,
211215
first: 50,
@@ -216,7 +220,7 @@ impl Application {
216220
self.should_render = true;
217221
}
218222
Command::MoveSubscribedFeed(direction) => {
219-
self.state.subscription.move_selection(direction);
223+
self.state.components.subscription.move_selection(direction);
220224
self.should_render = true;
221225
}
222226
Command::PromptFeedSubscription => {
@@ -239,15 +243,18 @@ impl Application {
239243
self.fetch_subscription(after, first)
240244
}
241245
Command::UpdateSubscription(sub) => {
242-
self.state.subscription.update_subscription(sub);
246+
self.state.components.subscription.update_subscription(sub);
243247
self.should_render = true;
244248
}
245249
Command::CompleteSubscribeFeed { feed } => {
246-
self.state.subscription.add_subscribed_feed(feed);
250+
self.state.components.subscription.add_subscribed_feed(feed);
247251
self.should_render = true;
248252
}
249253
Command::CompleteUnsubscribeFeed { url } => {
250-
self.state.subscription.remove_unsubscribed_feed(url);
254+
self.state
255+
.components
256+
.subscription
257+
.remove_unsubscribed_feed(url);
251258
self.should_render = true;
252259
}
253260
Command::OpenFeed => {
@@ -257,18 +264,18 @@ impl Application {
257264
self.fetch_entries(after, first);
258265
}
259266
Command::UpdateEntries(payload) => {
260-
self.state.entries.update_entries(payload);
267+
self.state.components.entries.update_entries(payload);
261268
self.should_render = true;
262269
}
263270
Command::MoveEntry(direction) => {
264-
self.state.entries.move_selection(direction);
271+
self.state.components.entries.move_selection(direction);
265272
self.should_render = true;
266273
}
267274
Command::OpenEntry => {
268275
self.open_entry();
269276
}
270277
Command::HandleError { message } => {
271-
self.state.prompt.set_error_message(message);
278+
self.state.components.prompt.set_error_message(message);
272279
self.should_render = true;
273280
}
274281
}
@@ -312,7 +319,7 @@ impl Application {
312319
KeyCode::BackTab => {
313320
return Some(Command::MoveTabSelection(Direction::Left))
314321
}
315-
_ => match self.state.tabs.current() {
322+
_ => match self.state.components.tabs.current() {
316323
Tab::Feeds => match key.code {
317324
KeyCode::Char('j') => {
318325
return Some(Command::MoveEntry(Direction::Down))
@@ -379,6 +386,7 @@ impl Application {
379386
// TODO: prompt deletion confirm
380387
let Some(url) = self
381388
.state
389+
.components
382390
.subscription
383391
.selected_feed_url()
384392
.map(ToOwned::to_owned)
@@ -414,14 +422,20 @@ impl Application {
414422

415423
impl Application {
416424
fn open_feed(&mut self) {
417-
let Some(feed_website_url) = self.state.subscription.selected_feed_website_url() else {
425+
let Some(feed_website_url) = self
426+
.state
427+
.components
428+
.subscription
429+
.selected_feed_website_url()
430+
else {
418431
return;
419432
};
420433
open::that(feed_website_url).ok();
421434
}
422435

423436
fn open_entry(&mut self) {
424-
let Some(entry_website_url) = self.state.entries.selected_entry_website_url() else {
437+
let Some(entry_website_url) = self.state.components.entries.selected_entry_website_url()
438+
else {
425439
return;
426440
};
427441
open::that(entry_website_url).ok();
File renamed without changes.

syndterm/src/ui/login.rs syndterm/src/ui/components/login.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,15 @@ use ratatui::{
99
use crate::{
1010
application::{AuthenticateMethod, AuthenticateState},
1111
auth::device_flow::DeviceAuthorizationResponse,
12+
ui::{extension::RectExt, Context},
1213
};
1314

14-
use super::{centered, Context};
15-
16-
pub struct LoginMethods {
15+
pub struct Login {
1716
state: ListState,
1817
methods: List<'static>,
1918
}
2019

21-
impl LoginMethods {
20+
impl Login {
2221
pub fn new() -> Self {
2322
let methods = {
2423
let items = vec![ListItem::new(Line::styled("Github", Style::default()))];
@@ -40,7 +39,7 @@ impl LoginMethods {
4039
}
4140

4241
pub fn render(&mut self, rect: Rect, frame: &mut Frame) {
43-
let rect = centered(50, 50, rect);
42+
let rect = rect.centered(50, 50);
4443

4544
let chunks = Layout::default()
4645
.direction(Direction::Vertical)
@@ -56,7 +55,7 @@ impl LoginMethods {
5655
}
5756

5857
fn render_device_flow(rect: Rect, frame: &mut Frame, res: &DeviceAuthorizationResponse) {
59-
let rect = centered(50, 50, rect);
58+
let rect = rect.centered(50, 50);
6059

6160
let chunks = Layout::default()
6261
.direction(Direction::Vertical)

syndterm/src/ui/components/mod.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use crate::ui::components::{
2+
entries::Entries, prompt::Prompt, subscription::Subscription, tabs::Tabs,
3+
};
4+
5+
pub mod entries;
6+
pub mod login;
7+
pub mod prompt;
8+
pub mod subscription;
9+
pub mod tabs;
10+
11+
pub struct Components {
12+
pub tabs: Tabs,
13+
pub prompt: Prompt,
14+
pub subscription: Subscription,
15+
pub entries: Entries,
16+
}

syndterm/src/ui/prompt.rs syndterm/src/ui/components/prompt.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ impl Prompt {
3939
.style(cx.theme.prompt.background)
4040
} else {
4141
let keys = [("q", "Quit"), ("Tab", "Next Tab"), ("j/k", "Up/Down")];
42-
let per_screen_keys = match cx.state.tabs.current() {
42+
let per_screen_keys = match cx.state.components.tabs.current() {
4343
Tab::Subscription => [
4444
("a", "Subscribe"),
4545
("d", "Unsubscribe"),
File renamed without changes.

syndterm/src/ui/tabs.rs syndterm/src/ui/components/tabs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ impl Tabs {
5252
TuiTabs::new(self.tabs.clone())
5353
.style(cx.theme.tabs)
5454
.divider("")
55-
.select(cx.state.tabs.selected)
55+
.select(cx.state.components.tabs.selected)
5656
.highlight_style(cx.theme.tabs_selected)
5757
.render(tabs, buf);
5858
}

syndterm/src/ui/extension.rs

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use ratatui::prelude::{Constraint, Direction, Layout, Rect};
2+
3+
pub(super) trait RectExt {
4+
/// Create centered Rect
5+
fn centered(self, percent_x: u16, percent_y: u16) -> Rect;
6+
}
7+
8+
impl RectExt for Rect {
9+
fn centered(self, percent_x: u16, percent_y: u16) -> Rect {
10+
// get vertically centered rect
11+
let layout = Layout::default()
12+
.direction(Direction::Vertical)
13+
.constraints([
14+
Constraint::Percentage((100 - percent_y) / 2),
15+
Constraint::Percentage(percent_y),
16+
Constraint::Percentage((100 - percent_y) / 2),
17+
])
18+
.split(self);
19+
20+
// then centered horizontally
21+
Layout::default()
22+
.direction(Direction::Horizontal)
23+
.constraints([
24+
Constraint::Percentage((100 - percent_x) / 2),
25+
Constraint::Percentage(percent_x),
26+
Constraint::Percentage((100 - percent_x) / 2),
27+
])
28+
.split(layout[1])[1]
29+
}
30+
}

syndterm/src/ui/mod.rs

+23-38
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
use ratatui::{
2-
prelude::{Constraint, Direction, Layout, Rect},
2+
prelude::{Constraint, Layout},
33
widgets::{Block, Widget},
44
Frame,
55
};
66

77
use crate::{
88
application::{Screen, State},
9-
ui::{tabs::Tab, theme::Theme},
9+
ui::{
10+
components::{login, tabs::Tab},
11+
theme::Theme,
12+
},
1013
};
1114

12-
pub mod entries;
13-
pub mod login;
14-
pub mod prompt;
15-
pub mod subscription;
16-
pub mod tabs;
15+
pub mod components;
16+
pub mod extension;
1717
pub mod theme;
1818

1919
pub const UNKNOWN_SYMBOL: &str = "???";
@@ -41,42 +41,27 @@ pub fn render(frame: &mut Frame, mut cx: Context<'_>) {
4141
Constraint::Length(1),
4242
]));
4343

44-
cx.state.tabs.render(tabs_area, frame.buffer_mut(), &cx);
44+
cx.state
45+
.components
46+
.tabs
47+
.render(tabs_area, frame.buffer_mut(), &cx);
4548

46-
match cx.state.tabs.current() {
47-
Tab::Subscription => cx
48-
.state
49-
.subscription
50-
.render(content_area, frame.buffer_mut(), &cx),
49+
match cx.state.components.tabs.current() {
50+
Tab::Subscription => {
51+
cx.state
52+
.components
53+
.subscription
54+
.render(content_area, frame.buffer_mut(), &cx)
55+
}
5156
Tab::Feeds => cx
5257
.state
58+
.components
5359
.entries
5460
.render(content_area, frame.buffer_mut(), &cx),
5561
};
5662

57-
cx.state.prompt.render(prompt_area, frame.buffer_mut(), &cx);
58-
}
59-
60-
/// Create centered Rect
61-
#[allow(dead_code)]
62-
fn centered(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
63-
// get vertically centered rect
64-
let layout = Layout::default()
65-
.direction(Direction::Vertical)
66-
.constraints([
67-
Constraint::Percentage((100 - percent_y) / 2),
68-
Constraint::Percentage(percent_y),
69-
Constraint::Percentage((100 - percent_y) / 2),
70-
])
71-
.split(r);
72-
73-
// then centered horizontally
74-
Layout::default()
75-
.direction(Direction::Horizontal)
76-
.constraints([
77-
Constraint::Percentage((100 - percent_x) / 2),
78-
Constraint::Percentage(percent_x),
79-
Constraint::Percentage((100 - percent_x) / 2),
80-
])
81-
.split(layout[1])[1]
63+
cx.state
64+
.components
65+
.prompt
66+
.render(prompt_area, frame.buffer_mut(), &cx);
8267
}

0 commit comments

Comments
 (0)