╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──────────╮ │src/event/Issue.zig│ ╰───────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │123 } │ │124 │ │106 -// remove an issue's order key from each of its tags' sets, pruning tags whose│ │107 -// set becomes empty. │ │125 +// `status`'s sorted set within `statuses`, keyed by status name │ │126 +fn statusSet( │ │127 + comptime DB: type, │ │128 + statuses: DB.SortedMap(.read_write), │ │129 + status: Status, │ │130 +) !DB.SortedSet(.read_write) { │ │131 + const cursor = try statuses.putCursor(@tagName(status)); │ │132 + return DB.SortedSet(.read_write).init(cursor); │ │133 +} │ │134 + │ │135 +// remove an issue's order key from its tags' `status` sets, pruning entries │ │136 +// whose set becomes empty │ │137 fn removeFromTagSets( │ │138 comptime DB: type, │ │139 tag_to_issues: DB.SortedMap(.read_write), │ ╰───────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────╮ │140 tags: []const u8, │ │141 + status: Status, │ │142 order_key: []const u8, │ │143 ) !void { │ │144 var tag_iter = tagIterator(tags);│ ╰──────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────╮ │145 while (tag_iter.next()) |tag| { │ │146 if (tag.len == 0) continue; │ │117 - const tag_set_cursor = try tag_to_issues.putCursor(tag); │ │147 + var key_buffer: TagStatusKey = undefined; │ │148 + const key = try tagStatusKey(&key_buffer, tag, status); │ │149 + const tag_set_cursor = try tag_to_issues.putCursor(key); │ │150 const tag_set = try DB.SortedSet(.read_write).init(tag_set_cursor);│ │151 _ = try tag_set.remove(order_key); │ │120 - if (0 == try tag_set.count()) _ = try tag_to_issues.remove(tag); │ │152 + if (0 == try tag_set.count()) _ = try tag_to_issues.remove(key); │ │153 } │ │154 } │ │155 │ ╰────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────╮ │src/test/event.zig│ ╰──────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │505 │ │506 // the ordered issue set unions both branches' additions │ │507 { │ │508 - const issue_id_set_cursor = try haxy_moment.getCursor(hash.hashInt(repo_opts.hash, "issue-id-set")) orelse return error.NotFound; │ │508 + const status_to_issues_cursor = try haxy_moment.getCursor(hash.hashInt(repo_opts.hash, "status->issue-id-set")) orelse return error.NotFound;│ │509 + const status_to_issues = try Repo.DB.SortedMap(.read_only).init(status_to_issues_cursor); │ │510 + const issue_id_set_cursor = try status_to_issues.getCursor("open") orelse return error.NotFound; │ │511 const issue_id_set = try Repo.DB.SortedSet(.read_only).init(issue_id_set_cursor); │ │512 try std.testing.expectEqual(6, try issue_id_set.count()); │ │513 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──╮ │src/try.zig│ ╰───────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │370 title: []const u8, │ │371 description: []const u8, │ │372 tags: []const u8, │ │373 + status: evt.Issue.Status = .open, │ │374 }{ │ │375 .{ │ │376 .title = "Crash on startup when config file is missing",│ ╰─────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │386 .title = "Memory leak when reconnecting after network failure", │ │387 .description = "Each reconnect allocates a new connection state without freeing the previous one. After a flaky network session, memory usage grows by several megabytes per hour.",│ │388 .tags = "bug,memory,networking", │ │389 + .status = .closed, │ │390 }, │ │391 .{ │ │392 .title = "Document the plugin API", │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │422 .title = "Progress bar overshoots 100% during resumed downloads", │ │423 .description = "Resuming a partial download counts the already-downloaded bytes twice, so the progress bar reads up to 150%. Subtract the resume offset from the total.",│ │424 .tags = "bug,ui,downloads", │ │425 + .status = .closed, │ │426 }, │ │427 .{ │ │428 .title = "Config parser rejects trailing commas", │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │458 .title = "Log rotation deletes the newest file instead of the oldest", │ │459 .description = "When the log directory hits its size cap, the rotation logic sorts by name rather than mtime and removes the most recent log. Sort by modification time.",│ │460 .tags = "bug,logging", │ │461 + .status = .closed, │ │462 }, │ │463 .{ │ │464 .title = "Add man pages for all subcommands", │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────╮ │513 .title = issue.title, │ │514 .description = issue.description,│ │515 .tags = issue.tags, │ │516 + .status = issue.status, │ │517 }, │ │518 }, │ │519 }; │ ╰──────────────────────────────────────────────────────────────────╯ ╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭───────────────────────────────────────────────────────────────────────────────╮ │84 // the tag the list is filtered to, url-encoded ("" = unfiltered). │ │85 tag: Array(issue_tag_route_max_len) = .{}, │ │86 selected: Array(evt.event_id_size * 2) = .{}, │ │87 - view: enum { results, tags } = .results, │ │87 + // the view the url names; a route with `selected` derives its view│ │88 + // from that issue's status instead. │ │89 + view: IssuesView = .open, │ │90 }, │ │91 repo_settings: Array(repo_route_max_len), │ │92 repo_auth: Array(repo_route_max_len), │ ╰───────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯