╭ changed ───────────────────╮ │src/ui/UserNotifications.zig│ ╰────────────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────╮ │29 next_start: ?usize, // the `start` for the "next" row, or null when this is the last window│ │30 // the sub tab the route names │ │31 view: ui.RoutablePage.UserView, │ │32 +ssh_keys: ui.UserKeys = .{}, │ │33 │ │34 const Self = @This(); │ │35 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────╮ │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 });│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯