╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──────────────╮ │src/ui/Repo/Patches.zig│ ╰───────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────╮ │166 io.random(&id_bytes); │ │167 const id = std.fmt.bytesToHex(id_bytes, .lower); │ │169 - var user_id: [evt.event_id_size]u8 = undefined; │ │170 - @memcpy(&user_id, user_id_slice); │ │171 - const path = try fork.create(.{}, io, allocator, repos_dir, admin_repo, .{ │ │168 + const fork_path = try fork.create(.{}, io, allocator, repos_dir, admin_repo, .{│ │169 .id = id, │ │170 .user_id = user_id, │ │171 .repo_id = repo_id, │ ╰────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────╮ │175 .author = author, │ │176 .timestamp = @intCast(std.Io.Timestamp.now(io, .real).toSeconds()),│ │177 }); │ │181 - allocator.free(path); │ │178 + allocator.free(fork_path); │ │179 return id; │ │180 } │ │181 │ ╰────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │185 const admin_repo = session.admin_repo orelse return error.NotFound; │ │186 const repo_source = data.repo_source orelse return error.NotFound; │ │187 const repo_id = data.repo_id orelse return error.NotFound; │ │191 - const user_id_slice = session.data.user_id orelse return error.NotFound;│ │192 - if (user_id_slice.len != evt.event_id_size) return error.NotFound; │ │188 + const user_id = session.userId() orelse return error.NotFound; │ │189 const patch_id = evt.parseEventId(id) catch return error.NotFound; │ │190 const author = (try session.eventAuthor()) orelse return error.NotFound;│ │191 │ ╰─────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────╮ │196 - var user_id: [evt.event_id_size]u8 = undefined; │ │197 - @memcpy(&user_id, user_id_slice); │ │192 const id_hex = std.fmt.bytesToHex(patch_id, .lower); │ │193 const fork_path = try fork.forkPath(allocator, repos_dir, &id_hex);│ │194 defer allocator.free(fork_path); │ ╰────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │204 }); │ │205 } │ │206 │ │207 +pub fn editDraft( │ │208 + data: *const Self, │ │209 + session: *ui.Session, │ │210 + allocator: std.mem.Allocator, │ │211 + id: []const u8, │ │212 + title: []const u8, │ │213 + tags: []const u8, │ │214 + description: []const u8, │ │215 +) !void { │ │216 + const io = session.io orelse return error.NotFound; │ │217 + const repos_dir = session.repos_dir orelse return error.NotFound; │ │218 + const admin_repo = session.admin_repo orelse return error.NotFound; │ │219 + const repo_id = data.repo_id orelse return error.NotFound; │ │220 + const user_id = session.userId() orelse return error.NotFound; │ │221 + const patch_id = evt.parseEventId(id) catch return error.NotFound; │ │222 + const author = (try session.eventAuthor()) orelse return error.NotFound; │ │223 + │ │224 + const id_hex = std.fmt.bytesToHex(patch_id, .lower); │ │225 + const fork_path = try fork.forkPath(allocator, repos_dir, &id_hex); │ │226 + defer allocator.free(fork_path); │ │227 + if (!try pch.editDraft(.{}, io, allocator, admin_repo, fork_path, .{ │ │228 + .id = id_hex, │ │229 + .user_id = user_id, │ │230 + .repo_id = repo_id, │ │231 + .title = title, │ │232 + .tags = tags, │ │233 + .description = description, │ │234 + .author = author, │ │235 + .timestamp = @intCast(std.Io.Timestamp.now(io, .real).toSeconds()), │ │236 + })) return error.NotFound; │ │237 +} │ │238 + │ │239 pub fn removeDraft(session: *ui.Session, allocator: std.mem.Allocator, id: *const [evt.event_id_size]u8) !void {│ │240 const io = session.io orelse return error.NotFound; │ │241 const repos_dir = session.repos_dir orelse return error.NotFound; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────╮ │242 const admin_repo = session.admin_repo orelse return error.NotFound; │ │217 - const user_id_slice = session.data.user_id orelse return error.NotFound; │ │218 - if (user_id_slice.len != evt.event_id_size) return error.NotFound; │ │243 + const user_id = session.userId() orelse return error.NotFound; │ │244 const author = (try session.eventAuthor()) orelse return error.NotFound; │ │245 │ │221 - var user_id: [evt.event_id_size]u8 = undefined; │ │222 - @memcpy(&user_id, user_id_slice); │ │246 const id_hex = std.fmt.bytesToHex(id.*, .lower); │ │247 try fork.remove(io, allocator, repos_dir, admin_repo, &id_hex, &user_id, author);│ │248 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────╮ │353 } │ │354 }; │ │355 empty.drafts = drafts_window; │ │333 - if (draft_selected and view != .remove) empty.view = .drafts; │ │356 + if (draft_selected and view != .edit and view != .remove) empty.view = .drafts;│ │357 │ │358 // an explicitly named published patch that doesn't exist is a bad url │ │359 // (NotFound -> 404); drafts, tags, and bare routes can use the empty fallback.│ ╰────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ───────────────╮ │src/ui/widget/thread.zig│ ╰────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1271 try addToolButton(allocator, row, "publish", "submit"); │ │1272 } │ │1273 if (self.session.data.user_id != null) { │ │1274 - const route = ui.RoutablePage.repoThreadRemoveRoute(kind, self.data.identity, entry.id, "") orelse return error.RouteTooLong; │ │1275 - try addToolButton(allocator, row, "✕", try std.fmt.allocPrint(pa, "a:{s}", .{try route.toUrl(self.session.page_arena)})); │ │1274 + const edit_route = ui.RoutablePage.repoThreadEditRoute(kind, self.data.identity, entry.id) orelse return error.RouteTooLong; │ │1275 + try addToolButton(allocator, row, "edit", try std.fmt.allocPrint(pa, "a:{s}", .{try edit_route.toUrl(self.session.page_arena)})); │ │1276 + const remove_route = ui.RoutablePage.repoThreadRemoveRoute(kind, self.data.identity, entry.id, "") orelse return error.RouteTooLong;│ │1277 + try addToolButton(allocator, row, "✕", try std.fmt.allocPrint(pa, "a:{s}", .{try remove_route.toUrl(self.session.page_arena)})); │ │1278 } │ │1279 if (row.children.count() > first_in_row_index) │ │1280 row.getFocus().child_id = row.children.keys()[first_in_row_index]; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────╮ │2280 fn submitEditedThread(self: *This, allocator: std.mem.Allocator) !void { │ │2281 if (comptime wasm) return; │ │2282 const io = self.session.io orelse return; │ │2281 - const src = self.data.repo_source orelse return; │ │2283 const entry = self.data.selectedThread() orelse return; │ │2283 - const author = (try self.session.eventAuthor()) orelse return; │ │2284 │ │2285 const form = self.threadForm() orelse return; │ │2286 const title_input = &form.children.values()[title_field_index].widget.text_input;│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2296 │ │2297 if (!Event.fieldsValid(title, tags)) return; │ │2298 │ │2299 + if (comptime supports_drafts) { │ │2300 + if (entryDraft(entry.*)) { │ │2301 + try self.data.editDraft(self.session, allocator, entry.id, title, tags, description); │ │2302 + const route = listRoute(self.data.identity, entryStatus(entry.*), "", entry.id) orelse return;│ │2303 + try self.session.navigate(route); │ │2304 + return; │ │2305 + } │ │2306 + } │ │2307 + │ │2308 const id_bytes = try evt.parseEventId(entry.id); │ │2309 + const src = self.data.repo_source orelse return; │ │2310 + const author = (try self.session.eventAuthor()) orelse return; │ │2311 │ │2312 switch (src.repo_kind) { │ │2313 inline else => |repo_kind| { │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯