╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ UserLogin.zig ────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯
153 154 if (box.children.getIndex(child_id)) |idx| { 155 try box.children.values()[idx].widget.input(allocator, key, root_focus); 156 } 157 } 158 159 pub fn clearGrid(self: *View) void { 160 self.center.clearGrid(); 161 } 162 163 pub fn getGrid(self: View) ?Grid { 164 return self.center.getGrid(); 165 } 166 167 pub fn getFocus(self: *View) *Focus { 168 return self.center.getFocus(); 169 } 170 171 pub fn atTop(self: View) bool { 172 return self.center.child.box.focus.child_id == self.nav_ids[username_index]; 173 } 174 175 fn indexOf(self: View, child_id: usize) ?usize { 176 for (self.nav_ids, 0..) |id, i| { 177 if (id == child_id) return i; 178 } 179 return null; 180 } 181 182 fn submit(self: *View, allocator: std.mem.Allocator) !void { 183 const box = &self.center.child.box; 184 const username_input = &box.children.values()[username_index].widget.text_input; 185 const password_input = &box.children.values()[password_index].widget.text_input; 186 187 const username = try username_input.text(allocator); 188 defer allocator.free(username); 189 190 const password = try password_input.text(allocator); 191 defer allocator.free(password); 192 193 if (comptime wasm) { 194 // no DB cursor available on the wasm render path 195 try self.rememberFailure(.unknown_user, username); 196 return; 197 } 198 199 const haxy_moment = self.session.haxy_moment orelse { 200 // no DB context (e.g. wasm/web rendering path); treat as unknown. 201 try self.rememberFailure(.unknown_user, username); 202 return; 203 }; 204 205 const result = try evt.User.verifyCredentials(evt.AdminDB, evt.admin_repo_opts.hash, haxy_moment, self.session.arena, username, password); 206 switch (result) { 207 .unknown_user => { 208 try self.rememberFailure(.unknown_user, username); 209 return; 210 }, 211 .wrong_password => { 212 try self.rememberFailure(.wrong_password, username); 213 return; 214 }, 215 .success => |user_id| { 216 // dupe user_id into the arena so the session can hold a stable slice 217 const user_id_stable = try self.session.arena.allocator().dupe(u8, &user_id); 218 self.session.data.user_id = user_id_stable; 219 try self.session.rememberSshKey(); 220 221 // adopt the user's name and persisted prefs 222 try self.session.loadUser(); 223 224 // wipe the entered credentials so they don't linger if the 225 // user returns to this page after logging out 226 username_input.clear(allocator); 227 password_input.clear(allocator); 228 229 // leave the user tab for the page it belongs to, where the 230 // web's /login redirect also lands 231 try self.session.navigate(self.session.data.current_page.pageRoot()); 232 }, 233 } 234 } 235 236 fn rememberFailure(self: *View, failure: ui.Session.FormFeedback.LoginFailure, username: []const u8) !void { 237 self.session.data.form_feedback = .{ .login = .{ 238 .failure = failure, 239 .username = try self.session.arena.allocator().dupe(u8, username), 240 } }; 241 } 242 }; 243