╭ changed ────╮ │src/event.zig│ ╰─────────────╯ ╭───────────────────────────────────────────────╮ │ 6 const obj = xit.object; │ │ 7 const rf = xit.ref; │ │ 8 const pch = @import("patch.zig"); │ │ 9 +const notify = @import("notify.zig"); │ │10 │ │11 pub const User = @import("event/User.zig");│ │12 pub const Repo = @import("event/Repo.zig");│ ╰───────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────╮ │13 pub const Fork = @import("event/Fork.zig"); │ │14 pub const Grant = @import("event/Grant.zig"); │ │15 +pub const Notify = @import("event/Notify.zig"); │ │16 pub const Discussion = @import("event/Discussion.zig");│ │17 pub const Issue = @import("event/Issue.zig"); │ │18 pub const Comment = @import("event/Comment.zig"); │ ╰───────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │139 }; │ │140 } │ │141 │ │140 -// bump the repo at `repo_path` to now, logging rather than returning a failure │ │141 -pub fn bumpRepoActivityAt(io: std.Io, allocator: std.mem.Allocator, users_dir: []const u8, repo_path: []const u8) void {│ │142 - const location = parseRepoPath(users_dir, repo_path) orelse return; │ │142 +// bump the repo at `repo_path` to now and queue it for notifying, logging rather than returning a failure │ │143 +pub fn bumpRepoActivityAt(io: std.Io, allocator: std.mem.Allocator, server: Host.Server, repo_path: []const u8) void { │ │144 + const location = parseRepoPath(server.users_dir, repo_path) orelse return; │ │145 const now: u64 = @intCast(std.Io.Timestamp.now(io, .real).toSeconds()); │ │144 - bumpRepoActivity(io, allocator, users_dir, &location.owner_id, &location.repo_id, now) catch |err| { │ │146 + bumpRepoActivity(io, allocator, server.users_dir, &location.owner_id, &location.repo_id, now) catch |err| { │ │147 std.log.warn("failed to record repo activity: {s}", .{@errorName(err)}); │ │148 }; │ │149 + if (server.dirty_repos) |dirty_repos| dirty_repos.mark(io, location); │ │150 } │ │151 │ │152 fn readActivityTime(moment: UserDB.HashMap(.read_only), repo_id: *const [event_id_size]u8) !?u64 { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────╮ │277 repo, │ │278 fork, │ │279 grant, │ │280 + notify, │ │281 discuss,│ │282 issue, │ │283 comment,│ ╰─────────────────╯ ╭───────────────────────────────────────────────────────────╮ │297 return switch (self) { │ │298 .admin => kind == .user, │ │299 .user => switch (kind) { │ │296 - .repo, .fork, .grant => true, │ │300 + .repo, .fork, .grant, .notify => true,│ │301 else => false, │ │302 }, │ │303 .repo => switch (kind) { │ ╰───────────────────────────────────────────────────────────╯ ╭──────────────────────────────╮ │328 repo: ?Repo, │ │329 fork: ?Fork, │ │330 grant: ?Grant, │ │331 + notify: ?Notify, │ │332 discuss: ?Discussion,│ │333 issue: ?Issue, │ │334 comment: ?Comment, │ ╰──────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │366 │ │367 pub const Server = struct { │ │368 users_dir: []const u8, │ │369 + // the notify worker's queue, null where no worker runs │ │370 + dirty_repos: ?*notify.DirtyRepos = null, │ │371 + // where repo writes resolve their participants to subscribe, null where they subscribe nobody│ │372 + admin_repo_path: ?[]const u8 = null, │ │373 }; │ │374 }; │ │375 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │444 else │ │445 null, │ │446 }, │ │447 + .notify => .{ │ │448 + .notify = if (json_event.data) |value| │ │449 + try std.json.parseFromValueLeaky(Notify, arena.allocator(), value, .{ .ignore_unknown_fields = true }) │ │450 + else │ │451 + null, │ │452 + }, │ │453 .discuss => .{ │ │454 .discuss = if (json_event.data) |value| │ │455 try std.json.parseFromValueLeaky(Discussion, arena.allocator(), value, .{ .ignore_unknown_fields = true })│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────╮ │537 .repo => .{ .repo = null }, │ │538 .fork => .{ .fork = null }, │ │539 .grant => .{ .grant = null }, │ │540 + .notify => .{ .notify = null }, │ │541 .discuss => .{ .discuss = null },│ │542 .issue => .{ .issue = null }, │ │543 .comment => .{ .comment = null },│ ╰──────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────╮ │695 } │ │696 │ │697 if (role == .repo and host == .server and events.len > 0) { │ │682 - bumpRepoActivityAt(io, allocator, host.server.users_dir, repo.core.work_path); │ │698 + // before the bump queues the repo, so its worker round sees the new subscribers │ │699 + notify.subscribeParticipants(io, allocator, host.server, repo.core.work_path, events);│ │700 + bumpRepoActivityAt(io, allocator, host.server, repo.core.work_path); │ │701 } │ │702 } │ │703 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯