From 941101702eccef87a0fde2fb73cc701cee1d9401 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Sun, 9 Nov 2025 18:18:16 +0200 Subject: [PATCH] ci: moving souvenirs into separate tab --- .../profile-content/profile-content.scss | 2 + .../profile-content/profile-content.tsx | 303 ++++-------------- .../profile/profile-content/profile-tabs.tsx | 29 +- .../profile/profile-content/souvenirs-tab.tsx | 115 +++++++ src/types/index.ts | 4 +- 5 files changed, 215 insertions(+), 238 deletions(-) create mode 100644 src/renderer/src/pages/profile/profile-content/souvenirs-tab.tsx 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 4f4cf7ba..5a80bfbe 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-content.scss +++ b/src/renderer/src/pages/profile/profile-content/profile-content.scss @@ -442,7 +442,9 @@ border-color: rgba(244, 67, 54, 0.4); color: #ff7961; } +} +.profile-content { &__images-section { margin-bottom: calc(globals.$spacing-unit * 3); } 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 186ec439..5d907e4a 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-content.tsx +++ b/src/renderer/src/pages/profile/profile-content/profile-content.tsx @@ -10,11 +10,7 @@ import { import { ProfileHero } from "../profile-hero/profile-hero"; import { useAppDispatch, useFormat, useUserDetails } from "@renderer/hooks"; import { setHeaderTitle } from "@renderer/features"; -import { - TelescopeIcon, - ChevronRightIcon, - SearchIcon, -} from "@primer/octicons-react"; +import { TelescopeIcon } from "@primer/octicons-react"; import { useTranslation } from "react-i18next"; import type { GameShop } from "@types"; import { LockedProfile } from "./locked-profile"; @@ -23,23 +19,16 @@ import { FriendsBox } from "./friends-box"; import { RecentGamesBox } from "./recent-games-box"; import { UserStatsBox } from "./user-stats-box"; import { UserKarmaBox } from "./user-karma-box"; -import { UserLibraryGameCard } from "./user-library-game-card"; -import { SortOptions } from "./sort-options"; -import { useSectionCollapse } from "@renderer/hooks/use-section-collapse"; -import { motion, AnimatePresence } from "framer-motion"; -import { - sectionVariants, - chevronVariants, - GAME_STATS_ANIMATION_DURATION_IN_MS, -} from "./profile-animations"; +import { logger } from "@renderer/logger"; +import { AnimatePresence } from "framer-motion"; +import { GAME_STATS_ANIMATION_DURATION_IN_MS } from "./profile-animations"; import { FullscreenImageModal } from "@renderer/components/fullscreen-image-modal"; import { DeleteReviewModal } from "@renderer/pages/game-details/modals/delete-review-modal"; -import { GAME_STATS_ANIMATION_DURATION_IN_MS } from "./profile-animations"; import { MAX_MINUTES_TO_SHOW_IN_PLAYTIME } from "@renderer/constants"; import { ProfileTabs } from "./profile-tabs"; import { LibraryTab } from "./library-tab"; import { ReviewsTab } from "./reviews-tab"; -import { AnimatePresence } from "framer-motion"; +import { SouvenirsTab } from "./souvenirs-tab"; import "./profile-content.scss"; type SortOption = "playtime" | "achievementCount" | "playedRecently"; @@ -114,7 +103,9 @@ export function ProfileContent() { } | null>(null); const statsAnimation = useRef(-1); - const [activeTab, setActiveTab] = useState<"library" | "reviews">("library"); + const [activeTab, setActiveTab] = useState< + "library" | "reviews" | "souvenirs" + >("library"); // User reviews state const [reviews, setReviews] = useState([]); @@ -226,7 +217,7 @@ export function ProfileContent() { setReviews((prev) => prev.filter((review) => review.id !== reviewId)); setReviewsTotalCount((prev) => prev - 1); } catch (error) { - console.error("Failed to delete review:", error); + logger.error("Failed to delete review:", error); } }; @@ -321,7 +312,7 @@ export function ProfileContent() { `/games/${review.game.shop}/${review.game.objectId}/reviews/${reviewId}/${endpoint}` ); } catch (error) { - console.error("Failed to vote on review:", error); + logger.error("Failed to vote on review:", error); // Rollback optimistic update on error setReviews((prev) => @@ -424,222 +415,56 @@ export function ProfileContent() { {hasAnyGames && (
- {hasPinnedGames && ( -
-
-
- -

{t("pinned")}

- - {pinnedGames.length} - -
-
+ - - {!isPinnedCollapsed && ( - -
    - {pinnedGames?.map((game) => ( -
  • - -
  • - ))} -
-
- )} -
-
- )} +
+ + {activeTab === "library" && ( + + )} - {userProfile?.achievements && - userProfile.achievements.length > 0 && ( -
-
-
-

{t("souvenirs")}

- - {userProfile.achievements.length} - -
-
+ {activeTab === "reviews" && ( + + )} -
- {userProfile.achievements.map((achievement, index) => ( -
-
-
- -
- -
-
-
- -
-
- - - {achievement.name} - -
- -
-
- - - {achievement.gameTitle} - -
-
-
- -
-
- ))} -
-
- )} - - {hasAnyGames && ( - - )} - - {hasGames && ( -
-
-
-

{t("library")}

- {userStats && ( - - {numberFormatter.format(userStats.libraryCount)} - - )} -
-
- -
    - {libraryGames?.map((game) => ( -
  • - -
  • - ))} -
-
- - -
- - {activeTab === "library" && ( - - )} - - {activeTab === "reviews" && ( - - )} - -
+ {activeTab === "souvenirs" && ( + + )} +
+
+
+ )} {shouldShowRightContent && ( @@ -669,15 +494,25 @@ export function ProfileContent() { statsIndex, libraryGames, pinnedGames, - sortBy, activeTab, - // ensure reviews UI updates correctly reviews, reviewsTotalCount, isLoadingReviews, votingReviews, deleteModalVisible, + handleOnMouseEnterGameCard, + handleOnMouseLeaveGameCard, + handleImageClick, + handleLoadMore, + formatPlayTime, + getRatingText, + handleVoteReview, + handleDeleteClick, + userDetails, + animatedGameIdsRef, + hasMoreLibraryGames, + isLoadingLibraryGames, ]); return ( diff --git a/src/renderer/src/pages/profile/profile-content/profile-tabs.tsx b/src/renderer/src/pages/profile/profile-content/profile-tabs.tsx index bc76f40c..f3f3ea08 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-tabs.tsx +++ b/src/renderer/src/pages/profile/profile-content/profile-tabs.tsx @@ -3,14 +3,16 @@ import { useTranslation } from "react-i18next"; import "./profile-content.scss"; interface ProfileTabsProps { - activeTab: "library" | "reviews"; + activeTab: "library" | "reviews" | "souvenirs"; reviewsTotalCount: number; - onTabChange: (tab: "library" | "reviews") => void; + souvenirsCount: number; + onTabChange: (tab: "library" | "reviews" | "souvenirs") => void; } export function ProfileTabs({ activeTab, reviewsTotalCount, + souvenirsCount, onTabChange, }: Readonly) { const { t } = useTranslation("user_profile"); @@ -62,6 +64,29 @@ export function ProfileTabs({ /> )} +
+ + {activeTab === "souvenirs" && ( + + )} +
); } diff --git a/src/renderer/src/pages/profile/profile-content/souvenirs-tab.tsx b/src/renderer/src/pages/profile/profile-content/souvenirs-tab.tsx new file mode 100644 index 00000000..fa8442c8 --- /dev/null +++ b/src/renderer/src/pages/profile/profile-content/souvenirs-tab.tsx @@ -0,0 +1,115 @@ +import { motion } from "framer-motion"; +import { useTranslation } from "react-i18next"; +import { SearchIcon } from "@primer/octicons-react"; +import "./profile-content.scss"; + +interface Achievement { + name: string; + imageUrl: string; + achievementIcon: string | null; + gameTitle: string; + gameIconUrl: string | null; +} + +interface SouvenirsTabProps { + achievements: Achievement[]; + onImageClick: (imageUrl: string, achievementName: string) => void; +} + +export function SouvenirsTab({ + achievements, + onImageClick, +}: Readonly) { + const { t } = useTranslation("user_profile"); + + return ( + + {achievements.length === 0 && ( +
+

{t("no_souvenirs", "No souvenirs yet")}

+
+ )} + {achievements.length > 0 && ( +
+ {achievements.map((achievement, index) => ( +
+
+
+ +
+ +
+
+
+ +
+
+ {achievement.achievementIcon && ( + + )} + + {achievement.name} + +
+ +
+
+ {achievement.gameIconUrl && ( + + )} + + {achievement.gameTitle} + +
+
+
+ +
+
+ ))} +
+ )} +
+ ); +} diff --git a/src/types/index.ts b/src/types/index.ts index e130fe8a..e939fb49 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -194,8 +194,8 @@ export interface ProfileAchievement { imageUrl: string; unlockTime: number; gameTitle: string; - gameIconUrl: string; - achievementIcon: string; + gameIconUrl: string | null; + achievementIcon: string | null; } export interface UserProfile {