╭ changed ─────────────╮ │src/ui/Repo/Issues.zig│ ╰──────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭───────────────────────────────────────╮ │30 pub const IssueWithId = struct { │ │31 id: []const u8, │ │32 issue: evt.Issue, │ │33 + // the creation commit's author│ │34 + author: ui.Author = .unknown, │ │35 }; │ │36 │ │37 // one status's windowed listing. │ ╰───────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │117 arena: *std.heap.ArenaAllocator, │ │118 repo: *rp.Repo(repo_kind, repo_opts), │ │119 io: std.Io, │ │120 + // the admin db's moment, for resolving author emails to user names (null│ │121 + // in local mode, which has no users) │ │122 + admin_moment: ?evt.AdminDB.HashMap(.read_only), │ │123 identity: []const u8, │ │124 tag: []const u8, │ │125 selected_id: []const u8, │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │206 }; │ │207 } │ │208 │ │204 - const open_window = try loadWindow(repo_opts.hash, arena, event_id_to_issue, open_set, open_root); │ │205 - const closed_window = try loadWindow(repo_opts.hash, arena, event_id_to_issue, closed_set, closed_root); │ │209 + const open_window = try loadWindow(repo_opts.hash, arena, admin_moment, event_id_to_issue, open_set, open_root); │ │210 + const closed_window = try loadWindow(repo_opts.hash, arena, admin_moment, event_id_to_issue, closed_set, closed_root);│ │211 │ │212 // every tag in the repo, in the tag map's sorted order. the keys are │ │213 // "tag,status", so a tag's entries are adjacent and dedup by prefix. │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────╮ │270 fn loadWindow( │ │271 comptime hash_kind: hash.HashKind, │ │272 arena: *std.heap.ArenaAllocator, │ │273 + admin_moment: ?evt.AdminDB.HashMap(.read_only), │ │274 event_id_to_issue: evt.EventDB(hash_kind).HashMap(.read_only),│ │275 set_maybe: ?evt.EventDB(hash_kind).SortedSet(.read_only), │ │276 root_key: ?[]const u8, │ ╰───────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │314 } │ │315 const issue_cursor = try event_id_to_issue.getCursor(hash.hashInt(hash_kind, order_key[@sizeOf(u64)..])) orelse continue;│ │316 const issue_map = try DB.HashMap(.read_only).init(issue_cursor); │ │317 + const issue_event = try evt.read(evt.Issue, DB, hash_kind, arena, issue_map); │ │318 try issues.append(aa, .{ │ │319 .id = try aa.dupe(u8, &id_hex), │ │313 - .issue = try evt.read(evt.Issue, DB, hash_kind, arena, issue_map), │ │320 + .issue = issue_event, │ │321 + .author = try ui.Author.initFromDb(admin_moment, arena, issue_event.author_email), │ │322 }); │ │323 } │ │324 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │338 data: *const Self, │ │339 session: *ui.Session, │ │340 // per-split state, indexed like the stack's split children: the issue the│ │333 - // pane shows, and its description text box's focus id. │ │341 + // pane shows, and its description and author text boxes' focus ids. │ │342 detailed_index: [split_count]?usize, │ │343 description_id: [split_count]?usize, │ │344 + author_id: [split_count]?usize, │ │345 │ │346 const header_index: usize = 0; │ │347 const stack_index: usize = 1; │ ╰───────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────╮ │455 .session = session, │ │456 .detailed_index = .{ null, null },│ │457 .description_id = .{ null, null },│ │458 + .author_id = .{ null, null }, │ │459 }; │ │460 } │ │461 │ ╰───────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │811 for (inner.children.values()) |*child| child.widget.deinit(allocator);│ │812 inner.children.clearAndFree(allocator); │ │813 inner.getFocus().child_id = null; │ │814 + self.author_id[index] = null; │ │815 │ │816 // the tool row: the open/close and edit buttons; the description page│ │817 // shows none. │ ╰───────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │879 tb.getFocus().focusable = true; │ │880 try inner.children.put(allocator, tb.getFocus().id, .{ .widget = .{ .text_box = tb }, .rect = null, .min_size = null });│ │881 } │ │882 + │ │883 + self.author_id[index] = blk: { │ │884 + var tb = try ui.authorBox(allocator, self.session.page_arena, entry.author); │ │885 + errdefer tb.deinit(allocator); │ │886 + try inner.children.put(allocator, tb.getFocus().id, .{ .widget = .{ .text_box = tb }, .rect = null, .min_size = null });│ │887 + break :blk tb.getFocus().id; │ │888 + }; │ │889 │ │890 // the issue's tags, each linking to this status's list filtered to │ │891 // that tag. │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────╮ │1015 try self.toolRowInput(allocator, index, key, root_focus);│ │1016 } else if (self.titleFocused(index)) { │ │1017 try self.titleInput(index, key, root_focus); │ │1018 + } else if (self.authorFocused(index)) { │ │1019 + try self.authorInput(index, key, root_focus); │ │1020 } else if (self.tagsFocused(index)) { │ │1021 try self.tagsInput(index, key, root_focus); │ │1022 } else { │ ╰───────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯