╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──────╮ │src/ui/Repo.zig│ ╰───────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │286 } │ │287 // tabs switch in-page, so every tab's data is read here │ │288 if (repo_kind == .xit and undo_allowed) { │ │289 - undo_data = Undo.init(opened.self_repo_opts, arena, opened, repo_identity.identity, undo_index) catch |err| switch (err) { │ │289 + undo_data = Undo.init(opened.self_repo_opts, arena, opened, session.haxy_moment, repo_identity.identity, undo_index) catch |err| switch (err) {│ │290 error.OutOfMemory => return err, │ │291 else => .{ .identity = repo_identity.identity, .failure = @errorName(err) }, │ │292 }; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ───────────╮ │src/ui/Repo/Undo.zig│ ╰────────────────────╯ ╭───────────────────────────────────────────╮ │ 2 const builtin = @import("builtin"); │ │ 3 const ui = @import("../../ui.zig"); │ │ 4 const evt = @import("../../event.zig");│ │ 5 +const pch = @import("../../patch.zig");│ │ 6 +const push = @import("../../push.zig");│ │ 7 +const fork = @import("../../fork.zig");│ │ 8 const inp = @import("../input.zig"); │ │ 9 const xit = @import("xit"); │ │10 const rp = xit.repo; │ ╰───────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────╮ │18 │ │19 pub const page_size = 20; │ │20 │ │18 -// replaces the description for a transaction that wrote events │ │19 -pub const EventsLink = struct { │ │21 +// stands in for the description, as a focusable box that may link somewhere│ │22 +pub const DetailButton = struct { │ │23 label: []const u8, │ │21 - link: []const u8, │ │24 + text: []const u8, │ │25 + link: ?[]const u8 = null, │ │26 }; │ │27 │ │28 pub const Item = struct { │ ╰────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────╮ │32 timestamp: []const u8, │ │33 link: []const u8, │ │34 form: []const u8, │ │31 - events: ?EventsLink = null, │ │35 + button: ?DetailButton = null,│ │36 }; │ │37 │ │38 identity: []const u8, │ ╰─────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │44 // the clear-history confirmation stands in for the list │ │45 clear: bool = false, │ │46 │ │43 -pub fn init(comptime opts: rp.RepoOpts(.xit), arena: *std.heap.ArenaAllocator, repo: *rp.Repo(.xit, opts), identity: []const u8, selected: ?u64) !Self { │ │47 +pub fn init(comptime opts: rp.RepoOpts(.xit), arena: *std.heap.ArenaAllocator, repo: *rp.Repo(.xit, opts), haxy_moment: ?evt.AdminDB.HashMap(.read_only), identity: []const u8, selected: ?u64) !Self {│ │48 const aa = arena.allocator(); │ │49 const DB = rp.Repo(.xit, opts).DB; │ │50 const history = try DB.ArrayList(.read_only).init(repo.core.db.rootCursor().readOnly()); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │59 const moment = try DB.HashMap(.read_only).init(cursor); │ │60 const record = try xit.undo.read(opts, moment, &buffer); │ │61 var description: std.Io.Writer.Allocating = .init(aa); │ │58 - const detail = if (record) |value| try eventDetail(opts, arena, identity, value, moment, remaining) else null; │ │62 + const detail = if (record) |value| try eventDetail(opts, arena, haxy_moment, identity, value, moment, remaining) else null;│ │63 if (detail) |value| { │ │64 try description.writer.writeAll(value.description); │ │65 } else if (record) |value| { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │73 .index = remaining, │ │74 .action = if (detail) |value| value.action else if (record) |value| try aa.dupe(u8, value.action) else "unknown",│ │75 .description = description.written(), │ │72 - .events = if (detail) |value| value.events else null, │ │76 + .button = if (detail) |value| value.button else null, │ │77 .timestamp = if (record) |value| try formatTimestamp(aa, value.timestamp) else "timestamp unavailable", │ │78 .link = try std.fmt.allocPrint(aa, "ai:{s}", .{url}), │ │79 .form = try std.fmt.allocPrint(aa, "form:{s}/undo", .{url}), │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │85 const Detail = struct { │ │86 action: []const u8, │ │87 description: []const u8, │ │84 - events: ?EventsLink = null, │ │88 + button: ?DetailButton = null, │ │89 }; │ │90 │ │91 // every haxy event transaction shares one action, and the events it wrote are│ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────╮ │93 fn eventDetail( │ │94 comptime opts: rp.RepoOpts(.xit), │ │95 arena: *std.heap.ArenaAllocator, │ │96 + haxy_moment: ?evt.AdminDB.HashMap(.read_only), │ │97 identity: []const u8, │ │98 record: xit.undo.UndoRecord, │ │99 moment: rp.Repo(.xit, opts).DB.HashMap(.read_only),│ ╰───────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │100 history_index: u64, │ │101 ) !?Detail { │ │102 if (std.mem.eql(u8, record.action, evt.merge_undo_action)) return .{ .action = "merge events", .description = "merged the events ref from a remote" }; │ │103 + if (std.mem.eql(u8, record.action, pch.merge_undo_action)) return .{ .action = "merge patch", .description = "merged a patch into its target branch" };│ │104 + if (std.mem.eql(u8, record.action, fork.undo_action)) return .{ .action = "fork", .description = "created a fork to draft a patch in" }; │ │105 + if (std.mem.eql(u8, record.action, push.undo_action)) return .{ │ │106 + .action = "push", │ │107 + .description = "received a push", │ │108 + .button = try pushUser(arena, haxy_moment, record.payload), │ │109 + }; │ │110 if (!std.mem.eql(u8, record.action, evt.undo_action)) return null; │ │111 │ │112 const count = try momentEventCount(opts, moment) orelse return .{ .action = "events", .description = "updated the event database" }; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯