╭ changed ─────────────╮ │src/ui/Repo/Events.zig│ ╰──────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │74 const gpa = arena.child_allocator; │ │75 var event_db_maybe: ?evt.LocalEventDB(repo_opts.hash) = if (repo_kind == .git) try evt.LocalEventDB(repo_opts.hash).openReadOnly(io, gpa, repo.core.repo_dir) else null;│ │76 defer if (event_db_maybe) |*event_db| event_db.deinit(io, gpa); │ │77 - const haxy_moment = (if (event_db_maybe) |*event_db| │ │77 + const haxy_moment = (if (moment_index) |index| │ │78 + transactionMoment(repo_kind, repo_opts, repo, index) │ │79 + else if (event_db_maybe) |*event_db| │ │80 evt.currentMomentFromDb(repo_opts.hash, event_db.db) │ │81 else if (repo_kind == .git) │ │82 return result │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │109 root_key = evt.orderKeyDesc(meta.updated_order, &id); │ │110 } │ │111 │ │110 - if (moment_index) |index| { │ │111 - try readMomentEvents(repo_kind, repo_opts, arena, &result, haxy_moment, kind_map, admin_moment, identity, index, selected);│ │112 + if (moment_index != null) { │ │113 + try readMomentEvents(repo_kind, repo_opts, arena, &result, haxy_moment, kind_map, admin_moment, identity, selected); │ │114 return result; │ │115 } │ │116 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │148 return result; │ │149 } │ │150 │ │151 +// the haxy state as of one transaction in the repo history. a moment page │ │152 +// reads from it, so undoing or rebasing later transactions cannot change what │ │153 +// it shows, and the events read as they were when the transaction ran │ │154 +fn transactionMoment( │ │155 + comptime repo_kind: rp.RepoKind, │ │156 + comptime repo_opts: rp.RepoOpts(repo_kind), │ │157 + repo: *rp.Repo(repo_kind, repo_opts), │ │158 + index: u64, │ │159 +) !evt.EventDB(repo_opts.hash).HashMap(.read_only) { │ │160 + if (repo_kind == .git) return error.NotFound; │ │161 + const DB = evt.EventDB(repo_opts.hash); │ │162 + const history = try DB.ArrayList(.read_only).init(repo.core.db.rootCursor().readOnly()); │ │163 + const cursor = try history.getCursor(index) orelse return error.NotFound; │ │164 + return try evt.currentMomentFromRepoMoment(repo_opts.hash, try DB.HashMap(.read_only).init(cursor));│ │165 +} │ │166 + │ │167 // the moment's set is keyed on the event order, not the update order the │ │168 // active and removed sets use, so it gets its own walk. one transaction's │ │169 // events are few, so the whole set is scanned to count both views. │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────╮ │176 kind_map: evt.EventDB(repo_opts.hash).HashMap(.read_only),│ │177 admin_moment: ?evt.AdminDB.HashMap(.read_only), │ │178 identity: []const u8, │ │161 - moment_index: u64, │ │179 selected: []const u8, │ │180 ) !void { │ │181 const DB = evt.EventDB(repo_opts.hash); │ ╰───────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │182 const aa = arena.allocator(); │ │166 - const ids = try evt.momentEventIds(DB, repo_opts.hash, haxy_moment, moment_index) orelse return error.NotFound; │ │183 + // the moment names its own position, so the page needs no index from the │ │184 + // url. a transaction that consumed nothing has no set of its own, which │ │185 + // leaves the list empty rather than failing the page: the terminal hosts │ │186 + // build their pages inside the run loop, where an error ends the session │ │187 + const index_cursor = try haxy_moment.getCursor(hash.hashInt(repo_opts.hash, evt.moment_index_key)) orelse return;│ │188 + const ids = try evt.momentEventIds(DB, repo_opts.hash, haxy_moment, try index_cursor.readUint()) orelse return; │ │189 │ │190 var events: [2]std.ArrayList(Item) = .{ .empty, .empty }; │ │191 // the selected event's window starts at it, not at the top │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │214 const item = (try readItem(repo_opts.hash, arena, haxy_moment, kind_map, admin_moment, identity, view == .active, kind, &id)) orelse continue;│ │215 try window_events.append(aa, item); │ │216 } │ │195 - if (awaiting_selected) return error.NotFound; │ │217 + // the window start can name an event this batch no longer holds │ │218 + if (awaiting_selected) return; │ │219 result.active.events = events[@intFromEnum(ui.RoutablePage.EventsView.active)].items; │ │220 result.removed.events = events[@intFromEnum(ui.RoutablePage.EventsView.removed)].items; │ │221 } │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯