login: prefill dialog after a failed login attempt

After a failed login the dialog was recreated empty, so a single
mistyped password meant retyping the server URL and username too, and
on a first-time login everything the user had just typed was lost.
Fill the form with the credentials of the last attempt so that a wrong
password only needs retyping. Nothing is written to the keyring on
failure: credentials are stored on success and cleared only on
explicit logout.
This commit is contained in:
Jeena 2026-09-21 00:14:31 +00:00
parent 36bac44395
commit 037e5a1c7e
2 changed files with 19 additions and 6 deletions

View file

@ -1,4 +1,6 @@
use gtk4::glib;
use gtk4::prelude::*;
use gtk4::subclass::prelude::*;
glib::wrapper! {
pub struct LoginDialog(ObjectSubclass<imp::LoginDialog>)
@ -10,12 +12,19 @@ impl LoginDialog {
pub fn new() -> Self {
glib::Object::new()
}
/// Pre-fill the form, e.g. after a failed login attempt so that the
/// server URL, username and password don't have to be retyped.
pub fn set_prefill(&self, server_url: &str, username: &str, password: &str) {
let imp = self.imp();
imp.server_url_row.set_text(server_url);
imp.username_row.set_text(username);
imp.password_row.set_text(password);
}
}
mod imp {
use super::*;
use gtk4::prelude::*;
use gtk4::subclass::prelude::*;
use gtk4::CompositeTemplate;
use libadwaita::prelude::*;
use libadwaita::subclass::prelude::*;

View file

@ -557,12 +557,15 @@ pub mod imp {
if let Some((server_url, username, password)) = credentials::load_credentials() {
self.do_login(server_url, username, password, false);
} else {
self.show_login_dialog();
self.show_login_dialog(None);
}
}
fn show_login_dialog(&self) {
fn show_login_dialog(&self, prefill: Option<(String, String, String)>) {
let dialog = LoginDialog::new();
if let Some((server_url, username, password)) = prefill {
dialog.set_prefill(&server_url, &username, &password);
}
let win = self.obj();
let win_weak = win.downgrade();
dialog.connect_local("logged-in", false, move |args| {
@ -580,6 +583,7 @@ pub mod imp {
fn do_login(&self, server_url: String, username: String, password: String, store: bool) {
let is_auto = !store;
let win_weak = self.obj().downgrade();
let prefill = (server_url.clone(), username.clone(), password.clone());
crate::runtime::spawn(
async move { Api::login(&server_url, &username, &password).await
.map(|api| (api, server_url, username, password)) },
@ -615,7 +619,7 @@ pub mod imp {
let toast = libadwaita::Toast::new("Offline — showing cached articles");
win.imp().toast_overlay.add_toast(toast);
} else {
win.imp().show_login_dialog();
win.imp().show_login_dialog(Some(prefill));
win.imp().show_error_dialog("Login Failed", &e);
}
}
@ -652,7 +656,7 @@ pub mod imp {
}
win.imp().sidebar_content.set_visible_child_name("placeholder");
win.imp().article_menu_button.set_visible(false);
win.imp().show_login_dialog();
win.imp().show_login_dialog(None);
}
}
});