mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-11 13:56:16 +00:00
feat: showing playtime in review and changed positions of elements in review
This commit is contained in:
@@ -223,6 +223,7 @@
|
||||
"show_more": "Show more",
|
||||
"show_less": "Show less",
|
||||
"reviews": "Reviews",
|
||||
"played_for": "Played for",
|
||||
"leave_a_review": "Leave a Review",
|
||||
"write_review_placeholder": "Share your thoughts about this game...",
|
||||
"sort_newest": "Newest",
|
||||
|
||||
@@ -22,7 +22,13 @@
|
||||
&__review-user-info {
|
||||
display: flex;
|
||||
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 {
|
||||
@@ -157,28 +163,28 @@
|
||||
&__review-score-stars {
|
||||
display: flex;
|
||||
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 {
|
||||
color: #666666;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
transition: color 0.2s ease;
|
||||
cursor: default;
|
||||
|
||||
&--filled {
|
||||
color: #ffffff;
|
||||
|
||||
&.game-details__review-score--red {
|
||||
color: #fca5a5;
|
||||
}
|
||||
|
||||
&.game-details__review-score--yellow {
|
||||
color: #fcd34d;
|
||||
}
|
||||
|
||||
&.game-details__review-score--green {
|
||||
color: #86efac;
|
||||
}
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
&--empty {
|
||||
@@ -198,6 +204,24 @@
|
||||
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 {
|
||||
color: globals.$body-color;
|
||||
line-height: 1.5;
|
||||
|
||||
@@ -7,9 +7,10 @@ import { useState } from "react";
|
||||
import type { GameReview } from "@types";
|
||||
|
||||
import { sanitizeHtml } from "@shared";
|
||||
import { useDate } from "@renderer/hooks";
|
||||
import { useDate, useFormat } from "@renderer/hooks";
|
||||
import { formatNumber } from "@renderer/helpers";
|
||||
import { Avatar } from "@renderer/components";
|
||||
import { MAX_MINUTES_TO_SHOW_IN_PLAYTIME } from "@renderer/constants";
|
||||
|
||||
import "./review-item.scss";
|
||||
|
||||
@@ -29,12 +30,7 @@ interface ReviewItemProps {
|
||||
) => 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 => {
|
||||
switch (score) {
|
||||
@@ -68,6 +64,7 @@ export function ReviewItem({
|
||||
const navigate = useNavigate();
|
||||
const { t, i18n } = useTranslation("game_details");
|
||||
const { formatDistance } = useDate();
|
||||
const { numberFormatter } = useFormat();
|
||||
|
||||
const [showOriginal, setShowOriginal] = useState(false);
|
||||
|
||||
@@ -100,6 +97,20 @@ 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
|
||||
? review.translations[i18n.language]
|
||||
@@ -144,34 +155,37 @@ export function ReviewItem({
|
||||
>
|
||||
{review.user.displayName || "Anonymous"}
|
||||
</button>
|
||||
<div className="game-details__review-date">
|
||||
<ClockIcon size={12} />
|
||||
{formatDistance(new Date(review.createdAt), new Date(), {
|
||||
addSuffix: true,
|
||||
})}
|
||||
<div className="game-details__review-meta-row">
|
||||
<div
|
||||
className="game-details__review-score-stars"
|
||||
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>
|
||||
{review.playTimeInSeconds && review.playTimeInSeconds > 0 && (
|
||||
<div className="game-details__review-playtime">
|
||||
<ClockIcon size={12} />
|
||||
<span>
|
||||
{t("played_for")} {formatPlayTime(review.playTimeInSeconds)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="game-details__review-score-stars"
|
||||
title={getRatingText(review.score, t)}
|
||||
>
|
||||
{[1, 2, 3, 4, 5].map((starValue) => (
|
||||
<Star
|
||||
key={starValue}
|
||||
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 className="game-details__review-right">
|
||||
<div className="game-details__review-date">
|
||||
<ClockIcon size={12} />
|
||||
{formatDistance(new Date(review.createdAt), new Date(), {
|
||||
addSuffix: true,
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -255,6 +255,7 @@ export interface GameReview {
|
||||
isBlocked: boolean;
|
||||
hasUpvoted: boolean;
|
||||
hasDownvoted: boolean;
|
||||
playTimeInSeconds?: number;
|
||||
user: {
|
||||
id: string;
|
||||
displayName: string;
|
||||
|
||||
Reference in New Issue
Block a user