Hammond-gtk: Stack, Content constructors return Results now.

Constructors now proxy underlying errors that migth occur during initialazation.
I think that's about the last unwraps in the main thread.
This commit is contained in:
Jordan Petridis 2018-02-07 03:17:37 +02:00
parent 89564996df
commit 2d33606251
No known key found for this signature in database
GPG Key ID: CEABAD9F5683B9A6
7 changed files with 92 additions and 61 deletions

View File

@ -65,7 +65,8 @@ impl App {
let (sender, receiver) = channel(); let (sender, receiver) = channel();
// Create a content instance // Create a content instance
let content = Arc::new(Content::new(sender.clone())); let content =
Arc::new(Content::new(sender.clone()).expect("Content Initialization failed."));
// Create the headerbar // Create the headerbar
let header = Arc::new(Header::new(content.clone(), &window, sender.clone())); let header = Arc::new(Header::new(content.clone(), &window, sender.clone()));
@ -87,7 +88,7 @@ impl App {
let sender = self.sender.clone(); let sender = self.sender.clone();
// Update the feeds right after the Application is initialized. // Update the feeds right after the Application is initialized.
gtk::timeout_add_seconds(2, move || { gtk::timeout_add_seconds(2, move || {
utils::refresh_feed(None, sender.clone()); utils::refresh_feed_wrapper(None, sender.clone());
glib::Continue(false) glib::Continue(false)
}); });
@ -95,7 +96,7 @@ impl App {
// Auto-updater, runs every hour. // Auto-updater, runs every hour.
// TODO: expose the interval in which it run to a user setting. // TODO: expose the interval in which it run to a user setting.
gtk::timeout_add_seconds(3600, move || { gtk::timeout_add_seconds(3600, move || {
utils::refresh_feed(None, sender.clone()); utils::refresh_feed_wrapper(None, sender.clone());
glib::Continue(true) glib::Continue(true)
}); });
@ -122,9 +123,9 @@ impl App {
match receiver.recv_timeout(Duration::from_millis(10)) { match receiver.recv_timeout(Duration::from_millis(10)) {
Ok(Action::UpdateSources(source)) => { Ok(Action::UpdateSources(source)) => {
if let Some(s) = source { if let Some(s) = source {
utils::refresh_feed(Some(vec![s]), sender.clone()); utils::refresh_feed_wrapper(Some(vec![s]), sender.clone());
} else { } else {
utils::refresh_feed(None, sender.clone()); utils::refresh_feed_wrapper(None, sender.clone());
} }
} }
Ok(Action::RefreshAllViews) => content.update(), Ok(Action::RefreshAllViews) => content.update(),

View File

@ -26,20 +26,20 @@ pub struct Content {
} }
impl Content { impl Content {
pub fn new(sender: Sender<Action>) -> Content { pub fn new(sender: Sender<Action>) -> Result<Content, Error> {
let stack = gtk::Stack::new(); let stack = gtk::Stack::new();
let episodes = Arc::new(EpisodeStack::new(sender.clone())); let episodes = Arc::new(EpisodeStack::new(sender.clone())?);
let shows = Arc::new(ShowStack::new(sender.clone())); let shows = Arc::new(ShowStack::new(sender.clone())?);
stack.add_titled(&episodes.stack, "episodes", "Episodes"); stack.add_titled(&episodes.stack, "episodes", "Episodes");
stack.add_titled(&shows.stack, "shows", "Shows"); stack.add_titled(&shows.stack, "shows", "Shows");
Content { Ok(Content {
stack, stack,
shows, shows,
episodes, episodes,
sender, sender,
} })
} }
pub fn update(&self) { pub fn update(&self) {
@ -107,7 +107,7 @@ pub struct ShowStack {
} }
impl ShowStack { impl ShowStack {
fn new(sender: Sender<Action>) -> ShowStack { fn new(sender: Sender<Action>) -> Result<ShowStack, Error> {
let stack = gtk::Stack::new(); let stack = gtk::Stack::new();
let show = ShowStack { let show = ShowStack {
@ -115,7 +115,7 @@ impl ShowStack {
sender: sender.clone(), sender: sender.clone(),
}; };
let pop = ShowsPopulated::new(sender.clone()); let pop = ShowsPopulated::new(sender.clone())?;
let widget = ShowWidget::default(); let widget = ShowWidget::default();
let empty = EmptyView::new(); let empty = EmptyView::new();
@ -129,7 +129,7 @@ impl ShowStack {
show.stack.set_visible_child_name("podcasts") show.stack.set_visible_child_name("podcasts")
} }
show Ok(show)
} }
// pub fn update(&self) { // pub fn update(&self) {
@ -138,7 +138,9 @@ impl ShowStack {
// } // }
pub fn update_podcasts(&self) -> Result<(), Error> { pub fn update_podcasts(&self) -> Result<(), Error> {
let vis = self.stack.get_visible_child_name().unwrap(); let vis = self.stack
.get_visible_child_name()
.ok_or_else(|| format_err!("Failed to get visible child name."))?;
let old = self.stack let old = self.stack
.get_child_by_name("podcasts") .get_child_by_name("podcasts")
@ -155,7 +157,7 @@ impl ShowStack {
.map_err(|_| format_err!("Failed to downcast stack child to a ScrolledWindow."))?; .map_err(|_| format_err!("Failed to downcast stack child to a ScrolledWindow."))?;
debug!("Name: {:?}", WidgetExt::get_name(&scrolled_window)); debug!("Name: {:?}", WidgetExt::get_name(&scrolled_window));
let pop = ShowsPopulated::new(self.sender.clone()); let pop = ShowsPopulated::new(self.sender.clone())?;
// Copy the vertical scrollbar adjustment from the old view into the new one. // Copy the vertical scrollbar adjustment from the old view into the new one.
scrolled_window scrolled_window
.get_vadjustment() .get_vadjustment()
@ -269,8 +271,8 @@ pub struct EpisodeStack {
} }
impl EpisodeStack { impl EpisodeStack {
fn new(sender: Sender<Action>) -> EpisodeStack { fn new(sender: Sender<Action>) -> Result<EpisodeStack, Error> {
let episodes = EpisodesView::new(sender.clone()); let episodes = EpisodesView::new(sender.clone())?;
let empty = EmptyView::new(); let empty = EmptyView::new();
let stack = gtk::Stack::new(); let stack = gtk::Stack::new();
@ -283,7 +285,7 @@ impl EpisodeStack {
stack.set_visible_child_name("episodes"); stack.set_visible_child_name("episodes");
} }
EpisodeStack { stack, sender } Ok(EpisodeStack { stack, sender })
} }
// Look into refactoring to a state-machine. // Look into refactoring to a state-machine.
@ -303,7 +305,7 @@ impl EpisodeStack {
.map_err(|_| format_err!("Failed to downcast stack child to a ScrolledWindow."))?; .map_err(|_| format_err!("Failed to downcast stack child to a ScrolledWindow."))?;
debug!("Name: {:?}", WidgetExt::get_name(&scrolled_window)); debug!("Name: {:?}", WidgetExt::get_name(&scrolled_window));
let eps = EpisodesView::new(self.sender.clone()); let eps = EpisodesView::new(self.sender.clone())?;
// Copy the vertical scrollbar adjustment from the old view into the new one. // Copy the vertical scrollbar adjustment from the old view into the new one.
scrolled_window scrolled_window
.get_vadjustment() .get_vadjustment()

View File

@ -90,7 +90,9 @@ impl Header {
self.add_toggle.set_popover(&add_popover); self.add_toggle.set_popover(&add_popover);
self.update_button.connect_clicked(move |_| { self.update_button.connect_clicked(move |_| {
sender.send(Action::UpdateSources(None)).unwrap(); sender
.send(Action::UpdateSources(None))
.expect("Action channel blew up.");
}); });
self.about_button self.about_button

View File

@ -112,8 +112,12 @@ pub fn add(id: i32, directory: &str, sender: Sender<Action>) -> Result<(), Error
// } // }
// } // }
sender.send(Action::RefreshEpisodesView).unwrap(); sender
sender.send(Action::RefreshWidgetIfSame(pid)).unwrap(); .send(Action::RefreshEpisodesView)
.expect("Action channel blew up.");
sender
.send(Action::RefreshWidgetIfSame(pid))
.expect("Action channel blew up.");
} }
}); });

View File

@ -17,14 +17,23 @@ use std::thread;
use app::Action; use app::Action;
pub fn refresh_feed_wrapper(source: Option<Vec<Source>>, sender: Sender<Action>) {
if let Err(err) = refresh_feed(source, sender) {
error!("An error occured while trying to update the feeds.");
error!("Error: {}", err);
}
}
/// Update the rss feed(s) originating from `source`. /// Update the rss feed(s) originating from `source`.
/// If `source` is None, Fetches all the `Source` entries in the database and updates them. /// If `source` is None, Fetches all the `Source` entries in the database and updates them.
/// When It's done,it queues up a `RefreshViews` action. /// When It's done,it queues up a `RefreshViews` action.
pub fn refresh_feed(source: Option<Vec<Source>>, sender: Sender<Action>) { fn refresh_feed(source: Option<Vec<Source>>, sender: Sender<Action>) -> Result<(), Error> {
sender.send(Action::HeaderBarShowUpdateIndicator).unwrap(); sender.send(Action::HeaderBarShowUpdateIndicator)?;
thread::spawn(move || { thread::spawn(move || {
let mut sources = source.unwrap_or_else(|| dbqueries::get_sources().unwrap()); let mut sources = source.unwrap_or_else(|| {
dbqueries::get_sources().expect("Failed to retrieve Sources from the database.")
});
// Work around to improve the feed addition experience. // Work around to improve the feed addition experience.
// Many times links to rss feeds are just redirects(usually to an https version). // Many times links to rss feeds are just redirects(usually to an https version).
@ -40,11 +49,11 @@ pub fn refresh_feed(source: Option<Vec<Source>>, sender: Sender<Action>) {
if let Err(err) = pipeline::index_single_source(source, false) { if let Err(err) = pipeline::index_single_source(source, false) {
error!("Error While trying to update the database."); error!("Error While trying to update the database.");
error!("Error msg: {}", err); error!("Error msg: {}", err);
let source = dbqueries::get_source_from_id(id).unwrap(); if let Ok(source) = dbqueries::get_source_from_id(id) {
if let Err(err) = pipeline::index_single_source(source, false) {
if let Err(err) = pipeline::index_single_source(source, false) { error!("Error While trying to update the database.");
error!("Error While trying to update the database."); error!("Error msg: {}", err);
error!("Error msg: {}", err); }
} }
} }
} else { } else {
@ -55,9 +64,14 @@ pub fn refresh_feed(source: Option<Vec<Source>>, sender: Sender<Action>) {
} }
} }
sender.send(Action::HeaderBarHideUpdateIndicator).unwrap(); sender
sender.send(Action::RefreshAllViews).unwrap(); .send(Action::HeaderBarHideUpdateIndicator)
.expect("Action channel blew up.");
sender
.send(Action::RefreshAllViews)
.expect("Action channel blew up.");
}); });
Ok(())
} }
lazy_static! { lazy_static! {

View File

@ -75,9 +75,9 @@ impl Default for EpisodesView {
// TODO: REFACTOR ME // TODO: REFACTOR ME
impl EpisodesView { impl EpisodesView {
pub fn new(sender: Sender<Action>) -> EpisodesView { pub fn new(sender: Sender<Action>) -> Result<EpisodesView, Error> {
let view = EpisodesView::default(); let view = EpisodesView::default();
let episodes = dbqueries::get_episodes_widgets_with_limit(50).unwrap(); let episodes = dbqueries::get_episodes_widgets_with_limit(50)?;
let now_utc = Utc::now(); let now_utc = Utc::now();
episodes.into_iter().for_each(|mut ep| { episodes.into_iter().for_each(|mut ep| {
@ -124,7 +124,7 @@ impl EpisodesView {
} }
view.container.show_all(); view.container.show_all();
view Ok(view)
} }
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {

View File

@ -33,41 +33,34 @@ impl Default for ShowsPopulated {
} }
impl ShowsPopulated { impl ShowsPopulated {
pub fn new(sender: Sender<Action>) -> ShowsPopulated { pub fn new(sender: Sender<Action>) -> Result<ShowsPopulated, Error> {
let pop = ShowsPopulated::default(); let pop = ShowsPopulated::default();
pop.init(sender); pop.init(sender)?;
pop Ok(pop)
} }
pub fn init(&self, sender: Sender<Action>) { pub fn init(&self, sender: Sender<Action>) -> Result<(), Error> {
use gtk::WidgetExt;
// TODO: handle unwraps.
self.flowbox.connect_child_activated(move |_, child| { self.flowbox.connect_child_activated(move |_, child| {
// This is such an ugly hack... if let Err(err) = on_child_activate(child, sender.clone()) {
let id = WidgetExt::get_name(child).unwrap().parse::<i32>().unwrap(); error!(
let pd = dbqueries::get_podcast_from_id(id).unwrap(); "Something went wrong during flowbox child activation: {}.",
err
sender )
.send(Action::HeaderBarShowTile(pd.title().into())) };
.unwrap();
sender.send(Action::ReplaceWidget(pd)).unwrap();
sender.send(Action::ShowWidgetAnimated).unwrap();
}); });
// Populate the flowbox with the Podcasts. // Populate the flowbox with the Podcasts.
self.populate_flowbox(); self.populate_flowbox()
} }
fn populate_flowbox(&self) { fn populate_flowbox(&self) -> Result<(), Error> {
let podcasts = dbqueries::get_podcasts(); let podcasts = dbqueries::get_podcasts()?;
if let Ok(pds) = podcasts { podcasts.iter().for_each(|parent| {
pds.iter().for_each(|parent| { let flowbox_child = ShowsChild::new(parent);
let flowbox_child = ShowsChild::new(parent); self.flowbox.add(&flowbox_child.child);
self.flowbox.add(&flowbox_child.child); });
}); self.flowbox.show_all();
self.flowbox.show_all(); Ok(())
}
} }
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
@ -80,6 +73,21 @@ impl ShowsPopulated {
} }
} }
fn on_child_activate(child: &gtk::FlowBoxChild, sender: Sender<Action>) -> Result<(), Error> {
use gtk::WidgetExt;
// This is such an ugly hack...
let id = WidgetExt::get_name(child)
.ok_or_else(|| format_err!("Faild to get \"episodes\" child from the stack."))?
.parse::<i32>()?;
let pd = dbqueries::get_podcast_from_id(id)?;
sender.send(Action::HeaderBarShowTile(pd.title().into()))?;
sender.send(Action::ReplaceWidget(pd))?;
sender.send(Action::ShowWidgetAnimated)?;
Ok(())
}
#[derive(Debug)] #[derive(Debug)]
struct ShowsChild { struct ShowsChild {
container: gtk::Box, container: gtk::Box,