mirror of
https://github.com/YGGverse/Yoda.git
synced 2025-01-15 17:20: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::{
|
use gtk::{
|
||||||
gdk::Texture,
|
gdk::Texture,
|
||||||
gdk_pixbuf::Pixbuf,
|
gdk_pixbuf::Pixbuf,
|
||||||
gio::{Cancellable, SocketClientEvent},
|
gio::SocketClientEvent,
|
||||||
glib::{
|
glib::{
|
||||||
gformat, GString, Priority, Regex, RegexCompileFlags, RegexMatchFlags, Uri, UriFlags,
|
gformat, GString, Priority, Regex, RegexCompileFlags, RegexMatchFlags, Uri, UriFlags,
|
||||||
UriHideFlags,
|
UriHideFlags,
|
||||||
},
|
},
|
||||||
prelude::{CancellableExt, EditableExt, FileExt, SocketClientExt, WidgetExt},
|
prelude::{EditableExt, FileExt, SocketClientExt},
|
||||||
};
|
};
|
||||||
use sqlite::Transaction;
|
use sqlite::Transaction;
|
||||||
use std::{rc::Rc, time::Duration};
|
use std::{rc::Rc, time::Duration};
|
||||||
@ -480,7 +480,7 @@ impl Page {
|
|||||||
&cancellable,
|
&cancellable,
|
||||||
{
|
{
|
||||||
let cancellable = cancellable.clone();
|
let cancellable = cancellable.clone();
|
||||||
move |file, download_status| {
|
move |file, action| {
|
||||||
match file.replace(
|
match file.replace(
|
||||||
None,
|
None,
|
||||||
false,
|
false,
|
||||||
@ -499,52 +499,28 @@ impl Page {
|
|||||||
0 // initial totals
|
0 // initial totals
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
// on chunk
|
||||||
{
|
{
|
||||||
let download_status = download_status.clone();
|
let action = action.clone();
|
||||||
move |_, total| {
|
move |_, total| action.update.activate(
|
||||||
// Update loading progress
|
|
||||||
download_status.set_label(
|
|
||||||
&format!("Received {total} bytes...")
|
&format!("Received {total} bytes...")
|
||||||
);
|
)
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
// on complete
|
||||||
{
|
{
|
||||||
let cancellable = cancellable.clone();
|
let action = action.clone();
|
||||||
move |result| match result {
|
move |result| match result {
|
||||||
Ok((_, total)) => {
|
Ok((_, total)) => action.complete.activate(
|
||||||
// Update loading progress
|
|
||||||
download_status.set_label(
|
|
||||||
&format!("Download ({total} bytes) completed!")
|
&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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// cancel uncompleted async operations
|
action.cancel.activate(&e.to_string())
|
||||||
// * 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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ use gtk::{
|
|||||||
gio::{Cancellable, File},
|
gio::{Cancellable, File},
|
||||||
glib::Uri,
|
glib::Uri,
|
||||||
prelude::{BoxExt, IsA, WidgetExt},
|
prelude::{BoxExt, IsA, WidgetExt},
|
||||||
Box, Label, Orientation,
|
Box, Orientation,
|
||||||
};
|
};
|
||||||
use std::{rc::Rc, time::Duration};
|
use std::{rc::Rc, time::Duration};
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ impl Content {
|
|||||||
&self,
|
&self,
|
||||||
initial_filename: &str,
|
initial_filename: &str,
|
||||||
cancellable: &Cancellable,
|
cancellable: &Cancellable,
|
||||||
on_choose: impl Fn(File, Label) + 'static,
|
on_choose: impl Fn(File, Rc<status::download::Action>) + 'static,
|
||||||
) -> Status {
|
) -> Status {
|
||||||
self.clean();
|
self.clean();
|
||||||
let status = Status::new_download(initial_filename, cancellable, on_choose);
|
let status = Status::new_download(initial_filename, cancellable, on_choose);
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
mod download;
|
pub mod download;
|
||||||
mod failure;
|
mod failure;
|
||||||
mod identity;
|
mod identity;
|
||||||
mod loading;
|
mod loading;
|
||||||
|
|
||||||
use crate::app::browser::window::tab::item::Action;
|
|
||||||
use adw::StatusPage;
|
use adw::StatusPage;
|
||||||
use gtk::{
|
use gtk::gio::{Cancellable, File};
|
||||||
gio::{Cancellable, File},
|
|
||||||
Label,
|
|
||||||
};
|
|
||||||
use std::{rc::Rc, time::Duration};
|
use std::{rc::Rc, time::Duration};
|
||||||
|
|
||||||
pub struct Status {
|
pub struct Status {
|
||||||
@ -22,7 +18,7 @@ impl Status {
|
|||||||
pub fn new_download(
|
pub fn new_download(
|
||||||
initial_filename: &str,
|
initial_filename: &str,
|
||||||
cancellable: &Cancellable,
|
cancellable: &Cancellable,
|
||||||
on_choose: impl Fn(File, Label) + 'static,
|
on_choose: impl Fn(File, Rc<download::Action>) + 'static,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
gobject: download::new(initial_filename, cancellable, on_choose),
|
gobject: download::new(initial_filename, cancellable, on_choose),
|
||||||
@ -42,7 +38,7 @@ impl Status {
|
|||||||
///
|
///
|
||||||
/// Useful as placeholder for 60 status code
|
/// Useful as placeholder for 60 status code
|
||||||
/// https://geminiprotocol.net/docs/protocol-specification.gmi#status-60
|
/// 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 {
|
Self {
|
||||||
gobject: identity::new_gobject(action),
|
gobject: identity::new_gobject(action),
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
|
mod action;
|
||||||
mod cancel;
|
mod cancel;
|
||||||
mod choose;
|
mod choose;
|
||||||
mod open;
|
mod open;
|
||||||
mod progress;
|
mod progress;
|
||||||
mod status;
|
mod status;
|
||||||
|
|
||||||
|
pub use action::Action;
|
||||||
use cancel::Cancel;
|
use cancel::Cancel;
|
||||||
use choose::Choose;
|
use choose::Choose;
|
||||||
use open::Open;
|
use open::Open;
|
||||||
@ -14,7 +16,7 @@ use adw::StatusPage;
|
|||||||
use gtk::{
|
use gtk::{
|
||||||
gio::{Cancellable, File},
|
gio::{Cancellable, File},
|
||||||
prelude::{BoxExt, CancellableExt, WidgetExt},
|
prelude::{BoxExt, CancellableExt, WidgetExt},
|
||||||
Box, FileDialog, FileLauncher, Label, Orientation, Window,
|
Box, FileDialog, FileLauncher, Orientation, Window,
|
||||||
};
|
};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
@ -32,19 +34,57 @@ const TITLE: &str = "Download";
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
initial_filename: &str,
|
initial_filename: &str,
|
||||||
cancellable: &Cancellable,
|
cancellable: &Cancellable,
|
||||||
on_choose: impl Fn(File, Label) + 'static,
|
on_choose: impl Fn(File, Rc<Action>) + 'static,
|
||||||
) -> StatusPage {
|
) -> StatusPage {
|
||||||
// Init components
|
// Init components
|
||||||
let dialog = FileDialog::builder().initial_name(initial_filename).build();
|
let dialog = FileDialog::builder().initial_name(initial_filename).build();
|
||||||
let file_launcher = FileLauncher::new(File::NONE);
|
let file_launcher = FileLauncher::new(File::NONE);
|
||||||
|
|
||||||
|
let action = Rc::new(Action::new()); // public callback API
|
||||||
|
|
||||||
let cancel = Rc::new(Cancel::new());
|
let cancel = Rc::new(Cancel::new());
|
||||||
let choose = Rc::new(Choose::new());
|
let choose = Rc::new(Choose::new());
|
||||||
let open = Rc::new(Open::new());
|
let open = Rc::new(Open::new());
|
||||||
let progress = Rc::new(Progress::new());
|
let progress = Rc::new(Progress::new());
|
||||||
let status = Rc::new(Status::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({
|
cancel.on_activate({
|
||||||
let cancellable = cancellable.clone();
|
let cancellable = cancellable.clone();
|
||||||
let progress = progress.clone();
|
let progress = progress.clone();
|
||||||
@ -75,6 +115,7 @@ pub fn new(
|
|||||||
button.set_sensitive(false);
|
button.set_sensitive(false);
|
||||||
dialog.save(Window::NONE, Some(&cancellable), {
|
dialog.save(Window::NONE, Some(&cancellable), {
|
||||||
// delegate shared references
|
// delegate shared references
|
||||||
|
let action = action.clone();
|
||||||
let cancel = cancel.clone();
|
let cancel = cancel.clone();
|
||||||
let file_launcher = file_launcher.clone();
|
let file_launcher = file_launcher.clone();
|
||||||
let progress = progress.clone();
|
let progress = progress.clone();
|
||||||
@ -96,7 +137,7 @@ pub fn new(
|
|||||||
// hide self
|
// hide self
|
||||||
button.set_visible(false);
|
button.set_visible(false);
|
||||||
// callback
|
// callback
|
||||||
on_choose(file, status.label.clone())
|
on_choose(file, action)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// update destination file
|
// 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