add sync, ui stuff

This commit is contained in:
cool-mist 2025-09-10 22:22:57 +05:30
parent 23f912d845
commit f30ef8b06c
5 changed files with 65 additions and 52 deletions

View File

@ -25,7 +25,7 @@ use debug::DebugWidget;
use futures::{FutureExt, StreamExt};
use ratatui::{
prelude::*,
widgets::{Block, Borders},
widgets::{Block, Borders, Padding},
};
use title::TitleWidget;
use tokio::sync::mpsc::UnboundedReceiver;
@ -86,7 +86,6 @@ pub enum UiCommandDispatchActions {
ListChannels(args::ListChannelArgs),
}
/// APP
/// - Listen Event
/// - Publish UiCommandDispatchActions
@ -142,7 +141,11 @@ pub async fn ui(args: &UiArgs, db_name: &str) -> Result<()> {
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();
std::thread::spawn(move || {
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 {
BackendEvent::ReloadState(channels) => {
state.channels = channels;
if state.highlighted_channel.is_none() && !state.channels.is_empty() {
state.highlighted_channel = Some(0);
}
}
},
Event::Tick => {}
@ -260,9 +266,9 @@ impl<'a> AppStateWidget<'a> {
impl<'a> Widget for AppStateWidget<'a> {
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) {
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
@ -282,24 +288,20 @@ impl<'a> Widget for AppStateWidget<'a> {
let main_area_splits = Layout::default()
.direction(Direction::Vertical)
.constraints(vec![
Constraint::Percentage(10), // Title
Constraint::Percentage(80), // Other app widgets
Constraint::Percentage(10), // Controls
Constraint::Percentage(100), // Channels + Articles
Constraint::Min(4), // Controls + Title
])
.split(main_area)
.to_vec();
// TITLE
let title_area = main_area_splits[0];
draw_app_widget_styled(Block::default(), &title_area, buf, TitleWidget);
// OTHER APP WIDGETS
let child_widgets_areas = Layout::default()
.direction(Direction::Horizontal)
.constraints(Constraint::from_percentages(vec![30, 70]))
.split(main_area_splits[1])
.constraints(Constraint::from_fills([4, 6]))
.split(main_area_splits[0])
.to_vec();
// CHANNELS
let channels_area = child_widgets_areas[0];
draw_app_widget_styled(
get_child_widget_style(
@ -311,6 +313,7 @@ impl<'a> Widget for AppStateWidget<'a> {
ChannelsWidget::new(self.app_state),
);
// ARTICLES
let articles_area = child_widgets_areas[1];
draw_app_widget_styled(
get_child_widget_style(
@ -322,8 +325,17 @@ impl<'a> Widget for AppStateWidget<'a> {
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
let controls_area = main_area_splits[2];
let controls_area = controls_title[1];
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> {
let title = Line::from(arg)
.centered()
.style(Style::default().fg(Color::Red).add_modifier(Modifier::BOLD));
.style(
Style::default()
.fg(Color::White)
.add_modifier(Modifier::BOLD),
)
.centered();
if focussed {
return Block::default()
.title_top(title)
.border_style(Style::default().fg(Color::Blue))
.borders(Borders::ALL);
return Block::default().title_top(title).bg(Color::DarkGray);
}
Block::default()
.title_top(title)
.fg(Color::DarkGray)
.padding(Padding::uniform(10))
.border_style(Style::default().fg(Color::DarkGray))
}

View File

@ -3,7 +3,7 @@ use ratatui::{
layout::{Alignment, Constraint, Direction, Layout, Rect},
style::{Color, Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, Paragraph, Widget},
widgets::{Block, Paragraph, Widget},
};
use super::AppState;
@ -26,10 +26,6 @@ impl<'a> Widget for ArticlesWidget<'a> {
};
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;
};
@ -42,7 +38,7 @@ impl<'a> Widget for ArticlesWidget<'a> {
let total_articles = channel.articles.len().min(total_articles as usize);
let article_rows = Layout::default()
.direction(Direction::Vertical)
.margin(1)
.margin(2)
.constraints(
(0..total_articles)
.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 {
if highlighted {
Style::default()
.bg(Color::LightYellow)
.bg(Color::White)
.add_modifier(Modifier::BOLD)
} else {
Style::default()

View File

@ -30,7 +30,7 @@ impl<'a> Widget for ChannelsWidget<'a> {
let total_channels = self.state.channels.len().min(total_channels as usize);
let channel_rows = Layout::default()
.direction(Direction::Vertical)
.margin(1)
.margin(2)
.constraints(
(0..total_channels)
.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 {
if highlighted {
Style::default()
.bg(Color::LightYellow)
.bg(Color::White)
.add_modifier(Modifier::BOLD)
} else {
Style::default()
@ -142,11 +142,11 @@ impl<'a> Widget for AddChannelWidget<'a> {
.title_top(Line::from("Add Channel").centered())
.title_style(
Style::default()
.fg(Color::Blue)
.add_modifier(Modifier::BOLD | Modifier::ITALIC | Modifier::UNDERLINED),
.fg(Color::White)
.add_modifier(Modifier::BOLD | Modifier::UNDERLINED),
)
.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);

View File

@ -78,7 +78,9 @@ macro_rules! control {
($key:literal) => {
Span::styled(
$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 {
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"),
description!(" to navigate up/down, "),
control!("h/l"),
@ -104,6 +106,14 @@ impl Widget for ControlsWidget {
description!(" delete an RSS channel, "),
control!("r"),
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"),
description!(" to exit"),
])
@ -111,10 +121,11 @@ impl Widget for ControlsWidget {
Style::default()
.fg(Color::DarkGray)
.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))
.alignment(Alignment::Center);
.alignment(Alignment::Left);
para.render(area, buf);
}
}

View File

@ -1,8 +1,8 @@
use ratatui::{
buffer::Buffer,
layout::{Alignment, Constraint, Layout, Rect},
layout::{Alignment, Rect},
style::{Color, Modifier, Style},
widgets::{Block, Paragraph, Widget},
widgets::{Block, Borders, Paragraph, Widget},
};
pub struct TitleWidget;
@ -10,21 +10,13 @@ pub struct TitleWidget;
impl Widget for TitleWidget {
fn render(self, area: Rect, buf: &mut Buffer) {
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(
Style::default()
.fg(Color::Black)
.fg(Color::White)
.add_modifier(Modifier::BOLD),
);
para.render(areas[1], buf);
Block::default()
.style(Style::default().bg(Color::LightCyan))
.render(area, buf);
para.render(area, buf);
Block::default().borders(Borders::RIGHT).render(area, buf);
}
}