mirror of
https://github.com/YGGverse/Yoda.git
synced 2025-01-15 09:10:08 +00:00
implement public api actions for download feature
This commit is contained in:
parent
6f1761dcd7
commit
054afd270a
@ -25,12 +25,12 @@ use crate::Profile;
|
||||
use gtk::{
|
||||
gdk::Texture,
|
||||
gdk_pixbuf::Pixbuf,
|
||||
gio::{Cancellable, SocketClientEvent},
|
||||
gio::SocketClientEvent,
|
||||
glib::{
|
||||
gformat, GString, Priority, Regex, RegexCompileFlags, RegexMatchFlags, Uri, UriFlags,
|
||||
UriHideFlags,
|
||||
},
|
||||
prelude::{CancellableExt, EditableExt, FileExt, SocketClientExt, WidgetExt},
|
||||
prelude::{EditableExt, FileExt, SocketClientExt},
|
||||
};
|
||||
use sqlite::Transaction;
|
||||
use std::{rc::Rc, time::Duration};
|
||||
@ -480,7 +480,7 @@ impl Page {
|
||||
&cancellable,
|
||||
{
|
||||
let cancellable = cancellable.clone();
|
||||
move |file, download_status| {
|
||||
move |file, action| {
|
||||
match file.replace(
|
||||
None,
|
||||
false,
|
||||
@ -499,52 +499,28 @@ impl Page {
|
||||
0 // initial totals
|
||||
),
|
||||
(
|
||||
// on chunk
|
||||
{
|
||||
let download_status = download_status.clone();
|
||||
move |_, total| {
|
||||
// Update loading progress
|
||||
download_status.set_label(
|
||||
&format!("Received {total} bytes...")
|
||||
);
|
||||
}
|
||||
let action = action.clone();
|
||||
move |_, total| action.update.activate(
|
||||
&format!("Received {total} bytes...")
|
||||
)
|
||||
},
|
||||
// on complete
|
||||
{
|
||||
let cancellable = cancellable.clone();
|
||||
let action = action.clone();
|
||||
move |result| match result {
|
||||
Ok((_, total)) => {
|
||||
// Update loading progress
|
||||
download_status.set_label(
|
||||
&format!("Download ({total} bytes) completed!")
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
// cancel uncompleted async operations
|
||||
// * this will also toggle download widget actions
|
||||
cancellable.cancel();
|
||||
|
||||
// update status message
|
||||
download_status.set_label(&e.to_string());
|
||||
download_status.set_css_classes(&["error"]);
|
||||
|
||||
// cleanup
|
||||
let _ = file.delete(Cancellable::NONE); // @TODO
|
||||
}
|
||||
Ok((_, total)) => action.complete.activate(
|
||||
&format!("Download ({total} bytes) completed!")
|
||||
),
|
||||
Err(e) => action.cancel.activate(&e.to_string())
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
},
|
||||
Err(e) => {
|
||||
// cancel uncompleted async operations
|
||||
// * this will also toggle download widget actions
|
||||
cancellable.cancel();
|
||||
|
||||
// update status message
|
||||
download_status.set_label(&e.to_string());
|
||||
download_status.set_css_classes(&["error"]);
|
||||
|
||||
// cleanup
|
||||
let _ = file.delete(Cancellable::NONE); // @TODO
|
||||
action.cancel.activate(&e.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ use gtk::{
|
||||
gio::{Cancellable, File},
|
||||
glib::Uri,
|
||||
prelude::{BoxExt, IsA, WidgetExt},
|
||||
Box, Label, Orientation,
|
||||
Box, Orientation,
|
||||
};
|
||||
use std::{rc::Rc, time::Duration};
|
||||
|
||||
@ -53,7 +53,7 @@ impl Content {
|
||||
&self,
|
||||
initial_filename: &str,
|
||||
cancellable: &Cancellable,
|
||||
on_choose: impl Fn(File, Label) + 'static,
|
||||
on_choose: impl Fn(File, Rc<status::download::Action>) + 'static,
|
||||
) -> Status {
|
||||
self.clean();
|
||||
let status = Status::new_download(initial_filename, cancellable, on_choose);
|
||||
|
@ -1,14 +1,10 @@
|
||||
mod download;
|
||||
pub mod download;
|
||||
mod failure;
|
||||
mod identity;
|
||||
mod loading;
|
||||
|
||||
use crate::app::browser::window::tab::item::Action;
|
||||
use adw::StatusPage;
|
||||
use gtk::{
|
||||
gio::{Cancellable, File},
|
||||
Label,
|
||||
};
|
||||
use gtk::gio::{Cancellable, File};
|
||||
use std::{rc::Rc, time::Duration};
|
||||
|
||||
pub struct Status {
|
||||
@ -22,7 +18,7 @@ impl Status {
|
||||
pub fn new_download(
|
||||
initial_filename: &str,
|
||||
cancellable: &Cancellable,
|
||||
on_choose: impl Fn(File, Label) + 'static,
|
||||
on_choose: impl Fn(File, Rc<download::Action>) + 'static,
|
||||
) -> Self {
|
||||
Self {
|
||||
gobject: download::new(initial_filename, cancellable, on_choose),
|
||||
@ -42,7 +38,7 @@ impl Status {
|
||||
///
|
||||
/// Useful as placeholder for 60 status code
|
||||
/// https://geminiprotocol.net/docs/protocol-specification.gmi#status-60
|
||||
pub fn new_identity(action: Rc<Action>) -> Self {
|
||||
pub fn new_identity(action: Rc<crate::app::browser::window::tab::item::Action>) -> Self {
|
||||
Self {
|
||||
gobject: identity::new_gobject(action),
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
mod action;
|
||||
mod cancel;
|
||||
mod choose;
|
||||
mod open;
|
||||
mod progress;
|
||||
mod status;
|
||||
|
||||
pub use action::Action;
|
||||
use cancel::Cancel;
|
||||
use choose::Choose;
|
||||
use open::Open;
|
||||
@ -14,7 +16,7 @@ use adw::StatusPage;
|
||||
use gtk::{
|
||||
gio::{Cancellable, File},
|
||||
prelude::{BoxExt, CancellableExt, WidgetExt},
|
||||
Box, FileDialog, FileLauncher, Label, Orientation, Window,
|
||||
Box, FileDialog, FileLauncher, Orientation, Window,
|
||||
};
|
||||
use std::rc::Rc;
|
||||
|
||||
@ -32,19 +34,57 @@ const TITLE: &str = "Download";
|
||||
pub fn new(
|
||||
initial_filename: &str,
|
||||
cancellable: &Cancellable,
|
||||
on_choose: impl Fn(File, Label) + 'static,
|
||||
on_choose: impl Fn(File, Rc<Action>) + 'static,
|
||||
) -> StatusPage {
|
||||
// Init components
|
||||
let dialog = FileDialog::builder().initial_name(initial_filename).build();
|
||||
let file_launcher = FileLauncher::new(File::NONE);
|
||||
|
||||
let action = Rc::new(Action::new()); // public callback API
|
||||
|
||||
let cancel = Rc::new(Cancel::new());
|
||||
let choose = Rc::new(Choose::new());
|
||||
let open = Rc::new(Open::new());
|
||||
let progress = Rc::new(Progress::new());
|
||||
let status = Rc::new(Status::new());
|
||||
|
||||
// Init events
|
||||
// Init action events
|
||||
action.cancel.on_activate({
|
||||
let cancel = cancel.clone();
|
||||
let cancellable = cancellable.clone();
|
||||
let progress = progress.clone();
|
||||
let status = status.clone();
|
||||
move |_, message| {
|
||||
cancellable.cancel();
|
||||
progress.disable();
|
||||
status.set_warning(&message);
|
||||
cancel.button.set_visible(false);
|
||||
}
|
||||
});
|
||||
|
||||
action.complete.on_activate({
|
||||
let cancel = cancel.clone();
|
||||
let cancellable = cancellable.clone();
|
||||
let open = open.clone();
|
||||
let progress = progress.clone();
|
||||
let status = status.clone();
|
||||
move |_, message| {
|
||||
cancellable.cancel();
|
||||
progress.disable();
|
||||
status.set_success(&message);
|
||||
cancel.button.set_visible(false);
|
||||
open.button.set_visible(true);
|
||||
}
|
||||
});
|
||||
|
||||
action.update.on_activate({
|
||||
let status = status.clone();
|
||||
move |_, message| {
|
||||
status.set_default(&message);
|
||||
}
|
||||
});
|
||||
|
||||
// Init widget events
|
||||
cancel.on_activate({
|
||||
let cancellable = cancellable.clone();
|
||||
let progress = progress.clone();
|
||||
@ -75,6 +115,7 @@ pub fn new(
|
||||
button.set_sensitive(false);
|
||||
dialog.save(Window::NONE, Some(&cancellable), {
|
||||
// delegate shared references
|
||||
let action = action.clone();
|
||||
let cancel = cancel.clone();
|
||||
let file_launcher = file_launcher.clone();
|
||||
let progress = progress.clone();
|
||||
@ -96,7 +137,7 @@ pub fn new(
|
||||
// hide self
|
||||
button.set_visible(false);
|
||||
// callback
|
||||
on_choose(file, status.label.clone())
|
||||
on_choose(file, action)
|
||||
}
|
||||
Err(e) => {
|
||||
// update destination file
|
||||
|
@ -0,0 +1,29 @@
|
||||
mod cancel;
|
||||
mod complete;
|
||||
mod update;
|
||||
|
||||
use cancel::Cancel;
|
||||
use complete::Complete;
|
||||
use update::Update;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Callback API for `Download` widget
|
||||
pub struct Action {
|
||||
pub cancel: Rc<Cancel>,
|
||||
pub complete: Rc<Complete>,
|
||||
pub update: Rc<Update>,
|
||||
}
|
||||
|
||||
impl Action {
|
||||
// Constructors
|
||||
|
||||
/// Create new `Self`
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
cancel: Rc::new(Cancel::new()),
|
||||
complete: Rc::new(Complete::new()),
|
||||
update: Rc::new(Update::new()),
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
// Defaults
|
||||
|
||||
use gtk::{
|
||||
gio::SimpleAction,
|
||||
glib::{uuid_string_random, SignalHandlerId},
|
||||
prelude::{ActionExt, StaticVariantType, ToVariant},
|
||||
};
|
||||
|
||||
/// Cancel [SimpleAction](https://docs.gtk.org/gio/class.SimpleAction.html)
|
||||
pub struct Cancel {
|
||||
pub action: SimpleAction,
|
||||
}
|
||||
|
||||
impl Cancel {
|
||||
// Constructors
|
||||
|
||||
/// Create new `Self`
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
action: SimpleAction::new(&uuid_string_random(), Some(&String::static_variant_type())),
|
||||
}
|
||||
}
|
||||
|
||||
// Actions
|
||||
|
||||
pub fn activate(&self, message: &str) {
|
||||
self.action.activate(Some(&message.to_variant()));
|
||||
}
|
||||
|
||||
/// Formatted action connector for external implementation
|
||||
pub fn on_activate(
|
||||
&self,
|
||||
callback: impl Fn(&SimpleAction, String) + 'static,
|
||||
) -> SignalHandlerId {
|
||||
self.action.connect_activate(move |this, message| {
|
||||
callback(
|
||||
this,
|
||||
message
|
||||
.expect("Variant required to call this action")
|
||||
.get::<String>()
|
||||
.expect("Parameter does not match `String` type"),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
// Defaults
|
||||
|
||||
use gtk::{
|
||||
gio::SimpleAction,
|
||||
glib::{uuid_string_random, SignalHandlerId},
|
||||
prelude::{ActionExt, StaticVariantType, ToVariant},
|
||||
};
|
||||
|
||||
/// Complete [SimpleAction](https://docs.gtk.org/gio/class.SimpleAction.html)
|
||||
pub struct Complete {
|
||||
pub action: SimpleAction,
|
||||
}
|
||||
|
||||
impl Complete {
|
||||
// Constructors
|
||||
|
||||
/// Create new `Self`
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
action: SimpleAction::new(&uuid_string_random(), Some(&String::static_variant_type())),
|
||||
}
|
||||
}
|
||||
|
||||
// Actions
|
||||
|
||||
pub fn activate(&self, message: &str) {
|
||||
self.action.activate(Some(&message.to_variant()));
|
||||
}
|
||||
|
||||
/// Formatted action connector for external implementation
|
||||
pub fn on_activate(
|
||||
&self,
|
||||
callback: impl Fn(&SimpleAction, String) + 'static,
|
||||
) -> SignalHandlerId {
|
||||
self.action.connect_activate(move |this, message| {
|
||||
callback(
|
||||
this,
|
||||
message
|
||||
.expect("Variant required to call this action")
|
||||
.get::<String>()
|
||||
.expect("Parameter does not match `String` type"),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
// Defaults
|
||||
|
||||
use gtk::{
|
||||
gio::SimpleAction,
|
||||
glib::{uuid_string_random, SignalHandlerId},
|
||||
prelude::{ActionExt, StaticVariantType, ToVariant},
|
||||
};
|
||||
|
||||
/// Update [SimpleAction](https://docs.gtk.org/gio/class.SimpleAction.html)
|
||||
pub struct Update {
|
||||
pub action: SimpleAction,
|
||||
}
|
||||
|
||||
impl Update {
|
||||
// Constructors
|
||||
|
||||
/// Create new `Self`
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
action: SimpleAction::new(&uuid_string_random(), Some(&String::static_variant_type())),
|
||||
}
|
||||
}
|
||||
|
||||
// Actions
|
||||
|
||||
pub fn activate(&self, message: &str) {
|
||||
self.action.activate(Some(&message.to_variant()));
|
||||
}
|
||||
|
||||
/// Formatted action connector for external implementation
|
||||
pub fn on_activate(
|
||||
&self,
|
||||
callback: impl Fn(&SimpleAction, String) + 'static,
|
||||
) -> SignalHandlerId {
|
||||
self.action.connect_activate(move |this, message| {
|
||||
callback(
|
||||
this,
|
||||
message
|
||||
.expect("Variant required to call this action")
|
||||
.get::<String>()
|
||||
.expect("Parameter does not match `String` type"),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user