Working non-state machine stack implementation.

Removed the stack state-machines. It was confusing trying to both
implement statemachines and re-design the stack architecture at the same time.
This commit is contained in:
Jordan Petridis 2017-12-12 16:01:19 +02:00
parent 211b36dfa3
commit 01310ee7fa
No known key found for this signature in database
GPG Key ID: CEABAD9F5683B9A6
6 changed files with 146 additions and 312 deletions

View File

@ -1,318 +1,167 @@
use gtk; use gtk;
use gtk::prelude::*; use gtk::prelude::*;
// use hammond_data::Podcast; use hammond_data::Podcast;
use hammond_data::dbqueries; use hammond_data::dbqueries;
use widgets::podcast::PodcastWidget;
use views::podcasts::PopulatedView; use views::podcasts::PopulatedView;
use views::empty::EmptyView; use views::empty::EmptyView;
use widgets::podcast::PodcastWidget;
use std::rc::Rc;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Content { pub struct Content {
pub stack: gtk::Stack, pub stack: gtk::Stack,
shows: ShowStateWrapper, shows: Rc<ShowStack>,
episodes: EpisodeStateWrapper, episodes: Rc<EpisodeStack>,
} }
impl Content { impl Content {
pub fn new() -> Content { pub fn new() -> Rc<Content> {
let stack = gtk::Stack::new(); let stack = gtk::Stack::new();
let shows = ShowStateWrapper::new(); let shows = ShowStack::new();
let episodes = EpisodeStateWrapper::new(); let episodes = EpisodeStack::new();
let shows_stack = shows.get_stack(); stack.add_titled(&episodes.stack, "episodes", "Episodes");
let ep_stack = episodes.get_stack(); stack.add_titled(&shows.stack, "shows", "Shows");
stack.add_titled(&ep_stack, "episodes", "Episodes"); Rc::new(Content {
stack.add_titled(&shows_stack, "shows", "Shows");
Content {
stack, stack,
shows, shows,
episodes, episodes,
} })
} }
pub fn update(&mut self) { pub fn update(&self) {
self.shows = self.shows.clone().update(); self.shows.update();
// FIXME: like above
self.episodes.update(); self.episodes.update();
} }
} }
// pub fn on_podcasts_child_activate(stack: &gtk::Stack, pd: &Podcast) {
// update_widget(stack, pd);
// stack.set_visible_child_full("widget", gtk::StackTransitionType::SlideLeft);
// }
// FIXME: Rename and remove aliases
type ShowsPopulated = PopulatedView;
type ShowsEmpty = EmptyView;
type EpisodesPopulated = PodcastWidget;
type EpisodesEmpty = EmptyView;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Populated; pub struct ShowStack {
#[derive(Debug, Clone)] pub stack: gtk::Stack,
struct Empty;
// struct Shows;
// struct Episodes;
// Thats probably too overengineered
// struct StackStateMachine<S, T> {
// shows: ShowsMachine<S>,
// episodes: EpisodesMachine<S>,
// stack: gtk::Stack,
// state: T,
// }
#[derive(Debug, Clone)]
struct ShowsMachine<S> {
populated: ShowsPopulated,
empty: ShowsEmpty,
stack: gtk::Stack,
state: S,
} }
impl<S> ShowsMachine<S> { impl ShowStack {
fn new(state: S) -> ShowsMachine<S> { fn new() -> Rc<ShowStack> {
let stack = gtk::Stack::new(); let stack = gtk::Stack::new();
let pop = ShowsPopulated::new_initialized();
let show = Rc::new(ShowStack { stack });
let pop = PopulatedView::new_initialized(show.clone());
let widget = PodcastWidget::new();
let empty = EmptyView::new(); let empty = EmptyView::new();
stack.add_named(&pop.container, "populated");
stack.add_named(&empty.container, "empty");
ShowsMachine { show.stack.add_named(&pop.container, "podcasts");
empty, show.stack.add_named(&widget.container, "widget");
populated: pop, show.stack.add_named(&empty.container, "empty");
stack,
state, if pop.is_empty() {
show.stack.set_visible_child_name("empty")
} else {
show.stack.set_visible_child_name("podcasts")
} }
show
} }
fn update(&mut self) { // fn is_empty(&self) -> bool {
// self.podcasts.is_empty()
// }
pub fn update(&self) {
self.update_podcasts();
self.update_widget();
}
pub fn update_podcasts(&self) {
let vis = self.stack.get_visible_child_name().unwrap(); let vis = self.stack.get_visible_child_name().unwrap();
let old = self.stack.get_child_by_name("populated").unwrap(); let old = self.stack.get_child_by_name("podcasts").unwrap();
let pop = PopulatedView::new();
pop.init(Rc::new(self.clone()));
self.stack.remove(&old); self.stack.remove(&old);
self.stack.add_named(&pop.container, "podcasts");
let pop = ShowsPopulated::new_initialized(); if pop.is_empty() {
self.populated = pop; self.stack.set_visible_child_name("empty");
self.stack.add_named(&self.populated.container, "populated"); } else if vis != "empty" {
self.stack.set_visible_child_name(&vis); self.stack.set_visible_child_name(&vis);
} else {
self.stack.set_visible_child_name("podcasts");
}
old.destroy();
} }
}
#[derive(Debug, Clone)] pub fn replace_widget(&self, pd: &Podcast) {
struct EpisodesMachine<S> { let old = self.stack.get_child_by_name("widget").unwrap();
populated: EpisodesPopulated, let new = PodcastWidget::new_initialized(Rc::new(self.clone()), pd);
empty: EpisodesEmpty,
stack: gtk::Stack,
state: S,
}
impl<S> EpisodesMachine<S> { self.stack.remove(&old);
// FIXME: self.stack.add_named(&new.container, "widget");
fn update(&mut self) { }
pub fn update_widget(&self) {
let vis = self.stack.get_visible_child_name().unwrap(); let vis = self.stack.get_visible_child_name().unwrap();
let old = self.stack.get_child_by_name("populated").unwrap(); let old = self.stack.get_child_by_name("widget").unwrap();
let id = WidgetExt::get_name(&old).unwrap(); let id = WidgetExt::get_name(&old).unwrap();
if id == "GtkBox" { if id == "GtkBox" {
return; return;
} }
let pd = dbqueries::get_podcast_from_id(id.parse::<i32>().unwrap()).unwrap();
let pdw = EpisodesPopulated::new_initialized(&self.stack, &pd);
self.populated = pdw; let pd = dbqueries::get_podcast_from_id(id.parse::<i32>().unwrap());
self.stack.remove(&old); if let Ok(pd) = pd {
self.stack.add_named(&self.populated.container, "populated"); self.replace_widget(&pd);
self.stack.set_visible_child_name(&vis); self.stack.set_visible_child_name(&vis);
} old.destroy();
}
// impl Into<StackStateMachine<Populated, Shows>> for StackStateMachine<Populated, Episodes> {
// fn into(self) -> StackStateMachine<Populated, Shows> {
// self.stack.set_visible_child_name("shows");
// StackStateMachine {
// shows: self.shows,
// episodes: self.episodes,
// stack: self.stack,
// state: Shows {},
// }
// }
// }
// impl Into<StackStateMachine<Populated, Episodes>> for StackStateMachine<Populated, Shows> {
// fn into(self) -> StackStateMachine<Populated, Episodes> {
// self.stack.set_visible_child_name("episodes");
// StackStateMachine {
// shows: self.shows,
// episodes: self.episodes,
// stack: self.stack,
// state: Episodes {},
// }
// }
// }
// TODO: Impl <From> instead of <Into>
impl Into<ShowsMachine<Populated>> for ShowsMachine<Empty> {
fn into(self) -> ShowsMachine<Populated> {
self.stack.set_visible_child_name("populated");
ShowsMachine {
populated: self.populated,
empty: self.empty,
stack: self.stack,
state: Populated {},
} }
} }
}
impl Into<ShowsMachine<Empty>> for ShowsMachine<Populated> { pub fn switch_podcasts_animated(&self) {
fn into(self) -> ShowsMachine<Empty> { self.stack
self.stack.set_visible_child_name("empty"); .set_visible_child_full("podcasts", gtk::StackTransitionType::SlideRight);
ShowsMachine {
populated: self.populated,
empty: self.empty,
stack: self.stack,
state: Empty {},
}
} }
}
impl Into<EpisodesMachine<Populated>> for EpisodesMachine<Empty> { pub fn switch_widget_animated(&self) {
fn into(self) -> EpisodesMachine<Populated> { self.stack
self.stack.set_visible_child_name("populated"); .set_visible_child_full("widget", gtk::StackTransitionType::SlideLeft)
EpisodesMachine {
populated: self.populated,
empty: self.empty,
stack: self.stack,
state: Populated {},
}
} }
} }
impl Into<EpisodesMachine<Empty>> for EpisodesMachine<Populated> {
fn into(self) -> EpisodesMachine<Empty> {
self.stack.set_visible_child_name("empty");
EpisodesMachine {
populated: self.populated,
empty: self.empty,
stack: self.stack,
state: Empty {},
}
}
}
// enum StackStateWrapper<S> {
// Shows(StackStateMachine<S, Shows>),
// Episodes(StackStateMachine<S, Episodes>),
// }
#[derive(Debug, Clone)]
enum ShowStateWrapper {
Populated(ShowsMachine<Populated>),
Empty(ShowsMachine<Empty>),
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum EpisodeStateWrapper { struct RecentEpisodes;
Populated(EpisodesMachine<Populated>),
Empty(EpisodesMachine<Empty>), #[derive(Debug, Clone)]
struct EpisodeStack {
// populated: RecentEpisodes,
// empty: EmptyView,
stack: gtk::Stack,
} }
// impl <S>StackStateWrapper<S> { impl EpisodeStack {
// fn switch(mut self) -> Self { fn new() -> Rc<EpisodeStack> {
// match self { let _pop = RecentEpisodes {};
// StackStateWrapper::Shows(val) => StackStateWrapper::Episodes(val.into()),
// StackStateWrapper::Episodes(val) => StackStateWrapper::Shows(val.into())
// }
// }
// }
impl ShowStateWrapper {
fn new() -> Self {
let machine = ShowsMachine::new(Populated {});
if machine.populated.flowbox.get_children().is_empty() {
machine.stack.set_visible_child_name("empty");
ShowStateWrapper::Empty(machine.into())
} else {
machine.stack.set_visible_child_name("populated");
ShowStateWrapper::Populated(machine)
}
}
fn update(mut self) -> Self {
match self {
ShowStateWrapper::Populated(ref mut val) => val.update(),
ShowStateWrapper::Empty(ref mut val) => val.update(),
}
if self.is_empty() {
match self {
ShowStateWrapper::Populated(val) => ShowStateWrapper::Empty(val.into()),
_ => self,
}
} else {
match self {
ShowStateWrapper::Empty(val) => ShowStateWrapper::Populated(val.into()),
_ => self,
}
}
}
fn get_stack(&self) -> gtk::Stack {
match *self {
ShowStateWrapper::Populated(ref val) => val.stack.clone(),
ShowStateWrapper::Empty(ref val) => val.stack.clone(),
}
}
fn is_empty(&self) -> bool {
match *self {
ShowStateWrapper::Populated(ref val) => val.populated.flowbox.get_children().is_empty(),
ShowStateWrapper::Empty(ref val) => val.populated.flowbox.get_children().is_empty(),
}
}
}
impl EpisodeStateWrapper {
// FIXME:
fn new() -> Self {
let pop = PodcastWidget::new();
let empty = EmptyView::new(); let empty = EmptyView::new();
let stack = gtk::Stack::new(); let stack = gtk::Stack::new();
stack.add_named(&pop.container, "populated"); // stack.add_named(&pop.container, "populated");
stack.add_named(&empty.container, "empty"); stack.add_named(&empty.container, "empty");
// FIXME:
stack.set_visible_child_name("empty"); stack.set_visible_child_name("empty");
EpisodeStateWrapper::Empty(EpisodesMachine { Rc::new(EpisodeStack {
empty, // empty,
populated: pop, // populated: pop,
stack, stack,
state: Empty {},
}) })
} }
fn update(&mut self) { fn update(&self) {
match *self { // unimplemented!()
EpisodeStateWrapper::Populated(ref mut val) => val.update(),
EpisodeStateWrapper::Empty(ref mut val) => val.update(),
}
}
fn get_stack(&self) -> gtk::Stack {
match *self {
EpisodeStateWrapper::Populated(ref val) => val.stack.clone(),
EpisodeStateWrapper::Empty(ref val) => val.stack.clone(),
}
} }
} }

View File

@ -4,7 +4,7 @@ use gtk::prelude::*;
use hammond_data::Source; use hammond_data::Source;
use hammond_data::utils::url_cleaner; use hammond_data::utils::url_cleaner;
use std::sync::{Arc, Mutex}; use std::rc::Rc;
use utils; use utils;
use content::Content; use content::Content;
@ -36,23 +36,19 @@ impl Header {
} }
} }
pub fn new_initialized(content: Arc<Mutex<Content>>) -> Header { pub fn new_initialized(content: Rc<Content>) -> Header {
let header = Header::new(); let header = Header::new();
header.init(content); header.init(content);
header header
} }
fn init(&self, content: Arc<Mutex<Content>>) { fn init(&self, content: Rc<Content>) {
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/headerbar.ui"); let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/headerbar.ui");
let add_popover: gtk::Popover = builder.get_object("add-popover").unwrap(); let add_popover: gtk::Popover = builder.get_object("add-popover").unwrap();
let new_url: gtk::Entry = builder.get_object("new-url").unwrap(); let new_url: gtk::Entry = builder.get_object("new-url").unwrap();
let add_button: gtk::Button = builder.get_object("add-button").unwrap(); let add_button: gtk::Button = builder.get_object("add-button").unwrap();
self.switch.set_stack(&content.stack);
{
let cont = content.lock().unwrap();
self.switch.set_stack(&cont.stack);
}
new_url.connect_changed(move |url| { new_url.connect_changed(move |url| {
println!("{:?}", url.get_text()); println!("{:?}", url.get_text());
@ -68,14 +64,13 @@ impl Header {
self.add_toggle.set_popover(&add_popover); self.add_toggle.set_popover(&add_popover);
// FIXME: There appears to be a memmory leak here. // FIXME: There appears to be a memmory leak here.
let cont = content.clone();
self.refresh.connect_clicked(move |_| { self.refresh.connect_clicked(move |_| {
utils::refresh_feed(cont.clone(), None, None); utils::refresh_feed(content.clone(), None, None);
}); });
} }
} }
fn on_add_bttn_clicked(content: Arc<Mutex<Content>>, entry: &gtk::Entry) { fn on_add_bttn_clicked(content: Rc<Content>, entry: &gtk::Entry) {
let url = entry.get_text().unwrap_or_default(); let url = entry.get_text().unwrap_or_default();
let url = url_cleaner(&url); let url = url_cleaner(&url);
let source = Source::from_url(&url); let source = Source::from_url(&url);

View File

@ -1,3 +1,5 @@
#![cfg_attr(feature = "cargo-clippy", allow(clone_on_ref_ptr))]
extern crate gdk; extern crate gdk;
extern crate gdk_pixbuf; extern crate gdk_pixbuf;
extern crate gio; extern crate gio;
@ -22,8 +24,6 @@ use hammond_data::utils::checkup;
use gtk::prelude::*; use gtk::prelude::*;
use gio::{ActionMapExt, ApplicationExt, MenuExt, SimpleActionExt}; use gio::{ActionMapExt, ApplicationExt, MenuExt, SimpleActionExt};
use std::sync::{Arc, Mutex};
// http://gtk-rs.org/tuto/closures // http://gtk-rs.org/tuto/closures
#[macro_export] #[macro_export]
macro_rules! clone { macro_rules! clone {
@ -72,7 +72,6 @@ fn build_ui(app: &gtk::Application) {
// let ct = content::Content::new_initialized(); // let ct = content::Content::new_initialized();
let ct = content::Content::new(); let ct = content::Content::new();
let stack = ct.stack.clone(); let stack = ct.stack.clone();
let ct = Arc::new(Mutex::new(ct));
window.add(&stack); window.add(&stack);
window.connect_delete_event(|w, _| { window.connect_delete_event(|w, _| {

View File

@ -1,5 +1,4 @@
use glib; use glib;
use gtk;
use gdk_pixbuf::Pixbuf; use gdk_pixbuf::Pixbuf;
use hammond_data::feed; use hammond_data::feed;
@ -9,14 +8,12 @@ use hammond_downloader::downloader;
use std::{thread, time}; use std::{thread, time};
use std::cell::RefCell; use std::cell::RefCell;
use std::sync::mpsc::{channel, Receiver}; use std::sync::mpsc::{channel, Receiver};
use std::borrow::Cow;
use content::Content; use content::Content;
use regex::Regex;
use std::sync::{Arc, Mutex}; use std::rc::Rc;
type Foo = RefCell<Option<(Arc<Mutex<Content>>, Receiver<bool>)>>; type Foo = RefCell<Option<(Rc<Content>, Receiver<bool>)>>;
// Create a thread local storage that will store the arguments to be transfered. // Create a thread local storage that will store the arguments to be transfered.
thread_local!(static GLOBAL: Foo = RefCell::new(None)); thread_local!(static GLOBAL: Foo = RefCell::new(None));
@ -25,13 +22,13 @@ thread_local!(static GLOBAL: Foo = RefCell::new(None));
/// 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.
/// `delay` represents the desired time in seconds for the thread to sleep before executing. /// `delay` represents the desired time in seconds for the thread to sleep before executing.
/// When It's done,it queues up a `podcast_view` refresh. /// When It's done,it queues up a `podcast_view` refresh.
pub fn refresh_feed(content: Arc<Mutex<Content>>, source: Option<Vec<Source>>, delay: Option<u64>) { pub fn refresh_feed(content: Rc<Content>, source: Option<Vec<Source>>, delay: Option<u64>) {
// Create a async channel. // Create a async channel.
let (sender, receiver) = channel(); let (sender, receiver) = channel();
// Pass the desired arguments into the Local Thread Storage. // Pass the desired arguments into the Local Thread Storage.
GLOBAL.with(clone!(content => move |global| { GLOBAL.with(clone!(content => move |global| {
*global.borrow_mut() = Some((content, receiver)); *global.borrow_mut() = Some((content.clone(), receiver));
})); }));
thread::spawn(move || { thread::spawn(move || {
@ -61,7 +58,6 @@ fn refresh_podcasts_view() -> glib::Continue {
GLOBAL.with(|global| { GLOBAL.with(|global| {
if let Some((ref content, ref reciever)) = *global.borrow() { if let Some((ref content, ref reciever)) = *global.borrow() {
if reciever.try_recv().is_ok() { if reciever.try_recv().is_ok() {
let mut content = content.lock().unwrap();
content.update(); content.update();
} }
} }
@ -74,18 +70,6 @@ pub fn get_pixbuf_from_path(pd: &Podcast) -> Option<Pixbuf> {
Pixbuf::new_from_file_at_scale(&img_path, 256, 256, true).ok() Pixbuf::new_from_file_at_scale(&img_path, 256, 256, true).ok()
} }
#[allow(dead_code)]
// WIP: parse html to markup
pub fn html_to_markup(s: &mut str) -> Cow<str> {
s.trim();
s.replace('&', "&amp;");
s.replace('<', "&lt;");
s.replace('>', "&gt;");
let re = Regex::new("(?P<url>https?://[^\\s&,)(\"]+(&\\w=[\\w._-]?)*(#[\\w._-]+)?)").unwrap();
re.replace_all(s, "<a href=\"$url\">$url</a>")
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use hammond_data::Source; use hammond_data::Source;

View File

@ -7,8 +7,9 @@ use hammond_data::dbqueries;
use hammond_data::Podcast; use hammond_data::Podcast;
use utils::get_pixbuf_from_path; use utils::get_pixbuf_from_path;
use content::ShowStack;
// use content; use std::rc::Rc;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct PopulatedView { pub struct PopulatedView {
@ -42,25 +43,29 @@ impl PopulatedView {
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn new_initialized() -> PopulatedView { pub fn new_initialized(show: Rc<ShowStack>) -> PopulatedView {
let pop = PopulatedView::new(); let pop = PopulatedView::new();
pop.init(); pop.init(show);
pop pop
} }
pub fn init(&self) { pub fn init(&self, show: Rc<ShowStack>) {
// pub fn init(&self, stack: &gtk::Stack) { use gtk::WidgetExt;
// use gtk::WidgetExt;
// // TODO: handle unwraps. // TODO: handle unwraps.
// self.flowbox // Note: flowbox_activation always adds "widnget" into the stack and switch to it,
// .connect_child_activated(clone!(stack => move |_, child| { // TODO: implement back button.
// // This is such an ugly hack... // so back button should always remove "widget" and destroy it.
// // let id = child.get_name().unwrap().parse::<i32>().unwrap(); let show = show.clone();
// let id = WidgetExt::get_name(child).unwrap().parse::<i32>().unwrap(); self.flowbox
// let parent = dbqueries::get_podcast_from_id(id).unwrap(); .connect_child_activated(clone!(show => move |_, child| {
// on_flowbox_child_activate(&stack, &parent); // This is such an ugly hack...
// })); let id = WidgetExt::get_name(child).unwrap().parse::<i32>().unwrap();
let pd = dbqueries::get_podcast_from_id(id).unwrap();
show.replace_widget(&pd);
show.switch_widget_animated();
}));
// Populate the flowbox with the Podcasts. // Populate the flowbox with the Podcasts.
self.populate_flowbox(); self.populate_flowbox();
} }
@ -76,6 +81,10 @@ impl PopulatedView {
self.flowbox.show_all(); self.flowbox.show_all();
} }
} }
pub fn is_empty(&self) -> bool {
self.flowbox.get_children().is_empty()
}
} }
impl PodcastChild { impl PodcastChild {
@ -139,7 +148,3 @@ impl PodcastChild {
} }
} }
} }
// fn on_flowbox_child_activate(stack: &gtk::Stack, parent: &Podcast) {
// content::on_podcasts_child_activate(stack, parent)
// }

View File

@ -10,7 +10,8 @@ use hammond_downloader::downloader;
use widgets::episode::episodes_listbox; use widgets::episode::episodes_listbox;
use utils::get_pixbuf_from_path; use utils::get_pixbuf_from_path;
// use content; use content::ShowStack;
use std::rc::Rc;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct PodcastWidget { pub struct PodcastWidget {
@ -47,18 +48,18 @@ impl PodcastWidget {
} }
} }
pub fn new_initialized(stack: &gtk::Stack, pd: &Podcast) -> PodcastWidget { pub fn new_initialized(shows: Rc<ShowStack>, pd: &Podcast) -> PodcastWidget {
let pdw = PodcastWidget::new(); let pdw = PodcastWidget::new();
pdw.init(stack, pd); pdw.init(shows, pd);
pdw pdw
} }
pub fn init(&self, stack: &gtk::Stack, pd: &Podcast) { pub fn init(&self, shows: Rc<ShowStack>, pd: &Podcast) {
WidgetExt::set_name(&self.container, &pd.id().to_string()); WidgetExt::set_name(&self.container, &pd.id().to_string());
// TODO: should spawn a thread to avoid locking the UI probably. // TODO: should spawn a thread to avoid locking the UI probably.
self.unsub.connect_clicked(clone!(stack, pd => move |bttn| { self.unsub.connect_clicked(clone!(shows, pd => move |bttn| {
on_unsub_button_clicked(&stack, &pd, bttn); on_unsub_button_clicked(shows.clone(), &pd, bttn);
})); }));
self.title.set_text(pd.title()); self.title.set_text(pd.title());
@ -77,8 +78,8 @@ impl PodcastWidget {
self.cover.set_from_pixbuf(&i); self.cover.set_from_pixbuf(&i);
} }
self.played.connect_clicked(clone!(stack, pd => move |_| { self.played.connect_clicked(clone!(shows, pd => move |_| {
on_played_button_clicked(&stack, &pd); on_played_button_clicked(shows.clone(), &pd);
})); }));
self.show_played_button(pd); self.show_played_button(pd);
@ -95,7 +96,7 @@ impl PodcastWidget {
} }
} }
fn on_unsub_button_clicked(stack: &gtk::Stack, pd: &Podcast, unsub_button: &gtk::Button) { fn on_unsub_button_clicked(shows: Rc<ShowStack>, pd: &Podcast, unsub_button: &gtk::Button) {
let res = dbqueries::remove_feed(pd); let res = dbqueries::remove_feed(pd);
if res.is_ok() { if res.is_ok() {
info!("{} was removed succesfully.", pd.title()); info!("{} was removed succesfully.", pd.title());
@ -111,11 +112,12 @@ fn on_unsub_button_clicked(stack: &gtk::Stack, pd: &Podcast, unsub_button: &gtk:
} }
}; };
} }
// content::update_podcasts(stack); shows.switch_podcasts_animated();
shows.update_podcasts();
} }
fn on_played_button_clicked(stack: &gtk::Stack, pd: &Podcast) { fn on_played_button_clicked(shows: Rc<ShowStack>, pd: &Podcast) {
let _ = dbqueries::update_none_to_played_now(pd); let _ = dbqueries::update_none_to_played_now(pd);
// content::update_widget_preserve_vis(stack, pd); shows.update_widget();
} }