╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ───────────────╮ │src/ui/widget/thread.zig│ ╰────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2481 if (comptime supports_drafts) { │ │2461 - const event_id_hex = try Data.createDraft(self.data, self.session, allocator, title, tags, description); │ │2482 + const event_id_hex = Data.createDraft(self.data, self.session, allocator, title, tags, description, target_branch) catch return;│ │2483 title_input.clear(allocator); │ │2484 tags_input.clear(allocator); │ │2485 description_input.clear(allocator); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2486 + form.children.values()[target_branch_field_index].widget.text_input.clear(allocator); │ │2487 const route = ui.RoutablePage.repoThreadCommentsRoute(kind, self.data.identity, &event_id_hex, 0) orelse return;│ │2488 try self.session.navigate(route); │ │2489 return; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2542 defer allocator.free(tags); │ │2543 const description = try description_input.text(allocator); │ │2544 defer allocator.free(description); │ │2545 + const target_branch = if (comptime supports_drafts) try form.children.values()[target_branch_field_index].widget.text_input.text(allocator) else "";│ │2546 + defer if (comptime supports_drafts) allocator.free(target_branch); │ │2547 │ │2548 if (!Event.fieldsValid(title, tags)) return; │ │2549 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2550 if (comptime supports_drafts) { │ │2551 if (entryDraft(entry.*)) { │ │2528 - try self.data.editDraft(self.session, allocator, entry.id, title, tags, description); │ │2552 + self.data.editDraft(self.session, allocator, entry.id, title, tags, description, target_branch) catch return;│ │2553 const route = listRoute(self.data.identity, entryStatus(entry.*), "", entry.id) orelse return; │ │2554 try self.session.navigate(route); │ │2555 return; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2565 var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, src.localInitOpts()); │ │2566 defer any_repo.deinit(io, allocator); │ │2567 switch (any_repo) { │ │2544 - inline else => |*repo| if (has_status) │ │2568 + inline else => |*repo| if (supports_drafts) { │ │2569 + try Event.update(repo_kind, repo.self_repo_opts, io, allocator, repo, &id_bytes, .{ .fields = .{│ │2570 + .title = title, │ │2571 + .tags = tags, │ │2572 + .description = description, │ │2573 + .target_branch = target_branch, │ │2574 + } }, author); │ │2575 + } else if (has_status) │ │2576 try Event.update(repo_kind, repo.self_repo_opts, io, allocator, repo, &id_bytes, .{ .fields = .{│ │2577 .title = title, │ │2578 .tags = tags, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──╮ │src/web.zig│ ╰───────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │522 // newlines so the text renders the same on every host │ │523 const description = try std.mem.replaceOwned(u8, allocator, description_crlf, "\r\n", "\n"); │ │524 defer allocator.free(description); │ │525 + const target_branch = (try parseFormField(allocator, body, "target_branch")) orelse try allocator.dupe(u8, "");│ │526 + defer allocator.free(target_branch); │ │527 │ │528 const valid = switch (kind) { │ │529 .issue => evt.Issue.fieldsValid(title, tags), │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │559 const user_id = request_author.user_id orelse return respondLoginRequired(request); │ │560 const request_repo = (try requestRepoSource(io, allocator, host, base)) orelse return respondRemoveNotFound(request); │ │561 defer request_repo.deinit(allocator); │ │562 + if (!try request_repo.source.hasBranch(io, allocator, target_branch)) { │ │563 + const form_location = try std.fmt.allocPrint(allocator, "{s}/patches/new", .{base}); │ │564 + defer allocator.free(form_location); │ │565 + try request.respond("", .{ .status = .see_other, .extra_headers = &.{.{ .name = "location", .value = form_location }} });│ │566 + return; │ │567 + } │ │568 const repo_id = evt.parseEventId(std.fs.path.basename(request_repo.source.path)) catch return respondRemoveNotFound(request);│ │569 var admin_repo = try rp.Repo(.xit, evt.admin_repo_opts).open(io, allocator, .{ .path = remote.admin_repo_path }); │ │570 defer admin_repo.deinit(io, allocator); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────╮ │577 .title = title, │ │578 .description = description, │ │579 .tags = tags, │ │580 + .target_branch = target_branch, │ │581 .author = author, │ │582 .timestamp = @intCast(std.Io.Timestamp.now(io, .real).toSeconds()),│ │583 }); │ ╰────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────╮ │1304 }); │ │1305 } │ │1306 │ │1298 -// replace the title, tags and description of a thread.│ │1307 +// replace a thread's editable fields. │ │1308 fn handleThreadEdit( │ │1309 io: std.Io, │ │1310 request: *std.http.Server.Request, │ ╰─────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1331 // newlines so the text renders the same on every host │ │1332 const description = try std.mem.replaceOwned(u8, allocator, description_crlf, "\r\n", "\n"); │ │1333 defer allocator.free(description); │ │1334 + const target_branch = (try parseFormField(allocator, body, "target_branch")) orelse try allocator.dupe(u8, "");│ │1335 + defer allocator.free(target_branch); │ │1336 │ │1337 // invalid fields send the user back to the edit form │ │1338 const valid = switch (parts.thread_kind) { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯