feat(reviews): detailed critics reviews
This commit is contained in:
parent
11f66c1a28
commit
db20643a61
2 changed files with 104 additions and 1 deletions
62
js/ui.js
62
js/ui.js
|
|
@ -13,7 +13,9 @@ import {
|
|||
calculateTotalDuration,
|
||||
formatDuration,
|
||||
escapeHtml,
|
||||
decodeHtml,
|
||||
getShareUrl,
|
||||
createModal,
|
||||
} from './utils.js';
|
||||
import { openLyricsPanel, renderLyricsInFullscreen, clearFullscreenLyricsSync } from './lyrics.js';
|
||||
import {
|
||||
|
|
@ -3853,11 +3855,69 @@ export class UIRenderer {
|
|||
);
|
||||
const data = await response.json();
|
||||
|
||||
rateCriticsEl.innerHTML = `<a href="${data.url}" target="_blank" style="color: var(--muted-foreground);">Critic Score: <span style="text-decoration: underline;">${data.critic.score}</span>, Based on ${data.critic.count} reviews</a>`;
|
||||
const critviews = data.critic.reviews || [];
|
||||
|
||||
rateCriticsEl.innerHTML = `<a href="javascript:void(0)" style="color: var(--muted-foreground); cursor: pointer;">Critic Score: ${data.critic.score}, Based on <span style="text-decoration: underline;">${data.critic.count} reviews</span></a>`;
|
||||
|
||||
if (data.critic.score == 'NR') {
|
||||
rateCriticsEl.innerHTML = `<a style="color: var(--muted-foreground);">Critic Score Not Available Yet</a>`;
|
||||
} else {
|
||||
rateCriticsEl.querySelector('a').onclick = () => {
|
||||
const con = document.createElement('div');
|
||||
con.style.display = 'flex';
|
||||
con.style.flexDirection = 'column';
|
||||
con.style.gap = '1.5rem';
|
||||
|
||||
critviews.forEach((review) => {
|
||||
const reviewdiv = document.createElement('div');
|
||||
reviewdiv.style.display = 'flex';
|
||||
reviewdiv.style.gap = '1rem';
|
||||
reviewdiv.style.paddingBottom = '1rem';
|
||||
reviewdiv.style.borderBottom = '1px solid var(--border)';
|
||||
|
||||
const publication = decodeHtml(review.publication || review.source || 'Unknown Publication');
|
||||
const author = decodeHtml(review.author || '');
|
||||
const quote = decodeHtml(review.text || review.quote || 'No review text available.');
|
||||
|
||||
reviewdiv.innerHTML = `
|
||||
<img src="${review.image}" width="50" height="50" style="border-radius: 8px; object-fit: cover; background: var(--highlight);"
|
||||
onerror="this.src='images/monochrome-logo.svg'; this.onerror=null;"
|
||||
loading="lazy"
|
||||
referrerpolicy="no-referrer">
|
||||
<div style="flex: 1;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.25rem;">
|
||||
<div class="pub-name" style="font-weight: 600; color: var(--foreground);"></div>
|
||||
<div style="font-weight: bold; color: var(--primary-foreground); background: var(--primary); padding: 2px 10px; border-radius: 6px; font-size: 0.85rem; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">${review.score}</div>
|
||||
</div>
|
||||
<div class="author-name" style="font-size: 0.8rem; color: var(--muted-foreground); margin-bottom: 0.5rem;"></div>
|
||||
<div class="quote-text" style="font-size: 0.95rem; line-height: 1.5; color: var(--muted-foreground); font-style: italic;"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
reviewdiv.querySelector('.pub-name').textContent = publication;
|
||||
if (author) {
|
||||
reviewdiv.querySelector('.author-name').textContent = `By ${author}`;
|
||||
} else {
|
||||
reviewdiv.querySelector('.author-name').remove();
|
||||
}
|
||||
reviewdiv.querySelector('.quote-text').textContent = `"${quote}"`;
|
||||
|
||||
con.appendChild(reviewdiv);
|
||||
});
|
||||
|
||||
if (critviews.length === 0) {
|
||||
con.innerHTML =
|
||||
'<div style="text-align: center; padding: 2rem; color: var(--muted-foreground);">No reviews found.</div>';
|
||||
}
|
||||
|
||||
createModal({
|
||||
title: 'Critics Reviews',
|
||||
content: con,
|
||||
className: 'extra-wide',
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
rateUsersEl.innerHTML = `<a href="${data.url}" target="_blank" style="color: var(--muted-foreground);">User Score: <span style="text-decoration: underline;">${data.user.score}</span>, Based on ${data.user.count} reviews</a>`;
|
||||
} catch (e) {
|
||||
rateCriticsEl.innerHTML = `<a style="color: var(--muted-foreground);">Unable to Fetch Critic Score</a>`;
|
||||
|
|
|
|||
43
js/utils.js
43
js/utils.js
|
|
@ -381,6 +381,13 @@ export const escapeHtml = (unsafe) => {
|
|||
.replace(/'/g, ''');
|
||||
};
|
||||
|
||||
export const decodeHtml = (html) => {
|
||||
if (!html) return '';
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = html;
|
||||
return div.textContent;
|
||||
};
|
||||
|
||||
export const getTrackTitle = (track, { fallback = 'Unknown Title' } = {}) => {
|
||||
if (!track?.title) return fallback;
|
||||
return track?.version ? `${track.title} (${track.version})` : track.title;
|
||||
|
|
@ -778,3 +785,39 @@ export function replaceTokens(template, tokens) {
|
|||
return key in tokens ? tokens[key] : match;
|
||||
});
|
||||
}
|
||||
|
||||
export function createModal({ title, content, className = '', onClose }) {
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'modal active';
|
||||
modal.style.zIndex = '10000';
|
||||
|
||||
modal.innerHTML = `
|
||||
<div class="modal-overlay"></div>
|
||||
<div class="modal-content ${className}" style="display: flex; flex-direction: column;">
|
||||
<div class="modal-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; border-bottom: 1px solid var(--border); padding-bottom: 1rem;">
|
||||
<h3 style="margin: 0;">${title}</h3>
|
||||
<button class="btn-close" style="background: none; border: none; font-size: 2rem; cursor: pointer; color: var(--foreground); padding: 0.2rem 0.5rem; line-height: 1;">×</button>
|
||||
</div>
|
||||
<div class="modal-body" style="max-height: 70vh; overflow-y: auto; padding-right: 0.5rem;"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const body = modal.querySelector('.modal-body');
|
||||
if (typeof content === 'string') {
|
||||
body.innerHTML = content;
|
||||
} else if (content instanceof HTMLElement) {
|
||||
body.appendChild(content);
|
||||
}
|
||||
|
||||
document.body.appendChild(modal);
|
||||
|
||||
const close = () => {
|
||||
modal.remove();
|
||||
if (onClose) onClose();
|
||||
};
|
||||
|
||||
modal.querySelector('.modal-overlay').onclick = close;
|
||||
modal.querySelector('.btn-close').onclick = close;
|
||||
|
||||
return { modal, close };
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue