╭ changed ──────────────╮ │src/ui/Repo/Patches.zig│ ╰───────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │150 return ui.RoutablePage.repoPatchPublishRoute(identity, selected); │ │151 } │ │152 │ │153 +pub fn mergeRoute(identity: []const u8, selected: []const u8) ?ui.RoutablePage {│ │154 + return ui.RoutablePage.repoPatchMergeRoute(identity, selected); │ │155 +} │ │156 + │ │157 +pub fn canMerge(entry: Entry, session: *const ui.Session) bool { │ │158 + return !entry.draft and │ │159 + entry.fork_exists and │ │160 + !entry.conflicted and │ │161 + entry.record.event.revision != null and │ │162 + entry.record.event.status == .open and │ │163 + !session.data.is_local and │ │164 + session.data.user_id != null; │ │165 +} │ │166 + │ │167 pub fn createDraft( │ │168 data: *const Self, │ │169 session: *ui.Session, │ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │216 .id = id_hex, │ │217 .user_id = user_id, │ │218 .repo_id = repo_id, │ │219 + .author = author, │ │220 + .timestamp = @intCast(std.Io.Timestamp.now(io, .real).toSeconds()), │ │221 + }); │ │222 +} │ │223 + │ │224 +pub fn mergePatch(data: *const Self, session: *ui.Session, allocator: std.mem.Allocator, id: []const u8, squash: bool) !void {│ │225 + const io = session.io orelse return error.NotFound; │ │226 + const repos_dir = session.repos_dir orelse return error.NotFound; │ │227 + const admin_repo = session.admin_repo orelse return error.NotFound; │ │228 + const repo_source = data.repo_source orelse return error.NotFound; │ │229 + if (repo_source.repo_kind != .xit) return error.NotFound; │ │230 + const patch_id = evt.parseEventId(id) catch return error.NotFound; │ │231 + const author = (try session.eventAuthor()) orelse return error.NotFound; │ │232 + │ │233 + const id_hex = std.fmt.bytesToHex(patch_id, .lower); │ │234 + var target_repo = try rp.Repo(.xit, .{}).open(io, allocator, repo_source.localInitOpts()); │ │235 + defer target_repo.deinit(io, allocator); │ │236 + try pch.mergeAndRemoveFork(.{}, io, allocator, repos_dir, admin_repo, &target_repo, .{ │ │237 + .id = id_hex, │ │238 + .revision = if (squash) .squash else .source, │ │239 .author = author, │ │240 .timestamp = @intCast(std.Io.Timestamp.now(io, .real).toSeconds()), │ │241 }); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │502 .merged => merged_root = order_key, │ │503 } │ │504 // form urls keep their view; otherwise the patch's status picks it. │ │471 - if (view != .edit and view != .new_comment and view != .edit_comment and view != .remove) resolved_view = switch (patch_event.event.status) { │ │505 + if (view != .edit and view != .merge and view != .new_comment and view != .edit_comment and view != .remove) resolved_view = switch (patch_event.event.status) {│ │506 .open => .open, │ │507 .closed => .closed, │ │508 .merged => .merged, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │824 const route = switch (data.view) { │ │825 .edit => ui.RoutablePage.repoThreadEditRoute(.patch, data.identity, data.selected_id) orelse return error.RouteTooLong, │ │826 .publish => ui.RoutablePage.repoPatchPublishRoute(data.identity, data.selected_id) orelse return error.RouteTooLong, │ │827 + .merge => ui.RoutablePage.repoPatchMergeRoute(data.identity, data.selected_id) orelse return error.RouteTooLong, │ │828 .new_comment => ui.RoutablePage.repoThreadCommentNewRoute(.patch, data.identity, data.selected_id, data.comment_id) orelse return error.RouteTooLong, │ │829 .edit_comment => ui.RoutablePage.repoThreadCommentEditRoute(.patch, data.identity, data.selected_id, data.comment_id) orelse return error.RouteTooLong,│ │830 .remove => ui.RoutablePage.repoThreadRemoveRoute(.patch, data.identity, data.selected_id, data.comment_id) orelse return error.RouteTooLong, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────╮ │835 const label: []const u8 = switch (data.view) {│ │836 .edit => "edit", │ │837 .publish => "publish", │ │838 + .merge => "merge", │ │839 .new_comment => "reply", │ │840 .edit_comment => "edit", │ │841 .remove => "remove", │ ╰───────────────────────────────────────────────────────────╯