╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──────╮ │src/ui/User.zig│ ╰───────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────╮ │176 .user_notifications = try ui.UserNotifications.init(arena, session, user_tab),│ │177 .quit = Quit.init(), │ │178 }; │ │179 +} │ │180 + │ │181 +// a first repos window without repos, which shows the empty message │ │182 +fn reposEmpty(self: *const Self) bool { │ │183 + return self.repos.len == 0 and self.repos_start == 0; │ │184 +} │ │185 + │ │186 +// a first forks window without forks, which shows the empty message │ │187 +fn forksEmpty(self: *const Self) bool { │ │188 + return self.forks.len == 0 and self.forks_start == 0; │ │189 } │ │190 │ │191 pub const View = struct { │ ╰───────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────╮ │192 box: wgt.Box(ui.Widget), │ │193 + data: *const Self, │ │194 │ │195 const header_index: usize = 0;│ │196 const stack_index: usize = 1; │ ╰───────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │197 + // the forks tab's position in the stack │ │198 + const forks_index: usize = 1; │ │199 │ │200 pub fn init(allocator: std.mem.Allocator, data: *const Self, session: *ui.Session) !View { │ │201 var box = try wgt.Box(ui.Widget).init(allocator, .{ .border = null, .round_corners = true, .direction = .vert });│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │220 try stack.children.put(allocator, repos_view.getFocus().id, .{ .user_repos = repos_view });│ │221 } │ │222 │ │210 - // forks list │ │211 - { │ │223 + // forks list, or the empty message for an empty first window │ │224 + if (data.forksEmpty()) { │ │225 + var center = try ui.widget.initEmpty(allocator); │ │226 + errdefer center.deinit(allocator); │ │227 + try stack.children.put(allocator, center.getFocus().id, .{ .center = center }); │ │228 + } else { │ │229 var list = try ui.widget.FlowBox.Scroll.init(allocator, .{}, !session.is_terminal); │ │230 errdefer list.deinit(allocator); │ │231 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │281 try box.children.put(allocator, stack.getFocus().id, .{ .widget = .{ .stack = stack }, .rect = null, .min_size = null });│ │282 } │ │283 │ │267 - var self = View{ .box = box }; │ │284 + var self = View{ .box = box, .data = data }; │ │285 // search results open in the tab's own search box │ │286 self.getFocus().child_id = box.children.keys()[if (data.repos_search != null) stack_index else header_index]; │ │287 return self; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │332 .down => { │ │333 switch (child.*) { │ │334 .user_header => { │ │335 + // the empty message has nothing to focus │ │336 + if (self.data.forksEmpty() and stack.getFocus().child_id == stack.children.keys()[forks_index]) return;│ │337 if (stack.getSelected()) |selected_widget| switch (selected_widget.*) { │ │338 .user_repos => |*v| return v.focusHeader(root_focus), │ │320 - // an empty list has nothing to focus │ │321 - .flow_box_scroll => |*v| if (v.isEmpty()) return, │ │339 .user_notifications => |*v| if (v.focusHeader(root_focus)) return, │ │340 else => {}, │ │341 }; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │404 try box.children.put(allocator, header.getFocus().id, .{ .widget = .{ .box = header }, .rect = null, .min_size = .{ .width = null, .height = ui.widget.SearchBox.min_size.height } });│ │405 } │ │406 │ │390 - { │ │407 + // an empty first window has no rows, not even paging ones │ │408 + if (data.reposEmpty()) { │ │409 + var center = try ui.widget.initEmpty(allocator); │ │410 + errdefer center.deinit(allocator); │ │411 + try box.children.put(allocator, center.getFocus().id, .{ .widget = .{ .center = center }, .rect = null, .min_size = null }); │ │412 + } else { │ │413 var list = try ui.widget.FlowBox.Scroll.init(allocator, .{}, !session.is_terminal); │ │414 errdefer list.deinit(allocator); │ │415 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────╮ │468 const direction = inp.vertDirection(key); │ │469 if (self.headerActive()) { │ │470 const search_box = self.searchBox(); │ │449 - if (direction == .down) return root_focus.setFocus(self.listScroll().getFocus().id); │ │471 + if (direction == .down) { │ │472 + // the empty message has nothing to focus │ │473 + if (!self.data.reposEmpty()) root_focus.setFocus(self.listScroll().getFocus().id);│ │474 + return; │ │475 + } │ │476 if (key == .enter) { │ │477 const text = try search_box.text(allocator); │ │478 defer allocator.free(text); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ───────────────────╮ │src/ui/UserNotifications.zig│ ╰────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────╮ │60 } │ │61 result.rows = rows; │ │62 return result; │ │63 +} │ │64 + │ │65 +// a first window without notifs, which shows the empty message │ │66 +fn isEmpty(self: *const Self) bool { │ │67 + return self.rows.len == 0 and self.start == 0; │ │68 } │ │69 │ │70 // leave the user tab for the page it belongs to, where the web's logout redirect also lands│ ╰────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │78 // the notifications and user settings sub tabs over their content, with the logout button across from them│ │79 pub const View = struct { │ │80 box: wgt.Box(ui.Widget), │ │81 + data: *const Self, │ │82 session: *ui.Session, │ │83 // the sub tabs' ids then the logout button's, in header order │ │84 control_ids: [3]usize, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯