From ffac677e3fd860d6c5a5cb131559a7fba4fc2313 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 8 Oct 2024 15:17:53 -0300 Subject: [PATCH] feat: update achievements on sidebar --- src/locales/en/translation.json | 3 +- src/locales/pt-BR/translation.json | 5 +- src/locales/pt-PT/translation.json | 3 +- .../events/catalogue/get-game-achievements.ts | 34 ++++++-- src/renderer/src/components/header/header.tsx | 1 + .../src/pages/achievement/achievements.tsx | 13 ++- .../pages/game-details/sidebar/sidebar.css.ts | 44 ++++++++++ .../pages/game-details/sidebar/sidebar.tsx | 87 +++++++++---------- src/types/index.ts | 10 +++ 9 files changed, 143 insertions(+), 57 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 931f295e..b80f8bd6 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -145,7 +145,8 @@ "no_backups": "You haven't created any backups for this game yet", "backup_uploaded": "Backup uploaded", "backup_deleted": "Backup deleted", - "backup_restored": "Backup restored" + "backup_restored": "Backup restored", + "see_all_achievements": "See all achievements" }, "activation": { "title": "Activate Hydra", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 5b811f7b..c5a15f6b 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -127,7 +127,7 @@ "executable_path_in_use": "Executável em uso por \"{{game}}\"", "warning": "Aviso:", "hydra_needs_to_remain_open": "para este download, o Hydra precisa ficar aberto até a conclusão. Caso o Hydra encerre antes da conclusão, perderá seu progresso.", - "achievements": "Conquistas {{unlockedCount}}/{{achievementsCount}}", + "achievements": "Conquistas ({{unlockedCount}}/{{achievementsCount}})", "cloud_save": "Salvamento em nuvem", "cloud_save_description": "Matenha seu progresso na nuvem e continue de onde parou em qualquer dispositivo", "backups": "Backups", @@ -141,7 +141,8 @@ "no_backups": "Você ainda não fez nenhum backup deste jogo", "backup_uploaded": "Backup criado", "backup_deleted": "Backup apagado", - "backup_restored": "Backup restaurado" + "backup_restored": "Backup restaurado", + "see_all_achievements": "Ver todas as conquistas" }, "activation": { "title": "Ativação", diff --git a/src/locales/pt-PT/translation.json b/src/locales/pt-PT/translation.json index fec0c366..a65ace2f 100644 --- a/src/locales/pt-PT/translation.json +++ b/src/locales/pt-PT/translation.json @@ -116,7 +116,8 @@ "executable_path_in_use": "Executável em uso por \"{{game}}\"", "warning": "Aviso:", "hydra_needs_to_remain_open": "para este download, o Hydra precisa ficar aberto até a conclusão. Caso o Hydra encerre antes da conclusão, perderá seu progresso.", - "achievements": "Conquistas {{unlockedCount}}/{{achievementsCount}}" + "achievements": "Conquistas ({{unlockedCount}}/{{achievementsCount}})", + "see_all_achievements": "Ver todas as conquistas" }, "activation": { "title": "Ativação", diff --git a/src/main/events/catalogue/get-game-achievements.ts b/src/main/events/catalogue/get-game-achievements.ts index 8ab6d5d9..07bc2e91 100644 --- a/src/main/events/catalogue/get-game-achievements.ts +++ b/src/main/events/catalogue/get-game-achievements.ts @@ -1,4 +1,9 @@ -import type { GameAchievement, GameShop, UnlockedAchievement } from "@types"; +import type { + AchievementData, + GameAchievement, + GameShop, + UnlockedAchievement, +} from "@types"; import { registerEvent } from "../register-event"; import { gameAchievementRepository, @@ -18,7 +23,7 @@ const getAchievements = async ( where: { objectId, shop }, }); - const achievementsData = cachedAchievements?.achievements + const achievementsData: AchievementData[] = cachedAchievements?.achievements ? JSON.parse(cachedAchievements.achievements) : await getGameAchievementData(objectId, shop); @@ -51,7 +56,7 @@ export const getGameAchievements = async ( return achievementsData .map((achievementData) => { - const unlockedAchiement = unlockedAchievements.find( + const unlockedAchiementData = unlockedAchievements.find( (localAchievement) => { return ( localAchievement.name.toUpperCase() == @@ -60,20 +65,33 @@ export const getGameAchievements = async ( } ); - if (unlockedAchiement) { + const icongray = achievementData.icongray.endsWith("/") + ? achievementData.icon + : achievementData.icongray; + + if (unlockedAchiementData) { return { ...achievementData, unlocked: true, - unlockTime: unlockedAchiement.unlockTime, + unlockTime: unlockedAchiementData.unlockTime, + icongray, }; } - return { ...achievementData, unlocked: false, unlockTime: null }; + return { + ...achievementData, + unlocked: false, + unlockTime: null, + icongray, + }; }) - .sort((a, b) => { + .sort((a: GameAchievement, b: GameAchievement) => { if (a.unlocked && !b.unlocked) return -1; if (!a.unlocked && b.unlocked) return 1; - return b.unlockTime - a.unlockTime; + if (a.unlocked && b.unlocked) { + return b.unlockTime! - a.unlockTime!; + } + return Number(a.hidden) - Number(b.hidden); }); }; diff --git a/src/renderer/src/components/header/header.tsx b/src/renderer/src/components/header/header.tsx index 49f5231d..e0721df4 100644 --- a/src/renderer/src/components/header/header.tsx +++ b/src/renderer/src/components/header/header.tsx @@ -39,6 +39,7 @@ export function Header({ onSearch, onClear, search }: HeaderProps) { const title = useMemo(() => { if (location.pathname.startsWith("/game")) return headerTitle; + if (location.pathname.startsWith("/achievements")) return headerTitle; if (location.pathname.startsWith("/profile")) return headerTitle; if (location.pathname.startsWith("/search")) return t("search_results"); diff --git a/src/renderer/src/pages/achievement/achievements.tsx b/src/renderer/src/pages/achievement/achievements.tsx index 17917045..dd50c0ab 100644 --- a/src/renderer/src/pages/achievement/achievements.tsx +++ b/src/renderer/src/pages/achievement/achievements.tsx @@ -1,4 +1,5 @@ -import { useDate } from "@renderer/hooks"; +import { setHeaderTitle } from "@renderer/features"; +import { useAppDispatch, useDate } from "@renderer/hooks"; import { SPACING_UNIT } from "@renderer/theme.css"; import { GameAchievement, GameShop } from "@types"; import { useEffect, useState } from "react"; @@ -8,10 +9,13 @@ export function Achievement() { const [searchParams] = useSearchParams(); const objectId = searchParams.get("objectId"); const shop = searchParams.get("shop"); + const title = searchParams.get("title"); const userId = searchParams.get("userId"); const { format } = useDate(); + const dispatch = useAppDispatch(); + const [achievements, setAchievements] = useState([]); useEffect(() => { @@ -24,6 +28,12 @@ export function Achievement() { } }, [objectId, shop, userId]); + useEffect(() => { + if (title) { + dispatch(setHeaderTitle(title + " Achievements")); + } + }, [dispatch, title]); + return (

Achievement

@@ -61,6 +71,7 @@ export function Achievement() { />

{achievement.displayName}

+

{achievement.description}

{achievement.unlockTime && format(achievement.unlockTime)}
diff --git a/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts b/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts index c909fba2..1f0e0515 100644 --- a/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts +++ b/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts @@ -1,6 +1,7 @@ import { globalStyle, style } from "@vanilla-extract/css"; import { SPACING_UNIT, vars } from "../../../theme.css"; +import { recipe } from "@vanilla-extract/recipes"; export const contentSidebar = style({ borderLeft: `solid 1px ${vars.color.border}`, @@ -110,3 +111,46 @@ globalStyle(`${requirementsDetails} a`, { display: "flex", color: vars.color.body, }); + +export const list = style({ + listStyle: "none", + margin: "0", + display: "flex", + flexDirection: "column", + gap: `${SPACING_UNIT * 2}px`, + padding: `${SPACING_UNIT * 2}px`, +}); + +export const listItem = style({ + display: "flex", + cursor: "pointer", + 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 * 2}px`, + alignItems: "center", + textAlign: "left", + ":hover": { + backgroundColor: "rgba(255, 255, 255, 0.15)", + textDecoration: "none", + }, +}); + +export const listItemImage = recipe({ + base: { + width: "54px", + height: "54px", + borderRadius: "4px", + objectFit: "cover", + }, + variants: { + unlocked: { + false: { + filter: "grayscale(100%)", + }, + }, + }, +}); diff --git a/src/renderer/src/pages/game-details/sidebar/sidebar.tsx b/src/renderer/src/pages/game-details/sidebar/sidebar.tsx index 6c6dca33..b2c07885 100644 --- a/src/renderer/src/pages/game-details/sidebar/sidebar.tsx +++ b/src/renderer/src/pages/game-details/sidebar/sidebar.tsx @@ -7,10 +7,10 @@ import * as styles from "./sidebar.css"; import { gameDetailsContext } from "@renderer/context"; import { useDate, useFormat } from "@renderer/hooks"; import { DownloadIcon, PeopleIcon } from "@primer/octicons-react"; -import { SPACING_UNIT } from "@renderer/theme.css"; import { HowLongToBeatSection } from "./how-long-to-beat-section"; import { howLongToBeatEntriesTable } from "@renderer/dexie"; import { SidebarSection } from "../sidebar-section/sidebar-section"; +import { useNavigate } from "react-router-dom"; export function Sidebar() { const [howLongToBeat, setHowLongToBeat] = useState<{ @@ -30,7 +30,12 @@ export function Sidebar() { const { numberFormatter } = useFormat(); const buildGameAchievementPath = () => { - const urlParams = new URLSearchParams({ objectId: objectId!, shop }); + const urlParams = new URLSearchParams({ + objectId: objectId!, + shop, + title: gameTitle, + }); + return `/achievements?${urlParams.toString()}`; }; @@ -80,49 +85,43 @@ export function Sidebar() { achievementsCount: achievements.length, })} > - - Ver todas - -
- {achievements.slice(0, 6).map((achievement, index) => ( -
- {achievement.displayName} -
-

{achievement.displayName}

- {achievement.unlockTime && format(achievement.unlockTime)} -
-
+
+ + + {t("see_all_achievements")} + + )} diff --git a/src/types/index.ts b/src/types/index.ts index 303d47ae..7c6028be 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -28,6 +28,15 @@ export interface GameRepack { updatedAt: Date; } +export interface AchievementData { + name: string; + displayName: string; + description?: string; + icon: string; + icongray: string; + hidden: boolean; +} + export interface GameAchievement { name: string; displayName: string; @@ -36,6 +45,7 @@ export interface GameAchievement { unlockTime: number | null; icon: string; icongray: string; + hidden: boolean; } export type ShopDetails = SteamAppDetails & {