╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │385 .closed => .closed, │ │386 }, │ │387 } }; │ │388 - } │ │389 - │ │390 - // build a selected issue route at one of its comment windows. │ │391 - pub fn repoIssueCommentsRoute(identity: []const u8, selected: []const u8, start: usize) ?RoutablePage { │ │392 - var route = repoIssuesRoute(identity, .open, "", selected) orelse return null; │ │393 - route.repo_issues.comments_start = start; │ │394 - return route; │ │395 - } │ │396 - │ │397 - // build a comment permalink route at one of its immediate-reply windows. │ │398 - pub fn repoCommentsRoute(identity: []const u8, thread: []const u8, selected: []const u8, start: usize) ?RoutablePage {│ │399 - if (thread.len == 0 or selected.len == 0) return null; │ │400 - var route = repoIssueCommentsRoute(identity, thread, start) orelse return null; │ │401 - route.repo_issues.comment = Array(evt.event_id_size * 2).from(selected) orelse return null; │ │402 - return route; │ │388 } │ │389 │ │405 - // build the form route for replying to an issue or one of its comments. │ │406 - pub fn repoCommentNewRoute(identity: []const u8, thread: []const u8, parent_id: []const u8) ?RoutablePage { │ │407 - if (thread.len == 0) return null; │ │408 - var route = repoIssueCommentsRoute(identity, thread, 0) orelse return null; │ │409 - route.repo_issues.comment = Array(evt.event_id_size * 2).from(parent_id) orelse return null; │ │410 - route.repo_issues.view = .new_comment; │ │411 - return route; │ │412 - } │ │413 - │ │414 - // build the form route for editing a comment. │ │415 - pub fn repoCommentEditRoute(identity: []const u8, thread: []const u8, comment_id: []const u8) ?RoutablePage { │ │416 - if (thread.len == 0 or comment_id.len == 0) return null; │ │417 - var route = repoCommentsRoute(identity, thread, comment_id, 0) orelse return null; │ │418 - route.repo_issues.view = .edit_comment; │ │419 - return route; │ │420 - } │ │421 - │ │422 - // build a `.repo_issues` route showing tags view, keeping the url-encoded │ │423 - // `tag` filter ("" = unfiltered). │ │424 - pub fn repoIssuesTagsRoute(identity: []const u8, tag: []const u8) ?RoutablePage { │ │425 - return .{ .repo_issues = .{ │ │426 - .name = Array(repo_route_max_len).from(identity) orelse return null, │ │427 - .tag = Array(issue_tag_route_max_len).from(tag) orelse return null, │ │428 - .view = .tags, │ │429 - } }; │ │430 - } │ │431 - │ │432 - // build a `.repo_issues` route showing new-issue form. │ │433 - pub fn repoIssuesNewRoute(identity: []const u8) ?RoutablePage { │ │434 - return .{ .repo_issues = .{ │ │435 - .name = Array(repo_route_max_len).from(identity) orelse return null, │ │436 - .view = .new, │ │437 - } }; │ │438 - } │ │439 - │ │440 - // build a `.repo_issues` route showing the edit form for the issue with │ │441 - // hex event id `selected`. │ │442 - pub fn repoIssuesEditRoute(identity: []const u8, selected: []const u8) ?RoutablePage { │ │443 - if (selected.len == 0) return null; │ │444 - return .{ .repo_issues = .{ │ │445 - .name = Array(repo_route_max_len).from(identity) orelse return null, │ │446 - .selected = Array(evt.event_id_size * 2).from(selected) orelse return null, │ │447 - .view = .edit, │ │448 - } }; │ │449 - } │ │450 - │ │451 - // build a `.repo_issues` route showing the whole description of the issue │ │452 - // with hex event id `selected`. │ │453 - pub fn repoIssuesDescriptionRoute(identity: []const u8, selected: []const u8) ?RoutablePage { │ │454 - if (selected.len == 0) return null; │ │455 - return .{ .repo_issues = .{ │ │456 - .name = Array(repo_route_max_len).from(identity) orelse return null, │ │457 - .selected = Array(evt.event_id_size * 2).from(selected) orelse return null, │ │458 - .view = .description, │ │459 - } }; │ │460 - } │ │461 - │ │390 pub fn repoDiscussionsRoute(identity: []const u8, tag: []const u8, selected: []const u8) ?RoutablePage { │ │391 return .{ .repo_discussions = .{ │ │392 .name = Array(repo_route_max_len).from(identity) orelse return null, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │395 } }; │ │396 } │ │397 │ │470 - pub fn repoDiscussionCommentsRoute(identity: []const u8, selected: []const u8, start: usize) ?RoutablePage { │ │471 - var route = repoDiscussionsRoute(identity, "", selected) orelse return null; │ │472 - route.repo_discussions.comments_start = start; │ │398 + // build the default list route for a thread event kind. │ │399 + pub fn repoThreadRoute(kind: evt.EventKind, identity: []const u8, tag: []const u8, selected: []const u8) ?RoutablePage { │ │400 + return switch (kind) { │ │401 + .issue => repoIssuesRoute(identity, .open, tag, selected), │ │402 + .discussion => repoDiscussionsRoute(identity, tag, selected), │ │403 + else => null, │ │404 + }; │ │405 + } │ │406 + │ │407 + // build a selected thread route at one of its comment windows. │ │408 + pub fn repoThreadCommentsRoute(kind: evt.EventKind, identity: []const u8, selected: []const u8, start: usize) ?RoutablePage {│ │409 + var route = repoThreadRoute(kind, identity, "", selected) orelse return null; │ │410 + switch (route) { │ │411 + .repo_issues => |*thread| thread.comments_start = start, │ │412 + .repo_discussions => |*thread| thread.comments_start = start, │ │413 + else => unreachable, │ │414 + } │ │415 return route; │ │416 } │ │417 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │476 - pub fn repoDiscussionCommentRoute(identity: []const u8, discussion: []const u8, selected: []const u8, start: usize) ?RoutablePage { │ │477 - if (discussion.len == 0 or selected.len == 0) return null; │ │478 - var route = repoDiscussionCommentsRoute(identity, discussion, start) orelse return null; │ │479 - route.repo_discussions.comment = Array(evt.event_id_size * 2).from(selected) orelse return null; │ │418 + // build a comment permalink route at one of its immediate-reply windows. │ │419 + pub fn repoThreadCommentRoute(kind: evt.EventKind, identity: []const u8, thread_id: []const u8, comment_id: []const u8, start: usize) ?RoutablePage {│ │420 + if (thread_id.len == 0 or comment_id.len == 0) return null; │ │421 + var route = repoThreadCommentsRoute(kind, identity, thread_id, start) orelse return null; │ │422 + const comment = Array(evt.event_id_size * 2).from(comment_id) orelse return null; │ │423 + switch (route) { │ │424 + .repo_issues => |*thread| thread.comment = comment, │ │425 + .repo_discussions => |*thread| thread.comment = comment, │ │426 + else => unreachable, │ │427 + } │ │428 return route; │ │429 } │ │430 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │483 - pub fn repoDiscussionCommentNewRoute(identity: []const u8, discussion: []const u8, parent_id: []const u8) ?RoutablePage { │ │484 - if (discussion.len == 0) return null; │ │485 - var route = repoDiscussionCommentsRoute(identity, discussion, 0) orelse return null; │ │486 - route.repo_discussions.comment = Array(evt.event_id_size * 2).from(parent_id) orelse return null; │ │487 - route.repo_discussions.view = .new_comment; │ │431 + // build the form route for replying to a thread or one of its comments. │ │432 + pub fn repoThreadCommentNewRoute(kind: evt.EventKind, identity: []const u8, thread_id: []const u8, parent_id: []const u8) ?RoutablePage {│ │433 + if (thread_id.len == 0) return null; │ │434 + var route = repoThreadCommentsRoute(kind, identity, thread_id, 0) orelse return null; │ │435 + const parent_event_id = Array(evt.event_id_size * 2).from(parent_id) orelse return null; │ │436 + switch (route) { │ │437 + .repo_issues => |*thread| { │ │438 + thread.comment = parent_event_id; │ │439 + thread.view = .new_comment; │ │440 + }, │ │441 + .repo_discussions => |*thread| { │ │442 + thread.comment = parent_event_id; │ │443 + thread.view = .new_comment; │ │444 + }, │ │445 + else => unreachable, │ │446 + } │ │447 return route; │ │448 } │ │449 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │491 - pub fn repoDiscussionCommentEditRoute(identity: []const u8, discussion: []const u8, comment_id: []const u8) ?RoutablePage { │ │492 - if (discussion.len == 0 or comment_id.len == 0) return null; │ │493 - var route = repoDiscussionCommentRoute(identity, discussion, comment_id, 0) orelse return null; │ │494 - route.repo_discussions.view = .edit_comment; │ │450 + // build the form route for editing a comment. │ │451 + pub fn repoThreadCommentEditRoute(kind: evt.EventKind, identity: []const u8, thread_id: []const u8, comment_id: []const u8) ?RoutablePage {│ │452 + var route = repoThreadCommentRoute(kind, identity, thread_id, comment_id, 0) orelse return null; │ │453 + switch (route) { │ │454 + .repo_issues => |*thread| thread.view = .edit_comment, │ │455 + .repo_discussions => |*thread| thread.view = .edit_comment, │ │456 + else => unreachable, │ │457 + } │ │458 return route; │ │459 } │ │460 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │498 - pub fn repoDiscussionsTagsRoute(identity: []const u8, tag: []const u8) ?RoutablePage { │ │499 - return .{ .repo_discussions = .{ │ │500 - .name = Array(repo_route_max_len).from(identity) orelse return null, │ │501 - .tag = Array(discussion_tag_route_max_len).from(tag) orelse return null, │ │502 - .view = .tags, │ │503 - } }; │ │461 + // build a thread tags route, keeping its url-encoded tag filter. │ │462 + pub fn repoThreadTagsRoute(kind: evt.EventKind, identity: []const u8, tag: []const u8) ?RoutablePage { │ │463 + var route = repoThreadRoute(kind, identity, tag, "") orelse return null; │ │464 + switch (route) { │ │465 + .repo_issues => |*thread| thread.view = .tags, │ │466 + .repo_discussions => |*thread| thread.view = .tags, │ │467 + else => unreachable, │ │468 + } │ │469 + return route; │ │470 } │ │471 │ │506 - pub fn repoDiscussionsNewRoute(identity: []const u8) ?RoutablePage { │ │507 - return .{ .repo_discussions = .{ │ │508 - .name = Array(repo_route_max_len).from(identity) orelse return null, │ │509 - .view = .new, │ │510 - } }; │ │472 + // build a new-thread form route. │ │473 + pub fn repoThreadNewRoute(kind: evt.EventKind, identity: []const u8) ?RoutablePage { │ │474 + var route = repoThreadRoute(kind, identity, "", "") orelse return null; │ │475 + switch (route) { │ │476 + .repo_issues => |*thread| thread.view = .new, │ │477 + .repo_discussions => |*thread| thread.view = .new, │ │478 + else => unreachable, │ │479 + } │ │480 + return route; │ │481 } │ │482 │ │513 - pub fn repoDiscussionsEditRoute(identity: []const u8, selected: []const u8) ?RoutablePage { │ │483 + // build the edit form route for a thread. │ │484 + pub fn repoThreadEditRoute(kind: evt.EventKind, identity: []const u8, selected: []const u8) ?RoutablePage { │ │485 if (selected.len == 0) return null; │ │515 - return .{ .repo_discussions = .{ │ │516 - .name = Array(repo_route_max_len).from(identity) orelse return null, │ │517 - .selected = Array(evt.event_id_size * 2).from(selected) orelse return null, │ │518 - .view = .edit, │ │519 - } }; │ │486 + var route = repoThreadRoute(kind, identity, "", selected) orelse return null; │ │487 + switch (route) { │ │488 + .repo_issues => |*thread| thread.view = .edit, │ │489 + .repo_discussions => |*thread| thread.view = .edit, │ │490 + else => unreachable, │ │491 + } │ │492 + return route; │ │493 } │ │494 │ │522 - pub fn repoDiscussionsDescriptionRoute(identity: []const u8, selected: []const u8) ?RoutablePage { │ │495 + // build a route showing a thread's whole description. │ │496 + pub fn repoThreadDescriptionRoute(kind: evt.EventKind, identity: []const u8, selected: []const u8) ?RoutablePage {│ │497 if (selected.len == 0) return null; │ │524 - return .{ .repo_discussions = .{ │ │525 - .name = Array(repo_route_max_len).from(identity) orelse return null, │ │526 - .selected = Array(evt.event_id_size * 2).from(selected) orelse return null, │ │527 - .view = .description, │ │528 - } }; │ │498 + var route = repoThreadRoute(kind, identity, "", selected) orelse return null; │ │499 + switch (route) { │ │500 + .repo_issues => |*thread| thread.view = .description, │ │501 + .repo_discussions => |*thread| thread.view = .description, │ │502 + else => unreachable, │ │503 + } │ │504 + return route; │ │505 } │ │506 │ │507 // build a `.repo_issues` route showing the conflicts list, rooted at the │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │809 const word = params.scanTail(&segments) catch return null; │ │810 if (word) |last| { │ │811 if (!params.only(&.{})) return null; │ │836 - if (std.mem.eql(u8, last, "new")) return repoCommentNewRoute(pair, issue_id, comment_id); │ │837 - if (std.mem.eql(u8, last, "edit")) return repoCommentEditRoute(pair, issue_id, comment_id); │ │812 + if (std.mem.eql(u8, last, "new")) return repoThreadCommentNewRoute(.issue, pair, issue_id, comment_id); │ │813 + if (std.mem.eql(u8, last, "edit")) return repoThreadCommentEditRoute(.issue, pair, issue_id, comment_id);│ │814 return null; │ │815 } │ │816 if (!params.only(&.{.start})) return null; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │841 - return repoCommentsRoute(pair, issue_id, comment_id, params.start() orelse return null); │ │817 + return repoThreadCommentRoute(.issue, pair, issue_id, comment_id, params.start() orelse return null);│ │818 } │ │819 } │ │820 const word = params.scanTail(&segments) catch return null; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │821 if (word) |tail| { │ │846 - if (std.mem.eql(u8, tail, "new")) return if (params.only(&.{})) repoCommentNewRoute(pair, issue_id, "") else null; │ │847 - if (std.mem.eql(u8, tail, "edit")) return if (params.only(&.{})) repoIssuesEditRoute(pair, issue_id) else null; │ │848 - if (std.mem.eql(u8, tail, "description")) return if (params.only(&.{})) repoIssuesDescriptionRoute(pair, issue_id) else null; │ │822 + if (std.mem.eql(u8, tail, "new")) return if (params.only(&.{})) repoThreadCommentNewRoute(.issue, pair, issue_id, "") else null; │ │823 + if (std.mem.eql(u8, tail, "edit")) return if (params.only(&.{})) repoThreadEditRoute(.issue, pair, issue_id) else null; │ │824 + if (std.mem.eql(u8, tail, "description")) return if (params.only(&.{})) repoThreadDescriptionRoute(.issue, pair, issue_id) else null;│ │825 if (std.mem.eql(u8, tail, "resolve")) { │ │826 if (!params.only(&.{.theirs})) return null; │ │827 return repoIssuesResolveRoute(pair, issue_id, params.values.get(.theirs) orelse ""); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │829 return null; │ │830 } │ │831 if (!params.only(&.{.start})) return null; │ │856 - return repoIssueCommentsRoute(pair, issue_id, params.start() orelse return null); │ │832 + return repoThreadCommentsRoute(.issue, pair, issue_id, params.start() orelse return null);│ │833 } │ │834 if (std.mem.startsWith(u8, tab, discussion_seg)) { │ │835 const discussion_id = tab[discussion_seg.len..]; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯