154 lines
7.8 KiB
JavaScript
154 lines
7.8 KiB
JavaScript
const { db } = require('../../db');
|
|
const notifications = require('./notifications');
|
|
|
|
// ── Lookup statements for notification context ───────────────────────────────
|
|
const songOwnerStmt = db.prepare('SELECT uploaded_by, title, slug FROM songs WHERE id = ?');
|
|
const playlistOwnerStmt = db.prepare('SELECT created_by, title, slug FROM playlists WHERE id = ?');
|
|
const userNameStmt = db.prepare('SELECT display_name FROM users WHERE id = ?');
|
|
|
|
function getActorName(actorId) {
|
|
return actorId ? (userNameStmt.get(actorId)?.display_name || null) : null;
|
|
}
|
|
|
|
// ── Song statements ──────────────────────────────────────────────────────────
|
|
const songLikeCountStmt = db.prepare('SELECT COUNT(*) AS c FROM song_likes WHERE song_id = ?');
|
|
const songFavCountStmt = db.prepare('SELECT COUNT(*) AS c FROM song_favorites WHERE song_id = ?');
|
|
const songUserLikedStmt = db.prepare('SELECT 1 FROM song_likes WHERE user_id = ? AND song_id = ?');
|
|
const songUserFavoritedStmt = db.prepare('SELECT 1 FROM song_favorites WHERE user_id = ? AND song_id = ?');
|
|
const insertSongLike = db.prepare('INSERT OR IGNORE INTO song_likes (user_id, song_id) VALUES (?, ?)');
|
|
const deleteSongLike = db.prepare('DELETE FROM song_likes WHERE user_id = ? AND song_id = ?');
|
|
const insertSongFav = db.prepare('INSERT OR IGNORE INTO song_favorites (user_id, song_id) VALUES (?, ?)');
|
|
const deleteSongFav = db.prepare('DELETE FROM song_favorites WHERE user_id = ? AND song_id = ?');
|
|
|
|
// ── Playlist statements ──────────────────────────────────────────────────────
|
|
const plLikeCountStmt = db.prepare('SELECT COUNT(*) AS c FROM playlist_likes WHERE playlist_id = ?');
|
|
const plFavCountStmt = db.prepare('SELECT COUNT(*) AS c FROM playlist_favorites WHERE playlist_id = ?');
|
|
const plUserLikedStmt = db.prepare('SELECT 1 FROM playlist_likes WHERE user_id = ? AND playlist_id = ?');
|
|
const plUserFavoritedStmt = db.prepare('SELECT 1 FROM playlist_favorites WHERE user_id = ? AND playlist_id = ?');
|
|
const insertPlLike = db.prepare('INSERT OR IGNORE INTO playlist_likes (user_id, playlist_id) VALUES (?, ?)');
|
|
const deletePlLike = db.prepare('DELETE FROM playlist_likes WHERE user_id = ? AND playlist_id = ?');
|
|
const insertPlFav = db.prepare('INSERT OR IGNORE INTO playlist_favorites (user_id, playlist_id) VALUES (?, ?)');
|
|
const deletePlFav = db.prepare('DELETE FROM playlist_favorites WHERE user_id = ? AND playlist_id = ?');
|
|
|
|
// ── Toggle functions ─────────────────────────────────────────────────────────
|
|
function toggleSongLike(userId, songId) {
|
|
const exists = songUserLikedStmt.get(userId, songId);
|
|
if (exists) {
|
|
deleteSongLike.run(userId, songId);
|
|
} else {
|
|
insertSongLike.run(userId, songId);
|
|
const song = songOwnerStmt.get(songId);
|
|
if (song) notifications.create({ userId: song.uploaded_by, actorId: userId, actorName: getActorName(userId), action: 'like', entityType: 'song', entityId: songId, entityTitle: song.title, entitySlug: song.slug });
|
|
}
|
|
return { liked: !exists, count: songLikeCountStmt.get(songId).c };
|
|
}
|
|
|
|
function toggleSongFavorite(userId, songId) {
|
|
const exists = songUserFavoritedStmt.get(userId, songId);
|
|
if (exists) {
|
|
deleteSongFav.run(userId, songId);
|
|
} else {
|
|
insertSongFav.run(userId, songId);
|
|
const song = songOwnerStmt.get(songId);
|
|
if (song) notifications.create({ userId: song.uploaded_by, actorId: userId, actorName: getActorName(userId), action: 'favorite', entityType: 'song', entityId: songId, entityTitle: song.title, entitySlug: song.slug });
|
|
}
|
|
return { favorited: !exists, count: songFavCountStmt.get(songId).c };
|
|
}
|
|
|
|
function togglePlaylistLike(userId, playlistId) {
|
|
const exists = plUserLikedStmt.get(userId, playlistId);
|
|
if (exists) {
|
|
deletePlLike.run(userId, playlistId);
|
|
} else {
|
|
insertPlLike.run(userId, playlistId);
|
|
const pl = playlistOwnerStmt.get(playlistId);
|
|
if (pl) notifications.create({ userId: pl.created_by, actorId: userId, actorName: getActorName(userId), action: 'like', entityType: 'playlist', entityId: playlistId, entityTitle: pl.title, entitySlug: pl.slug });
|
|
}
|
|
return { liked: !exists, count: plLikeCountStmt.get(playlistId).c };
|
|
}
|
|
|
|
function togglePlaylistFavorite(userId, playlistId) {
|
|
const exists = plUserFavoritedStmt.get(userId, playlistId);
|
|
if (exists) {
|
|
deletePlFav.run(userId, playlistId);
|
|
} else {
|
|
insertPlFav.run(userId, playlistId);
|
|
const pl = playlistOwnerStmt.get(playlistId);
|
|
if (pl) notifications.create({ userId: pl.created_by, actorId: userId, actorName: getActorName(userId), action: 'favorite', entityType: 'playlist', entityId: playlistId, entityTitle: pl.title, entitySlug: pl.slug });
|
|
}
|
|
return { favorited: !exists, count: plFavCountStmt.get(playlistId).c };
|
|
}
|
|
|
|
// ── Enrichment helpers ───────────────────────────────────────────────────────
|
|
// Mutates each item in-place, adding likeCount/favoriteCount/userLiked/userFavorited.
|
|
function enrichSongs(songs, userId = null) {
|
|
for (const s of songs) {
|
|
s.likeCount = songLikeCountStmt.get(s.id).c;
|
|
s.favoriteCount = songFavCountStmt.get(s.id).c;
|
|
s.userLiked = userId ? !!songUserLikedStmt.get(userId, s.id) : false;
|
|
s.userFavorited = userId ? !!songUserFavoritedStmt.get(userId, s.id) : false;
|
|
}
|
|
return songs;
|
|
}
|
|
|
|
function enrichPlaylists(playlists, userId = null) {
|
|
for (const p of playlists) {
|
|
p.likeCount = plLikeCountStmt.get(p.id).c;
|
|
p.favoriteCount = plFavCountStmt.get(p.id).c;
|
|
p.userLiked = userId ? !!plUserLikedStmt.get(userId, p.id) : false;
|
|
p.userFavorited = userId ? !!plUserFavoritedStmt.get(userId, p.id) : false;
|
|
}
|
|
return playlists;
|
|
}
|
|
|
|
// ── Profile liked/favorited queries ─────────────────────────────────────────
|
|
const likedSongsGuestStmt = db.prepare(`
|
|
SELECT s.* FROM song_likes sl
|
|
JOIN songs s ON s.id = sl.song_id
|
|
WHERE sl.user_id = ? AND s.visibility = 'public'
|
|
ORDER BY sl.created_at DESC
|
|
`);
|
|
const likedSongsUserStmt = db.prepare(`
|
|
SELECT s.* FROM song_likes sl
|
|
JOIN songs s ON s.id = sl.song_id
|
|
WHERE sl.user_id = ? AND s.visibility IN ('public', 'logged_in')
|
|
ORDER BY sl.created_at DESC
|
|
`);
|
|
const likedPlaylistsGuestStmt = db.prepare(`
|
|
SELECT p.* FROM playlist_likes pl
|
|
JOIN playlists p ON p.id = pl.playlist_id
|
|
WHERE pl.user_id = ? AND p.visibility = 'public'
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM playlist_songs ps
|
|
JOIN songs s ON s.id = ps.song_id
|
|
WHERE ps.playlist_id = p.id AND s.visibility IN ('logged_in', 'private')
|
|
)
|
|
ORDER BY pl.created_at DESC
|
|
`);
|
|
const likedPlaylistsUserStmt = db.prepare(`
|
|
SELECT p.* FROM playlist_likes pl
|
|
JOIN playlists p ON p.id = pl.playlist_id
|
|
WHERE pl.user_id = ? AND p.visibility IN ('public', 'logged_in')
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM playlist_songs ps
|
|
JOIN songs s ON s.id = ps.song_id
|
|
WHERE ps.playlist_id = p.id AND s.visibility = 'private'
|
|
)
|
|
ORDER BY pl.created_at DESC
|
|
`);
|
|
|
|
function getLikedSongs(userId, loggedIn = false) {
|
|
return (loggedIn ? likedSongsUserStmt : likedSongsGuestStmt).all(userId);
|
|
}
|
|
|
|
function getLikedPlaylists(userId, loggedIn = false) {
|
|
return (loggedIn ? likedPlaylistsUserStmt : likedPlaylistsGuestStmt).all(userId);
|
|
}
|
|
|
|
module.exports = {
|
|
toggleSongLike, toggleSongFavorite,
|
|
togglePlaylistLike, togglePlaylistFavorite,
|
|
enrichSongs, enrichPlaylists,
|
|
getLikedSongs, getLikedPlaylists,
|
|
};
|