Bri-Tunes/src/services/social.js

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,
};