╭ changed ─────────────╮ │src/ui/Repo/Issues.zig│ ╰──────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │70 }; │ │71 } │ │72 │ │73 +// the issue `selected_id` names, in whichever window holds it. │ │74 +pub fn selectedIssue(self: *const Self) ?*const IssueWithId { │ │75 + if (self.selected_id.len == 0) return null; │ │76 + for ([_]*const Window{ &self.open, &self.closed }) |win| { │ │77 + for (win.issues) |*entry| { │ │78 + if (std.mem.eql(u8, entry.id, self.selected_id)) return entry; │ │79 + } │ │80 + } │ │81 + return null; │ │82 +} │ │83 + │ │84 // an empty listing, for the wasm / no-repo paths. │ │85 pub fn emptyResult(aa: std.mem.Allocator, identity: []const u8, tag: []const u8, selected_id: []const u8, view: ui.RoutablePage.IssuesView) !Self {│ │86 return .{ │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │181 if (!try set.contains(order_key)) return error.NotFound; │ │182 │ │183 switch (issue_event.status) { │ │173 - .open => { │ │174 - open_root = order_key; │ │175 - resolved_view = .open; │ │176 - }, │ │177 - .closed => { │ │178 - closed_root = order_key; │ │179 - resolved_view = .closed; │ │180 - }, │ │184 + .open => open_root = order_key, │ │185 + .closed => closed_root = order_key, │ │186 } │ │187 + // an edit url keeps its view; otherwise the issue's status picks it. │ │188 + if (empty.view != .edit) resolved_view = switch (issue_event.status) { │ │189 + .open => .open, │ │190 + .closed => .closed, │ │191 + }; │ │192 } │ │193 │ │194 const open_window = try loadWindow(repo_opts.hash, arena, event_id_to_issue, open_set, open_root);│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────╮ │329 const open_view_index: usize = 0; │ │330 const closed_view_index: usize = 1; │ │331 const tags_view_index: usize = 2; │ │322 - const new_view_index: usize = 3; │ │332 + // the new-issue form, or the edit form when the page was loaded at an│ │333 + // edit url. │ │334 + const form_view_index: usize = 3; │ │335 const split_count: usize = 2; │ │336 // indices within a split (the horizontal box inside the stack). │ │337 const list_index: usize = 0; │ ╰───────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────╮ │349 .open => open_view_index, │ │350 .closed => closed_view_index, │ │351 .tags => tags_view_index, │ │340 - .new => new_view_index, │ │352 + .new, .edit => form_view_index,│ │353 }; │ │354 } │ │355 │ ╰────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │399 try stack.children.put(allocator, tf.getFocus().id, .{ .tag_flow = tf }); │ │400 } │ │401 │ │390 - // the new-issue form │ │402 + // the new-issue form, or — on an edit url — the edit form in its │ │403 + // place, prefilled with the selected issue's content. │ │404 { │ │392 - var form = try initNewForm(allocator, session, data); │ │405 + const aa = session.page_arena.allocator(); │ │406 + const action = if (data.view == .edit) │ │407 + (if (data.identity.len == 0) │ │408 + try std.fmt.allocPrint(aa, "form:/issues/{s}/edit", .{data.selected_id}) │ │409 + else │ │410 + try std.fmt.allocPrint(aa, "form:/repo/{s}/issues/{s}/edit", .{ data.identity, data.selected_id }))│ │411 + else if (data.identity.len == 0) │ │412 + "form:/issue" │ │413 + else │ │414 + try std.fmt.allocPrint(aa, "form:/repo/{s}/issue", .{data.identity}); │ │415 + const issue = if (data.view == .edit) │ │416 + (if (data.selectedIssue()) |entry| &entry.issue else null) │ │417 + else │ │418 + null; │ │419 + var form = try initIssueForm(allocator, action, issue); │ │420 errdefer form.deinit(allocator); │ │421 try stack.children.put(allocator, form.getFocus().id, .{ .box = form }); │ │422 } │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │503 return box; │ │504 } │ │505 │ │479 - // the new-issue form: title/tags/description inputs and a submit button. │ │480 - // its form: subtree makes the web overlay wrap them in a <form> POSTing to │ │481 - // the issue route. │ │482 - fn initNewForm(allocator: std.mem.Allocator, session: *ui.Session, data: *const Self) !wgt.Box(ui.Widget) { │ │506 + // an issue form: title/tags/description inputs and a submit button, │ │507 + // prefilled from `issue` when given. its form: subtree makes the web │ │508 + // overlay wrap them in a <form> POSTing to `action`'s route. │ │509 + fn initIssueForm(allocator: std.mem.Allocator, action: []const u8, issue: ?*const evt.Issue) !wgt.Box(ui.Widget) { │ │510 var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .vert }); │ │511 errdefer box.deinit(allocator); │ │485 - box.getFocus().kind = .{ .custom = if (data.identity.len == 0) │ │486 - "form:/issue" │ │487 - else │ │488 - try std.fmt.allocPrint(session.page_arena.allocator(), "form:/repo/{s}/issue", .{data.identity}) }; │ │512 + box.getFocus().kind = .{ .custom = action }; │ │513 │ │514 { │ │515 var title = try wgt.TextInput(ui.Widget).init(allocator, .{ .label = " title ", .name = "title", .visible_width = null, .rounded_corners = true, .render_content = !wasm });│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │516 errdefer title.deinit(allocator); │ │517 title.getFocus().focusable = true; │ │518 + if (issue) |i| try title.setContent(allocator, i.title); │ │519 try box.children.put(allocator, title.getFocus().id, .{ .widget = .{ .text_input = title }, .rect = null, .min_size = null });│ │520 } │ │521 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │523 var tags = try wgt.TextInput(ui.Widget).init(allocator, .{ .label = " tags (separate with spaces) ", .name = "tags", .visible_width = null, .rounded_corners = true, .render_content = !wasm });│ │524 errdefer tags.deinit(allocator); │ │525 tags.getFocus().focusable = true; │ │526 + if (issue) |i| try tags.setContent(allocator, i.tags); │ │527 try box.children.put(allocator, tags.getFocus().id, .{ .widget = .{ .text_input = tags }, .rect = null, .min_size = null }); │ │528 } │ │529 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │531 var description = try wgt.TextInput(ui.Widget).init(allocator, .{ .label = " description ", .name = "description", .visible_width = null, .rounded_corners = true, .render_content = !wasm, .multiline = true, .visible_height = 5 });│ │532 errdefer description.deinit(allocator); │ │533 description.getFocus().focusable = true; │ │534 + if (issue) |i| try description.setContent(allocator, i.description); │ │535 try box.children.put(allocator, description.getFocus().id, .{ .widget = .{ .text_input = description }, .rect = null, .min_size = null }); │ │536 } │ │537 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │587 return &self.viewStack().children.values()[tags_view_index].tag_flow;│ │588 } │ │589 │ │563 - // the new-issue form inside the stack. │ │564 - fn newForm(self: *View) *wgt.Box(ui.Widget) { │ │565 - return &self.viewStack().children.values()[new_view_index].box; │ │590 + // the new-issue (or edit) form inside the stack. │ │591 + fn issueForm(self: *View) *wgt.Box(ui.Widget) { │ │592 + return &self.viewStack().children.values()[form_view_index].box; │ │593 } │ │594 │ │595 fn listScroll(self: *View, index: usize) *wgt.Scroll(ui.Widget) { │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯