╭ changed ───────────╮ │src/ui/Repo/Undo.zig│ ╰────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────────╮ │32 index: u64, │ │33 action: []const u8, │ │34 description: []const u8, │ │35 - timestamp: []const u8, │ │35 + timestamp: i64, │ │36 link: []const u8, │ │37 form: []const u8, │ │38 buttons: []const DetailButton = &.{},│ ╰─────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 95 .action = shown.action, │ │ 96 .description = shown.description, │ │ 97 .buttons = shown.buttons, │ │ 98 - .timestamp = if (record) |value| try formatTimestamp(aa, value.timestamp) else "timestamp unavailable",│ │ 98 + .timestamp = if (record) |value| value.timestamp else -1, │ │ 99 .link = try aa.print("ai:{s}", .{url}), │ │100 .form = try aa.print("form:{s}/undo", .{url}), │ │101 .undone = row_undone, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │308 return try cursor.readUint(); │ │309 } │ │310 │ │311 -pub fn formatTimestamp(aa: std.mem.Allocator, timestamp: i64) ![]const u8 { │ │312 - if (timestamp < 0) return aa.dupe(u8, "timestamp unavailable"); │ │313 - const seconds = std.time.epoch.EpochSeconds{ .secs = @intCast(timestamp) }; │ │314 - const year = seconds.getEpochDay().calculateYearDay(); │ │315 - const month = year.calculateMonthDay(); │ │316 - const day = seconds.getDaySeconds(); │ │317 - const months = [_][]const u8{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; │ │318 - return aa.print("{s} {d}, {d}, {d:0>2}:{d:0>2}:{d:0>2} UTC", .{ months[month.month.numeric() - 1], month.day_index + 1, year.year, day.getHoursIntoDay(), day.getMinutesIntoHour(), day.getSecondsIntoMinute() });│ │319 -} │ │320 - │ │311 pub fn execute(io: std.Io, allocator: std.mem.Allocator, source: ui.RepoSource, index: u64) !void { │ │312 if (source.repo_kind != .xit) return error.NotFound; │ │313 var any_repo = try rp.AnyRepo(.xit, .{}).open(io, allocator, source.localInitOpts()); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────╮ │382 const detail_min_width = 40; │ │383 const header_index = 0; │ │384 const content_index = 1; │ │395 - // the undo button and the timestamp; everything below them is rebuilt │ │385 + // the undo button and timestamp stay; everything below them is rebuilt │ │386 const fixed_rows = 2; │ │387 │ │388 pub fn init(allocator: std.mem.Allocator, data: *const Self, session: *ui.Session) !View {│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │437 errdefer details.deinit(allocator); │ │438 if (data.items.len > 0) { │ │439 try addText(allocator, &details, "", null, .single); │ │450 - try addLabeled(allocator, &details, " timestamp ", "", null); │ │440 + try addText(allocator, &details, "", null, .single); │ │441 details.getFocus().child_id = details.children.keys()[0]; │ │442 } │ │443 break :blk try wgt.Scroll(ui.Widget).init(allocator, .{ .box = details }, .{ .direction = .vert, .web_native = !session.is_terminal, .fill = true });│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │481 try box.children.put(allocator, row.getFocus().id, .{ .widget = .{ .text_box = row }, .rect = null, .min_size = null }); │ │482 } │ │483 │ │494 - // a labeled row below the timestamp, added as the selected action needs it │ │484 + // a labeled box, added as the selected action needs it │ │485 fn addLabeled(allocator: std.mem.Allocator, box: *wgt.Box(ui.Widget), label: []const u8, text: []const u8, link: ?[]const u8) !void {│ │486 try addText(allocator, box, text, link, .single); │ │487 box.children.values()[box.children.count() - 1].widget.text_box.options.top_label.text = label; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │547 try button.setContent(allocator, if (item.index == 0) "initial state cannot be undone" else if (item.index == self.data.count - 1) "undo this" else "undo this and all above it");│ │548 button.getFocus().kind = if (enabled) .{ .custom = "submit" } else .text_box; │ │549 } │ │560 - try details.children.values()[1].widget.text_box.setContent(allocator, item.timestamp); │ │550 + const timestamp = try ui.timestamp.format(self.session.page_arena.allocator(), item.timestamp, try self.session.nowSeconds()); │ │551 + try details.children.values()[1].widget.text_box.setContent(allocator, timestamp.relative); │ │552 + details.children.values()[1].widget.text_box.options.bottom_label.text = timestamp.exact; │ │553 │ │554 // some actions put buttons where the description would go │ │555 if (item.buttons.len == 0) { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │614 return; │ │615 } │ │616 } │ │625 - if (key == .arrow_left) root_focus.setFocus(self.listScroll().getFocus().id) else if (inp.rowDelta(key, @intCast(details.children.count()))) |delta| ui.widget.moveRowFocus(details, self.detailScroll(), root_focus, delta);│ │617 + if (inp.rowDelta(key, @intCast(details.children.count()))) |delta| { │ │618 + ui.widget.moveRowFocus(details, self.detailScroll(), root_focus, delta); │ │619 + } else if (key == .arrow_left) { │ │620 + root_focus.setFocus(self.listScroll().getFocus().id); │ │621 + } │ │622 } else if (inp.rowDelta(key, @intCast(self.listScroll().child.box.children.count()))) |delta| { │ │623 ui.widget.moveRowFocus(&self.listScroll().child.box, self.listScroll(), root_focus, delta); │ │624 } else switch (key) { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯