╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ───────────────────╮ │src/ui/UserNotifications.zig│ ╰────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────╮ │41 // local mode has no users │ │42 if (session.data.host_kind != .server) return result; │ │43 const user_id = session.userId() orelse return result; │ │44 + result.ssh_keys = try ui.UserKeys.init(arena, session, &user_id);│ │45 const io = session.io orelse return error.NoMoment; │ │46 const users_dir = session.users_dir orelse return error.NoMoment;│ │47 const gpa = arena.child_allocator; │ ╰─────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │78 const repos_label = "view repos"; │ │79 const logout_label = "logout"; │ │80 │ │79 -// the notifications and user settings sub tabs over their content, with the logout button across from them │ │81 +// the notifications, user settings and ssh keys sub tabs over their content, with the logout button across from them│ │82 pub const View = struct { │ │83 box: wgt.Box(ui.Widget), │ │84 data: *const Self, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────╮ │85 session: *ui.Session, │ │86 // the sub tabs' ids then the repos and logout buttons', in header order│ │85 - control_ids: [4]usize, │ │87 + control_ids: [5]usize, │ │88 // the sub tab whose content shows │ │89 view_index: usize, │ │90 │ ╰────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │91 const header_index = 0; │ │92 const content_index = 1; │ │93 // the sub tabs come first in `control_ids` │ │92 - const tab_count = 2; │ │93 - const repos_index = 2; │ │94 - const logout_index = 3; │ │94 + const tab_count = 3; │ │95 + const repos_index = 3; │ │96 + const logout_index = 4; │ │97 │ │98 // `user_route` is the page's user tab route │ │99 pub fn init(allocator: std.mem.Allocator, data: *const Self, session: *ui.Session, user_route: ui.RoutablePage) !View {│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │100 var box = try wgt.Box(ui.Widget).init(allocator, .{ .border = null, .direction = .vert }); │ │101 errdefer box.deinit(allocator); │ │102 │ │101 - var control_ids: [4]usize = undefined; │ │103 + var control_ids: [5]usize = undefined; │ │104 const settings_route = user_route.withUserTab(.{ .view = .settings }); │ │105 + const keys_route = user_route.withUserTab(.{ .view = .keys }); │ │106 { │ │107 var header = try wgt.Box(ui.Widget).init(allocator, .{ .border = null, .direction = .horiz });│ │108 errdefer header.deinit(allocator); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │109 const on_tab = std.meta.activeTag(session.data.current_page) == std.meta.activeTag(user_route); │ │107 - const routes = [_]ui.RoutablePage{ user_route.withUserTab(.{}), settings_route }; │ │108 - for ([_]ui.RoutablePage.UserView{ .notifications, .settings }, [_][]const u8{ "notifications", "user settings" }, routes, 0..) |view, label, route, index| { │ │110 + const routes = [_]ui.RoutablePage{ user_route.withUserTab(.{}), settings_route, keys_route }; │ │111 + for ([_]ui.RoutablePage.UserView{ .notifications, .settings, .keys }, [_][]const u8{ "notifications", "user settings", "ssh keys" }, routes, 0..) |view, label, route, index| {│ │112 var tab = try wgt.TextBox.init(allocator, label, .{ .border = .hidden, .round_corners = true, .wrap_kind = .none }); │ │113 errdefer tab.deinit(allocator); │ │114 tab.getFocus().mode = .all; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │185 var settings = try ui.UserSettings.View.init(allocator, session, settings_route); │ │186 errdefer settings.deinit(allocator); │ │187 try stack.children.put(allocator, settings.getFocus().id, .{ .user_settings = settings }); │ │188 + } │ │189 + { │ │190 + var keys = try ui.UserKeys.View.init(allocator, &data.ssh_keys, session, keys_route); │ │191 + errdefer keys.deinit(allocator); │ │192 + try stack.children.put(allocator, keys.getFocus().id, .{ .user_keys = keys }); │ │193 } │ │194 stack.getFocus().child_id = stack.children.keys()[@backingInt(data.view)]; │ │195 try box.children.put(allocator, stack.getFocus().id, .{ .widget = .{ .stack = stack }, .rect = null, .min_size = null });│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────────╮ │src/ui/widget.zig│ ╰─────────────────╯ ╭──────────────────────────────────────────────────────╮ │76 new_user: ui.NewUser.View, │ │77 user_login: ui.UserLogin.View, │ │78 user_settings: ui.UserSettings.View, │ │79 + user_keys: ui.UserKeys.View, │ │80 user_notifications: ui.UserNotifications.View,│ │81 footer: Footer, │ │82 │ ╰──────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │119 // whether moving up should return to the page header │ │120 pub fn atTop(self: *Widget, root_focus: *Focus) bool { │ │121 return switch (self.*) { │ │121 - inline .home_about, .home_users, .user_repos, .flow_box_scroll, .new_repo, .new_user, .user_login, .user_notifications, .quit, .repo_files, .repo_commits, .diff_view, .repo_refs, .repo_issues, .repo_patches, .repo_discussions, .repo_events, .repo_undo, .repo_settings, .repo_settings_form, .repo_roles => |*view| view.atTop(), │ │122 + inline .home_about, .home_users, .user_repos, .flow_box_scroll, .new_repo, .new_user, .user_login, .user_notifications, .quit, .repo_files, .repo_commits, .diff_view, .repo_refs, .repo_issues, .repo_patches, .repo_discussions, .repo_events, .repo_undo, .repo_settings, .repo_settings_form, .repo_roles, .user_keys => |*view| view.atTop(),│ │123 inline .repo_patch_detail, .user_settings => |*view| view.atTop(root_focus), │ │124 else => false, │ │125 }; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ added ────────────╮ │src/ui/UserKeys.zig│ ╰───────────────────╯ ╭──────╮ │next →│ ╰──────╯