h-gtk: Wire the import_shows button on the hamburger menu to the the opml import.

This commit is contained in:
Jordan Petridis 2018-05-12 22:55:35 +03:00
parent 2d8164cf0a
commit 00e747eb5f
No known key found for this signature in database
GPG Key ID: CEABAD9F5683B9A6
3 changed files with 76 additions and 9 deletions

View File

@ -7,7 +7,9 @@ use models::Source;
use xml::reader; use xml::reader;
use std::collections::HashSet; use std::collections::HashSet;
use std::fs;
use std::io::Read; use std::io::Read;
use std::path::Path;
// use std::fs::{File, OpenOptions}; // use std::fs::{File, OpenOptions};
// use std::io::BufReader; // use std::io::BufReader;
@ -25,15 +27,35 @@ pub struct Opml {
} }
/// Import feed url's from a `R` into the `Source` table. /// Import feed url's from a `R` into the `Source` table.
pub fn opml_import<R: Read>(reader: R) -> Result<Vec<Result<Source, DataError>>, reader::Error> { // TODO: Write test
pub fn import_to_db<R: Read>(reader: R) -> Result<Vec<Source>, reader::Error> {
let feeds = extract_sources(reader)? let feeds = extract_sources(reader)?
.iter() .iter()
.map(|opml| Source::from_url(&opml.url)) .map(|opml| Source::from_url(&opml.url))
.filter_map(|s| {
if let Err(ref err) = s {
let txt = "If you think this might be a bug please consider filling a report over \
at https://gitlab.gnome.org/World/hammond/issues/new";
error!("Failed to import a Show: {}", err);
error!("{}", txt);
}
s.ok()
})
.collect(); .collect();
Ok(feeds) Ok(feeds)
} }
/// Open a File from `P`, try to parse the OPML then insert the Feeds in the database and
/// return the new `Source`s
// TODO: Write test
pub fn import_from_file<P: AsRef<Path>>(path: P) -> Result<Vec<Source>, DataError> {
let content = fs::read_to_string(path)?;
import_to_db(content.as_bytes()).map_err(From::from)
}
/// Extracts the `outline` elemnts from a reader `R` and returns a `HashSet` of `Opml` structs. /// Extracts the `outline` elemnts from a reader `R` and returns a `HashSet` of `Opml` structs.
pub fn extract_sources<R: Read>(reader: R) -> Result<HashSet<Opml>, reader::Error> { pub fn extract_sources<R: Read>(reader: R) -> Result<HashSet<Opml>, reader::Error> {
let mut list = HashSet::new(); let mut list = HashSet::new();

View File

@ -323,9 +323,8 @@ Tobias Bernard
<child> <child>
<object class="GtkModelButton" id="import"> <object class="GtkModelButton" id="import">
<property name="visible">True</property> <property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">True</property>
<property name="text" translatable="yes">Import Shows</property> <property name="text" translatable="yes">Import Shows</property>
</object> </object>
<packing> <packing>

View File

@ -4,16 +4,16 @@ use gtk::prelude::*;
use failure::Error; use failure::Error;
use failure::ResultExt; use failure::ResultExt;
use rayon;
use url::Url; use url::Url;
use hammond_data::dbqueries; use hammond_data::{dbqueries, opml, Source};
use hammond_data::Source;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use app::Action; use app::Action;
use stacks::Content; use stacks::Content;
use utils::{itunes_to_rss, refresh}; use utils::{self, itunes_to_rss, refresh};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
// TODO: split this into smaller // TODO: split this into smaller
@ -107,9 +107,12 @@ impl Header {
})); }));
})); }));
self.about.connect_clicked(clone!(window => move |_| { self.about
about_dialog(&window); .connect_clicked(clone!(window => move |_| about_dialog(&window)));
}));
self.import.connect_clicked(
clone!(window, sender => move |_| on_import_clicked(&window, &sender)),
);
// Add the Headerbar to the window. // Add the Headerbar to the window.
window.set_titlebar(&self.container); window.set_titlebar(&self.container);
@ -226,6 +229,49 @@ fn on_url_change(
} }
} }
fn on_import_clicked(window: &gtk::Window, sender: &Sender<Action>) {
use glib::translate::ToGlib;
use gtk::{FileChooserAction, FileChooserDialog, ResponseType};
// let dialog = FileChooserDialog::new(title, Some(&window), FileChooserAction::Open);
// TODO: It might be better to use a FileChooserNative widget.
// Create the FileChooser Dialog
let dialog = FileChooserDialog::with_buttons(
Some("Select the file from which to you want to Import Shows."),
Some(window),
FileChooserAction::Open,
&[
("_Cancel", ResponseType::Cancel),
("_Open", ResponseType::Accept),
],
);
dialog.connect_response(clone!(sender => move |dialog, resp| {
debug!("Dialong Response {}", resp);
if resp == ResponseType::Accept.to_glib() {
// TODO: Show an in-app notifictaion if the file can not be accessed
if let Some(filename) = dialog.get_filename() {
debug!("File selected: {:?}", filename);
rayon::spawn(clone!(sender => move || {
// Parse the file and import the feeds
if let Ok(sources) = opml::import_from_file(filename) {
// Refresh the succesfully parsed feeds to index them
utils::refresh(Some(sources), sender)
} else {
// TODO: Show an in-app notification if file can not be parsed
error!("Failed to parse the Import file")
}
}))
}
}
dialog.destroy();
}));
dialog.run();
}
// Totally copied it from fractal. // Totally copied it from fractal.
// https://gitlab.gnome.org/danigm/fractal/blob/503e311e22b9d7540089d735b92af8e8f93560c5/fractal-gtk/src/app.rs#L1883-1912 // https://gitlab.gnome.org/danigm/fractal/blob/503e311e22b9d7540089d735b92af8e8f93560c5/fractal-gtk/src/app.rs#L1883-1912
fn about_dialog(window: &gtk::Window) { fn about_dialog(window: &gtk::Window) {