╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ notify.zig ───────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
60 const location = try evt.createRepo(io, allocator, users_dir, &owner_id, author, "repo", "", .private, repo_opts.hash); 61 62 // 63 // notifs 64 // 65 66 const thread_a = evt.EventWithId.randomId(prng.random()); 67 const thread_b = evt.EventWithId.randomId(prng.random()); 68 var child_ids: [4][evt.event_id_size]u8 = undefined; 69 for (&child_ids) |*id| id.* = evt.EventWithId.randomId(prng.random()); 70 const notif_a: notify.Notif = .{ 71 .repo_id = location.repo_id, 72 .owner_name = "owner", 73 .repo_name = "repo", 74 .thread_id = thread_a, 75 .thread_kind = .issue, 76 .title = "a", 77 .last_child_id = child_ids[0], 78 }; 79 var notif_b = notif_a; 80 notif_b.thread_id = thread_b; 81 notif_b.title = "b"; 82 notif_b.last_child_id = child_ids[1]; 83 const id_a = notify.idOf(&location.repo_id, &thread_a); 84 85 try notify.deliver(io, allocator, users_dir, &recipient_id, &.{ notif_a, notif_b }); 86 try std.testing.expectEqual(2, try unreadCount(io, allocator, users_dir, &recipient_id)); 87 try std.testing.expectEqual(2, (try readRecent(io, allocator, &arena, users_dir, &recipient_id)).len); 88 89 // a bump updates the thread's one notif. times tie within a second, so order isn't checked. 90 var bump = notif_a; 91 bump.last_child_id = child_ids[2]; 92 try notify.deliver(io, allocator, users_dir, &recipient_id, &.{bump}); 93 bump.last_child_id = child_ids[3]; 94 try notify.deliver(io, allocator, users_dir, &recipient_id, &.{bump}); 95 { 96 const recent = try readRecent(io, allocator, &arena, users_dir, &recipient_id); 97 try std.testing.expectEqual(2, recent.len); 98 for (recent) |item| { 99 if (std.mem.eql(u8, &item.notif.thread_id, &thread_a)) try std.testing.expectEqualSlices(u8, &child_ids[3], &item.notif.last_child_id); 100 } 101 } 102 103 // a read of a child the thread has moved past leaves it unread 104 try notify.markRead(io, allocator, users_dir, &recipient_id, &id_a, &child_ids[2]); 105 try std.testing.expectEqual(2, try unreadCount(io, allocator, users_dir, &recipient_id)); 106 try notify.markRead(io, allocator, users_dir, &recipient_id, &id_a, &child_ids[3]); 107 try std.testing.expectEqual(1, try unreadCount(io, allocator, users_dir, &recipient_id)); 108 for (try readRecent(io, allocator, &arena, users_dir, &recipient_id)) |item| { 109 try std.testing.expectEqual(!std.mem.eql(u8, &item.notif.thread_id, &thread_a), item.unread); 110 } 111 112 try notify.dismiss(io, allocator, users_dir, &recipient_id, &id_a); 113 { 114 const recent = try readRecent(io, allocator, &arena, users_dir, &recipient_id); 115 try std.testing.expectEqual(1, recent.len); 116 try std.testing.expectEqualSlices(u8, &thread_b, &recent[0].notif.thread_id); 117 } 118 119 // 120 // notify records 121 // 122 123 // muting a thread drops its notif 124 try notify.mute(io, allocator, users_dir, &recipient_id, location, &thread_b, author); 125 try std.testing.expectEqual(0, (try readRecent(io, allocator, &arena, users_dir, &recipient_id)).len); 126 127 // subscribing replaces the mute record and indexes the subscriber in the owner's repo 128 const id_b = notify.idOf(&location.repo_id, &thread_b); 129 try std.testing.expectEqual(.muted, try readMode(io, allocator, &arena, users_dir, &recipient_id, &location.repo_id, &thread_b)); 130 try notify.subscribe(io, allocator, users_dir, &recipient_id, location, &thread_b, author); 131 try std.testing.expectEqual(.subscribed, try readMode(io, allocator, &arena, users_dir, &recipient_id, &location.repo_id, &thread_b)); 132 try notify.deliver(io, allocator, users_dir, &recipient_id, &.{notif_b}); 133 try std.testing.expectEqual(1, (try readRecent(io, allocator, &arena, users_dir, &recipient_id)).len); 134 { 135 const notified = try readNotified(io, allocator, &arena, users_dir, &owner_id, &id_b); 136 try std.testing.expectEqual(1, notified.len); 137 try std.testing.expectEqualSlices(u8, &recipient_id, &notified[0]); 138 } 139 140 // muting drops the subscriber from the index 141 try notify.mute(io, allocator, users_dir, &recipient_id, location, &thread_b, author); 142 try std.testing.expectEqual(0, (try readNotified(io, allocator, &arena, users_dir, &owner_id, &id_b)).len); 143 144 // a full subscribed tier refuses one more 145 for (0..notify.max_subscribed) |_| { 146 const user_id = evt.EventWithId.randomId(prng.random()); 147 try initUserRepo(io, allocator, users_dir, &user_id); 148 try notify.subscribe(io, allocator, users_dir, &user_id, location, &thread_a, author); 149 } 150 try notify.subscribe(io, allocator, users_dir, &recipient_id, location, &thread_a, author); 151 try std.testing.expectEqual(null, try readMode(io, allocator, &arena, users_dir, &recipient_id, &location.repo_id, &thread_a)); 152 153 // past the cap, the oldest go. times tie within a second, so which one goes isn't checked. 154 { 155 const many = try allocator.alloc(notify.Notif, notify.max_notifs + 1); 156 defer allocator.free(many); 157 for (many, 0..) |*notif, i| { 158 notif.* = notif_a; 159 notif.thread_id = @splat(0); 160 std.mem.writeInt(u64, notif.thread_id[0..@sizeOf(u64)], i, .big); 161 } 162 try notify.deliver(io, allocator, users_dir, &recipient_id, many); 163 } 164 try std.testing.expectEqual(notify.max_notifs, (try readRecent(io, allocator, &arena, users_dir, &recipient_id)).len); 165 } 166