╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──────────────╮ │src/ui/Repo/Patches.zig│ ╰───────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │260 return ui.RoutablePage.repoPatchesRoute(identity, status, tag, selected); │ │261 } │ │262 │ │263 +pub fn commitsRoute(allocator: std.mem.Allocator, identity: []const u8, entry: PatchWithId) !?ui.RoutablePage { │ │264 + if (entry.fork_exists and entry.record.event.status.kind() != .merged) │ │265 + return ui.RoutablePage.forkCommitsRoute(identity, entry.id, "", 0, "") orelse error.RouteTooLong; │ │266 + if (entry.revision_oid.len == 0 or entry.base_oid.len == 0) return null; │ │267 + // open patches follow the source branch; closed and merged patches keep their recorded revision │ │268 + if (entry.record.event.status.kind() == .open) if (entry.record.event.source_branch) |branch| { │ │269 + const encoded = try ui.urlEncodeRef(allocator, branch); │ │270 + defer allocator.free(encoded); │ │271 + return ui.RoutablePage.repoCommitsRoute(identity, .branch, encoded, 0, "", entry.base_oid) orelse error.RouteTooLong; │ │272 + }; │ │273 + return ui.RoutablePage.repoCommitsRoute(identity, .object, entry.revision_oid, 0, "", entry.base_oid) orelse error.RouteTooLong;│ │274 +} │ │275 + │ │276 pub fn draftsRoute(identity: []const u8) ?ui.RoutablePage { │ │277 return ui.RoutablePage.repoPatchesDraftsRoute(identity); │ │278 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │699 var closed_window = try thread.loadWindow(Self, repo_opts.hash, arena, admin_moment, haxy_moment, event_id_to_patch, closed_set, closed_root, conflict_set, empty.selected_id, thread_comments_start); │ │700 var merged_window = try thread.loadWindow(Self, repo_opts.hash, arena, admin_moment, haxy_moment, event_id_to_patch, merged_set, merged_root, conflict_set, empty.selected_id, thread_comments_start); │ │701 var conflicts_window = try thread.loadWindow(Self, repo_opts.hash, arena, admin_moment, haxy_moment, event_id_to_patch, conflict_set, conflicts_root, conflict_set, empty.selected_id, thread_comments_start);│ │688 - try setPatchDetails(repo_kind, repo_opts, io, arena, admin_moment, repo, &open_window); │ │689 - try setPatchDetails(repo_kind, repo_opts, io, arena, admin_moment, repo, &closed_window); │ │690 - try setPatchDetails(repo_kind, repo_opts, io, arena, admin_moment, repo, &merged_window); │ │691 - try setPatchDetails(repo_kind, repo_opts, io, arena, admin_moment, repo, &conflicts_window); │ │702 + try setPatchDetails(repo_kind, repo_opts, io, arena, admin_moment, haxy_moment, repo, &open_window); │ │703 + try setPatchDetails(repo_kind, repo_opts, io, arena, admin_moment, haxy_moment, repo, &closed_window); │ │704 + try setPatchDetails(repo_kind, repo_opts, io, arena, admin_moment, haxy_moment, repo, &merged_window); │ │705 + try setPatchDetails(repo_kind, repo_opts, io, arena, admin_moment, haxy_moment, repo, &conflicts_window); │ │706 if (view == .conflicts and conflicts_window.count > 0) resolved_view = .conflicts; │ │707 │ │708 const comment_page = if (empty.comment_id.len == 0) │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────╮ │739 io: std.Io, │ │740 arena: *std.heap.ArenaAllocator, │ │741 admin_moment: ?evt.AdminDB.HashMap(.read_only), │ │742 + haxy_moment: evt.EventDB(repo_opts.hash).HashMap(.read_only),│ │743 repo: *rp.Repo(repo_kind, repo_opts), │ │744 target: *Window, │ │745 ) !void { │ ╰──────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │746 if (target.items.len == 0) return; │ │747 const items = try arena.allocator().dupe(PatchWithId, target.items); │ │748 for (items) |*item| { │ │749 + const id = try evt.parseEventId(item.id); │ │750 + const status = item.record.event.status.kind(); │ │751 if (admin_moment) |moment| { │ │735 - const id = try evt.parseEventId(item.id); │ │752 const record = try evt.Fork.readById(evt.AdminDB, evt.admin_repo_opts.hash, moment, arena, &id);│ │753 item.fork_exists = if (record) |value| !value.removed else false; │ │754 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │759 .merged => |oid| oid, │ │760 else => revision.source_oid, │ │761 }; │ │746 - if (item.record.event.status.kind() == .merged) continue; │ │762 + if (!item.fork_exists or status == .merged) { │ │763 + const revision_id = try evt.parseEventId(&revision.id); │ │764 + if (try evt.PatchRev.readById(evt.EventDB(repo_opts.hash), repo_opts.hash, haxy_moment, arena, &revision_id)) |record| {│ │765 + item.base_oid = record.event.base_oid; │ │766 + } │ │767 + } │ │768 + if (status == .merged) { │ │769 + if (std.mem.eql(u8, item.revision_oid, revision.squash_oid)) item.commit_count = 1; │ │770 + continue; │ │771 + } │ │772 if (comptime repo_kind == .xit) { │ │773 var moment = try repo.core.latestMoment(); │ │774 const state = rp.Repo(.xit, repo_opts).State(.read_only){ .core = &repo.core, .extra = .{ .moment = &moment } }; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │750 - const id = try evt.parseEventId(item.id); │ │775 item.mergeability = pch.readMergeability(repo_opts, state, io, arena, &id, item.record.event) catch .{};│ │776 } │ │777 const target_oid = try repo.readRef(io, .{ .kind = .head, .name = target_branch }); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ───────────────╮ │src/ui/widget/thread.zig│ ╰────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────╮ │453 │ │454 fn entryDraft(entry: Entry) bool { │ │455 return if (supports_drafts) entry.draft else false; │ │456 - } │ │457 - │ │458 - fn entryHasFork(entry: Entry) bool { │ │459 - return if (supports_forks) entry.fork_exists else false;│ │456 } │ │457 │ │458 const StatusChange = struct { │ ╰─────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │532 const row = self.toolRow(); │ │533 const pa = self.session.page_arena.allocator(); │ │534 │ │539 - if (entryHasFork(entry) and self.session.data.current_page.parent() != .fork) { │ │540 - const diff_route = ui.RoutablePage.forkDiffRoute(self.data.identity, entry.id, 0, "") orelse return error.RouteTooLong; │ │541 - try addToolButton(allocator, row, "view diff", "", try std.fmt.allocPrint(pa, "a:{s}", .{try diff_route.toUrl(self.session.page_arena)})); │ │542 - const commits_route = ui.RoutablePage.forkCommitsRoute(self.data.identity, entry.id, "", 0, "") orelse return error.RouteTooLong; │ │543 - const commit_count = if (supports_forks) entry.commit_count else null; │ │544 - const label = if (commit_count) |count| try std.fmt.allocPrint(pa, "view commits ({d})", .{count}) else "view commits"; │ │545 - try addToolButton(allocator, row, label, "", try std.fmt.allocPrint(pa, "a:{s}", .{try commits_route.toUrl(self.session.page_arena)})); │ │535 + if (supports_forks and self.session.data.current_page.parent() != .fork) { │ │536 + if (entry.fork_exists) { │ │537 + const diff_route = ui.RoutablePage.forkDiffRoute(self.data.identity, entry.id, 0, "") orelse return error.RouteTooLong; │ │538 + try addToolButton(allocator, row, "view diff", "", try std.fmt.allocPrint(pa, "a:{s}", .{try diff_route.toUrl(self.session.page_arena)}));│ │539 + } │ │540 + if (try Data.commitsRoute(pa, self.data.identity, entry)) |route| { │ │541 + const label = if (entry.commit_count) |count| try std.fmt.allocPrint(pa, "view commits ({d})", .{count}) else "view commits"; │ │542 + try addToolButton(allocator, row, label, "", try std.fmt.allocPrint(pa, "a:{s}", .{try route.toUrl(self.session.page_arena)})); │ │543 + } │ │544 } │ │545 │ │546 { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │595 const route = ui.RoutablePage.repoThreadRemoveRoute(kind, self.data.identity, entry.id, "") orelse return error.RouteTooLong;│ │596 try addToolButton(allocator, row, "✕", "", try std.fmt.allocPrint(pa, "a:{s}", .{try route.toUrl(self.session.page_arena)}));│ │597 } │ │600 - } │ │601 - │ │602 - for (row.children.keys(), row.children.values()) |id, *child| { │ │603 - if (child.widget.getFocus().mode != .all) continue; │ │604 - row.getFocus().child_id = id; │ │605 - break; │ │598 } │ │599 } │ │600 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │749 if (child_id == self.title_id) { │ │750 switch (key) { │ │751 .arrow_left => self.exit = .list, │ │760 - .arrow_up => if (!self.moveVertical(root_focus, false)) self.focusToolRow(root_focus),│ │752 + .arrow_up => if (!self.moveVertical(root_focus, false)) { │ │753 + self.exit = .header; │ │754 + }, │ │755 .arrow_down => _ = self.moveVertical(root_focus, true), │ │756 else => {}, │ │757 } │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯