╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ─────────────╮ │src/ui/Repo/Issues.zig│ ╰──────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │302 } │ │303 } │ │304 │ │299 - const open_window = try loadWindow(repo_opts.hash, arena, admin_moment, haxy_moment, event_id_to_issue, open_set, open_root, conflict_set, empty.selected_id, comments_start); │ │300 - const closed_window = try loadWindow(repo_opts.hash, arena, admin_moment, haxy_moment, event_id_to_issue, closed_set, closed_root, conflict_set, empty.selected_id, comments_start); │ │301 - const conflicts_window = try loadWindow(repo_opts.hash, arena, admin_moment, haxy_moment, event_id_to_issue, conflict_set, conflicts_root, conflict_set, empty.selected_id, comments_start); │ │305 + const thread_comments_start = if (empty.comment_id.len == 0) comments_start else 0; │ │306 + const open_window = try loadWindow(repo_opts.hash, arena, admin_moment, haxy_moment, event_id_to_issue, open_set, open_root, conflict_set, empty.selected_id, thread_comments_start); │ │307 + const closed_window = try loadWindow(repo_opts.hash, arena, admin_moment, haxy_moment, event_id_to_issue, closed_set, closed_root, conflict_set, empty.selected_id, thread_comments_start); │ │308 + const conflicts_window = try loadWindow(repo_opts.hash, arena, admin_moment, haxy_moment, event_id_to_issue, conflict_set, conflicts_root, conflict_set, empty.selected_id, thread_comments_start);│ │309 if (view == .conflicts and conflicts_window.count > 0) resolved_view = .conflicts; │ │310 + │ │311 + const comment_page = if (empty.comment_id.len == 0) │ │312 + null │ │313 + else │ │314 + try Comment.init(repo_opts.hash, arena, admin_moment, haxy_moment, empty.selected_id, empty.comment_id, comments_start); │ │315 │ │316 // every tag in the repo, in the tag map's sorted order. the keys are │ │317 // "tag,status", so a tag's entries are adjacent and dedup by prefix. │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────╮ │337 .identity = empty.identity, │ │338 .tag = empty.tag, │ │339 .selected_id = empty.selected_id,│ │340 + .comment_id = empty.comment_id, │ │341 .comments_start = comments_start,│ │342 + .comment_page = comment_page, │ │343 .open = open_window, │ │344 .closed = closed_window, │ │345 .conflicts = conflicts_window, │ ╰──────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ │434 .issue = issue_event, │ │435 .author = try ui.Author.initFromEmail(admin_moment, arena, issue_event.author_email),│ │436 .conflicted = if (conflict_set) |cs| try cs.contains(&order_key) else false, │ │423 - .comments = try Comments.loadWindow( │ │437 + .comments = try Comment.loadWindow( │ │438 hash_kind, │ │439 arena, │ │440 admin_moment, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1193 else if (index == viewIndex(self.data.view) and self.data.selected_id.len != 0) │ │1194 (if (self.data.description_page) │ │1195 ui.RoutablePage.repoIssuesDescriptionRoute(self.data.identity, self.data.selected_id) │ │1196 + else if (self.data.comment_page) |page| │ │1197 + ui.RoutablePage.repoCommentsRoute(self.data.identity, self.data.selected_id, page.selected.id, page.replies.start)│ │1198 else │ │1199 ui.RoutablePage.repoIssueCommentsRoute(self.data.identity, self.data.selected_id, self.data.comments_start)) │ │1200 else if (index == tags_view_index) │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1218 const entry = &self.window(i).issues[sel]; │ │1219 const mirrored = if (self.data.description_page and std.mem.eql(u8, entry.id, self.data.selected_id)) │ │1220 ui.RoutablePage.repoIssuesDescriptionRoute(self.data.identity, entry.id) │ │1221 + else if (self.data.comment_page) |page| │ │1222 + if (std.mem.eql(u8, entry.id, self.data.selected_id)) │ │1223 + ui.RoutablePage.repoCommentsRoute(self.data.identity, entry.id, page.selected.id, page.replies.start)│ │1224 + else │ │1225 + ui.RoutablePage.repoIssueCommentsRoute(self.data.identity, entry.id, 0) │ │1226 else if (std.mem.eql(u8, entry.id, self.data.selected_id)) │ │1227 ui.RoutablePage.repoIssueCommentsRoute(self.data.identity, entry.id, entry.comments.start) │ │1228 else │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1311 // the /description page replaces its issue's detail with a back link │ │1312 // and the whole description; other issues keep their normal detail. │ │1313 const description_page = self.data.description_page and std.mem.eql(u8, entry.id, self.data.selected_id); │ │1314 + const comment_page = if (std.mem.eql(u8, entry.id, self.data.selected_id)) self.data.comment_page else null;│ │1315 │ │1316 for (inner.children.values()) |*child| child.widget.deinit(allocator); │ │1317 inner.children.clearAndFree(allocator); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────╮ │1327 row.getFocus().child_id = null; │ │1328 row.getFocus().kind = .container; │ │1329 } │ │1308 - if (!description_page) { │ │1330 + if (!description_page and comment_page == null) { │ │1331 const row = self.toolRow(index); │ │1332 const pa = self.session.page_arena.allocator();│ │1333 │ ╰─────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1384 // the resolve button on a conflicted issue, else the open/close │ │1385 // button when present, the edit button otherwise │ │1386 row.getFocus().child_id = row.children.keys()[button_in_row_index]; │ │1387 + } │ │1388 + │ │1389 + if (comment_page) |page| { │ │1390 + var back = try Comment.linkBox(allocator, self.session, "← back to issue", ui.RoutablePage.repoIssueCommentsRoute(self.data.identity, entry.id, 0) orelse return error.RouteTooLong);│ │1391 + errdefer back.deinit(allocator); │ │1392 + try inner.children.put(allocator, back.getFocus().id, .{ .widget = .{ .text_box = back }, .rect = null, .min_size = null }); │ │1393 + │ │1394 + try Comment.appendComment(allocator, inner, self.session, self.data.identity, page.selected); │ │1395 + try Comment.appendCount(allocator, inner, page.replies.count, "reply", "replies", self.session.page_arena.allocator()); │ │1396 + for (page.replies.comments) |comment| try Comment.appendComment(allocator, inner, self.session, self.data.identity, comment); │ │1397 + try Comment.appendWindowNav(allocator, inner, self.session, self.data.identity, page.thread_id, page.selected.id, page.replies); │ │1398 + │ │1399 + var spacer = try ui.Spacer.init(allocator); │ │1400 + errdefer spacer.deinit(allocator); │ │1401 + try inner.children.put(allocator, spacer.getFocus().id, .{ .widget = .{ .spacer = spacer }, .rect = null, .min_size = null }); │ │1402 + │ │1403 + inner.getFocus().child_id = inner.children.keys()[0]; │ │1404 + const sc = self.detailScroll(index); │ │1405 + sc.x = 0; │ │1406 + sc.y = 0; │ │1407 + sc.getFocus().version +%= 1; │ │1408 + return; │ │1409 } │ │1410 │ │1411 if (description_page) { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1478 }; │ │1479 │ │1480 if (!description_page) { │ │1437 - try Comments.appendCount(allocator, inner, entry.comments.count, "comment", "comments", self.session.page_arena.allocator()); │ │1438 - for (entry.comments.comments) |comment| try Comments.appendComment(allocator, inner, self.session, self.data.identity, comment);│ │1439 - try Comments.appendWindowNav(allocator, inner, self.session, self.data.identity, entry.id, null, entry.comments); │ │1481 + try Comment.appendCount(allocator, inner, entry.comments.count, "comment", "comments", self.session.page_arena.allocator()); │ │1482 + for (entry.comments.comments) |comment| try Comment.appendComment(allocator, inner, self.session, self.data.identity, comment); │ │1483 + try Comment.appendWindowNav(allocator, inner, self.session, self.data.identity, entry.id, null, entry.comments); │ │1484 + │ │1485 + var spacer = try ui.Spacer.init(allocator); │ │1486 + errdefer spacer.deinit(allocator); │ │1487 + try inner.children.put(allocator, spacer.getFocus().id, .{ .widget = .{ .spacer = spacer }, .rect = null, .min_size = null }); │ │1488 } │ │1489 │ │1490 // select the title by default │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1578 } │ │1579 │ │1580 fn detailInput(self: *View, allocator: std.mem.Allocator, index: usize, key: Key, root_focus: *Focus) !void { │ │1533 - if (self.toolRowFocused(index)) { │ │1581 + if (self.data.comment_page != null and std.mem.eql(u8, self.window(index).issues[self.detailed_index[index] orelse return].id, self.data.selected_id)) {│ │1582 + try self.commentInput(index, key, root_focus); │ │1583 + } else if (self.toolRowFocused(index)) { │ │1584 try self.toolRowInput(allocator, index, key, root_focus); │ │1585 } else if (self.titleFocused(index)) { │ │1586 try self.titleInput(index, key, root_focus); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯