╭ changed ────────────╮ │src/event/Comment.zig│ ╰─────────────────────╯ ╭────────────────────────────────────────╮ │1 const std = @import("std"); │ │2 const evt = @import("../event.zig"); │ │3 const xit = @import("xit"); │ │4 +const rp = xit.repo; │ │5 const hash = xit.hash; │ │6 │ │7 thread_id: [evt.event_id_size * 2]u8,│ ╰────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────╮ │120 const replies = try DB.SortedSet(.read_write).init(replies_cursor); │ │121 try replies.put(&thread_order_key); │ │122 } │ │123 + │ │124 +// create a comment and return its event id. `repo` must be writable. │ │125 +pub fn create( │ │126 + comptime repo_kind: rp.RepoKind, │ │127 + comptime repo_opts: rp.RepoOpts(repo_kind), │ │128 + io: std.Io, │ │129 + allocator: std.mem.Allocator, │ │130 + repo: *rp.Repo(repo_kind, repo_opts), │ │131 + thread_id: *const [evt.event_id_size * 2]u8, │ │132 + parent_id: *const [evt.event_id_size * 2]u8, │ │133 + body: []const u8, │ │134 + author_email: []const u8, │ │135 +) ![evt.event_id_size * 2]u8 { │ │136 + if (!fieldsValid(body)) return error.InvalidFields; │ │137 │ │138 + var id_bytes: [evt.event_id_size]u8 = undefined; │ │139 + io.random(&id_bytes); │ │140 + const event_id = std.fmt.bytesToHex(id_bytes, .lower); │ │141 + try evt.consume(repo_kind, repo_opts, io, allocator, repo, evt.events_ref, &.{.{│ │142 + .id = event_id, │ │143 + .timestamp = @intCast(std.Io.Timestamp.now(io, .real).toSeconds()), │ │144 + .author_email = author_email, │ │145 + .event = .{ .comment = .{ │ │146 + .thread_id = thread_id.*, │ │147 + .parent_id = parent_id.*, │ │148 + .body = body, │ │149 + } }, │ │150 + }}); │ │151 + return event_id; │ │152 +} │ │153 + │ │154 // read a comment by event id, or null if the id isn't a known comment. field │ │155 // byte slices are allocated in `arena`. │ │156 pub fn readById( │ ╰─────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──────────────╮ │src/ui/Repo/Comment.zig│ ╰───────────────────────╯ ╭─────────────────────────────────────────╮ │13 pub const page_size = 10; │ │14 │ │15 pub const CommentWithId = struct { │ │16 - id: []const u8, │ │16 + id: [evt.event_id_size * 2]u8, │ │17 comment: evt.Comment.Record, │ │18 author: ui.Author = .unknown, │ │19 parent_author: ?ui.Author = null,│ ╰─────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │23 comments: []const CommentWithId, │ │24 start: usize, │ │25 count: usize, │ │26 - has_prev: bool, │ │27 - has_more: bool, │ │26 │ │29 - pub const empty: Window = .{ .comments = &.{}, .start = 0, .count = 0, .has_prev = false, .has_more = false };│ │27 + pub const empty: Window = .{ .comments = &.{}, .start = 0, .count = 0 }; │ │28 }; │ │29 │ │30 pub const Permalink = struct { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────╮ │33 - thread_id: []const u8, │ │31 selected: CommentWithId,│ │32 replies: Window, │ │33 }; │ ╰────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │46 if (!std.mem.eql(u8, &selected.comment.event.thread_id, thread_id)) return error.NotFound; │ │47 │ │48 return .{ │ │52 - .thread_id = try arena.allocator().dupe(u8, thread_id), │ │49 .selected = selected, │ │50 .replies = try loadWindow(hash_kind, arena, admin_moment, haxy_moment, evt.Comment.parent_id_to_comment_id_set_key, selected_id, start),│ │51 }; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ │66 .comments = &.{}, │ │67 .start = start, │ │68 .count = 0, │ │73 - .has_prev = start > 0, │ │74 - .has_more = false, │ │69 }; │ │70 const index = try DB.HashMap(.read_only).init(index_cursor); │ │71 const set_cursor = try index.getCursor(hash.hashInt(hash_kind, owner_id)) orelse return .{│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────╮ │72 .comments = &.{}, │ │73 .start = start, │ │74 .count = 0, │ │81 - .has_prev = start > 0, │ │82 - .has_more = false, │ │75 }; │ │76 const set = try DB.SortedSet(.read_only).init(set_cursor);│ │77 const count: usize = @intCast(try set.count()); │ ╰──────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────╮ │86 - const end = @min(start + page_size, count); │ │78 + const end = start + @min(page_size, count -| start);│ │79 var iter = try set.iteratorFromIndex(start); │ │80 var comments: std.ArrayList(CommentWithId) = .empty;│ │81 var i = start; │ ╰────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │84 const kv = try cursor.readKeyValuePair(); │ │85 var order_key: [@sizeOf(u64) + evt.event_id_size]u8 = undefined; │ │86 _ = try kv.key_cursor.readBytes(&order_key); │ │95 - const id_hex = std.fmt.bytesToHex(order_key[@sizeOf(u64)..].*, .lower); │ │96 - const entry = (try readOneBytes(hash_kind, arena, admin_moment, haxy_moment, order_key[@sizeOf(u64)..], &id_hex)) orelse continue;│ │87 + const id_bytes = order_key[@sizeOf(u64)..].*; │ │88 + const entry = (try readOneBytes(hash_kind, arena, admin_moment, haxy_moment, &id_bytes)) orelse continue; │ │89 try comments.append(arena.allocator(), entry); │ │90 } │ │91 return .{ │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯