mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-28 13:21:02 +00:00
Merge branch 'main' into feat/displaying-new-game-update
This commit is contained in:
@@ -224,6 +224,7 @@
|
|||||||
"show_more": "Show more",
|
"show_more": "Show more",
|
||||||
"show_less": "Show less",
|
"show_less": "Show less",
|
||||||
"reviews": "Reviews",
|
"reviews": "Reviews",
|
||||||
|
"review_played_for": "Played for",
|
||||||
"leave_a_review": "Leave a Review",
|
"leave_a_review": "Leave a Review",
|
||||||
"write_review_placeholder": "Share your thoughts about this game...",
|
"write_review_placeholder": "Share your thoughts about this game...",
|
||||||
"sort_newest": "Newest",
|
"sort_newest": "Newest",
|
||||||
|
|||||||
@@ -22,7 +22,13 @@
|
|||||||
&__review-user-info {
|
&__review-user-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: calc(globals.$spacing-unit * 0.25);
|
gap: calc(globals.$spacing-unit * 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__review-meta-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: calc(globals.$spacing-unit * 0.75);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__review-display-name {
|
&__review-display-name {
|
||||||
@@ -157,28 +163,28 @@
|
|||||||
&__review-score-stars {
|
&__review-score-stars {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 2px;
|
gap: 4px;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__review-right {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__review-star {
|
&__review-star {
|
||||||
color: #666666;
|
color: rgba(255, 255, 255, 0.7);
|
||||||
transition: color 0.2s ease;
|
transition: color 0.2s ease;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
|
|
||||||
&--filled {
|
&--filled {
|
||||||
color: #ffffff;
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
|
||||||
&.game-details__review-score--red {
|
|
||||||
color: #fca5a5;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.game-details__review-score--yellow {
|
|
||||||
color: #fcd34d;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.game-details__review-score--green {
|
|
||||||
color: #86efac;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&--empty {
|
&--empty {
|
||||||
@@ -198,6 +204,24 @@
|
|||||||
font-size: globals.$small-font-size;
|
font-size: globals.$small-font-size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__review-playtime {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 500;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
margin-top: 0;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__review-content {
|
&__review-content {
|
||||||
color: globals.$body-color;
|
color: globals.$body-color;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ import { useState } from "react";
|
|||||||
import type { GameReview } from "@types";
|
import type { GameReview } from "@types";
|
||||||
|
|
||||||
import { sanitizeHtml } from "@shared";
|
import { sanitizeHtml } from "@shared";
|
||||||
import { useDate } from "@renderer/hooks";
|
import { useDate, useFormat } from "@renderer/hooks";
|
||||||
import { formatNumber } from "@renderer/helpers";
|
import { formatNumber } from "@renderer/helpers";
|
||||||
import { Avatar } from "@renderer/components";
|
import { Avatar } from "@renderer/components";
|
||||||
|
import { MAX_MINUTES_TO_SHOW_IN_PLAYTIME } from "@renderer/constants";
|
||||||
|
|
||||||
import "./review-item.scss";
|
import "./review-item.scss";
|
||||||
|
|
||||||
@@ -29,13 +30,6 @@ interface ReviewItemProps {
|
|||||||
) => void;
|
) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getScoreColorClass = (score: number): string => {
|
|
||||||
if (score >= 1 && score <= 2) return "game-details__review-score--red";
|
|
||||||
if (score >= 3 && score <= 3) return "game-details__review-score--yellow";
|
|
||||||
if (score >= 4 && score <= 5) return "game-details__review-score--green";
|
|
||||||
return "";
|
|
||||||
};
|
|
||||||
|
|
||||||
const getRatingText = (score: number, t: (key: string) => string): string => {
|
const getRatingText = (score: number, t: (key: string) => string): string => {
|
||||||
switch (score) {
|
switch (score) {
|
||||||
case 1:
|
case 1:
|
||||||
@@ -68,6 +62,7 @@ export function ReviewItem({
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { t, i18n } = useTranslation("game_details");
|
const { t, i18n } = useTranslation("game_details");
|
||||||
const { formatDistance } = useDate();
|
const { formatDistance } = useDate();
|
||||||
|
const { numberFormatter } = useFormat();
|
||||||
|
|
||||||
const [showOriginal, setShowOriginal] = useState(false);
|
const [showOriginal, setShowOriginal] = useState(false);
|
||||||
|
|
||||||
@@ -93,6 +88,21 @@ export function ReviewItem({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Format playtime similar to hero panel
|
||||||
|
const formatPlayTime = (playTimeInSeconds: number) => {
|
||||||
|
const minutes = playTimeInSeconds / 60;
|
||||||
|
|
||||||
|
if (minutes < MAX_MINUTES_TO_SHOW_IN_PLAYTIME) {
|
||||||
|
return t("amount_minutes", {
|
||||||
|
amount: minutes.toFixed(0),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const hours = minutes / 60;
|
||||||
|
return t("amount_hours", { amount: numberFormatter.format(hours) });
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine which content to show - always show original for own reviews
|
||||||
const displayContent = needsTranslation
|
const displayContent = needsTranslation
|
||||||
? review.translations[i18n.language]
|
? review.translations[i18n.language]
|
||||||
: review.reviewHtml;
|
: review.reviewHtml;
|
||||||
@@ -136,34 +146,40 @@ export function ReviewItem({
|
|||||||
>
|
>
|
||||||
{review.user.displayName || "Anonymous"}
|
{review.user.displayName || "Anonymous"}
|
||||||
</button>
|
</button>
|
||||||
<div className="game-details__review-date">
|
<div className="game-details__review-meta-row">
|
||||||
<ClockIcon size={12} />
|
<div
|
||||||
{formatDistance(new Date(review.createdAt), new Date(), {
|
className="game-details__review-score-stars"
|
||||||
addSuffix: true,
|
title={getRatingText(review.score, t)}
|
||||||
})}
|
>
|
||||||
|
<Star
|
||||||
|
size={12}
|
||||||
|
className="game-details__review-star game-details__review-star--filled"
|
||||||
|
/>
|
||||||
|
<span className="game-details__review-score-text">
|
||||||
|
{review.score}/5
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{Boolean(
|
||||||
|
review.playTimeInSeconds && review.playTimeInSeconds > 0
|
||||||
|
) && (
|
||||||
|
<div className="game-details__review-playtime">
|
||||||
|
<ClockIcon size={12} />
|
||||||
|
<span>
|
||||||
|
{t("review_played_for")}{" "}
|
||||||
|
{formatPlayTime(review.playTimeInSeconds || 0)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className="game-details__review-right">
|
||||||
className="game-details__review-score-stars"
|
<div className="game-details__review-date">
|
||||||
title={getRatingText(review.score, t)}
|
<ClockIcon size={12} />
|
||||||
>
|
{formatDistance(new Date(review.createdAt), new Date(), {
|
||||||
{[1, 2, 3, 4, 5].map((starValue) => (
|
addSuffix: true,
|
||||||
<Star
|
})}
|
||||||
key={starValue}
|
</div>
|
||||||
size={20}
|
|
||||||
fill={starValue <= review.score ? "currentColor" : "none"}
|
|
||||||
className={`game-details__review-star ${
|
|
||||||
starValue <= review.score
|
|
||||||
? "game-details__review-star--filled"
|
|
||||||
: "game-details__review-star--empty"
|
|
||||||
} ${
|
|
||||||
starValue <= review.score
|
|
||||||
? getScoreColorClass(review.score)
|
|
||||||
: ""
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -245,6 +245,7 @@ export interface GameReview {
|
|||||||
isBlocked: boolean;
|
isBlocked: boolean;
|
||||||
hasUpvoted: boolean;
|
hasUpvoted: boolean;
|
||||||
hasDownvoted: boolean;
|
hasDownvoted: boolean;
|
||||||
|
playTimeInSeconds?: number;
|
||||||
user: {
|
user: {
|
||||||
id: string;
|
id: string;
|
||||||
displayName: string;
|
displayName: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user