╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ────╮ │src/event.zig│ ╰─────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │1711 -// remove an event a thread's page names: the thread itself, or a comment or│ │1712 -// attachment, which must belong to it │ │1736 +// remove an event a thread's page names: the thread itself, or a comment, │ │1737 +// attachment or assignment, which must belong to it │ │1738 pub fn removeInThread( │ │1739 host: Host, │ │1740 comptime repo_kind: rp.RepoKind, │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────╮ │1751 const child_maybe: ?ThreadChild = switch (kind) {│ │1752 .comment => .{ .comment = id }, │ │1753 .attach => .{ .attach = id }, │ │1754 + .assign => .{ .assign = id }, │ │1755 else => null, │ │1756 }; │ │1757 if (child_maybe) |child| { │ ╰───────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────╮ │1920 .issue => Issue.record_map_key, │ │1921 .comment => Comment.record_map_key, │ │1922 .attach => Attachment.record_map_key, │ │1923 + .assign => Assignment.record_map_key, │ │1924 .patchrev => PatchRev.record_map_key, │ │1925 .patch => Patch.record_map_key, │ │1926 .user, .repo, .fork, .grant => return null,│ ╰─────────────────────────────────────────────────────────╯ ╭ added ─────────────────╮ │src/event/Assignment.zig│ ╰────────────────────────╯ ╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2097 const user = (try evt.User.readById(evt.AdminDB, evt.admin_repo_opts.hash, moment, arena, &user_id)) orelse return null; │ │2098 return if (user.removed) null else user; │ │2099 } │ │2100 + │ │2101 +// what the typed assignee resolves to │ │2102 +pub const AssigneeLookup = union(enum) { │ │2103 + email: []const u8, │ │2104 + failure: Session.FormFeedback.AssignmentFailure, │ │2105 +}; │ │2106 │ │2107 +// the email `text` assigns: itself when it holds an @, else the email of the │ │2108 +// user it names. a null moment is local mode, which has no users to look up. │ │2109 +pub fn assigneeEmail(moment_maybe: ?evt.AdminDB.HashMap(.read_only), arena: *std.heap.ArenaAllocator, text: []const u8) !AssigneeLookup { │ │2110 + const trimmed = std.mem.trim(u8, text, " \t\r\n"); │ │2111 + if (std.mem.indexOfScalar(u8, trimmed, '@') != null) return .{ .email = trimmed }; │ │2112 + const moment = moment_maybe orelse return .{ .failure = .not_an_email }; │ │2113 + const user_id = (try evt.User.readIdByName(evt.AdminDB, evt.admin_repo_opts.hash, moment, trimmed)) orelse return .{ .failure = .not_found };│ │2114 + const user = (try activeUser(moment, arena, user_id)) orelse return .{ .failure = .not_found }; │ │2115 + return .{ .email = user.event.email }; │ │2116 +} │ │2117 + │ │2118 // whether the user may write to the repo `identity` names with at least │ │2119 // `min_role`, and to the `thread_kind` tab when there is one │ │2120 pub fn authorizeUser( │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────╮ │2268 │ │2269 pub const RolesFailure = enum { not_found, already_added }; │ │2270 │ │2271 + pub const AssignmentFailure = enum { not_found, not_an_email, already_assigned };│ │2272 + │ │2273 pub const UserFailure = enum { │ │2274 required_name, │ │2275 invalid_name, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────╮ │2336 failure: RolesFailure, │ │2337 name: []const u8, │ │2338 }, │ │2339 + assignment: struct { │ │2340 + failure: AssignmentFailure,│ │2341 + text: []const u8, │ │2342 + }, │ │2343 issue: struct { │ │2344 failure: ThreadFailure, │ │2345 fields: ?struct { │ ╰─────────────────────────────────────────────╯ ╭ changed ──────────────────╮ │src/ui/Repo/Discussions.zig│ ╰───────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────╮ │25 author: ui.Author = .unknown, │ │26 comments: Comment.Window = .empty, │ │27 attachments: []const Attachment.WithId = &.{}, │ │28 + assignees: []const []const u8 = &.{}, │ │29 + // whether the viewer may change the thread, computed here so the web ui has it│ │30 + can_modify: bool = false, │ │31 }; │ │32 │ │33 pub const Entry = DiscussionWithId; │ ╰───────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────────╮ │src/ui/Repo/Events.zig│ ╰──────────────────────╯ ╭───────────────────────────────────────────────╮ │265 .issue => evt.Issue, │ │266 .comment => evt.Comment, │ │267 .attach => evt.Attachment,│ │268 + .assign => evt.Assignment,│ │269 .patchrev => evt.PatchRev,│ │270 .patch => evt.Patch, │ │271 }; │ ╰───────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │345 const route = ui.RoutablePage.repoThreadCommentsRoute(parent_kind, identity, &record.event.parent_id, 0) orelse return item; │ │346 item.view_url = try route.toUrl(arena); │ │347 }, │ │348 + .assign => { │ │349 + const record = (try evt.readRecordSubset(evt.Assignment, Authored, DB, hash_kind, haxy_moment, arena, id)) orelse return null;│ │350 + item.author = try ui.Author.initFromEmail(admin_moment, arena, record.author_email); │ │351 + }, │ │352 .patch => { │ │353 const record = (try evt.readRecordSubset(evt.Patch, Authored, DB, hash_kind, haxy_moment, arena, id)) orelse return null; │ │354 item.author = try ui.Author.initFromEmail(admin_moment, arena, record.author_email); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯