Skip to content

Commit

Permalink
Async migration ok
Browse files Browse the repository at this point in the history
  • Loading branch information
veeso committed Nov 15, 2021
1 parent 9f969da commit e87d13f
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 71 deletions.
6 changes: 3 additions & 3 deletions src/feed/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ impl From<RssEntry> for Article {
.map(|x| str_helpers::strip_html_tags(x.content.as_str()))
.unwrap_or_default(),
url: entry
.content
.map(|x| x.src.map(|x| x.href))
.flatten()
.links
.get(0)
.map(|x| x.href.clone())
.unwrap_or(entry.id),
date: entry.updated.map(DateTime::<Local>::from),
}
Expand Down
40 changes: 29 additions & 11 deletions src/ui/components/lists/feed_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ use tuirealm::tui::{
use tuirealm::{Frame, MockComponent, State};

const SEQUENCE: [char; 8] = ['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'];
pub const FEED_LIST_PROP_ITEMS: &str = "items";

pub const FEED_STATE_ERROR: u8 = 1;
pub const FEED_STATE_LOADING: u8 = 2;
pub const FEED_STATE_SUCCESS: u8 = 0;

struct OwnStates {
step: usize,
Expand Down Expand Up @@ -88,22 +93,17 @@ impl FeedList {
.color(Color::LightBlue)
.modifiers(BorderType::Rounded),
)
.rows(
[..items.len()]
.iter()
.map(|_| vec![TextSpan::new("")])
.collect(),
),
.rows((0..items.len()).map(|_| vec![TextSpan::new("")]).collect()),
items,
states: OwnStates::default(),
}
}

fn feed_state_to_span(state: &FlatFeedState, loading_step: char) -> Span {
match state {
&FlatFeedState::Success => Span::from(" "),
&FlatFeedState::Loading => Span::from(format!("{} ", loading_step)),
&FlatFeedState::Error => Span::styled(
match *state {
FlatFeedState::Success => Span::from(" "),
FlatFeedState::Loading => Span::from(format!("{} ", loading_step)),
FlatFeedState::Error => Span::styled(
"✘ ",
Style::default()
.fg(Color::Red)
Expand Down Expand Up @@ -159,7 +159,25 @@ impl MockComponent for FeedList {
}

fn attr(&mut self, attr: Attribute, value: AttrValue) {
self.list.attr(attr, value)
if matches!(attr, Attribute::Custom(FEED_LIST_PROP_ITEMS)) {
let (name, state) = value.unwrap_payload().unwrap_tup2();
let name = name.unwrap_str();
let state = state.unwrap_u8();
let state = match state {
FEED_STATE_ERROR => FlatFeedState::Error,
FEED_STATE_LOADING => FlatFeedState::Loading,
FEED_STATE_SUCCESS => FlatFeedState::Success,
_ => panic!("Invalid state {}", state),
};
for (i_name, i_state) in self.items.iter_mut() {
if i_name == &name {
*i_state = state;
break;
}
}
} else {
self.list.attr(attr, value)
}
}

fn state(&self) -> State {
Expand Down
4 changes: 4 additions & 0 deletions src/ui/components/lists/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
*/
mod feed_list;

pub use feed_list::{
FEED_LIST_PROP_ITEMS, FEED_STATE_ERROR, FEED_STATE_LOADING, FEED_STATE_SUCCESS,
};

use crate::ui::lib::FlatFeedState;

use super::Msg;
Expand Down
2 changes: 1 addition & 1 deletion src/ui/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
use super::Msg;

mod article;
mod lists;
pub mod lists;
mod popups;

pub use article::{ArticleAuthors, ArticleDate, ArticleLink, ArticleSummary, ArticleTitle};
Expand Down
8 changes: 4 additions & 4 deletions src/ui/lib/kiosk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,10 @@ impl Kiosk {

impl From<&FeedState> for FlatFeedState {
fn from(f: &FeedState) -> Self {
match f {
&FeedState::Error(_) => Self::Error,
&FeedState::Loading => Self::Loading,
&FeedState::Success(_) => Self::Success,
match *f {
FeedState::Error(_) => Self::Error,
FeedState::Loading => Self::Loading,
FeedState::Success(_) => Self::Success,
}
}
}
Expand Down
114 changes: 71 additions & 43 deletions src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,15 @@ use std::time::Duration;
use tuirealm::{
application::PollStrategy,
event::{Key, KeyEvent, KeyModifiers},
props::{PropPayload, PropValue},
terminal::TerminalBridge,
Application, Attribute, EventListenerCfg, NoUserEvent, Sub, SubClause, SubEventClause,
Application, AttrValue, Attribute, EventListenerCfg, NoUserEvent, Sub, SubClause,
SubEventClause,
};

const FORCED_REDRAW_INTERVAL: Duration = Duration::from_millis(100);
use self::lib::FlatFeedState;

const FORCED_REDRAW_INTERVAL: Duration = Duration::from_millis(50);

/// ## Id
///
Expand Down Expand Up @@ -103,7 +107,7 @@ impl Ui {
///
/// Instantiates a new Ui
pub fn new(config: Config, tick: u64) -> Self {
let model = Model::new(Self::init_terminal());
let model = Model::new(&config, Self::init_terminal());
let app = Self::init_application(&model, tick);
Self {
config,
Expand All @@ -130,7 +134,7 @@ impl Ui {
// Run tasks
self.run_tasks();
// Check whether to force redraw
self.should_force_redraw();
self.check_force_redraw();
// View
self.model.view(&mut self.app);
}
Expand All @@ -157,8 +161,10 @@ impl Ui {
}
}

/// ### should_force_redraw
fn should_force_redraw(&mut self) {
/// ### check_force_redraw
///
/// Check whether should force redraw
fn check_force_redraw(&mut self) {
// If source are loading and at least 100ms has elapsed since last redraw...
if self.client.running() && self.model.since_last_redraw() >= FORCED_REDRAW_INTERVAL {
self.model.force_redraw();
Expand All @@ -170,6 +176,7 @@ impl Ui {
/// ### fetch_all_sources
///
/// Fetch all sources and update Ui
#[allow(clippy::needless_collect)]
fn fetch_all_sources(&mut self) {
// Fetch sources
let sources: Vec<(String, String)> = self
Expand All @@ -190,6 +197,9 @@ impl Ui {
self.client.fetch(name, uri);
// Mark source as Loading
self.model.update_source(name, FeedState::Loading);
self.update_feed_list(name, FlatFeedState::Loading);
// Force redraw
self.model.force_redraw();
}

/// ### poll_fetched_sources
Expand All @@ -202,24 +212,41 @@ impl Ui {
Ok(feed) => FeedState::Success(feed),
Err(err) => {
// Mount error and return err
self.mount_error_popup(format!(r#"Could not fetch feed "{}"": {}"#, name, err));
self.mount_error_popup(format!(r#"Could not fetch feed "{}": {}"#, name, err));
FeedState::Error(err)
}
};
// Update source
let flat_state = FlatFeedState::from(&state);
self.model.update_source(name.as_str(), state);
// Update feed list and initialize article
self.update_feed_list();
self.update_feed_list(name.as_str(), flat_state);
if self.is_article_list_empty() {
self.init_article();
}
// Force redraw
self.model.force_redraw();
}
}

fn update_feed_list(&mut self) {
fn update_feed_list(&mut self, name: &str, state: FlatFeedState) {
// Update item
let state = match state {
FlatFeedState::Error => components::lists::FEED_STATE_ERROR,
FlatFeedState::Loading => components::lists::FEED_STATE_LOADING,
FlatFeedState::Success => components::lists::FEED_STATE_SUCCESS,
};
let prop_value = AttrValue::Payload(PropPayload::Tup2((
PropValue::Str(name.to_string()),
PropValue::U8(state),
)));
assert!(self
.app
.remount(Id::FeedList, Box::new(self.model.get_feed_list()), vec![])
.attr(
&Id::FeedList,
Attribute::Custom(components::lists::FEED_LIST_PROP_ITEMS),
prop_value
)
.is_ok());
}

Expand All @@ -231,41 +258,42 @@ impl Ui {
/// This function should be called only if article list is empty
fn init_article(&mut self) {
if let Some(source) = self.model.sorted_sources().get(0) {
let feed = self.model.kiosk().get_feed(source.as_str()).unwrap();
assert!(self
.app
.remount(
Id::ArticleList,
Box::new(Model::get_article_list(
feed,
self.model.max_article_name_len()
)),
vec![]
)
.is_ok());
// Mount first article
if let Some(article) = feed.articles().next() {
let (authors, date, link, summary, title) = Model::get_article_view(article);
if let Some(feed) = self.model.kiosk().get_feed(source.as_str()) {
assert!(self
.app
.remount(Id::ArticleAuthors, Box::new(authors), vec![])
.is_ok());
assert!(self
.app
.remount(Id::ArticleDate, Box::new(date), vec![])
.is_ok());
assert!(self
.app
.remount(Id::ArticleLink, Box::new(link), vec![])
.is_ok());
assert!(self
.app
.remount(Id::ArticleSummary, Box::new(summary), vec![])
.is_ok());
assert!(self
.app
.remount(Id::ArticleTitle, Box::new(title), vec![])
.remount(
Id::ArticleList,
Box::new(Model::get_article_list(
feed,
self.model.max_article_name_len()
)),
vec![]
)
.is_ok());
// Mount first article
if let Some(article) = feed.articles().next() {
let (authors, date, link, summary, title) = Model::get_article_view(article);
assert!(self
.app
.remount(Id::ArticleAuthors, Box::new(authors), vec![])
.is_ok());
assert!(self
.app
.remount(Id::ArticleDate, Box::new(date), vec![])
.is_ok());
assert!(self
.app
.remount(Id::ArticleLink, Box::new(link), vec![])
.is_ok());
assert!(self
.app
.remount(Id::ArticleSummary, Box::new(summary), vec![])
.is_ok());
assert!(self
.app
.remount(Id::ArticleTitle, Box::new(title), vec![])
.is_ok());
}
}
}
}
Expand All @@ -278,7 +306,7 @@ impl Ui {
.query(&Id::ArticleList, Attribute::Content)
.ok()
.flatten()
.map(|x| x.unwrap_table().len() == 0)
.map(|x| x.unwrap_table().is_empty())
.unwrap_or(true)
}

Expand Down
25 changes: 16 additions & 9 deletions src/ui/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ use crate::feed::{Article, Feed};
use crate::helpers::open as open_helpers;
use crate::helpers::strings as str_helpers;
use crate::helpers::ui as ui_helpers;
use crate::Config;

use std::time::{Duration, Instant};
use tuirealm::terminal::TerminalBridge;
Expand All @@ -56,9 +57,14 @@ impl Model {
/// ### new
///
/// Instantiates a new `Model`
pub fn new(terminal: TerminalBridge) -> Self {
pub fn new(config: &Config, terminal: TerminalBridge) -> Self {
// Initialize kiosk
let mut kiosk = Kiosk::default();
for name in config.sources.keys() {
kiosk.insert_feed(name, FeedState::Loading);
}
Self {
kiosk: Kiosk::default(),
kiosk,
last_redraw: Instant::now(),
quit: false,
redraw: true,
Expand Down Expand Up @@ -209,7 +215,7 @@ impl Model {
f.render_widget(Clear, popup);
app.view(&Id::QuitPopup, f, popup);
} else if app.mounted(&Id::ErrorPopup) {
let popup = ui_helpers::draw_area_in(f.size(), 50, 10);
let popup = ui_helpers::draw_area_in(f.size(), 50, 15);
f.render_widget(Clear, popup);
app.view(&Id::ErrorPopup, f, popup);
}
Expand Down Expand Up @@ -268,7 +274,7 @@ impl Model {
pub fn get_feed_list(&self) -> FeedList {
let mut sources = self.kiosk.get_state();
sources.sort_by(|a, b| a.0.cmp(&b.0));
FeedList::new(self.kiosk.get_state())
FeedList::new(sources)
}

/// ### view_quit
Expand Down Expand Up @@ -359,11 +365,12 @@ impl Update<Id, Msg, NoUserEvent> for Model {
}
Msg::FeedChanged(feed) => {
let feed = &(*self.sorted_sources().get(feed).unwrap()).clone();
let feed = self.kiosk.get_feed(feed.as_str()).unwrap();
let articles = Self::get_article_list(feed, self.max_article_name_len());
assert!(view.remount(Id::ArticleList, Box::new(articles)).is_ok());
// Then load the first article of feed
self.update_article(view, 0);
if let Some(feed) = self.kiosk.get_feed(feed.as_str()) {
let articles = Self::get_article_list(feed, self.max_article_name_len());
assert!(view.remount(Id::ArticleList, Box::new(articles)).is_ok());
// Then load the first article of feed
self.update_article(view, 0);
}
}
Msg::FeedListBlur => {
assert!(view.active(&Id::ArticleList).is_ok());
Expand Down

0 comments on commit e87d13f

Please sign in to comment.