diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 1ba8b6e2..b5576cd1 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -168,8 +168,7 @@ "select_folder": "Select folder", "backup_from": "Backup from {{date}}", "custom_backup_location_set": "Custom backup location set", - "no_directory_selected": "No directory selected", - "available_points": "Available points:" + "no_directory_selected": "No directory selected" }, "activation": { "title": "Activate Hydra", @@ -367,11 +366,14 @@ "uploading_banner": "Uploading banner…", "background_image_updated": "Background image updated", "stats": "Stats", - "achievements": "Achievements", + "achievements": "achievements", "games": "Games", "top_percentile": "Top {{percentile}}%", "ranking_updated_weekly": "Ranking is updated weekly", - "playing": "Playing {{game}}" + "playing": "Playing {{game}}", + "achievements_unlocked": "Achievements Unlocked", + "earned_points": "Earned points", + "show_achievements_on_profile": "Show your achievements and earned points on your profile" }, "achievement": { "achievement_unlocked": "Achievement unlocked", @@ -383,7 +385,9 @@ "achievement_progress": "{{unlockedCount}}/{{totalCount}} achievements", "achievements_unlocked_for_game": "Unlocked {{achievementCount}} new achievements for {{gameTitle}}", "hidden_achievement_tooltip": "This is a hidden achievement", - "achievement_earn_points": "Earn {{points}} points with this achievement" + "achievement_earn_points": "Earn {{points}} points with this achievement", + "earned_points": "Earned points:", + "available_points": "Available points:" }, "hydra_cloud": { "subscription_tour_title": "Hydra Cloud Subscription", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index f716d517..7e783d5f 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -164,8 +164,7 @@ "select_folder": "Selecione a pasta", "manage_files_description": "Gerencie quais arquivos serão feitos backup", "clear": "Limpar", - "no_directory_selected": "Nenhum diretório selecionado", - "available_points": "Pontos disponíveis:" + "no_directory_selected": "Nenhum diretório selecionado" }, "activation": { "title": "Ativação", @@ -365,10 +364,13 @@ "uploading_banner": "Carregando banner…", "background_image_updated": "Imagem de fundo salva", "stats": "Estatísticas", - "achievements": "Conquistas", + "achievements": "conquistas", "games": "Jogos", - "ranking_updated_weekly": "Ranking é atualizado semanalmente", - "playing": "Jogando {{game}}" + "ranking_updated_weekly": "O ranking é atualizado semanalmente", + "playing": "Jogando {{game}}", + "achievements_unlocked": "Conquistas desbloqueadas", + "earned_points": "Pontos ganhos", + "show_achievements_on_profile": "Exiba suas conquistas e pontos no perfil" }, "achievement": { "achievement_unlocked": "Conquista desbloqueada", @@ -380,7 +382,9 @@ "achievement_progress": "{{unlockedCount}}/{{totalCount}} conquistas", "achievements_unlocked_for_game": "Desbloqueadas {{achievementCount}} novas conquistas em {{gameTitle}}", "hidden_achievement_tooltip": "Está é uma conquista oculta", - "achievement_earn_points": "Ganhe {{points}} pontos com essa conquista" + "achievement_earn_points": "Ganhe {{points}} pontos com essa conquista", + "earned_points": "Pontos ganhos:", + "available_points": "Pontos disponíveis:" }, "hydra_cloud": { "subscription_tour_title": "Assinatura Hydra Cloud", diff --git a/src/main/events/user/get-compared-unlocked-achievements.ts b/src/main/events/user/get-compared-unlocked-achievements.ts index 0c117140..0b665212 100644 --- a/src/main/events/user/get-compared-unlocked-achievements.ts +++ b/src/main/events/user/get-compared-unlocked-achievements.ts @@ -13,6 +13,9 @@ const getComparedUnlockedAchievements = async ( where: { id: 1 }, }); + const showHiddenAchievementsDescription = + userPreferences?.showHiddenAchievementsDescription || false; + return HydraApi.get( `/users/${userId}/games/achievements/compare`, { @@ -21,15 +24,35 @@ const getComparedUnlockedAchievements = async ( language: userPreferences?.language || "en", } ).then((achievements) => { - const sortedAchievements = achievements.achievements.sort((a, b) => { - if (a.targetStat.unlocked && !b.targetStat.unlocked) return -1; - if (!a.targetStat.unlocked && b.targetStat.unlocked) return 1; - if (a.targetStat.unlocked && b.targetStat.unlocked) { - return b.targetStat.unlockTime! - a.targetStat.unlockTime!; - } + const sortedAchievements = achievements.achievements + .sort((a, b) => { + if (a.targetStat.unlocked && !b.targetStat.unlocked) return -1; + if (!a.targetStat.unlocked && b.targetStat.unlocked) return 1; + if (a.targetStat.unlocked && b.targetStat.unlocked) { + return b.targetStat.unlockTime! - a.targetStat.unlockTime!; + } - return Number(a.hidden) - Number(b.hidden); - }); + return Number(a.hidden) - Number(b.hidden); + }) + .map((achievement) => { + if (!achievement.hidden) return achievement; + + if (!achievement.ownerStat) { + return { + ...achievement, + description: "", + }; + } + + if (!showHiddenAchievementsDescription && achievement.hidden) { + return { + ...achievement, + description: "", + }; + } + + return achievement; + }); return { ...achievements, diff --git a/src/renderer/src/pages/achievements/achievement-panel.tsx b/src/renderer/src/pages/achievements/achievement-panel.tsx index bf48e517..b1233bd0 100644 --- a/src/renderer/src/pages/achievements/achievement-panel.tsx +++ b/src/renderer/src/pages/achievements/achievement-panel.tsx @@ -4,13 +4,25 @@ import { gameDetailsContext } from "@renderer/context"; import * as styles from "./achievement-panel.css"; import HydraIcon from "@renderer/assets/icons/hydra.svg?react"; +import { UserAchievement } from "@types"; -export interface HeroPanelProps { - isHeaderStuck: boolean; +export interface AchievementPanelProps { + achievements: UserAchievement[]; } -export function AchievementPanel({ isHeaderStuck }: HeroPanelProps) { - const { t } = useTranslation("game_details"); +export function AchievementPanel({ achievements }: AchievementPanelProps) { + const { t } = useTranslation("achievement"); + + const achievementsPointsTotal = achievements.reduce( + (acc, achievement) => acc + (achievement.points ?? 0), + 0 + ); + + const achievementsPointsEarnedSum = achievements.reduce( + (acc, achievement) => + acc + (achievement.unlocked ? (achievement.points ?? 0) : 0), + 0 + ); const {} = useContext(gameDetailsContext); @@ -18,7 +30,8 @@ export function AchievementPanel({ isHeaderStuck }: HeroPanelProps) { <>
- Pontos desbloqueados: 69/420 + {t("earned_points")} + {achievementsPointsEarnedSum} / {achievementsPointsTotal}
diff --git a/src/renderer/src/pages/achievements/achievements-content.tsx b/src/renderer/src/pages/achievements/achievements-content.tsx index 59139768..87865280 100644 --- a/src/renderer/src/pages/achievements/achievements-content.tsx +++ b/src/renderer/src/pages/achievements/achievements-content.tsx @@ -329,7 +329,7 @@ export function AchievementsContent({ ) : ( <> - + )} diff --git a/src/renderer/src/pages/achievements/compared-achievement-list.tsx b/src/renderer/src/pages/achievements/compared-achievement-list.tsx index 349ef755..44aec686 100644 --- a/src/renderer/src/pages/achievements/compared-achievement-list.tsx +++ b/src/renderer/src/pages/achievements/compared-achievement-list.tsx @@ -1,8 +1,13 @@ import type { ComparedAchievements } from "@types"; import * as styles from "./achievements.css"; -import { CheckCircleIcon, LockIcon } from "@primer/octicons-react"; +import { + CheckCircleIcon, + EyeClosedIcon, + LockIcon, +} from "@primer/octicons-react"; import { useDate } from "@renderer/hooks"; import { SPACING_UNIT } from "@renderer/theme.css"; +import { useTranslation } from "react-i18next"; export interface ComparedAchievementListProps { achievements: ComparedAchievements; @@ -11,6 +16,7 @@ export interface ComparedAchievementListProps { export function ComparedAchievementList({ achievements, }: ComparedAchievementListProps) { + const { t } = useTranslation("achievement"); const { formatDateTime } = useDate(); return ( @@ -43,7 +49,17 @@ export function ComparedAchievementList({ loading="lazy" />
-

{achievement.displayName}

+

+ {achievement.hidden && ( + + + + )} + {achievement.displayName} +

{achievement.description}

diff --git a/src/renderer/src/pages/achievements/compared-achievement-panel.tsx b/src/renderer/src/pages/achievements/compared-achievement-panel.tsx index 0f35aa5d..3ac76d8d 100644 --- a/src/renderer/src/pages/achievements/compared-achievement-panel.tsx +++ b/src/renderer/src/pages/achievements/compared-achievement-panel.tsx @@ -14,7 +14,7 @@ export interface ComparedAchievementPanelProps { export function ComparedAchievementPanel({ achievements, }: ComparedAchievementPanelProps) { - const { t } = useTranslation("game_details"); + const { t } = useTranslation("achievement"); const {} = useContext(gameDetailsContext); diff --git a/src/renderer/src/pages/profile/profile-content/friends-box.tsx b/src/renderer/src/pages/profile/profile-content/friends-box.tsx index 4d3df7e8..454d13f0 100644 --- a/src/renderer/src/pages/profile/profile-content/friends-box.tsx +++ b/src/renderer/src/pages/profile/profile-content/friends-box.tsx @@ -41,9 +41,7 @@ export function FriendsBox() { {friend.displayName} {friend.currentGame && ( - -

{t("playing", { game: friend.currentGame.title })}

- +

{t("playing", { game: friend.currentGame.title })}

)} diff --git a/src/renderer/src/pages/profile/profile-content/profile-content.css.ts b/src/renderer/src/pages/profile/profile-content/profile-content.css.ts index b078e53d..cc462a95 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-content.css.ts +++ b/src/renderer/src/pages/profile/profile-content/profile-content.css.ts @@ -105,6 +105,22 @@ export const listItem = style({ }, }); +export const statsListItem = style({ + display: "flex", + flexDirection: "column", + transition: "all ease 0.1s", + color: vars.color.muted, + width: "100%", + overflow: "hidden", + borderRadius: "4px", + padding: `${SPACING_UNIT}px ${SPACING_UNIT}px`, + gap: `${SPACING_UNIT}px`, + ":hover": { + backgroundColor: "rgba(255, 255, 255, 0.15)", + textDecoration: "none", + }, +}); + export const gamesGrid = style({ listStyle: "none", margin: "0", 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 f4baccd9..2217d569 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-content.tsx +++ b/src/renderer/src/pages/profile/profile-content/profile-content.tsx @@ -22,6 +22,7 @@ import { } from "@renderer/helpers"; import { MAX_MINUTES_TO_SHOW_IN_PLAYTIME } from "@renderer/constants"; import { UserStatsBox } from "./user-stats-box"; +import HydraIcon from "@renderer/assets/icons/hydra.svg?react"; export function ProfileContent() { const { userProfile, isMe, userStats } = useContext(userProfileContext); @@ -157,7 +158,7 @@ export function ProfileContent() { height: "100%", width: "100%", background: - "linear-gradient(0deg, rgba(0, 0, 0, 0.7) 20%, transparent 100%)", + "linear-gradient(0deg, rgba(0, 0, 0, 0.75) 25%, transparent 100%)", padding: 8, }} > @@ -187,6 +188,22 @@ export function ProfileContent() { flexDirection: "column", }} > + {game.achievementsPointsEarnedSum > 0 && ( +
+ + {numberFormatter.format( + game.achievementsPointsEarnedSum + )} +
+ )}
    -
  • -

    {t("achievements")}

    - {userStats.achievementsPointsEarnedSum !== undefined ? ( - <> + {(isMe || userStats.unlockedAchievementSum !== undefined) && ( +
  • +

    + {t("achievements_unlocked")} +

    + {userStats.unlockedAchievementSum !== undefined ? (
    -

    +

    + {userStats.unlockedAchievementSum}{" "} + {t("achievements")} +

    +
    + ) : ( + + )} +
  • + )} + + {(isMe || userStats.achievementsPointsEarnedSum !== undefined) && ( +
  • +

    {t("earned_points")}

    + {userStats.achievementsPointsEarnedSum !== undefined ? ( +
    +

    - {userStats.achievementsPointsEarnedSum.value} + {numberFormatter.format( + userStats.achievementsPointsEarnedSum.value + )}

    {t("top_percentile", { @@ -67,25 +90,27 @@ export function UserStatsBox() { })}

    -

    Unlock count: {userStats.unlockedAchievementSum}

    - - ) : ( - - )} -
  • + ) : ( + + )} + + )} -
  • +
  • {t("total_play_time")}

    -

    {formatPlayTime(userStats.totalPlayTimeInSeconds.value)}

    +

    + + {formatPlayTime(userStats.totalPlayTimeInSeconds.value)} +

    {t("top_percentile", { percentile: userStats.totalPlayTimeInSeconds.topPercentile, diff --git a/src/types/index.ts b/src/types/index.ts index 7cca371c..8bdace2f 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -99,6 +99,7 @@ export interface UserGame { lastTimePlayed: Date | null; unlockedAchievementCount: number; achievementCount: number; + achievementsPointsEarnedSum: number; } export interface DownloadQueue {