v3: added content slugs, fixed real-time updates in client, added @mentions across the app, added new file selector and drop zone

This commit is contained in:
khannurien
2026-03-22 16:06:26 +00:00
parent 39a0cc397e
commit 34e908d1bc
42 changed files with 2170 additions and 628 deletions

View File

@@ -7,6 +7,9 @@ import { APIErrorCode, APIException } from "../model/interfaces.ts";
import { db, isNotificationRow, notificationRowToApi } from "../model/db.ts";
import { sendToUser } from "./ws-service.ts";
// Regex: matches @username not already inside a markdown link ([...] or (...)
const MENTION_RE = /(?<![[(\\w])@([\w]+)/g;
// ── Core CRUD ─────────────────────────────────────────────────────────────────
// sourceKey: if set, INSERT OR IGNORE — same (user_id, source_key) pair is a no-op.
@@ -191,6 +194,45 @@ export function notifyDumpOwnerUpvote(
);
}
export function notifyMentions(
mentionerUserId: string,
body: string,
contextType: "comment" | "dump" | "playlist",
contextId: string,
contextTitle: string,
dumpId?: string,
): void {
const mentionerRow = db.prepare(
`SELECT username FROM users WHERE id = ?;`,
).get(mentionerUserId) as { username: string } | undefined;
if (!mentionerRow) return;
const usernames = [...new Set(
[...body.matchAll(MENTION_RE)].map((m) => m[1].toLowerCase()),
)];
for (const username of usernames) {
const mentionedRow = db.prepare(
`SELECT id FROM users WHERE lower(username) = ?;`,
).get(username) as { id: string } | undefined;
if (!mentionedRow || mentionedRow.id === mentionerUserId) continue;
createNotification(
mentionedRow.id,
"user_mentioned",
{
mentionerId: mentionerUserId,
mentionerUsername: mentionerRow.username,
contextType,
contextId,
contextTitle,
dumpId,
},
`mention:${contextType}:${contextId}:${mentionedRow.id}`,
);
}
}
export function notifyPlaylistFollowersNewDump(
playlistId: string,
playlistTitle: string,