╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │170 comments_start: usize = 0, │ │171 // the comment shown in the detail pane (empty = the issue thread). │ │172 comment: Array(evt.event_id_size * 2) = .{}, │ │173 + // the attachment a remove view confirms (empty = the issue or comment).│ │174 + attachment: Array(evt.event_id_size * 2) = .{}, │ │175 }, │ │176 repo_patches: struct { │ │177 name: Array(repo_route_max_len), │ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────╮ │181 view: PatchesView = .open, │ │182 comments_start: usize = 0, │ │183 comment: Array(evt.event_id_size * 2) = .{}, │ │184 + attachment: Array(evt.event_id_size * 2) = .{},│ │185 }, │ │186 repo_discussions: struct { │ │187 name: Array(repo_route_max_len), │ ╰────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────╮ │190 view: DiscussionsView = .recent, │ │191 comments_start: usize = 0, │ │192 comment: Array(evt.event_id_size * 2) = .{}, │ │193 + attachment: Array(evt.event_id_size * 2) = .{},│ │194 }, │ │195 repo_events: RepoEventsRoute, │ │196 repo_undo: RepoUndoRoute, │ ╰────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │247 │ │248 // the "key:value" segments of a url tail, collected in any order │ │249 const Params = struct { │ │246 - const ParamKey = enum { start, line, branch, tag, object, base, patchrev, theirs, find, search, from, moment, merge, user, key }; │ │250 + const ParamKey = enum { start, line, branch, tag, object, base, patchrev, theirs, find, search, from, moment, merge, user, key, attachment };│ │251 │ │252 // a branch:/tag:/object: param; the value stays url-encoded │ │253 const RefParam = struct { kind: RefOrOid, value: []const u8 }; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────╮ │340 const base_seg = @tagName(Params.ParamKey.base) ++ ":"; │ │341 const merge_seg = @tagName(Params.ParamKey.merge) ++ ":"; │ │342 const theirs_seg = @tagName(Params.ParamKey.theirs) ++ ":"; │ │343 + const attachment_seg = @tagName(Params.ParamKey.attachment) ++ ":";│ │344 const user_seg = @tagName(Params.ParamKey.user) ++ ":"; │ │345 const branch_seg = @tagName(Params.ParamKey.branch) ++ ":"; │ │346 const key_seg = @tagName(Params.ParamKey.key) ++ ":"; │ ╰────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │958 } │ │959 │ │960 // build the confirmation route for removing a thread or comment. │ │956 - pub fn repoThreadRemoveRoute(kind: evt.EventKind, identity: []const u8, thread_id: []const u8, comment_id: []const u8) ?RoutablePage { │ │961 + pub fn repoThreadRemoveRoute(kind: evt.EventKind, identity: []const u8, thread_id: []const u8, comment_id: []const u8, attachment_id: []const u8) ?RoutablePage {│ │962 var route = if (comment_id.len == 0) │ │963 repoThreadCommentsRoute(kind, identity, thread_id, 0) orelse return null │ │964 else │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮ │965 repoThreadCommentRoute(kind, identity, thread_id, comment_id, 0) orelse return null;│ │966 + const attachment = Array(evt.event_id_size * 2).from(attachment_id) orelse return null; │ │967 switch (route) { │ │962 - .repo_issues => |*thread| thread.view = .remove, │ │963 - .repo_patches => |*thread| thread.view = .remove, │ │964 - .repo_discussions => |*thread| thread.view = .remove, │ │968 + inline .repo_issues, .repo_patches, .repo_discussions => |*thread| { │ │969 + thread.view = .remove; │ │970 + thread.attachment = attachment; │ │971 + }, │ │972 else => unreachable, │ │973 } │ │974 return route; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │975 + } │ │976 + │ │977 + // the attachment a thread remove route confirms, or "" for any other page │ │978 + pub fn threadAttachment(self: *const RoutablePage) []const u8 { │ │979 + return switch (self.*) { │ │980 + inline .repo_issues, .repo_patches, .repo_discussions => |*thread| thread.attachment.slice(),│ │981 + else => "", │ │982 + }; │ │983 } │ │984 │ │985 // build a thread labels route. │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1186 else │ │1187 try arena.allocator().print("{s}/" ++ issue_seg ++ "{s}/" ++ comment_seg ++ "{s}/new", .{ prefix, i.selected.slice(), i.comment.slice() }); │ │1188 if (i.view == .edit_comment) break :blk try arena.allocator().print("{s}/" ++ issue_seg ++ "{s}/" ++ comment_seg ++ "{s}/edit", .{ prefix, i.selected.slice(), i.comment.slice() });│ │1174 - if (i.view == .remove) break :blk if (i.comment.len == 0) │ │1175 - try arena.allocator().print("{s}/" ++ issue_seg ++ "{s}/remove", .{ prefix, i.selected.slice() }) │ │1176 - else │ │1177 - try arena.allocator().print("{s}/" ++ issue_seg ++ "{s}/" ++ comment_seg ++ "{s}/remove", .{ prefix, i.selected.slice(), i.comment.slice() }); │ │1189 + if (i.view == .remove) break :blk try threadRemoveUrl(arena, prefix, issue_seg, i.selected.slice(), i.comment.slice(), i.attachment.slice()); │ │1190 if (i.view == .conflicts) break :blk if (i.selected.len == 0) │ │1191 try arena.allocator().print("{s}/issues/conflicts", .{prefix}) │ │1192 else │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1223 else │ │1224 try arena.allocator().print("{s}/" ++ patch_seg ++ "{s}/" ++ comment_seg ++ "{s}/new", .{ prefix, p.selected.slice(), p.comment.slice() }); │ │1225 if (p.view == .edit_comment) break :blk try arena.allocator().print("{s}/" ++ patch_seg ++ "{s}/" ++ comment_seg ++ "{s}/edit", .{ prefix, p.selected.slice(), p.comment.slice() });│ │1214 - if (p.view == .remove) break :blk if (p.comment.len == 0) │ │1215 - try arena.allocator().print("{s}/" ++ patch_seg ++ "{s}/remove", .{ prefix, p.selected.slice() }) │ │1216 - else │ │1217 - try arena.allocator().print("{s}/" ++ patch_seg ++ "{s}/" ++ comment_seg ++ "{s}/remove", .{ prefix, p.selected.slice(), p.comment.slice() }); │ │1226 + if (p.view == .remove) break :blk try threadRemoveUrl(arena, prefix, patch_seg, p.selected.slice(), p.comment.slice(), p.attachment.slice()); │ │1227 if (p.view == .conflicts) break :blk if (p.selected.len == 0) │ │1228 try arena.allocator().print("{s}/patches/conflicts", .{prefix}) │ │1229 else │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯