add sync, ui stuff
This commit is contained in:
parent
23f912d845
commit
f30ef8b06c
58
src/ui.rs
58
src/ui.rs
@ -25,7 +25,7 @@ use debug::DebugWidget;
|
|||||||
use futures::{FutureExt, StreamExt};
|
use futures::{FutureExt, StreamExt};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
widgets::{Block, Borders},
|
widgets::{Block, Borders, Padding},
|
||||||
};
|
};
|
||||||
use title::TitleWidget;
|
use title::TitleWidget;
|
||||||
use tokio::sync::mpsc::UnboundedReceiver;
|
use tokio::sync::mpsc::UnboundedReceiver;
|
||||||
@ -86,7 +86,6 @@ pub enum UiCommandDispatchActions {
|
|||||||
ListChannels(args::ListChannelArgs),
|
ListChannels(args::ListChannelArgs),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// APP
|
/// APP
|
||||||
/// - Listen Event
|
/// - Listen Event
|
||||||
/// - Publish UiCommandDispatchActions
|
/// - Publish UiCommandDispatchActions
|
||||||
@ -142,7 +141,11 @@ pub async fn ui(args: &UiArgs, db_name: &str) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_backend(db_name: &str, app_recv: std::sync::mpsc::Receiver<UiCommandDispatchActions>, executor_dispatch: tokio::sync::mpsc::UnboundedSender<BackendEvent>) {
|
fn start_backend(
|
||||||
|
db_name: &str,
|
||||||
|
app_recv: std::sync::mpsc::Receiver<UiCommandDispatchActions>,
|
||||||
|
executor_dispatch: tokio::sync::mpsc::UnboundedSender<BackendEvent>,
|
||||||
|
) {
|
||||||
let db_name = db_name.to_string();
|
let db_name = db_name.to_string();
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
backend::start(db_name, app_recv, executor_dispatch);
|
backend::start(db_name, app_recv, executor_dispatch);
|
||||||
@ -227,6 +230,9 @@ async fn handle_events(state: &mut AppState) -> Result<()> {
|
|||||||
Event::BackendEvent(backend_event) => match backend_event {
|
Event::BackendEvent(backend_event) => match backend_event {
|
||||||
BackendEvent::ReloadState(channels) => {
|
BackendEvent::ReloadState(channels) => {
|
||||||
state.channels = channels;
|
state.channels = channels;
|
||||||
|
if state.highlighted_channel.is_none() && !state.channels.is_empty() {
|
||||||
|
state.highlighted_channel = Some(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Event::Tick => {}
|
Event::Tick => {}
|
||||||
@ -260,9 +266,9 @@ impl<'a> AppStateWidget<'a> {
|
|||||||
|
|
||||||
impl<'a> Widget for AppStateWidget<'a> {
|
impl<'a> Widget for AppStateWidget<'a> {
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
let mut horizontal_constraints = vec![Constraint::Percentage(100)];
|
let mut horizontal_constraints = vec![Constraint::Fill(5)];
|
||||||
if is_debug_mode(self.app_state) {
|
if is_debug_mode(self.app_state) {
|
||||||
horizontal_constraints.push(Constraint::Percentage(20));
|
horizontal_constraints.push(Constraint::Fill(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split the area into 2 horizontal sections, one for the main app and
|
// Split the area into 2 horizontal sections, one for the main app and
|
||||||
@ -282,24 +288,20 @@ impl<'a> Widget for AppStateWidget<'a> {
|
|||||||
let main_area_splits = Layout::default()
|
let main_area_splits = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints(vec![
|
.constraints(vec![
|
||||||
Constraint::Percentage(10), // Title
|
Constraint::Percentage(100), // Channels + Articles
|
||||||
Constraint::Percentage(80), // Other app widgets
|
Constraint::Min(4), // Controls + Title
|
||||||
Constraint::Percentage(10), // Controls
|
|
||||||
])
|
])
|
||||||
.split(main_area)
|
.split(main_area)
|
||||||
.to_vec();
|
.to_vec();
|
||||||
|
|
||||||
// TITLE
|
|
||||||
let title_area = main_area_splits[0];
|
|
||||||
draw_app_widget_styled(Block::default(), &title_area, buf, TitleWidget);
|
|
||||||
|
|
||||||
// OTHER APP WIDGETS
|
// OTHER APP WIDGETS
|
||||||
let child_widgets_areas = Layout::default()
|
let child_widgets_areas = Layout::default()
|
||||||
.direction(Direction::Horizontal)
|
.direction(Direction::Horizontal)
|
||||||
.constraints(Constraint::from_percentages(vec![30, 70]))
|
.constraints(Constraint::from_fills([4, 6]))
|
||||||
.split(main_area_splits[1])
|
.split(main_area_splits[0])
|
||||||
.to_vec();
|
.to_vec();
|
||||||
|
|
||||||
|
// CHANNELS
|
||||||
let channels_area = child_widgets_areas[0];
|
let channels_area = child_widgets_areas[0];
|
||||||
draw_app_widget_styled(
|
draw_app_widget_styled(
|
||||||
get_child_widget_style(
|
get_child_widget_style(
|
||||||
@ -311,6 +313,7 @@ impl<'a> Widget for AppStateWidget<'a> {
|
|||||||
ChannelsWidget::new(self.app_state),
|
ChannelsWidget::new(self.app_state),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// ARTICLES
|
||||||
let articles_area = child_widgets_areas[1];
|
let articles_area = child_widgets_areas[1];
|
||||||
draw_app_widget_styled(
|
draw_app_widget_styled(
|
||||||
get_child_widget_style(
|
get_child_widget_style(
|
||||||
@ -322,8 +325,17 @@ impl<'a> Widget for AppStateWidget<'a> {
|
|||||||
ArticlesWidget::new(self.app_state),
|
ArticlesWidget::new(self.app_state),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let controls_title = Layout::default()
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.constraints(Constraint::from_fills([1, 7]))
|
||||||
|
.split(main_area_splits[1]);
|
||||||
|
|
||||||
|
// TITLE
|
||||||
|
let title_area = controls_title[0];
|
||||||
|
draw_app_widget_styled(Block::default(), &title_area, buf, TitleWidget);
|
||||||
|
|
||||||
// CONTROLS
|
// CONTROLS
|
||||||
let controls_area = main_area_splits[2];
|
let controls_area = controls_title[1];
|
||||||
draw_app_widget_styled(Block::default(), &controls_area, buf, ControlsWidget);
|
draw_app_widget_styled(Block::default(), &controls_area, buf, ControlsWidget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -361,18 +373,20 @@ where
|
|||||||
|
|
||||||
fn get_child_widget_style<'a>(arg: &'a str, focussed: bool) -> Block<'a> {
|
fn get_child_widget_style<'a>(arg: &'a str, focussed: bool) -> Block<'a> {
|
||||||
let title = Line::from(arg)
|
let title = Line::from(arg)
|
||||||
.centered()
|
.style(
|
||||||
.style(Style::default().fg(Color::Red).add_modifier(Modifier::BOLD));
|
Style::default()
|
||||||
|
.fg(Color::White)
|
||||||
|
.add_modifier(Modifier::BOLD),
|
||||||
|
)
|
||||||
|
.centered();
|
||||||
if focussed {
|
if focussed {
|
||||||
return Block::default()
|
return Block::default().title_top(title).bg(Color::DarkGray);
|
||||||
.title_top(title)
|
|
||||||
.border_style(Style::default().fg(Color::Blue))
|
|
||||||
.borders(Borders::ALL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Block::default()
|
Block::default()
|
||||||
.title_top(title)
|
.title_top(title)
|
||||||
|
.fg(Color::DarkGray)
|
||||||
|
.padding(Padding::uniform(10))
|
||||||
.border_style(Style::default().fg(Color::DarkGray))
|
.border_style(Style::default().fg(Color::DarkGray))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@ use ratatui::{
|
|||||||
layout::{Alignment, Constraint, Direction, Layout, Rect},
|
layout::{Alignment, Constraint, Direction, Layout, Rect},
|
||||||
style::{Color, Modifier, Style},
|
style::{Color, Modifier, Style},
|
||||||
text::{Line, Span},
|
text::{Line, Span},
|
||||||
widgets::{Block, Borders, Paragraph, Widget},
|
widgets::{Block, Paragraph, Widget},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::AppState;
|
use super::AppState;
|
||||||
@ -26,10 +26,6 @@ impl<'a> Widget for ArticlesWidget<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let Some(channel) = selected_channel else {
|
let Some(channel) = selected_channel else {
|
||||||
let para = Paragraph::new("j/k to navigate channels, q to exit")
|
|
||||||
.block(Block::default().borders(Borders::NONE))
|
|
||||||
.alignment(Alignment::Center);
|
|
||||||
para.render(area, buf);
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -42,7 +38,7 @@ impl<'a> Widget for ArticlesWidget<'a> {
|
|||||||
let total_articles = channel.articles.len().min(total_articles as usize);
|
let total_articles = channel.articles.len().min(total_articles as usize);
|
||||||
let article_rows = Layout::default()
|
let article_rows = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.margin(1)
|
.margin(2)
|
||||||
.constraints(
|
.constraints(
|
||||||
(0..total_articles)
|
(0..total_articles)
|
||||||
.map(|_| Constraint::Length(height_per_entry))
|
.map(|_| Constraint::Length(height_per_entry))
|
||||||
@ -94,7 +90,7 @@ fn get_article_id_style(highlighted: bool) -> Style {
|
|||||||
fn get_channel_list_item_block_style(highlighted: bool) -> Style {
|
fn get_channel_list_item_block_style(highlighted: bool) -> Style {
|
||||||
if highlighted {
|
if highlighted {
|
||||||
Style::default()
|
Style::default()
|
||||||
.bg(Color::LightYellow)
|
.bg(Color::White)
|
||||||
.add_modifier(Modifier::BOLD)
|
.add_modifier(Modifier::BOLD)
|
||||||
} else {
|
} else {
|
||||||
Style::default()
|
Style::default()
|
||||||
|
|||||||
@ -30,7 +30,7 @@ impl<'a> Widget for ChannelsWidget<'a> {
|
|||||||
let total_channels = self.state.channels.len().min(total_channels as usize);
|
let total_channels = self.state.channels.len().min(total_channels as usize);
|
||||||
let channel_rows = Layout::default()
|
let channel_rows = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.margin(1)
|
.margin(2)
|
||||||
.constraints(
|
.constraints(
|
||||||
(0..total_channels)
|
(0..total_channels)
|
||||||
.map(|_| Constraint::Length(height_per_entry))
|
.map(|_| Constraint::Length(height_per_entry))
|
||||||
@ -109,7 +109,7 @@ fn get_channel_id_style(highlighted: bool) -> Style {
|
|||||||
fn get_channel_list_item_block_style(highlighted: bool) -> Style {
|
fn get_channel_list_item_block_style(highlighted: bool) -> Style {
|
||||||
if highlighted {
|
if highlighted {
|
||||||
Style::default()
|
Style::default()
|
||||||
.bg(Color::LightYellow)
|
.bg(Color::White)
|
||||||
.add_modifier(Modifier::BOLD)
|
.add_modifier(Modifier::BOLD)
|
||||||
} else {
|
} else {
|
||||||
Style::default()
|
Style::default()
|
||||||
@ -142,11 +142,11 @@ impl<'a> Widget for AddChannelWidget<'a> {
|
|||||||
.title_top(Line::from("Add Channel").centered())
|
.title_top(Line::from("Add Channel").centered())
|
||||||
.title_style(
|
.title_style(
|
||||||
Style::default()
|
Style::default()
|
||||||
.fg(Color::Blue)
|
.fg(Color::White)
|
||||||
.add_modifier(Modifier::BOLD | Modifier::ITALIC | Modifier::UNDERLINED),
|
.add_modifier(Modifier::BOLD | Modifier::UNDERLINED),
|
||||||
)
|
)
|
||||||
.borders(ratatui::widgets::Borders::ALL)
|
.borders(ratatui::widgets::Borders::ALL)
|
||||||
.border_style(Style::default().fg(Color::DarkGray));
|
.border_style(Style::default().fg(Color::White));
|
||||||
|
|
||||||
let para = Paragraph::new(Line::from(self.state)).block(block);
|
let para = Paragraph::new(Line::from(self.state)).block(block);
|
||||||
|
|
||||||
|
|||||||
@ -78,7 +78,9 @@ macro_rules! control {
|
|||||||
($key:literal) => {
|
($key:literal) => {
|
||||||
Span::styled(
|
Span::styled(
|
||||||
$key,
|
$key,
|
||||||
Style::default().fg(Color::Red).add_modifier(Modifier::BOLD),
|
Style::default()
|
||||||
|
.fg(Color::White)
|
||||||
|
.add_modifier(Modifier::BOLD),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -91,7 +93,7 @@ macro_rules! description {
|
|||||||
|
|
||||||
impl Widget for ControlsWidget {
|
impl Widget for ControlsWidget {
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
let controls_text = Line::from(vec![
|
let controls_text_line_1 = Line::from(vec![
|
||||||
control!("j/k"),
|
control!("j/k"),
|
||||||
description!(" to navigate up/down, "),
|
description!(" to navigate up/down, "),
|
||||||
control!("h/l"),
|
control!("h/l"),
|
||||||
@ -104,6 +106,14 @@ impl Widget for ControlsWidget {
|
|||||||
description!(" delete an RSS channel, "),
|
description!(" delete an RSS channel, "),
|
||||||
control!("r"),
|
control!("r"),
|
||||||
description!(" toggle read state of article, "),
|
description!(" toggle read state of article, "),
|
||||||
|
])
|
||||||
|
.style(
|
||||||
|
Style::default()
|
||||||
|
.fg(Color::DarkGray)
|
||||||
|
.add_modifier(Modifier::BOLD),
|
||||||
|
);
|
||||||
|
|
||||||
|
let controls_text_line_2 = Line::from(vec![
|
||||||
control!("q"),
|
control!("q"),
|
||||||
description!(" to exit"),
|
description!(" to exit"),
|
||||||
])
|
])
|
||||||
@ -111,10 +121,11 @@ impl Widget for ControlsWidget {
|
|||||||
Style::default()
|
Style::default()
|
||||||
.fg(Color::DarkGray)
|
.fg(Color::DarkGray)
|
||||||
.add_modifier(Modifier::BOLD),
|
.add_modifier(Modifier::BOLD),
|
||||||
);
|
)
|
||||||
let para = Paragraph::new(controls_text)
|
.centered();
|
||||||
|
let para = Paragraph::new(vec![controls_text_line_1, controls_text_line_2])
|
||||||
.block(Block::default().borders(Borders::NONE))
|
.block(Block::default().borders(Borders::NONE))
|
||||||
.alignment(Alignment::Center);
|
.alignment(Alignment::Left);
|
||||||
para.render(area, buf);
|
para.render(area, buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
use ratatui::{
|
use ratatui::{
|
||||||
buffer::Buffer,
|
buffer::Buffer,
|
||||||
layout::{Alignment, Constraint, Layout, Rect},
|
layout::{Alignment, Rect},
|
||||||
style::{Color, Modifier, Style},
|
style::{Color, Modifier, Style},
|
||||||
widgets::{Block, Paragraph, Widget},
|
widgets::{Block, Borders, Paragraph, Widget},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct TitleWidget;
|
pub struct TitleWidget;
|
||||||
@ -10,21 +10,13 @@ pub struct TitleWidget;
|
|||||||
impl Widget for TitleWidget {
|
impl Widget for TitleWidget {
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
let title = "Terminal RSS Manager";
|
let title = "Terminal RSS Manager";
|
||||||
let areas = Layout::default()
|
|
||||||
.constraints(Constraint::from_ratios([(1, 3), (1, 3), (1, 3)]))
|
|
||||||
.split(area)
|
|
||||||
.to_vec();
|
|
||||||
|
|
||||||
let para = Paragraph::new(title).alignment(Alignment::Center).style(
|
let para = Paragraph::new(title).alignment(Alignment::Center).style(
|
||||||
Style::default()
|
Style::default()
|
||||||
.fg(Color::Black)
|
.fg(Color::White)
|
||||||
.add_modifier(Modifier::BOLD),
|
.add_modifier(Modifier::BOLD),
|
||||||
);
|
);
|
||||||
|
|
||||||
para.render(areas[1], buf);
|
para.render(area, buf);
|
||||||
|
Block::default().borders(Borders::RIGHT).render(area, buf);
|
||||||
Block::default()
|
|
||||||
.style(Style::default().bg(Color::LightCyan))
|
|
||||||
.render(area, buf);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user