╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──────╮ │src/ui/Fork.zig│ ╰───────────────╯ ╭─────────────────────────────────────────────────╮ │24 commits: Commits, │ │25 patch: Patches, │ │26 diff: Diff, │ │27 +// the delete tab's form, for the forker only│ │28 +delete: ?ui.Delete, │ │29 user_notifications: ui.UserNotifications, │ │30 quit: Quit, │ │31 │ ╰─────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │36 const users_dir = session.users_dir orelse return error.NotFound; │ │37 const haxy_moment = session.haxy_moment orelse return error.NoMoment; │ │38 const route_fork = route.forkRoute() orelse return error.UnexpectedRoute; │ │37 - // the route's identity names the forker and the target repo │ │39 + // the route's identity names the forker and the repo name the fork was made under │ │40 const identity = ui.RoutablePage.RepoIdentity.parse(route_fork.name.slice()) orelse return error.NotFound;│ │41 const id = evt.parseEventId(route_fork.id.slice()) catch return error.NotFound; │ │42 const gpa = arena.child_allocator; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │43 const forker_id = (try evt.User.readIdByName(evt.AdminDB, evt.admin_repo_opts.hash, haxy_moment, identity.owner)) orelse return error.NotFound;│ │44 const fork_record = (try evt.readForkById(io, gpa, arena, users_dir, &forker_id, &id)) orelse return error.NotFound; │ │45 if (fork_record.removed) return error.NotFound; │ │46 + const forker = if (session.userId()) |user_id| std.mem.eql(u8, &user_id, &forker_id) else false; │ │47 + if (route == .fork_delete and !forker) return error.NotFound; │ │48 const target_id = try evt.parseEventId(&fork_record.event.repo_id); │ │49 const owner_id = try evt.parseEventId(&fork_record.event.repo_user_id); │ │50 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │47 - const target = (try evt.readRepoById(io, gpa, arena, users_dir, &owner_id, &target_id, session.userId())) orelse return error.NotFound; │ │51 + // a deleted target, or one whose owner was deleted, leaves the fork to its forker │ │52 + const target = try evt.readRepoById(io, gpa, arena, users_dir, &owner_id, &target_id, session.userId()); │ │53 // a draft is as readable as the repo it targets │ │49 - const target_role = target.role orelse return error.NotFound; │ │50 - const target_record = target.repo; │ │51 - // patches that are off take their drafts with them │ │52 - if (target_record.event.patch_role == null) return error.NotFound; │ │54 + const target_role: evt.Repo.Role = if (target) |found| role: { │ │55 + // patches that are off take their drafts with them │ │56 + if (found.repo.event.patch_role == null) return error.NotFound; │ │57 + break :role found.role orelse return error.NotFound; │ │58 + } else if (forker) .read else return error.NotFound; │ │59 + // the url keeps the name the fork was made under, even after a rename │ │60 + if (!std.mem.eql(u8, fork_record.event.repo_name, identity.name)) return error.NotFound; │ │61 const owner = (try evt.User.readById(evt.AdminDB, evt.admin_repo_opts.hash, haxy_moment, arena, &owner_id)) orelse return error.NotFound;│ │54 - if (!std.mem.eql(u8, target_record.event.name, identity.name)) return error.NotFound; │ │62 │ │63 const aa = arena.allocator(); │ │57 - const target_identity = try aa.print("{s}:{s}", .{ owner.event.name, target_record.event.name }); │ │64 + const target_identity = try aa.print("{s}:{s}", .{ owner.event.name, if (target) |found| found.repo.event.name else identity.name }); │ │65 const id_hex = std.fmt.bytesToHex(id, .lower); │ │66 // the fork's viewer is the target repo's │ │67 const handle = ui.RepoHandle{ │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │87 │ │88 const target_path = try evt.repoPath(aa, users_dir, &owner_id, &target_id); │ │89 const target_source = ui.RepoSource{ .path = target_path, .repo_kind = .xit }; │ │83 - var target_repo_maybe: ?rp.Repo(.xit, repo_opts) = if (!target_record.removed) │ │90 + var target_repo_maybe: ?rp.Repo(.xit, repo_opts) = if (target != null) │ │91 rp.Repo(.xit, repo_opts).open(io, arena.child_allocator, target_source.localInitOpts()) catch null│ │92 else │ │93 null; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────╮ │102 .draft = fork_record.event.stage == .draft, │ │103 .revision_oid = try aa.dupe(u8, &fork_oid), │ │104 .fork_exists = true, │ │ 98 - .forker = try aa.dupe(u8, identity.owner), │ │105 + .fork_identity = try aa.dupe(u8, identity.identity), │ │106 + .target_deleted = target == null, │ │107 }; │ │108 var patch_data = try Patches.detailResult(aa, target_identity, retained_entry);│ │109 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │110 if (target_repo_maybe) |*target_repo| switch (fork_record.event.stage) { │ │103 - .draft => if (try Patches.loadDraftEntry(.xit, repo_opts, arena, io, haxy_moment, fork_repo, target_repo, id, identity.owner)) |entry| { │ │111 + .draft => if (try Patches.loadDraftEntry(.xit, repo_opts, arena, io, haxy_moment, fork_repo, target_repo, id, identity.identity)) |entry| {│ │112 patch_data = try Patches.detailResult(aa, target_identity, entry); │ │113 patch_data.repo_source = target_source; │ │114 }, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │169 }; │ │170 │ │171 return .{ │ │164 - .header = try Header.init(arena, target_record.event.name, identity.owner, &id_hex, requested_value), │ │172 + .header = try Header.init(arena, try aa.dupe(u8, identity.name), identity.owner, &id_hex, requested_value),│ │173 .files = files, │ │174 .commits = commits, │ │175 .patch = patch_data, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │178 .path = diff_path, │ │179 .window = try Diff.render(.xit, repo_opts, io, arena.child_allocator, aa, fork_repo, if (diff_base) |*base| base else null, diff_head, diff_start, diff_path),│ │180 }, │ │181 + .delete = if (forker) .{ .target = .{ .fork = .{ .identity = try aa.dupe(u8, identity.identity), .id = id_hex } } } else null, │ │182 .user_notifications = try ui.UserNotifications.init(arena, session, route.userTab()), │ │183 .quit = Quit.init(), │ │184 }; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │196 var box = try wgt.Box(ui.Widget).init(allocator, .{ .border = null, .direction = .vert }); │ │197 errdefer box.deinit(allocator); │ │198 { │ │190 - var header = try Header.View.init(allocator, &data.header, data.commits.commit_count, session); │ │199 + var header = try Header.View.init(allocator, &data.header, data.commits.commit_count, data.delete != null, session); │ │200 errdefer header.deinit(allocator); │ │201 try box.children.put(allocator, header.getFocus().id, .{ .widget = .{ .fork_header = header }, .rect = null, .min_size = null });│ │202 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯