From cea5afc7f7d57af7d8bfee7b57f62c3c99d42ad4 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Wed, 24 Dec 2025 13:22:57 +0200 Subject: [PATCH] feat: enhance add friend modal with friend code display and copy functionality --- .../profile-content/add-friend-modal.scss | 41 +++++++++++++++++++ .../profile-content/add-friend-modal.tsx | 28 +++++++++++++ .../profile/profile-content/friends-box.scss | 5 ++- .../profile/profile-content/friends-box.tsx | 28 ++++++++----- .../profile-content/profile-content.tsx | 7 ---- .../profile-content/recent-games-box.scss | 4 +- .../profile-content/user-karma-box.scss | 37 ----------------- .../profile-content/user-karma-box.tsx | 37 ----------------- .../profile-content/user-stats-box.scss | 10 ++++- .../profile-content/user-stats-box.tsx | 26 +++++++++++- .../profile/profile-hero/profile-hero.scss | 4 +- .../profile/profile-hero/profile-hero.tsx | 8 ++-- 12 files changed, 132 insertions(+), 103 deletions(-) delete mode 100644 src/renderer/src/pages/profile/profile-content/user-karma-box.scss delete mode 100644 src/renderer/src/pages/profile/profile-content/user-karma-box.tsx diff --git a/src/renderer/src/pages/profile/profile-content/add-friend-modal.scss b/src/renderer/src/pages/profile/profile-content/add-friend-modal.scss index f33b5515..6e89ae1a 100644 --- a/src/renderer/src/pages/profile/profile-content/add-friend-modal.scss +++ b/src/renderer/src/pages/profile/profile-content/add-friend-modal.scss @@ -7,6 +7,47 @@ width: 100%; min-width: 400px; + &__my-code { + display: flex; + align-items: center; + gap: calc(globals.$spacing-unit * 1.5); + padding: calc(globals.$spacing-unit * 1.5); + border-radius: 8px; + border: 1px solid rgba(255, 255, 255, 0.1); + } + + &__my-code-label { + font-size: 0.875rem; + color: globals.$muted-color; + font-weight: 500; + } + + &__my-code-value { + font-size: 0.875rem; + color: globals.$body-color; + font-family: monospace; + font-weight: 600; + flex: 1; + } + + &__copy-icon-button { + display: flex; + align-items: center; + justify-content: center; + background: none; + border: none; + color: globals.$body-color; + cursor: pointer; + padding: calc(globals.$spacing-unit / 2); + border-radius: 4px; + transition: all ease 0.2s; + + &:hover { + background-color: rgba(255, 255, 255, 0.1); + color: globals.$body-color; + } + } + &__actions { display: flex; flex-direction: row; diff --git a/src/renderer/src/pages/profile/profile-content/add-friend-modal.tsx b/src/renderer/src/pages/profile/profile-content/add-friend-modal.tsx index efe95ec5..7f370a39 100644 --- a/src/renderer/src/pages/profile/profile-content/add-friend-modal.tsx +++ b/src/renderer/src/pages/profile/profile-content/add-friend-modal.tsx @@ -3,6 +3,7 @@ import { useToast, useUserDetails } from "@renderer/hooks"; import { useState, useEffect } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; +import { CopyIcon } from "@primer/octicons-react"; import "./add-friend-modal.scss"; interface AddFriendModalProps { @@ -22,10 +23,18 @@ export function AddFriendModal({ visible, onClose }: AddFriendModalProps) { updateFriendRequestState, friendRequests, fetchFriendRequests, + userDetails, } = useUserDetails(); const { showSuccessToast, showErrorToast } = useToast(); + const copyMyFriendCode = () => { + if (userDetails?.id) { + navigator.clipboard.writeText(userDetails.id); + showSuccessToast(t("friend_code_copied")); + } + }; + useEffect(() => { if (visible) { setFriendCode(""); @@ -88,6 +97,25 @@ export function AddFriendModal({ visible, onClose }: AddFriendModalProps) { return (
+ {userDetails?.id && ( +
+ + {t("your_friend_code")} + + + {userDetails.id} + + +
+ )} +
MAX_VISIBLE_FRIENDS; + return ( <>
    - {userProfile?.friends.map((friend) => ( + {visibleFriends.map((friend) => (
  • ))}
-
- -
+ {showViewAllButton && ( +
+ +
+ )}
{userProfile && ( 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 8c200b06..d3be6091 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-content.tsx +++ b/src/renderer/src/pages/profile/profile-content/profile-content.tsx @@ -18,7 +18,6 @@ import { BadgesBox } from "./badges-box"; import { FriendsBox, FriendsBoxAddButton } from "./friends-box"; import { RecentGamesBox } from "./recent-games-box"; import { UserStatsBox } from "./user-stats-box"; -import { UserKarmaBox } from "./user-karma-box"; import { ProfileSection } from "../profile-section/profile-section"; import { DeleteReviewModal } from "@renderer/pages/game-details/modals/delete-review-modal"; import { GAME_STATS_ANIMATION_DURATION_IN_MS } from "./profile-animations"; @@ -440,12 +439,6 @@ export function ProfileContent() { )} - {userProfile?.karma !== undefined && - userProfile?.karma !== null && ( - - - - )} {userProfile?.recentGames.length > 0 && ( diff --git a/src/renderer/src/pages/profile/profile-content/recent-games-box.scss b/src/renderer/src/pages/profile/profile-content/recent-games-box.scss index 4bfb51f0..394fbca7 100644 --- a/src/renderer/src/pages/profile/profile-content/recent-games-box.scss +++ b/src/renderer/src/pages/profile/profile-content/recent-games-box.scss @@ -47,13 +47,15 @@ } &__game-title { - font-weight: bold; + font-size: 0.8rem; + font-weight: 600; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } &__game-description { + font-size: 0.75rem; display: flex; align-items: center; gap: globals.$spacing-unit; diff --git a/src/renderer/src/pages/profile/profile-content/user-karma-box.scss b/src/renderer/src/pages/profile/profile-content/user-karma-box.scss deleted file mode 100644 index 79261aa1..00000000 --- a/src/renderer/src/pages/profile/profile-content/user-karma-box.scss +++ /dev/null @@ -1,37 +0,0 @@ -@use "../../../scss/globals.scss"; - -.user-karma { - &__box { - padding: calc(globals.$spacing-unit * 2); - } - - &__content { - display: flex; - flex-direction: column; - gap: calc(globals.$spacing-unit * 1.5); - } - - &__stats-row { - display: flex; - align-items: center; - color: globals.$body-color; - } - - &__description { - display: flex; - align-items: center; - gap: globals.$spacing-unit; - font-weight: 600; - font-size: 1.1rem; - } - - &__info { - padding-top: calc(globals.$spacing-unit * 0.5); - } - - &__info-text { - color: globals.$muted-color; - font-size: 0.85rem; - line-height: 1.4; - } -} diff --git a/src/renderer/src/pages/profile/profile-content/user-karma-box.tsx b/src/renderer/src/pages/profile/profile-content/user-karma-box.tsx deleted file mode 100644 index b8dd7244..00000000 --- a/src/renderer/src/pages/profile/profile-content/user-karma-box.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { useContext } from "react"; -import { userProfileContext } from "@renderer/context"; -import { useTranslation } from "react-i18next"; -import { useFormat, useUserDetails } from "@renderer/hooks"; -import { Award } from "lucide-react"; -import "./user-karma-box.scss"; - -export function UserKarmaBox() { - const { isMe, userProfile } = useContext(userProfileContext); - const { userDetails } = useUserDetails(); - const { t } = useTranslation("user_profile"); - const { numberFormatter } = useFormat(); - - // Get karma from userDetails (for current user) or userProfile (for other users) - const karma = isMe ? userDetails?.karma : userProfile?.karma; - - // Don't show if karma is not available - if (karma === undefined || karma === null) return null; - - return ( -
-
-
-

- {numberFormatter.format(karma)}{" "} - {t("karma_count")} -

-
-
- - {t("karma_description")} - -
-
-
- ); -} diff --git a/src/renderer/src/pages/profile/profile-content/user-stats-box.scss b/src/renderer/src/pages/profile/profile-content/user-stats-box.scss index fc92e466..72a4d580 100644 --- a/src/renderer/src/pages/profile/profile-content/user-stats-box.scss +++ b/src/renderer/src/pages/profile/profile-content/user-stats-box.scss @@ -32,13 +32,15 @@ } &__list-title { - font-weight: bold; + font-size: 0.8rem; + font-weight: 600; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } &__list-description { + font-size: 0.75rem; display: flex; align-items: center; gap: globals.$spacing-unit; @@ -62,4 +64,10 @@ cursor: pointer; } } + + &__karma-info-text { + color: globals.$muted-color; + font-size: 0.75rem; + line-height: 1.4; + } } diff --git a/src/renderer/src/pages/profile/profile-content/user-stats-box.tsx b/src/renderer/src/pages/profile/profile-content/user-stats-box.tsx index f7e76e79..6fbabdca 100644 --- a/src/renderer/src/pages/profile/profile-content/user-stats-box.tsx +++ b/src/renderer/src/pages/profile/profile-content/user-stats-box.tsx @@ -1,16 +1,18 @@ import { useCallback, useContext } from "react"; import { userProfileContext } from "@renderer/context"; import { useTranslation } from "react-i18next"; -import { useFormat } from "@renderer/hooks"; +import { useFormat, useUserDetails } from "@renderer/hooks"; import { MAX_MINUTES_TO_SHOW_IN_PLAYTIME } from "@renderer/constants"; import HydraIcon from "@renderer/assets/icons/hydra.svg?react"; import { useSubscription } from "@renderer/hooks/use-subscription"; import { ClockIcon, TrophyIcon } from "@primer/octicons-react"; +import { Award } from "lucide-react"; import "./user-stats-box.scss"; export function UserStatsBox() { const { showHydraCloudModal } = useSubscription(); - const { userStats, isMe } = useContext(userProfileContext); + const { userStats, isMe, userProfile } = useContext(userProfileContext); + const { userDetails } = useUserDetails(); const { t } = useTranslation("user_profile"); const { numberFormatter } = useFormat(); @@ -33,6 +35,9 @@ export function UserStatsBox() { if (!userStats) return null; + const karma = isMe ? userDetails?.karma : userProfile?.karma; + const hasKarma = karma !== undefined && karma !== null; + return (
    @@ -108,6 +113,23 @@ export function UserStatsBox() {

+ + {hasKarma && karma !== undefined && karma !== null && ( +
  • +

    {t("karma")}

    +
    +

    + {numberFormatter.format(karma)}{" "} + {t("karma_count")} +

    +
    +
    + + {t("karma_description")} + +
    +
  • + )}
    ); diff --git a/src/renderer/src/pages/profile/profile-hero/profile-hero.scss b/src/renderer/src/pages/profile/profile-hero/profile-hero.scss index 7f3b757a..7061eceb 100644 --- a/src/renderer/src/pages/profile/profile-hero/profile-hero.scss +++ b/src/renderer/src/pages/profile/profile-hero/profile-hero.scss @@ -30,7 +30,7 @@ &__copy-button { display: flex; align-items: center; - justify-content: flex-start; + justify-content: flex-end; background: none; border: none; color: globals.$body-color; @@ -51,7 +51,7 @@ } &__friend-code { - font-size: globals.$small-font-size; + font-size: 0.875rem; font-family: monospace; white-space: nowrap; } diff --git a/src/renderer/src/pages/profile/profile-hero/profile-hero.tsx b/src/renderer/src/pages/profile/profile-hero/profile-hero.tsx index 7c4c1e75..b5392935 100644 --- a/src/renderer/src/pages/profile/profile-hero/profile-hero.tsx +++ b/src/renderer/src/pages/profile/profile-hero/profile-hero.tsx @@ -323,22 +323,22 @@ export function ProfileHero() { onMouseLeave={() => setIsCopyButtonHovered(false)} initial={{ width: 28 }} animate={{ - width: isCopyButtonHovered ? 94 : 28, + width: isCopyButtonHovered ? 105 : 28, }} transition={{ duration: 0.2, ease: "easeInOut" }} > - {userProfile?.id} +
    ) : (