mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-11 22:06:17 +00:00
Feat: Rating score display redesign, Rating choosing redesign, added avg rating on the game page
This commit is contained in:
@@ -200,6 +200,7 @@
|
||||
"stats": "Stats",
|
||||
"download_count": "Downloads",
|
||||
"player_count": "Active players",
|
||||
"rating_count": "Rating",
|
||||
"download_error": "This download option is not available",
|
||||
"download": "Download",
|
||||
"executable_path_in_use": "Executable already in use by \"{{game}}\"",
|
||||
@@ -220,6 +221,8 @@
|
||||
"sort_lowest_score": "Lowest Score",
|
||||
"sort_most_voted": "Most Voted",
|
||||
"rating": "Rating",
|
||||
"rating_stats": "Rating",
|
||||
"select_rating": "Select Rating",
|
||||
"submit_review": "Submit Review",
|
||||
"submitting": "Submitting...",
|
||||
"loading_reviews": "Loading reviews...",
|
||||
|
||||
@@ -24,6 +24,14 @@ import { useUserDetails, useLibrary, useDate } from "@renderer/hooks";
|
||||
import { useSubscription } from "@renderer/hooks/use-subscription";
|
||||
import "./game-details.scss";
|
||||
|
||||
// Helper function to get score color class
|
||||
const getScoreColorClass = (score: number): string => {
|
||||
if (score >= 0 && score <= 3) return "game-details__review-score--red";
|
||||
if (score >= 4 && score <= 6) return "game-details__review-score--yellow";
|
||||
if (score >= 7 && score <= 10) return "game-details__review-score--green";
|
||||
return "";
|
||||
};
|
||||
|
||||
export function GameDetailsContent() {
|
||||
const heroRef = useRef<HTMLDivElement | null>(null);
|
||||
const navigate = useNavigate();
|
||||
@@ -103,7 +111,7 @@ export function GameDetailsContent() {
|
||||
// Reviews state management
|
||||
const [reviews, setReviews] = useState<GameReview[]>([]);
|
||||
const [reviewsLoading, setReviewsLoading] = useState(false);
|
||||
const [reviewScore, setReviewScore] = useState(5);
|
||||
const [reviewScore, setReviewScore] = useState<number | null>(null);
|
||||
const [submittingReview, setSubmittingReview] = useState(false);
|
||||
const [reviewCharCount, setReviewCharCount] = useState(0);
|
||||
const MAX_REVIEW_CHARS = 1000;
|
||||
@@ -302,6 +310,7 @@ export function GameDetailsContent() {
|
||||
if (
|
||||
!objectId ||
|
||||
!reviewHtml.trim() ||
|
||||
reviewScore === null ||
|
||||
submittingReview ||
|
||||
reviewCharCount > MAX_REVIEW_CHARS
|
||||
) {
|
||||
@@ -322,7 +331,7 @@ export function GameDetailsContent() {
|
||||
|
||||
console.log("Review submitted successfully");
|
||||
editor?.commands.clearContent();
|
||||
setReviewScore(5);
|
||||
setReviewScore(null);
|
||||
await loadReviews(true); // Reload reviews after submission
|
||||
setShowReviewForm(false); // Hide the review form after successful submission
|
||||
setShowReviewPrompt(false); // Hide the prompt banner
|
||||
@@ -606,10 +615,14 @@ export function GameDetailsContent() {
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<EditorContent
|
||||
editor={editor}
|
||||
<div
|
||||
className="game-details__review-input"
|
||||
/>
|
||||
onClick={() => editor?.commands.focus()}
|
||||
>
|
||||
<EditorContent
|
||||
editor={editor}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="game-details__review-form-bottom">
|
||||
@@ -618,12 +631,23 @@ export function GameDetailsContent() {
|
||||
{t("rating")}
|
||||
</label>
|
||||
<select
|
||||
className="game-details__review-score-select"
|
||||
value={reviewScore}
|
||||
className={`game-details__review-score-select ${
|
||||
reviewScore
|
||||
? reviewScore <= 3
|
||||
? 'game-details__review-score-select--red'
|
||||
: reviewScore <= 7
|
||||
? 'game-details__review-score-select--yellow'
|
||||
: 'game-details__review-score-select--green'
|
||||
: ''
|
||||
}`}
|
||||
value={reviewScore || ""}
|
||||
onChange={(e) =>
|
||||
setReviewScore(Number(e.target.value))
|
||||
setReviewScore(e.target.value ? Number(e.target.value) : null)
|
||||
}
|
||||
>
|
||||
<option value="" disabled>
|
||||
{t("select_rating")}
|
||||
</option>
|
||||
<option value={1}>1/10</option>
|
||||
<option value={2}>2/10</option>
|
||||
<option value={3}>3/10</option>
|
||||
@@ -642,6 +666,7 @@ export function GameDetailsContent() {
|
||||
onClick={handleSubmitReview}
|
||||
disabled={
|
||||
!editor?.getHTML().trim() ||
|
||||
reviewScore === null ||
|
||||
submittingReview ||
|
||||
reviewCharCount > MAX_REVIEW_CHARS
|
||||
}
|
||||
@@ -737,7 +762,7 @@ export function GameDetailsContent() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="game-details__review-score">
|
||||
<div className={`game-details__review-score ${getScoreColorClass(review.score)}`}>
|
||||
{review.score}/10
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -75,10 +75,30 @@ $hero-height: 300px;
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s ease, background-color 0.2s ease;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: #0078d4;
|
||||
}
|
||||
|
||||
&--red {
|
||||
border-color: #e74c3c;
|
||||
background-color: rgba(231, 76, 60, 0.1);
|
||||
}
|
||||
|
||||
&--yellow {
|
||||
border-color: #f39c12;
|
||||
background-color: rgba(243, 156, 18, 0.1);
|
||||
}
|
||||
|
||||
&--green {
|
||||
border-color: #27ae60;
|
||||
background-color: rgba(39, 174, 96, 0.1);
|
||||
}
|
||||
|
||||
option {
|
||||
background-color: #2a2a2a;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,6 +393,25 @@ $hero-height: 300px;
|
||||
font-size: globals.$small-font-size;
|
||||
font-weight: 600;
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
|
||||
// Color variants based on score
|
||||
&--red {
|
||||
background: rgba(239, 68, 68, 0.2);
|
||||
color: #fca5a5;
|
||||
border-color: rgba(239, 68, 68, 0.4);
|
||||
}
|
||||
|
||||
&--yellow {
|
||||
background: rgba(245, 158, 11, 0.2);
|
||||
color: #fcd34d;
|
||||
border-color: rgba(245, 158, 11, 0.4);
|
||||
}
|
||||
|
||||
&--green {
|
||||
background: rgba(34, 197, 94, 0.2);
|
||||
color: #86efac;
|
||||
border-color: rgba(34, 197, 94, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
&__review-date {
|
||||
@@ -953,12 +992,16 @@ $hero-height: 300px;
|
||||
&__review-input {
|
||||
min-height: 120px;
|
||||
padding: 12px;
|
||||
cursor: text;
|
||||
|
||||
.ProseMirror {
|
||||
outline: none;
|
||||
color: #ffffff;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
min-height: 96px; // 120px - 24px padding
|
||||
width: 100%;
|
||||
cursor: text;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
.stats {
|
||||
&__section {
|
||||
display: flex;
|
||||
gap: calc(globals.$spacing-unit * 2);
|
||||
gap: calc(globals.$spacing-unit * 1);
|
||||
padding: calc(globals.$spacing-unit * 2);
|
||||
justify-content: space-between;
|
||||
transition: max-height ease 0.5s;
|
||||
@@ -116,9 +116,6 @@
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
&__category-title {
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
DownloadIcon,
|
||||
LockIcon,
|
||||
PeopleIcon,
|
||||
StarIcon,
|
||||
} from "@primer/octicons-react";
|
||||
import { HowLongToBeatSection } from "./how-long-to-beat-section";
|
||||
import { howLongToBeatEntriesTable } from "@renderer/dexie";
|
||||
@@ -225,6 +226,16 @@ export function Sidebar() {
|
||||
</p>
|
||||
<p>{numberFormatter.format(stats?.playerCount)}</p>
|
||||
</div>
|
||||
|
||||
{stats?.averageScore && (
|
||||
<div className="stats__category">
|
||||
<p className="stats__category-title">
|
||||
<StarIcon size={18} />
|
||||
{t("rating_count")}
|
||||
</p>
|
||||
<p>{stats.averageScore.toFixed(1)}/10</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</SidebarSection>
|
||||
)}
|
||||
|
||||
@@ -24,7 +24,7 @@ function decodeHtmlEntities(text: string): string {
|
||||
function removeHtmlTags(html: string): string {
|
||||
let result = "";
|
||||
let inTag = false;
|
||||
|
||||
|
||||
for (const char of html) {
|
||||
if (char === "<") {
|
||||
inTag = true;
|
||||
@@ -34,7 +34,7 @@ function removeHtmlTags(html: string): string {
|
||||
result += char;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user