v4: a dump or playlist going public notifies its followers — a private dump notified no one when posted, and nothing told its followers when it was made public: the poster's followers never learnt of it, nor did the followers of the public playlists holding it. Likewise, the dumps added to a playlist while it was private were never notified to its followers. updateDump and replaceFileDump now call notifyIfWentPublic, which notifies the poster's followers (user_dump_posted) and, through the new notifyPlaylistFollowersNowPublic, the followers of each public playlist holding the dump (playlist_dump_added). updatePlaylist calls the same function when a playlist goes public, for each public dump in it, from the bottom up. A follower hears of a dump once both it and the playlist are public; the source keys stop a dump made private and public again from notifying twice. 256 backend tests pass, and deno check, deno fmt and doc:lint are clean.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -601,6 +601,46 @@ test("PUT /api/dumps/:id: going private hides the dump from everyone else", asyn
|
||||
assertEquals((await get(`/api/dumps/${id}`)).status, 200);
|
||||
});
|
||||
|
||||
test("PUT /api/dumps/:id: going public notifies the poster's and its playlists' followers, once", async () => {
|
||||
const owner = await createUser();
|
||||
const fan = await createUser();
|
||||
const listener = await createUser();
|
||||
db.prepare(
|
||||
`INSERT INTO follows (id, follower_id, followed_user_id, created_at) VALUES (?, ?, ?, ?);`,
|
||||
).run(crypto.randomUUID(), fan.id, owner.id, new Date().toISOString());
|
||||
const mix = insertPlaylist({ userId: owner.id, title: "Mix" });
|
||||
const hidden = insertPlaylist({
|
||||
userId: owner.id,
|
||||
title: "Hidden",
|
||||
isPublic: false,
|
||||
});
|
||||
await put(`/api/me/follow/playlists/${mix.id}`, { auth: listener.auth });
|
||||
const { id } = insertDump({ userId: owner.id, isPrivate: true });
|
||||
for (const p of [mix, hidden]) {
|
||||
await post(`/api/playlists/${p.id}/dumps/${id}`, { auth: owner.auth });
|
||||
}
|
||||
db.exec(`DELETE FROM notifications;`); // the follow notified the owner
|
||||
const setPrivate = (isPrivate: boolean) =>
|
||||
put(`/api/dumps/${id}`, { auth: owner.auth, json: { isPrivate } });
|
||||
|
||||
await setPrivate(false);
|
||||
const notes = db.prepare(
|
||||
`SELECT user_id, type, data FROM notifications ORDER BY type;`,
|
||||
).all() as { user_id: string; type: string; data: string }[];
|
||||
assertEquals(
|
||||
notes.map((n) => [n.user_id, n.type, JSON.parse(n.data).playlistId]),
|
||||
[
|
||||
[listener.id, "playlist_dump_added", mix.id],
|
||||
[fan.id, "user_dump_posted", undefined],
|
||||
],
|
||||
);
|
||||
// Neither going private and public again nor an edit notifies twice.
|
||||
await setPrivate(true);
|
||||
await setPrivate(false);
|
||||
await put(`/api/dumps/${id}`, { auth: owner.auth, json: { title: "New" } });
|
||||
assertEquals(count(`SELECT COUNT(*) as n FROM notifications;`), 2);
|
||||
});
|
||||
|
||||
test("PUT /api/dumps/:id: a file dump edits title, comment and privacy", async () => {
|
||||
const owner = await createUser();
|
||||
const { id } = insertDump({
|
||||
|
||||
Reference in New Issue
Block a user