Three bugs fixed: - No tokio reactor: glib::spawn_future_local does not provide a tokio context, so reqwest/hyper panicked at runtime. Introduce src/runtime.rs with a multi-thread tokio Runtime (init() called from main before the GTK app starts). runtime::spawn() posts the async result back to GTK via a tokio oneshot channel awaited by glib::spawn_future_local, which only polls a flag (no I/O). runtime::spawn_bg() is used for fire-and-forget background calls. - Enter key didn't submit login: connect_apply on AdwEntryRow only fires when show-apply-button is true. Switch to connect_entry_activated which fires on Return in all three login rows. - Wrong API URL: the app constructed /accounts/ClientLogin directly off the server host, yielding a 404. Add normalize_base_url() in api.rs that appends /api/greader.php when the URL doesn't already contain it, so users can enter just https://rss.example.com.
124 lines
4 KiB
Rust
124 lines
4 KiB
Rust
use gtk4::glib;
|
|
|
|
glib::wrapper! {
|
|
pub struct LoginDialog(ObjectSubclass<imp::LoginDialog>)
|
|
@extends libadwaita::Dialog, gtk4::Widget,
|
|
@implements gtk4::Accessible, gtk4::Buildable, gtk4::ConstraintTarget;
|
|
}
|
|
|
|
impl LoginDialog {
|
|
pub fn new() -> Self {
|
|
glib::Object::new()
|
|
}
|
|
}
|
|
|
|
mod imp {
|
|
use super::*;
|
|
use gtk4::prelude::*;
|
|
use gtk4::subclass::prelude::*;
|
|
use gtk4::CompositeTemplate;
|
|
use libadwaita::prelude::*;
|
|
use libadwaita::subclass::prelude::*;
|
|
|
|
#[derive(CompositeTemplate, Default)]
|
|
#[template(resource = "/net/jeena/FeedTheMonkey/ui/login_dialog.ui")]
|
|
pub struct LoginDialog {
|
|
#[template_child]
|
|
pub server_url_row: TemplateChild<libadwaita::EntryRow>,
|
|
#[template_child]
|
|
pub username_row: TemplateChild<libadwaita::EntryRow>,
|
|
#[template_child]
|
|
pub password_row: TemplateChild<libadwaita::PasswordEntryRow>,
|
|
#[template_child]
|
|
pub login_button: TemplateChild<libadwaita::ButtonRow>,
|
|
}
|
|
|
|
#[glib::object_subclass]
|
|
impl ObjectSubclass for LoginDialog {
|
|
const NAME: &'static str = "LoginDialog";
|
|
type Type = super::LoginDialog;
|
|
type ParentType = libadwaita::Dialog;
|
|
|
|
fn class_init(klass: &mut Self::Class) {
|
|
klass.bind_template();
|
|
}
|
|
|
|
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
|
|
obj.init_template();
|
|
}
|
|
}
|
|
|
|
impl ObjectImpl for LoginDialog {
|
|
fn signals() -> &'static [glib::subclass::Signal] {
|
|
use std::sync::OnceLock;
|
|
static SIGNALS: OnceLock<Vec<glib::subclass::Signal>> = OnceLock::new();
|
|
SIGNALS.get_or_init(|| {
|
|
vec![
|
|
glib::subclass::Signal::builder("logged-in")
|
|
.param_types([
|
|
String::static_type(),
|
|
String::static_type(),
|
|
String::static_type(),
|
|
])
|
|
.build(),
|
|
]
|
|
})
|
|
}
|
|
|
|
fn constructed(&self) {
|
|
self.parent_constructed();
|
|
|
|
// Login button
|
|
let obj_weak = self.obj().downgrade();
|
|
self.login_button.connect_activated(move |_| {
|
|
if let Some(dialog) = obj_weak.upgrade() {
|
|
dialog.imp().on_login_clicked();
|
|
}
|
|
});
|
|
|
|
// Enter in any row submits the form (connect_entry_activated fires on Return)
|
|
for weak in [
|
|
self.server_url_row.downgrade(),
|
|
self.username_row.downgrade(),
|
|
] {
|
|
let obj_weak = self.obj().downgrade();
|
|
weak.upgrade().unwrap().connect_entry_activated(move |_| {
|
|
if let Some(dialog) = obj_weak.upgrade() {
|
|
dialog.imp().on_login_clicked();
|
|
}
|
|
});
|
|
}
|
|
let obj_weak2 = self.obj().downgrade();
|
|
self.password_row.connect_entry_activated(move |_| {
|
|
if let Some(dialog) = obj_weak2.upgrade() {
|
|
dialog.imp().on_login_clicked();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
impl LoginDialog {
|
|
fn on_login_clicked(&self) {
|
|
let raw_url = self.server_url_row.text().trim().to_string();
|
|
let username = self.username_row.text().trim().to_string();
|
|
let password = self.password_row.text().to_string();
|
|
|
|
if raw_url.is_empty() || username.is_empty() || password.is_empty() {
|
|
return;
|
|
}
|
|
|
|
// Prepend https:// if no scheme given
|
|
let server_url = if raw_url.starts_with("http://") || raw_url.starts_with("https://") {
|
|
raw_url
|
|
} else {
|
|
format!("https://{raw_url}")
|
|
};
|
|
|
|
self.obj().close();
|
|
self.obj().emit_by_name::<()>("logged-in", &[&server_url, &username, &password]);
|
|
}
|
|
}
|
|
|
|
impl WidgetImpl for LoginDialog {}
|
|
impl AdwDialogImpl for LoginDialog {}
|
|
}
|