╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──────╮ │src/ui/Repo.zig│ ╰───────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │438 try stack.children.put(allocator, discussions_view.getFocus().id, .{ .repo_discussions = discussions_view });│ │439 } │ │440 │ │442 - if (session.data.host_kind == .local or data.undo != null) { │ │441 + if (data.handle.canWrite()) { │ │442 var events_view = try Events.View.init(allocator, &data.events, session); │ │443 errdefer events_view.deinit(allocator); │ │444 try stack.children.put(allocator, events_view.getFocus().id, .{ .repo_events = events_view }); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──────────────╮ │src/ui/Repo/Comment.zig│ ╰───────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │20 // whether the viewer may edit or remove the comment │ │21 can_modify: bool = false, │ │22 }; │ │23 - │ │24 -// who a thread page is built for, which picks the comment buttons it shows │ │25 -pub const Viewer = struct { │ │26 - role: evt.Repo.Role, │ │27 - email: []const u8, │ │23 │ │29 - // the session's viewer of `repo`, or null when logged out. local mode owns everything. │ │30 - pub fn init(session: *ui.Session, arena: *std.heap.ArenaAllocator, repo: evt.Repo.Record) !?Viewer {│ │31 - if (session.data.host_kind == .local) return .{ .role = .owner, .email = "" }; │ │32 - const user_id = session.userId() orelse return null; │ │33 - const moment = session.haxy_moment orelse unreachable; │ │34 - const user = (try ui.activeUser(moment, arena, user_id)) orelse return null; │ │35 - return .{ .role = evt.Repo.roleOf(repo, user_id), .email = user.event.email }; │ │36 - } │ │37 -}; │ │38 - │ │24 pub const Window = struct { │ │25 comments: []const CommentWithId, │ │26 start: usize, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │43 thread_id: []const u8, │ │44 selected_id: []const u8, │ │45 start: usize, │ │61 - viewer: ?Viewer, │ │46 + viewer: ?ui.Viewer, │ │47 ) !Permalink { │ │48 const selected = (try readOne(hash_kind, arena, admin_moment, haxy_moment, selected_id, viewer)) orelse return error.NotFound;│ │49 if (!std.mem.eql(u8, &selected.comment.event.thread_id, thread_id)) return error.NotFound; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │63 index_key: []const u8, │ │64 owner_id: []const u8, │ │65 start: usize, │ │81 - viewer: ?Viewer, │ │66 + viewer: ?ui.Viewer, │ │67 ) !Window { │ │68 const DB = evt.EventDB(hash_kind); │ │69 const index_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, index_key)) orelse return .{│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────╮ │105 admin_moment: ?evt.AdminDB.HashMap(.read_only), │ │106 haxy_moment: evt.EventDB(hash_kind).HashMap(.read_only), │ │107 id: []const u8, │ │123 - viewer: ?Viewer, │ │108 + viewer: ?ui.Viewer, │ │109 ) !?CommentWithId { │ │110 const bytes = idBytes(id) orelse return null; │ │111 return readOneBytes(hash_kind, arena, admin_moment, haxy_moment, &bytes, viewer);│ ╰──────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │117 admin_moment: ?evt.AdminDB.HashMap(.read_only), │ │118 haxy_moment: evt.EventDB(hash_kind).HashMap(.read_only), │ │119 id_bytes: *const [evt.event_id_size]u8, │ │135 - viewer: ?Viewer, │ │120 + viewer: ?ui.Viewer, │ │121 ) !?CommentWithId { │ │122 const DB = evt.EventDB(hash_kind); │ │123 const comment = (try evt.Comment.readById(DB, hash_kind, haxy_moment, arena, id_bytes)) orelse return null;│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──────────────╮ │src/ui/Repo/Commits.zig│ ╰───────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────╮ │43 window: Diff.Window = .{}, │ │44 }; │ │45 │ │46 -// where links and clone commands for this repository are rooted. │ │47 -location: ui.RoutablePage.RepoLocation, │ │46 +// where links and clone commands for this repository are rooted, and who views it.│ │47 +handle: ui.RepoHandle, │ │48 // the resolved ref/oid this log walks from (the default branch when the route │ │49 // didn't name one), so the page can canonicalize its url to it. │ │50 ref_or_oid: ui.RoutablePage.RefOrOid, │ ╰───────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │92 // the admin db's moment, for resolving author emails to user names (null│ │93 // in local mode, which has no users) │ │94 admin_moment: ?evt.AdminDB.HashMap(.read_only), │ │95 - location: ui.RoutablePage.RepoLocation, │ │95 + handle: ui.RepoHandle, │ │96 requested_ref_or_oid: ?ui.RoutablePage.RefOrOid, │ │97 requested_value: []const u8, │ │98 content: ui.RoutablePage.RepoCommitsRoute.Content, │ ╰─────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │127 // (NotFound -> 404); the default-branch path falls through to empty. │ │128 var resolved = (try ui.ResolvedRefOrOid(repo_kind, repo_opts).init(repo, io, aa, requested_ref_or_oid, requested_value)) orelse { │ │129 if (requested_ref_or_oid != null) return error.NotFound; │ │130 - return emptyResult(aa, location, .branch, requested_value, content, base_oid); │ │130 + return emptyResult(aa, handle, .branch, requested_value, content, base_oid); │ │131 }; │ │132 │ │133 - var moment = repo.core.latestMoment() catch return emptyResult(aa, location, resolved.ref_or_oid, resolved.value, content, base_oid);│ │133 + var moment = repo.core.latestMoment() catch return emptyResult(aa, handle, resolved.ref_or_oid, resolved.value, content, base_oid); │ │134 const state = rp.Repo(repo_kind, repo_opts).State(.read_only){ .core = &repo.core, .extra = .{ .moment = &moment } }; │ │135 │ │136 // resolve annotated tags once, independently of the page's starting commit. │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │137 { │ │138 var tip = obj.Object(repo_kind, repo_opts).initCommit(state, io, gpa, &resolved.oid) catch { │ │139 if (query != null) return error.NotFound; │ │140 - return emptyResult(aa, location, resolved.ref_or_oid, resolved.value, content, base_oid);│ │140 + return emptyResult(aa, handle, resolved.ref_or_oid, resolved.value, content, base_oid); │ │141 }; │ │142 defer tip.deinit(); │ │143 resolved.oid = tip.oid; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯