╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──────────────╮ │src/ui/Repo/Comment.zig│ ╰───────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │132 admin_moment: ?evt.AdminDB.HashMap(.read_only), │ │133 haxy_moment: evt.EventDB(hash_kind).HashMap(.read_only), │ │134 id_bytes: *const [evt.event_id_size]u8, │ │135 + viewer: ?Viewer, │ │136 ) !?CommentWithId { │ │137 const DB = evt.EventDB(hash_kind); │ │138 const comment = (try evt.Comment.readById(DB, hash_kind, haxy_moment, arena, id_bytes)) orelse return null;│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ │146 .comment = comment, │ │147 .author = try ui.Author.initFromEmail(admin_moment, arena, comment.author_email), │ │148 .parent_author = parent_author, │ │149 + .can_modify = if (viewer) |v| v.role.canModify(v.email, comment.author_email) else false,│ │150 }; │ │151 } │ │152 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │165 const body_index: usize = 1; │ │166 const gap_index: usize = 2; │ │167 │ │146 - pub fn init(allocator: std.mem.Allocator, session: *ui.Session, identity: []const u8, thread_kind: evt.EventKind, entry: CommentWithId) !Item { │ │168 + pub fn init(allocator: std.mem.Allocator, session: *ui.Session, identity: []const u8, thread_kind: evt.EventKind, can_reply: bool, entry: CommentWithId) !Item {│ │169 var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .vert }); │ │170 errdefer box.deinit(allocator); │ │171 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │182 errdefer spacer.deinit(allocator); │ │183 try bar.children.put(allocator, spacer.getFocus().id, .{ .widget = .{ .spacer = spacer }, .rect = null, .min_size = null }); │ │184 │ │163 - var reply = try linkBox(allocator, session, "new reply", commentNewRoute(thread_kind, identity, &entry.comment.event.thread_id, &entry.id) orelse return error.RouteTooLong); │ │164 - errdefer reply.deinit(allocator); │ │165 - try bar.children.put(allocator, reply.getFocus().id, .{ .widget = .{ .text_box = reply }, .rect = null, .min_size = .{ .width = "new reply".len + 2, .height = null } }); │ │185 + if (can_reply) { │ │186 + var reply = try linkBox(allocator, session, "new reply", commentNewRoute(thread_kind, identity, &entry.comment.event.thread_id, &entry.id) orelse return error.RouteTooLong); │ │187 + errdefer reply.deinit(allocator); │ │188 + try bar.children.put(allocator, reply.getFocus().id, .{ .widget = .{ .text_box = reply }, .rect = null, .min_size = .{ .width = "new reply".len + 2, .height = null } }); │ │189 + } │ │190 │ │167 - if (!entry.comment.removed) { │ │191 + if (entry.can_modify and !entry.comment.removed) { │ │192 var edit = try linkBox(allocator, session, "edit comment", commentEditRoute(thread_kind, identity, &entry.comment.event.thread_id, &entry.id) orelse return error.RouteTooLong);│ │193 errdefer edit.deinit(allocator); │ │194 try bar.children.put(allocator, edit.getFocus().id, .{ .widget = .{ .text_box = edit }, .rect = null, .min_size = .{ .width = "edit comment".len + 2, .height = null } }); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │195 } │ │172 - │ │173 - var permalink = try linkBox(allocator, session, "permalink", commentsRoute(thread_kind, identity, &entry.comment.event.thread_id, &entry.id, 0) orelse return error.RouteTooLong);│ │174 - errdefer permalink.deinit(allocator); │ │175 - try bar.children.put(allocator, permalink.getFocus().id, .{ .widget = .{ .text_box = permalink }, .rect = null, .min_size = .{ .width = "permalink".len + 2, .height = null } }); │ │196 │ │197 if (!std.mem.eql(u8, &entry.comment.event.parent_id, &entry.comment.event.thread_id)) { │ │198 const parent_text = if (entry.parent_author) |parent_author| switch (parent_author) { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │212 try bar.children.put(allocator, parent.getFocus().id, .{ .widget = .{ .text_box = parent }, .rect = null, .min_size = .{ .width = @max(parent_text.len, " replying to ".len) + 2, .height = null } });│ │213 } │ │214 │ │195 - if (!entry.comment.removed) { │ │215 + var permalink = try linkBox(allocator, session, "permalink", commentsRoute(thread_kind, identity, &entry.comment.event.thread_id, &entry.id, 0) orelse return error.RouteTooLong); │ │216 + errdefer permalink.deinit(allocator); │ │217 + try bar.children.put(allocator, permalink.getFocus().id, .{ .widget = .{ .text_box = permalink }, .rect = null, .min_size = .{ .width = "permalink".len + 2, .height = null } }); │ │218 + │ │219 + if (entry.can_modify and !entry.comment.removed) { │ │220 var remove = try linkBox(allocator, session, "✕", removeRoute(thread_kind, identity, &entry.comment.event.thread_id, &entry.id) orelse return error.RouteTooLong); │ │221 errdefer remove.deinit(allocator); │ │222 try bar.children.put(allocator, remove.getFocus().id, .{ .widget = .{ .text_box = remove }, .rect = null, .min_size = .{ .width = 3, .height = null } }); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │310 }; │ │311 │ │312 // append one comment's metadata bar and body. │ │289 -pub fn appendComment(allocator: std.mem.Allocator, box: *wgt.Box(ui.Widget), session: *ui.Session, identity: []const u8, thread_kind: evt.EventKind, entry: CommentWithId) !void { │ │290 - var item = try Item.init(allocator, session, identity, thread_kind, entry); │ │313 +pub fn appendComment(allocator: std.mem.Allocator, box: *wgt.Box(ui.Widget), session: *ui.Session, identity: []const u8, thread_kind: evt.EventKind, can_reply: bool, entry: CommentWithId) !void {│ │314 + var item = try Item.init(allocator, session, identity, thread_kind, can_reply, entry); │ │315 errdefer item.deinit(allocator); │ │316 try box.children.put(allocator, item.getFocus().id, .{ .widget = .{ .repo_comment = item }, .rect = null, .min_size = null }); │ │317 } │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──────────────────╮ │src/ui/Repo/Discussions.zig│ ╰───────────────────────────╯ ╭───────────────────────────────────────────────╮ │48 comment_id: []const u8, │ │49 comments_start: usize, │ │50 comment_page: ?Comment.Permalink = null, │ │51 +// whether the viewer may reply to comments│ │52 +can_reply: bool = false, │ │53 recent: Window, │ │54 view: ui.RoutablePage.DiscussionsView, │ │55 description_page: bool = false, │ ╰───────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │118 comment_id: []const u8, │ │119 comments_start: usize, │ │120 view: ui.RoutablePage.DiscussionsView, │ │121 + viewer: ?Comment.Viewer, │ │122 ) !Self { │ │123 const empty = try emptyResult(arena.allocator(), identity, label, search, selected_id, comment_id, comments_start, view);│ │124 const aa = arena.allocator(); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │171 } │ │172 │ │173 const discussion_comments_start = if (empty.comment_id.len == 0) comments_start else 0; │ │171 - const loaded_window = try thread.loadWindow(Self, .discuss, repo_opts.hash, arena, admin_moment, haxy_moment, records, set_maybe, root_key, null, empty.selected_id, discussion_comments_start, empty.search); │ │174 + const loaded_window = try thread.loadWindow(Self, .discuss, repo_opts.hash, arena, admin_moment, haxy_moment, records, set_maybe, root_key, null, empty.selected_id, discussion_comments_start, empty.search, viewer);│ │175 const comment_page = if (empty.comment_id.len == 0) │ │176 null │ │177 else │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯