Skip to content

Commit 80d8ed8

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

File tree

16 files changed

+333
-235
lines changed

16 files changed

+333
-235
lines changed

syndapi/src/persistence/memory.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ pub struct MemoryDatastore {
1313

1414
const TEST_DATA: &[&str] = &[
1515
"https://seanmonstar.com/rss",
16-
"https://thesquareplanet.com/blog/feed.xml",
16+
"https://thesquareplanet.com/feed.xml",
1717
"https://thiscute.world/en/index.xml",
1818
"https://blog.m-ou.se/index.xml",
1919
"https://keens.github.io/index.xml",

syndterm/src/application/mod.rs

+34-77
Original file line numberDiff line numberDiff line change
@@ -2,65 +2,34 @@ use std::{pin::Pin, time::Duration};
22

33
use crossterm::event::{Event as CrosstermEvent, KeyCode, KeyEvent, KeyEventKind};
44
use futures_util::{FutureExt, Stream, StreamExt};
5+
use ratatui::widgets::Widget;
56
use tokio::time::{Instant, Sleep};
67

78
use crate::{
89
auth::{
910
self,
1011
device_flow::{DeviceAccessTokenResponse, DeviceAuthorizationResponse},
11-
Authentication,
12+
Credential,
1213
},
1314
client::Client,
1415
command::Command,
1516
job::Jobs,
1617
terminal::Terminal,
1718
ui::{
1819
self,
19-
entries::Entries,
20-
login::LoginMethods,
21-
prompt::Prompt,
22-
subscription::Subscription,
23-
tabs::{Tab, Tabs},
20+
components::{authentication::Authentication, root::Root, tabs::Tab, Components},
2421
theme::Theme,
2522
},
2623
};
2724

2825
mod direction;
2926
pub use direction::{Direction, IndexOutOfRange};
3027

31-
/// Cureent ui screen
32-
pub enum Screen {
33-
Login,
34-
Browse,
35-
}
36-
37-
/// Handle user authentication
38-
#[derive(PartialEq, Eq)]
39-
pub enum AuthenticateState {
40-
NotAuthenticated,
41-
DeviceFlow(DeviceAuthorizationResponse),
42-
Authenticated,
43-
}
44-
45-
pub struct LoginState {
46-
pub login_methods: LoginMethods,
47-
pub auth_state: AuthenticateState,
48-
}
49-
50-
pub struct State {
51-
pub screen: Screen,
52-
pub login: LoginState,
53-
pub tabs: Tabs,
54-
pub prompt: Prompt,
55-
pub subscription: Subscription,
56-
pub entries: Entries,
57-
}
58-
5928
pub struct Application {
6029
terminal: Terminal,
6130
client: Client,
6231
jobs: Jobs,
63-
state: State,
32+
components: Components,
6433
theme: Theme,
6534
idle_timer: Pin<Box<Sleep>>,
6635

@@ -75,34 +44,21 @@ pub enum EventLoopControlFlow {
7544

7645
impl Application {
7746
pub fn new(terminal: Terminal, client: Client) -> Self {
78-
let state = State {
79-
screen: Screen::Login,
80-
login: LoginState {
81-
login_methods: LoginMethods::new(),
82-
auth_state: AuthenticateState::NotAuthenticated,
83-
},
84-
tabs: Tabs::new(),
85-
subscription: Subscription::new(),
86-
entries: Entries::new(),
87-
prompt: Prompt::new(),
88-
};
89-
9047
Self {
9148
terminal,
9249
client,
50+
components: Components::new(),
9351
jobs: Jobs::new(),
94-
state,
9552
theme: Theme::new(),
9653
idle_timer: Box::pin(tokio::time::sleep(Duration::from_millis(250))),
9754
should_quit: false,
9855
should_render: false,
9956
}
10057
}
10158

102-
pub fn set_auth(&mut self, auth: Authentication) {
103-
self.client.set_credential(auth);
104-
self.state.login.auth_state = AuthenticateState::Authenticated;
105-
self.state.screen = Screen::Browse;
59+
pub fn set_credential(&mut self, cred: Credential) {
60+
self.client.set_credential(cred);
61+
self.components.auth.authenticated();
10662
self.initial_fetch();
10763
self.should_render = true;
10864
}
@@ -170,7 +126,7 @@ impl Application {
170126
if self.should_render {
171127
self.render();
172128
self.should_render = false;
173-
self.state.prompt.clear_error_message();
129+
self.components.prompt.clear_error_message();
174130
}
175131

176132
if self.should_quit {
@@ -204,8 +160,8 @@ impl Application {
204160
self.complete_device_authroize_flow(device_access_token)
205161
}
206162
Command::MoveTabSelection(direction) => {
207-
match self.state.tabs.move_selection(direction) {
208-
Tab::Subscription if !self.state.subscription.has_subscription() => {
163+
match self.components.tabs.move_selection(direction) {
164+
Tab::Subscription if !self.components.subscription.has_subscription() => {
209165
next = Some(Command::FetchSubscription {
210166
after: None,
211167
first: 50,
@@ -216,7 +172,7 @@ impl Application {
216172
self.should_render = true;
217173
}
218174
Command::MoveSubscribedFeed(direction) => {
219-
self.state.subscription.move_selection(direction);
175+
self.components.subscription.move_selection(direction);
220176
self.should_render = true;
221177
}
222178
Command::PromptFeedSubscription => {
@@ -239,15 +195,15 @@ impl Application {
239195
self.fetch_subscription(after, first)
240196
}
241197
Command::UpdateSubscription(sub) => {
242-
self.state.subscription.update_subscription(sub);
198+
self.components.subscription.update_subscription(sub);
243199
self.should_render = true;
244200
}
245201
Command::CompleteSubscribeFeed { feed } => {
246-
self.state.subscription.add_subscribed_feed(feed);
202+
self.components.subscription.add_subscribed_feed(feed);
247203
self.should_render = true;
248204
}
249205
Command::CompleteUnsubscribeFeed { url } => {
250-
self.state.subscription.remove_unsubscribed_feed(url);
206+
self.components.subscription.remove_unsubscribed_feed(url);
251207
self.should_render = true;
252208
}
253209
Command::OpenFeed => {
@@ -257,31 +213,30 @@ impl Application {
257213
self.fetch_entries(after, first);
258214
}
259215
Command::UpdateEntries(payload) => {
260-
self.state.entries.update_entries(payload);
216+
self.components.entries.update_entries(payload);
261217
self.should_render = true;
262218
}
263219
Command::MoveEntry(direction) => {
264-
self.state.entries.move_selection(direction);
220+
self.components.entries.move_selection(direction);
265221
self.should_render = true;
266222
}
267223
Command::OpenEntry => {
268224
self.open_entry();
269225
}
270226
Command::HandleError { message } => {
271-
self.state.prompt.set_error_message(message);
227+
self.components.prompt.set_error_message(message);
272228
self.should_render = true;
273229
}
274230
}
275231
}
276232
}
277233

278234
fn render(&mut self) {
279-
let cx = ui::Context {
280-
state: &mut self.state,
281-
theme: &self.theme,
282-
};
235+
let cx = ui::Context { theme: &self.theme };
236+
let root = Root::new(&self.components, cx);
283237

284-
self.terminal.render(|frame| ui::render(frame, cx)).unwrap();
238+
self.terminal
239+
.render(|frame| Widget::render(root, frame.size(), frame.buffer_mut()));
285240
}
286241

287242
#[allow(clippy::single_match)]
@@ -312,7 +267,7 @@ impl Application {
312267
KeyCode::BackTab => {
313268
return Some(Command::MoveTabSelection(Direction::Left))
314269
}
315-
_ => match self.state.tabs.current() {
270+
_ => match self.state.components.tabs.current() {
316271
Tab::Feeds => match key.code {
317272
KeyCode::Char('j') => {
318273
return Some(Command::MoveEntry(Direction::Down))
@@ -379,6 +334,7 @@ impl Application {
379334
// TODO: prompt deletion confirm
380335
let Some(url) = self
381336
.state
337+
.components
382338
.subscription
383339
.selected_feed_url()
384340
.map(ToOwned::to_owned)
@@ -414,14 +370,20 @@ impl Application {
414370

415371
impl Application {
416372
fn open_feed(&mut self) {
417-
let Some(feed_website_url) = self.state.subscription.selected_feed_website_url() else {
373+
let Some(feed_website_url) = self
374+
.state
375+
.components
376+
.subscription
377+
.selected_feed_website_url()
378+
else {
418379
return;
419380
};
420381
open::that(feed_website_url).ok();
421382
}
422383

423384
fn open_entry(&mut self) {
424-
let Some(entry_website_url) = self.state.entries.selected_entry_website_url() else {
385+
let Some(entry_website_url) = self.state.components.entries.selected_entry_website_url()
386+
else {
425387
return;
426388
};
427389
open::that(entry_website_url).ok();
@@ -444,11 +406,6 @@ impl Application {
444406
}
445407
}
446408

447-
#[derive(Debug)]
448-
pub enum AuthenticateMethod {
449-
Github,
450-
}
451-
452409
impl Application {
453410
fn authenticate(&mut self, method: AuthenticateMethod) {
454411
match method {
@@ -498,9 +455,9 @@ impl Application {
498455
};
499456

500457
// TODO: handle error
501-
auth::persist_authentication(auth.clone()).ok();
458+
auth::persist_credential(auth.clone()).ok();
502459

503-
self.set_auth(auth);
460+
self.set_credential(auth);
504461
}
505462
}
506463

syndterm/src/auth/mod.rs

+16-11
Original file line numberDiff line numberDiff line change
@@ -8,31 +8,36 @@ use crate::config;
88
pub mod device_flow;
99
pub mod github;
1010

11+
#[derive(Debug, Clone, Copy)]
12+
pub enum AuthenticationProvider {
13+
Github,
14+
}
15+
1116
#[derive(Serialize, Deserialize, Clone)]
12-
pub enum Authentication {
17+
pub enum Credential {
1318
Github { access_token: String },
1419
}
1520

16-
pub fn persist_authentication(auth: Authentication) -> anyhow::Result<()> {
17-
let auth_path = auth_file();
18-
if let Some(parent) = auth_path.parent() {
21+
pub fn persist_credential(cred: Credential) -> anyhow::Result<()> {
22+
let cred_path = cred_file();
23+
if let Some(parent) = cred_path.parent() {
1924
std::fs::create_dir_all(parent)?;
2025
}
21-
let mut auth_file = std::fs::File::create(&auth_path)?;
26+
let mut cred_file = std::fs::File::create(&cred_path)?;
2227

23-
debug!(path = ?auth_path.display(), "Create auth cache file");
28+
debug!(path = ?cred_path.display(), "Create credential cache file");
2429

25-
serde_json::to_writer(&mut auth_file, &auth)?;
30+
serde_json::to_writer(&mut cred_file, &cred)?;
2631

2732
Ok(())
2833
}
2934

30-
fn auth_file() -> PathBuf {
31-
config::cache_dir().join("auth.json")
35+
fn cred_file() -> PathBuf {
36+
config::cache_dir().join("credential.json")
3237
}
3338

34-
pub fn authenticate_from_cache() -> Option<Authentication> {
35-
std::fs::File::open(auth_file())
39+
pub fn credential_from_cache() -> Option<Credential> {
40+
std::fs::File::open(cred_file())
3641
.ok()
3742
.and_then(|f| serde_json::from_reader(f).ok())
3843
}

syndterm/src/client/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use serde::{de::DeserializeOwned, Serialize};
77
use tracing::error;
88
use url::Url;
99

10-
use crate::{auth::Authentication, config, types};
10+
use crate::{auth::Credential, config, types};
1111

1212
use self::query::subscription::SubscriptionOutput;
1313

@@ -39,9 +39,9 @@ impl Client {
3939
})
4040
}
4141

42-
pub fn set_credential(&mut self, auth: Authentication) {
42+
pub fn set_credential(&mut self, auth: Credential) {
4343
let mut token = HeaderValue::try_from(match auth {
44-
Authentication::Github { access_token } => format!("github {access_token}"),
44+
Credential::Github { access_token } => format!("github {access_token}"),
4545
})
4646
.unwrap();
4747
token.set_sensitive(true);

syndterm/src/main.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ async fn main() {
6969
Application::new(terminal, client)
7070
};
7171

72-
if let Some(auth) = auth::authenticate_from_cache() {
72+
if let Some(auth) = auth::credential_from_cache() {
7373
info!("Use authentication cache");
74-
app.set_auth(auth);
74+
app.set_credential(auth);
7575
}
7676

7777
info!("Running...");

0 commit comments

Comments
 (0)