From fdc3fecd6f0ae4455067b79d512e42e255bc095b Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Sun, 2 Nov 2025 20:42:42 +0000 Subject: [PATCH] feat: adding reviews to profile --- .../src/pages/game-details/review-item.scss | 16 +- .../src/pages/game-details/review-item.tsx | 86 ++++----- .../profile/profile-content/library-tab.tsx | 10 +- .../profile-content/profile-content.scss | 39 +++- .../profile-content/profile-content.tsx | 4 + .../profile-content/profile-review-item.tsx | 169 ++++++++++++------ .../profile/profile-content/reviews-tab.tsx | 5 +- 7 files changed, 225 insertions(+), 104 deletions(-) diff --git a/src/renderer/src/pages/game-details/review-item.scss b/src/renderer/src/pages/game-details/review-item.scss index b3577a75..64657bfd 100644 --- a/src/renderer/src/pages/game-details/review-item.scss +++ b/src/renderer/src/pages/game-details/review-item.scss @@ -8,11 +8,23 @@ &__review-header { display: flex; - justify-content: space-between; - align-items: center; + flex-direction: column; + gap: calc(globals.$spacing-unit * 1); margin-bottom: calc(globals.$spacing-unit * 1.5); } + &__review-header-top { + display: flex; + justify-content: space-between; + align-items: flex-start; + } + + &__review-header-bottom { + display: flex; + justify-content: flex-start; + align-items: center; + } + &__review-user { display: flex; align-items: center; diff --git a/src/renderer/src/pages/game-details/review-item.tsx b/src/renderer/src/pages/game-details/review-item.tsx index bfc02cfe..09d91df8 100644 --- a/src/renderer/src/pages/game-details/review-item.tsx +++ b/src/renderer/src/pages/game-details/review-item.tsx @@ -126,60 +126,62 @@ export function ReviewItem({ return (
-
- -
+
+
-
-
+
- {Boolean( - review.playTimeInSeconds && review.playTimeInSeconds > 0 - ) && ( -
- - - {t("review_played_for")}{" "} - {formatPlayTime(review.playTimeInSeconds || 0)} - -
- )} + {review.user.displayName || "Anonymous"} +
-
-
{formatDistance(new Date(review.createdAt), new Date(), { addSuffix: true, })}
+
+
+
+ + + {review.score}/5 + +
+ {Boolean( + review.playTimeInSeconds && review.playTimeInSeconds > 0 + ) && ( +
+ + + {t("review_played_for")}{" "} + {formatPlayTime(review.playTimeInSeconds || 0)} + +
+ )} +
+
- {hasAnyGames && } + {hasAnyGames && ( + + )} {!hasAnyGames && (
@@ -123,8 +125,9 @@ export function LibraryTab({ >
    {libraryGames?.map((game, index) => { - const hasAnimated = - animatedGameIdsRef.current.has(game.objectId); + const hasAnimated = animatedGameIdsRef.current.has( + game.objectId + ); const isNewGame = !hasAnimated && !isLoadingLibraryGames; return ( @@ -173,4 +176,3 @@ export function LibraryTab({ ); } - diff --git a/src/renderer/src/pages/profile/profile-content/profile-content.scss b/src/renderer/src/pages/profile/profile-content/profile-content.scss index d41e3530..958fe52d 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-content.scss +++ b/src/renderer/src/pages/profile/profile-content/profile-content.scss @@ -235,10 +235,22 @@ } .user-reviews__review-header { + display: flex; + flex-direction: column; + gap: calc(globals.$spacing-unit * 1); + margin-bottom: calc(globals.$spacing-unit * 1.5); +} + +.user-reviews__review-header-top { display: flex; justify-content: space-between; align-items: flex-start; - margin-bottom: calc(globals.$spacing-unit * 1.5); +} + +.user-reviews__review-header-bottom { + display: flex; + justify-content: space-between; + align-items: center; } .user-reviews__review-meta-row { @@ -343,6 +355,31 @@ .user-reviews__review-content { color: rgba(255, 255, 255, 0.85); line-height: 1.5; + word-wrap: break-word; + word-break: break-word; + overflow-wrap: break-word; + white-space: pre-wrap; + max-width: 100%; +} + +.user-reviews__review-translation-toggle { + display: inline-flex; + align-items: center; + gap: calc(globals.$spacing-unit * 1); + margin-top: calc(globals.$spacing-unit * 1.5); + padding: 0; + background: none; + border: none; + color: rgba(255, 255, 255, 0.6); + font-size: 0.875rem; + cursor: pointer; + text-decoration: none; + transition: all 0.2s ease; + + &:hover { + text-decoration: underline; + color: rgba(255, 255, 255, 0.9); + } } .user-reviews__review-actions { diff --git a/src/renderer/src/pages/profile/profile-content/profile-content.tsx b/src/renderer/src/pages/profile/profile-content/profile-content.tsx index ab9fdf01..bb6842d7 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-content.tsx +++ b/src/renderer/src/pages/profile/profile-content/profile-content.tsx @@ -48,6 +48,10 @@ interface UserReview { objectId: string; shop: string; }; + translations: { + [key: string]: string; + }; + detectedLanguage: string | null; } interface UserReviewsResponse { diff --git a/src/renderer/src/pages/profile/profile-content/profile-review-item.tsx b/src/renderer/src/pages/profile/profile-content/profile-review-item.tsx index 79e0440e..bea569e7 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-review-item.tsx +++ b/src/renderer/src/pages/profile/profile-content/profile-review-item.tsx @@ -1,8 +1,11 @@ import { motion, AnimatePresence } from "framer-motion"; import { useNavigate } from "react-router-dom"; import { ClockIcon } from "@primer/octicons-react"; -import { Star, ThumbsUp, ThumbsDown, TrashIcon } from "lucide-react"; +import { Star, ThumbsUp, ThumbsDown, TrashIcon, Languages } from "lucide-react"; import { useTranslation } from "react-i18next"; +import { useState } from "react"; +import type { GameShop } from "@types"; +import { sanitizeHtml } from "@shared"; import { useDate } from "@renderer/hooks"; import { buildGameDetailsPath } from "@renderer/helpers"; import "./profile-content.scss"; @@ -25,8 +28,12 @@ interface UserReview { title: string; iconUrl: string; objectId: string; - shop: string; + shop: GameShop; }; + translations: { + [key: string]: string; + }; + detectedLanguage: string | null; } interface ProfileReviewItemProps { @@ -51,7 +58,32 @@ export function ProfileReviewItem({ const navigate = useNavigate(); const { formatDistance } = useDate(); const { t } = useTranslation("user_profile"); - const { t: tGameDetails } = useTranslation("game_details"); + const { t: tGameDetails, i18n } = useTranslation("game_details"); + const [showOriginal, setShowOriginal] = useState(false); + + const getBaseLanguage = (lang: string | null) => lang?.split("-")[0] || ""; + + const isDifferentLanguage = + getBaseLanguage(review.detectedLanguage) !== getBaseLanguage(i18n.language); + + const needsTranslation = + !isOwnReview && isDifferentLanguage && review.translations[i18n.language]; + + const getLanguageName = (languageCode: string | null) => { + if (!languageCode) return ""; + try { + const displayNames = new Intl.DisplayNames([i18n.language], { + type: "language", + }); + return displayNames.of(languageCode) || languageCode.toUpperCase(); + } catch { + return languageCode.toUpperCase(); + } + }; + + const displayContent = needsTranslation + ? review.translations[i18n.language] + : review.reviewHtml; return (
    -
    -
    - - - {review.score}/5 - +
    +
    +
    +
    + {review.game.title} + +
    +
    - {Boolean( - review.playTimeInSeconds && review.playTimeInSeconds > 0 - ) && ( -
    - - - {tGameDetails("review_played_for")}{" "} - {formatPlayTime(review.playTimeInSeconds || 0)} +
    + {formatDistance(new Date(review.createdAt), new Date(), { + addSuffix: true, + })} +
    +
    +
    +
    +
    + + + {review.score}/5
    - )} -
    -
    - {formatDistance(new Date(review.createdAt), new Date(), { - addSuffix: true, - })} + {Boolean( + review.playTimeInSeconds && review.playTimeInSeconds > 0 + ) && ( +
    + + + {tGameDetails("review_played_for")}{" "} + {formatPlayTime(review.playTimeInSeconds || 0)} + +
    + )} +
    -
    - -
    -
    -
    -
    - {review.game.title} +
    + {needsTranslation && ( + <> + + {showOriginal && ( +
    - -
    -
    -
    + )} + + )}
    @@ -188,4 +250,3 @@ export function ProfileReviewItem({ ); } - diff --git a/src/renderer/src/pages/profile/profile-content/reviews-tab.tsx b/src/renderer/src/pages/profile/profile-content/reviews-tab.tsx index 97924040..9fcea37e 100644 --- a/src/renderer/src/pages/profile/profile-content/reviews-tab.tsx +++ b/src/renderer/src/pages/profile/profile-content/reviews-tab.tsx @@ -23,6 +23,10 @@ interface UserReview { objectId: string; shop: string; }; + translations: { + [key: string]: string; + }; + detectedLanguage: string | null; } interface ReviewsTabProps { @@ -89,4 +93,3 @@ export function ReviewsTab({ ); } -