╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ notify.zig ───────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯
557 const created: Created = switch ((try evt.readEventKind(repo_opts.hash, kinds, &id)) orelse continue) { 558 inline .comment, .assign => |kind| blk: { 559 const Fields = struct { removed: bool, author_email: ?[]const u8, created_order: u64, event: struct { thread_id: [evt.event_id_size * 2]u8 } }; 560 const T = if (kind == .comment) evt.Comment else evt.Assignment; 561 const record = (try evt.readRecordSubset(T, Fields, DB, repo_opts.hash, haxy_moment, &arena, &id)) orelse continue; 562 if (record.removed or record.created_order != order) continue; 563 break :blk .{ .thread_id = record.event.thread_id, .actor_email = record.author_email }; 564 }, 565 else => continue, 566 }; 567 568 const thread_id = try evt.parseEventId(&created.thread_id); 569 const Titled = struct { event: struct { title: []const u8 } }; 570 const thread_kind = (try evt.readEventKind(repo_opts.hash, kinds, &thread_id)) orelse continue; 571 const thread = (switch (thread_kind) { 572 .issue => try evt.readRecordSubset(evt.Issue, Titled, DB, repo_opts.hash, haxy_moment, &arena, &thread_id), 573 .patch => try evt.readRecordSubset(evt.Patch, Titled, DB, repo_opts.hash, haxy_moment, &arena, &thread_id), 574 .discuss => try evt.readRecordSubset(evt.Discussion, Titled, DB, repo_opts.hash, haxy_moment, &arena, &thread_id), 575 else => null, 576 }) orelse continue; 577 578 const notif: Notif = .{ 579 .repo_id = location.repo_id, 580 .owner_name = owner.event.name, 581 .repo_name = repo_record.event.name, 582 .thread_id = thread_id, 583 .thread_kind = thread_kind, 584 .title = thread.event.title, 585 .last_child_id = id, 586 }; 587 588 // subscribers hear of every comment and assignment but their own 589 const actor_id = if (created.actor_email) |email| try evt.User.readIdByEmail(evt.AdminDB, evt.admin_repo_opts.hash, admin_moment, email) else null; 590 for (try readSubscribers(owner_latest, &arena, &idOf(&location.repo_id, &thread_id))) |user_id| { 591 if (actor_id) |actor| if (std.mem.eql(u8, &actor, &user_id)) continue; 592 try inboxes.add(user_id, notif); 593 } 594 } 595 } 596 597 // a failure leaves the cursor, so the repo's next round redelivers 598 var failed = false; 599 for (inboxes.by_user.keys(), inboxes.by_user.values()) |*user_id, notifs| { 600 deliver(io, allocator, users_dir, user_id, notifs.items) catch |err| { 601 std.log.warn("failed to deliver notifications: {s}", .{@errorName(err)}); 602 failed = true; 603 }; 604 } 605 if (failed) return error.DeliveryFailed; 606 } 607 608 try writeLastNotified(io, &owner_repo, &location.repo_id, current); 609 } 610 611 fn readLastNotified(owner_repo: *rp.Repo(.xit, evt.user_repo_opts), repo_id: *const [evt.event_id_size]u8) !?u64 { 612 const map_cursor = try (try owner_repo.core.latestMoment()).getCursor(hash.hashInt(hash_kind, last_notified_map_key)) orelse return null; 613 const map = try UserDB.HashMap(.read_only).init(map_cursor); 614 const cursor = try map.getCursor(hash.hashInt(hash_kind, repo_id)) orelse return null; 615 return try cursor.readUint(); 616 } 617 618 fn writeLastNotified(io: std.Io, owner_repo: *rp.Repo(.xit, evt.user_repo_opts), repo_id: *const [evt.event_id_size]u8, moment_index: u64) !void { 619 const Ctx = struct { 620 repo_id: *const [evt.event_id_size]u8, 621 moment_index: u64, 622 623 pub fn run(ctx: @This(), cursor: *UserDB.Cursor(.read_write)) !void { 624 const moment = try UserDB.HashMap(.read_write).init(cursor.*); 625 const map = try UserDB.HashMap(.read_write).init(try moment.putCursor(hash.hashInt(hash_kind, last_notified_map_key))); 626 try map.put(hash.hashInt(hash_kind, ctx.repo_id), .{ .uint = ctx.moment_index }); 627 } 628 }; 629 630 try transactIn(io, owner_repo, Ctx{ .repo_id = repo_id, .moment_index = moment_index }); 631 } 632