╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──────────────╮ │src/ui/Repo/Threads.zig│ ╰───────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1619 try inner.children.put(allocator, tb.getFocus().id, .{ .widget = .{ .text_box = tb }, .rect = null, .min_size = null }); │ │1620 break :blk tb.getFocus().id; │ │1621 }; │ │1622 + │ │1623 + if (has_drafts and entryDraft(entry)) { │ │1624 + if (self.data.target_branch.len != 0 and (self.session.data.clone_http_port != null or self.session.data.clone_ssh_port != null)) { │ │1625 + var push = try ui.CloneUrl.View.initPush(allocator, self.session, self.data.identity, entry.id, self.data.target_branch); │ │1626 + errdefer push.deinit(allocator); │ │1627 + try inner.children.put(allocator, push.getFocus().id, .{ .widget = .{ .clone_url = push }, .rect = null, .min_size = null }); │ │1628 + } │ │1629 + inner.getFocus().child_id = inner.children.keys()[title_child_index]; │ │1630 + const sc = self.detailScroll(index); │ │1631 + sc.x = 0; │ │1632 + sc.y = 0; │ │1633 + sc.getFocus().version +%= 1; │ │1634 + return; │ │1635 + } │ │1636 │ │1637 const parent_route = commentsRoute(self.data.identity, entry.id, 0) orelse return error.RouteTooLong; │ │1638 try Attachment.appendRows(allocator, inner, self.session, self.data.identity, try parent_route.toUrl(self.session.page_arena), entry.attachments);│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1777 try self.tagsInput(index, key, root_focus); │ │1778 } else if (self.descriptionFocused(index)) { │ │1779 try self.descriptionInput(index, key, root_focus); │ │1358 - } else if (try self.attachmentInput(allocator, index, key, root_focus)) {} else { │ │1780 + } else if (try self.cloneUrlInput(allocator, index, key, root_focus)) {} else if (try self.attachmentInput(allocator, index, key, root_focus)) {} else {│ │1781 try self.commentInput(allocator, index, key, root_focus); │ │1782 + } │ │1783 + } │ │1784 + │ │1785 + fn cloneUrlInput(self: *This, allocator: std.mem.Allocator, index: usize, key: Key, root_focus: *Focus) !bool { │ │1786 + const child_index = self.focusedDetailChild(index, root_focus) orelse return false; │ │1787 + const child = &self.detailInner(index).children.values()[child_index].widget; │ │1788 + const clone_url = switch (child.*) { │ │1789 + .clone_url => |*view| view, │ │1790 + else => return false, │ │1791 + }; │ │1792 + switch (key) { │ │1793 + .arrow_up => _ = self.moveDetailVertical(index, root_focus, false), │ │1794 + .arrow_down => _ = self.moveDetailVertical(index, root_focus, true), │ │1795 + .arrow_left => if (clone_url.selected == 0) │ │1796 + try self.focusList(index, root_focus) │ │1797 + else │ │1798 + try clone_url.input(allocator, key, root_focus), │ │1799 + else => try clone_url.input(allocator, key, root_focus), │ │1800 } │ │1801 + return true; │ │1802 } │ │1803 │ │1804 fn attachmentInput(self: *This, allocator: std.mem.Allocator, index: usize, key: Key, root_focus: *Focus) !bool { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1823 return true; │ │1824 } │ │1825 │ │1385 - // the tool row: enter or a click on the open/close button flips the │ │1386 - // issue's status; the edit and resolve buttons are a: links the host │ │1387 - // follows. arrows cross between the buttons and to the neighboring │ │1388 - // widgets. │ │1826 + // enter or a click runs the primary action; links are handled by the host │ │1827 fn toolRowInput(self: *This, allocator: std.mem.Allocator, index: usize, key: Key, root_focus: *Focus) !void {│ │1828 const row = self.toolRow(index); │ │1829 const cur = if (row.getFocus().child_id) |cid| row.children.getIndex(cid) orelse return else return; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1392 - const on_status = if (self.status_button_id[index]) |id| row.getFocus().child_id == id else false; │ │1830 + const on_primary = if (self.primary_button_id[index]) |id| row.getFocus().child_id == id else false; │ │1831 switch (key) { │ │1832 .arrow_left => if (cur > first_in_row_index) root_focus.setFocus(row.children.keys()[cur - 1]) else try self.focusList(index, root_focus),│ │1833 .arrow_right => if (cur + 1 < row.children.count()) root_focus.setFocus(row.children.keys()[cur + 1]), │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1836 self.focusDetailEdge(index, root_focus, false); │ │1837 self.detailScroll(index).y = 0; │ │1838 }, │ │1401 - .enter => if (on_status) try self.toggleStatus(allocator, index), │ │1402 - .mouse => |mouse| if (self.status_button_id[index]) |id| { │ │1403 - if (inp.leftClickOn(root_focus, id, mouse)) try self.toggleStatus(allocator, index); │ │1839 + .enter => if (on_primary) try self.primaryAction(allocator, index), │ │1840 + .mouse => |mouse| if (self.primary_button_id[index]) |id| { │ │1841 + if (inp.leftClickOn(root_focus, id, mouse)) try self.primaryAction(allocator, index);│ │1842 }, │ │1843 else => {}, │ │1844 } │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1845 + } │ │1846 + │ │1847 + fn primaryAction(self: *This, allocator: std.mem.Allocator, index: usize) !void { │ │1848 + const selected = self.detailed_index[index] orelse return; │ │1849 + const entry = self.window(index).items[selected]; │ │1850 + if (has_drafts and entryDraft(entry)) │ │1851 + try self.postDraft(allocator, index) │ │1852 + else │ │1853 + try self.toggleStatus(allocator, index); │ │1854 + } │ │1855 + │ │1856 + fn postDraft(self: *This, allocator: std.mem.Allocator, index: usize) !void { │ │1857 + if (!has_drafts or comptime wasm) return; │ │1858 + const selected = self.detailed_index[index] orelse return; │ │1859 + const entry = self.window(index).items[selected]; │ │1860 + if (!entryDraft(entry) or !entry.revision_ready) return; │ │1861 + try self.data.postDraft(self.session, allocator, entry.id); │ │1862 + const route = listRoute(self.data.identity, @enumFromInt(0), "", entry.id) orelse return;│ │1863 + try self.session.navigate(route); │ │1864 } │ │1865 │ │1866 fn titleInput(self: *This, index: usize, key: Key, root_focus: *Focus) !void { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │2321 else => {}, │ │2322 }; │ │2323 │ │1867 - var id_bytes: [evt.event_id_size]u8 = undefined; │ │1868 - _ = try std.fmt.hexToBytes(&id_bytes, entry.id[0 .. evt.event_id_size * 2]);│ │2324 + const id_bytes = try evt.parseEventId(entry.id); │ │2325 │ │2326 switch (src.repo_kind) { │ │2327 inline else => |repo_kind| { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2328 var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, src.localInitOpts()); │ │2329 defer any_repo.deinit(io, allocator); │ │2330 switch (any_repo) { │ │1875 - inline else => |*repo| Event.update(repo_kind, repo.self_repo_opts, io, allocator, repo, &id_bytes, .{ .resolve = .{ │ │1876 - .title = title, │ │1877 - .tags = tags, │ │1878 - .hunks = hunks.items, │ │1879 - } }, author) catch |err| switch (err) { │ │1880 - // leave the form up for correction │ │1881 - error.InvalidFields => return, │ │1882 - else => |e| return e, │ │2331 + inline else => |*repo| { │ │2332 + const change: Event.Update = if (comptime kind == .patch) │ │2333 + .{ .resolve = .{ │ │2334 + .title = title, │ │2335 + .tags = tags, │ │2336 + .hunks = hunks.items, │ │2337 + .theirs = self.data.theirs_picks, │ │2338 + } } │ │2339 + else │ │2340 + .{ .resolve = .{ │ │2341 + .title = title, │ │2342 + .tags = tags, │ │2343 + .hunks = hunks.items, │ │2344 + } }; │ │2345 + Event.update(repo_kind, repo.self_repo_opts, io, allocator, repo, &id_bytes, change, author) catch |err| switch (err) {│ │2346 + // leave the form up for correction │ │2347 + error.InvalidFields => return, │ │2348 + else => |e| return e, │ │2349 + }; │ │2350 }, │ │2351 } │ │2352 }, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2384 defer any_repo.deinit(io, allocator); │ │2385 switch (any_repo) { │ │2386 inline else => |*repo| if (editing) { │ │1920 - var comment_id: [evt.event_id_size]u8 = undefined; │ │1921 - _ = std.fmt.hexToBytes(&comment_id, self.data.comment_id) catch return; │ │2387 + const comment_id = evt.parseEventId(self.data.comment_id) catch return; │ │2388 try evt.Comment.update(repo_kind, repo.self_repo_opts, io, allocator, repo, &thread_id, &comment_id, body, author);│ │2389 event_id_hex = std.fmt.bytesToHex(comment_id, .lower); │ │2390 } else { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2433 │ │2434 if (!Event.fieldsValid(title, tags)) return; │ │2435 │ │2436 + if (comptime kind == .patch) { │ │2437 + const event_id_hex = try Data.createDraft(self.data, self.session, allocator, title, tags, description);│ │2438 + title_input.clear(allocator); │ │2439 + tags_input.clear(allocator); │ │2440 + description_input.clear(allocator); │ │2441 + const route = commentsRoute(self.data.identity, &event_id_hex, 0) orelse return; │ │2442 + try self.session.navigate(route); │ │2443 + return; │ │2444 + } │ │2445 + │ │2446 var id_bytes: [evt.event_id_size]u8 = undefined; │ │2447 io.random(&id_bytes); │ │2448 const event_id_hex = std.fmt.bytesToHex(id_bytes, .lower); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯