╭ changed ─────────────────╮ │src/ui/Home/Auth/Login.zig│ ╰──────────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │20 │ │21 const Self = @This(); │ │22 │ │23 +// classifies a failed /login attempt; null in session.data.login_failure means│ │24 +// "no failure to surface". │ │25 +pub const Failure = enum { wrong_password, unknown_user }; │ │26 + │ │27 pub fn init() Self { │ │28 return .{}; │ │29 } │ ╰───────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────╮ │33 data: *const Self, │ │34 session: *ui.Session, │ │35 nav_ids: [3]usize, │ │32 - status: Status, │ │36 // focus id of the header's "users" tab; on a successful submit we jump│ │37 // focus there so the user isn't stranded on the login button. │ │38 users_tab_id: usize, │ ╰───────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────╮ │39 │ │37 - pub const Status = enum { none, success, wrong_password, unknown_user };│ │38 - │ │40 const username_index: usize = 0; │ │41 const password_index: usize = 1; │ │42 const button_index: usize = 2; │ ╰────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────╮ │93 .data = data, │ │94 .session = session, │ │95 .nav_ids = nav_ids, │ │95 - .status = .none, │ │96 .users_tab_id = users_tab_id,│ │97 }; │ │98 } │ ╰─────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────╮ │109 .double │ │110 else │ │111 .single; │ │112 + │ │113 + const failure = self.session.data.login_failure; │ │114 │ │115 const username_input = &box.children.values()[username_index].widget.text_input;│ │114 - username_input.options.label = if (self.status == .unknown_user) │ │116 + username_input.options.label = if (failure == .unknown_user) │ │117 " username (invalid) " │ │118 else │ │119 " username "; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────╮ │120 │ │121 const password_input = &box.children.values()[password_index].widget.text_input;│ │120 - password_input.options.label = if (self.status == .wrong_password) │ │122 + password_input.options.label = if (failure == .wrong_password) │ │123 " password (invalid) " │ │124 else │ │125 " password "; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────╮ │185 if (username_input.content.items.len != username_len_before or │ │186 password_input.content.items.len != password_len_before) │ │187 { │ │186 - self.status = .none; │ │188 + // any edit clears the failure flag so the "(invalid)" label│ │189 + // doesn't linger after the user has typed a correction. │ │190 + self.session.data.login_failure = null; │ │191 } │ │192 } │ │193 │ ╰─────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────╮ │218 fn submit(self: *View, allocator: std.mem.Allocator, root_focus: *Focus) !void {│ │219 if (comptime wasm) { │ │220 // no DB cursor available on the wasm render path │ │217 - self.status = .unknown_user; │ │221 + self.session.data.login_failure = .unknown_user; │ │222 return; │ │223 } │ │224 │ ╰─────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ │228 │ │229 var username_buf: [256]u8 = undefined; │ │230 const username = joinCodepoints(&username_buf, username_input.content.items) orelse {│ │227 - self.status = .unknown_user; │ │231 + self.session.data.login_failure = .unknown_user; │ │232 return; │ │233 }; │ │234 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ │235 var password_buf: [256]u8 = undefined; │ │236 const password = joinCodepoints(&password_buf, password_input.content.items) orelse {│ │233 - self.status = .wrong_password; │ │237 + self.session.data.login_failure = .wrong_password; │ │238 return; │ │239 }; │ │240 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯