feat(stars): add DELETE endpoint and support rating removal
- Implement DELETE /api/stars to remove star ratings by mediaId - Update media's star count and average rating on deletion - Modify frontend rating handlers to support unstarring by sending DELETE requests - Update multiple pages to handle rating removal when rating is zero - Adjust photo viewer to toggle rating off if same rating clicked - Improve error handling and success responses for star rating API interactions
This commit is contained in:
parent
5fac68e809
commit
b8b46fcf0e
BIN
data/media.db
BIN
data/media.db
Binary file not shown.
|
|
@ -73,6 +73,37 @@ export async function POST(request: Request) {
|
|||
WHERE id = ?
|
||||
`).run(mediaId, mediaId, mediaId);
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error: any) {
|
||||
return NextResponse.json({ error: error.message }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function DELETE(request: Request) {
|
||||
try {
|
||||
const db = getDatabase();
|
||||
const { mediaId } = await request.json();
|
||||
|
||||
if (!mediaId) {
|
||||
return NextResponse.json({ error: 'mediaId is required' }, { status: 400 });
|
||||
}
|
||||
|
||||
// Delete star rating by media_id
|
||||
const result = db.prepare(`DELETE FROM stars WHERE media_id = ?`).run(mediaId);
|
||||
|
||||
if (result.changes === 0) {
|
||||
return NextResponse.json({ error: 'No rating found for this media' }, { status: 404 });
|
||||
}
|
||||
|
||||
// Update media star count and average rating
|
||||
db.prepare(`
|
||||
UPDATE media
|
||||
SET
|
||||
star_count = (SELECT COUNT(*) FROM stars WHERE media_id = ?),
|
||||
avg_rating = COALESCE((SELECT AVG(rating) FROM stars WHERE media_id = ?), 0.0)
|
||||
WHERE id = ?
|
||||
`).run(mediaId, mediaId, mediaId);
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error: any) {
|
||||
return NextResponse.json({ error: error.message }, { status: 500 });
|
||||
|
|
|
|||
|
|
@ -61,11 +61,21 @@ export default function BookmarksPage() {
|
|||
|
||||
const handleRate = async (id: number, rating: number) => {
|
||||
try {
|
||||
await fetch(`/api/stars/${id}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ rating })
|
||||
});
|
||||
if (rating === 0) {
|
||||
// For unstarring (rating = 0), delete the existing rating
|
||||
await fetch(`/api/stars`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ mediaId: id })
|
||||
});
|
||||
} else {
|
||||
// For setting/updating a rating
|
||||
await fetch(`/api/stars`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ mediaId: id, rating })
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error rating item:', error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -215,16 +215,28 @@ const FolderViewerPage = () => {
|
|||
// Handle rating operations
|
||||
const handleRate = async (mediaId: number, rating: number) => {
|
||||
try {
|
||||
const response = await fetch(`/api/stars`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ mediaId, rating })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
console.log('Rating added successfully');
|
||||
if (rating === 0) {
|
||||
// For unstarring (rating = 0), delete the existing rating
|
||||
await fetch(`/api/stars`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ mediaId })
|
||||
});
|
||||
} else {
|
||||
// For setting/updating a rating
|
||||
const response = await fetch(`/api/stars`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ mediaId, rating })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
console.log('Rating added successfully');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error rating item:', error);
|
||||
|
|
|
|||
|
|
@ -85,11 +85,21 @@ export default function PhotosPage() {
|
|||
|
||||
const handleRate = async (photoId: number, rating: number) => {
|
||||
try {
|
||||
await fetch(`/api/stars/${photoId}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ rating })
|
||||
});
|
||||
if (rating === 0) {
|
||||
// For unstarring (rating = 0), delete the existing rating
|
||||
await fetch(`/api/stars`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ mediaId: photoId })
|
||||
});
|
||||
} else {
|
||||
// For setting/updating a rating
|
||||
await fetch(`/api/stars`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ mediaId: photoId, rating })
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error rating photo:', error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,11 +57,21 @@ const TextsPage = () => {
|
|||
|
||||
const handleRate = async (textId: number, rating: number) => {
|
||||
try {
|
||||
await fetch(`/api/stars/${textId}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ rating })
|
||||
});
|
||||
if (rating === 0) {
|
||||
// For unstarring (rating = 0), delete the existing rating
|
||||
await fetch(`/api/stars`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ mediaId: textId })
|
||||
});
|
||||
} else {
|
||||
// For setting/updating a rating
|
||||
await fetch(`/api/stars`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ mediaId: textId, rating })
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error rating text:', error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,11 +49,31 @@ const VideosPage = () => {
|
|||
|
||||
const handleRate = async (videoId: number, rating: number) => {
|
||||
try {
|
||||
await fetch(`/api/stars/${videoId}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ rating })
|
||||
});
|
||||
if (rating === 0) {
|
||||
// For unstarring (rating = 0), we need to delete the existing rating
|
||||
// First, get the current rating record to find the star ID
|
||||
const getResponse = await fetch(`/api/stars/${videoId}`);
|
||||
if (getResponse.ok) {
|
||||
const data = await getResponse.json();
|
||||
if (data.hasRating) {
|
||||
// We need to get the actual star record ID to delete it
|
||||
// Since the API structure doesn't return star ID, we'll use a different approach
|
||||
// Delete by media_id instead of star id
|
||||
await fetch(`/api/stars`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ mediaId: videoId })
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For setting/updating a rating
|
||||
await fetch(`/api/stars`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ mediaId: videoId, rating })
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error rating video:', error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,7 +130,10 @@ export default function PhotoViewer({
|
|||
|
||||
const handleRate = (rating: number) => {
|
||||
if (onRate && 'id' in photo && photo.id !== undefined) {
|
||||
onRate(photo.id, rating);
|
||||
const currentRating = Math.round(getAvgRating());
|
||||
// If clicking the same star as current rating, cancel the rating
|
||||
const newRating = currentRating === rating ? 0 : rating;
|
||||
onRate(photo.id, newRating);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue