╭ changed ───────────────╮ │src/ui/widget/thread.zig│ ╰────────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────╮ │← previous│ ╰──────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1524 const id_hex = std.fmt.bytesToHex(id, .lower); │ │1525 const route = ui.RoutablePage.repoEventsRoute(self.data.identity, .removed, event_kind, &id_hex, null) orelse return; │ │1526 try self.session.navigate(route); │ │1527 + } │ │1528 + │ │1529 + // assign the typed email, or the email of the user the text names │ │1530 + fn assign(self: *This, allocator: std.mem.Allocator, text: []const u8) !void { │ │1531 + if (comptime wasm) return; │ │1532 + const entry = self.entry orelse return; │ │1533 + const io = self.session.io orelse return; │ │1534 + const source = self.data.repo_source orelse return; │ │1535 + const actor = (try self.session.authorize(self.data.identity, .read, kind)) orelse return; │ │1536 + const thread_id = try evt.parseEventId(entry.id); │ │1537 + if (!try source.canModify(io, allocator, actor, kind, &thread_id)) return; │ │1538 + │ │1539 + var arena = std.heap.ArenaAllocator.init(allocator); │ │1540 + defer arena.deinit(); │ │1541 + const failure: ?ui.Session.FormFeedback.AssignmentFailure = blk: { │ │1542 + const moment = if (self.session.data.host_kind == .local) null else self.session.haxy_moment; │ │1543 + const email = switch (try ui.assigneeEmail(moment, &arena, text)) { │ │1544 + .email => |email| email, │ │1545 + .failure => |value| break :blk value, │ │1546 + }; │ │1547 + const thread_id_hex = std.fmt.bytesToHex(thread_id, .lower); │ │1548 + switch (source.repo_kind) { │ │1549 + inline else => |repo_kind| { │ │1550 + var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, source.localInitOpts()); │ │1551 + defer any_repo.deinit(io, allocator); │ │1552 + switch (any_repo) { │ │1553 + inline else => |*repo| _ = evt.Assignment.create(self.session.eventHost(), repo_kind, repo.self_repo_opts, io, allocator, repo, kind, &thread_id_hex, email, actor.author) catch |err| switch (err) { │ │1554 + error.AlreadyAssigned => break :blk .already_assigned, │ │1555 + error.InvalidFields => break :blk .not_an_email, │ │1556 + else => |e| return e, │ │1557 + }, │ │1558 + } │ │1559 + }, │ │1560 + } │ │1561 + break :blk null; │ │1562 + }; │ │1563 + if (failure) |value| { │ │1564 + self.session.data.form_feedback = .{ .assignment = .{ .failure = value, .text = try self.session.arena.allocator().dupe(u8, text) } }; │ │1565 + return; │ │1566 + } │ │1567 + try self.session.navigate(ui.RoutablePage.repoThreadCommentsRoute(kind, self.data.identity, entry.id, 0) orelse return error.RouteTooLong); │ │1568 + } │ │1569 + │ │1570 + fn unassign(self: *This, allocator: std.mem.Allocator, email: []const u8) !void { │ │1571 + if (comptime wasm) return; │ │1572 + const entry = self.entry orelse return; │ │1573 + const io = self.session.io orelse return; │ │1574 + const source = self.data.repo_source orelse return; │ │1575 + const actor = (try self.session.authorize(self.data.identity, .read, kind)) orelse return; │ │1576 + const thread_id = try evt.parseEventId(entry.id); │ │1577 + if (!try source.canModify(io, allocator, actor, kind, &thread_id)) return; │ │1578 + │ │1579 + switch (source.repo_kind) { │ │1580 + inline else => |repo_kind| { │ │1581 + var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, source.localInitOpts()); │ │1582 + defer any_repo.deinit(io, allocator); │ │1583 + switch (any_repo) { │ │1584 + inline else => |*repo| evt.removeInThread(self.session.eventHost(), repo_kind, repo.self_repo_opts, io, allocator, repo, kind, &thread_id, .assign, &evt.Assignment.idOf(&thread_id, email), actor.author) catch |err| switch (err) {│ │1585 + // a stale button; the refresh below shows the row as it is │ │1586 + error.EventNotFound => {}, │ │1587 + else => |e| return e, │ │1588 + }, │ │1589 + } │ │1590 + }, │ │1591 + } │ │1592 + try self.session.navigate(ui.RoutablePage.repoThreadCommentsRoute(kind, self.data.identity, entry.id, 0) orelse return error.RouteTooLong); │ │1593 } │ │1594 │ │1595 fn toggleStatus(self: *This, allocator: std.mem.Allocator) !void { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯