mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-02-02 16:23:48 +03:00
feat: ui improvement
This commit is contained in:
parent
313ebc6055
commit
c8566dd2be
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -13,6 +13,9 @@ const getComparedUnlockedAchievements = async (
|
||||
where: { id: 1 },
|
||||
});
|
||||
|
||||
const showHiddenAchievementsDescription =
|
||||
userPreferences?.showHiddenAchievementsDescription || false;
|
||||
|
||||
return HydraApi.get<ComparedAchievements>(
|
||||
`/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,
|
||||
|
@ -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) {
|
||||
<>
|
||||
<div className={styles.panel}>
|
||||
<div className={styles.content}>
|
||||
Pontos desbloqueados: <HydraIcon width={20} height={20} /> 69/420
|
||||
{t("earned_points")} <HydraIcon width={20} height={20} />
|
||||
{achievementsPointsEarnedSum} / {achievementsPointsTotal}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
@ -329,7 +329,7 @@ export function AchievementsContent({
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<AchievementPanel isHeaderStuck={false} />
|
||||
<AchievementPanel achievements={achievements!} />
|
||||
<AchievementList achievements={achievements!} />
|
||||
</>
|
||||
)}
|
||||
|
@ -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"
|
||||
/>
|
||||
<div>
|
||||
<h4>{achievement.displayName}</h4>
|
||||
<h4 style={{ display: "flex", alignItems: "center", gap: "4px" }}>
|
||||
{achievement.hidden && (
|
||||
<span
|
||||
style={{ display: "flex" }}
|
||||
title={t("hidden_achievement_tooltip")}
|
||||
>
|
||||
<EyeClosedIcon size={12} />
|
||||
</span>
|
||||
)}
|
||||
{achievement.displayName}
|
||||
</h4>
|
||||
<p>{achievement.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -14,7 +14,7 @@ export interface ComparedAchievementPanelProps {
|
||||
export function ComparedAchievementPanel({
|
||||
achievements,
|
||||
}: ComparedAchievementPanelProps) {
|
||||
const { t } = useTranslation("game_details");
|
||||
const { t } = useTranslation("achievement");
|
||||
|
||||
const {} = useContext(gameDetailsContext);
|
||||
|
||||
|
@ -41,9 +41,7 @@ export function FriendsBox() {
|
||||
{friend.displayName}
|
||||
</span>
|
||||
{friend.currentGame && (
|
||||
<Link to={buildGameDetailsPath({ ...friend.currentGame })}>
|
||||
<p>{t("playing", { game: friend.currentGame.title })}</p>
|
||||
</Link>
|
||||
<p>{t("playing", { game: friend.currentGame.title })}</p>
|
||||
)}
|
||||
</div>
|
||||
</Link>
|
||||
|
@ -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",
|
||||
|
@ -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 && (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "start",
|
||||
gap: 8,
|
||||
marginBottom: 4,
|
||||
color: vars.color.muted,
|
||||
}}
|
||||
>
|
||||
<HydraIcon width={16} height={16} />
|
||||
{numberFormatter.format(
|
||||
game.achievementsPointsEarnedSum
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
|
@ -6,14 +6,13 @@ import { useFormat } 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 { vars } from "@renderer/theme.css";
|
||||
|
||||
export function UserStatsBox() {
|
||||
const { showHydraCloudModal } = useSubscription();
|
||||
|
||||
const { userStats } = useContext(userProfileContext);
|
||||
|
||||
const { userStats, isMe } = useContext(userProfileContext);
|
||||
const { t } = useTranslation("user_profile");
|
||||
|
||||
const { numberFormatter } = useFormat();
|
||||
|
||||
const formatPlayTime = useCallback(
|
||||
@ -43,22 +42,46 @@ export function UserStatsBox() {
|
||||
|
||||
<div className={styles.box}>
|
||||
<ul className={styles.list}>
|
||||
<li>
|
||||
<h3 className={styles.listItemTitle}>{t("achievements")}</h3>
|
||||
{userStats.achievementsPointsEarnedSum !== undefined ? (
|
||||
<>
|
||||
{(isMe || userStats.unlockedAchievementSum !== undefined) && (
|
||||
<li className={styles.statsListItem}>
|
||||
<h3 className={styles.listItemTitle}>
|
||||
{t("achievements_unlocked")}
|
||||
</h3>
|
||||
{userStats.unlockedAchievementSum !== undefined ? (
|
||||
<div
|
||||
style={{ display: "flex", justifyContent: "space-between" }}
|
||||
>
|
||||
<p
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "4px",
|
||||
}}
|
||||
>
|
||||
<p className={styles.listItemDescription}>
|
||||
<TrophyIcon /> {userStats.unlockedAchievementSum}{" "}
|
||||
{t("achievements")}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={showHydraCloudModal}
|
||||
className={styles.link}
|
||||
>
|
||||
<small style={{ color: vars.color.warning }}>
|
||||
{t("show_achievements_on_profile")}
|
||||
</small>
|
||||
</button>
|
||||
)}
|
||||
</li>
|
||||
)}
|
||||
|
||||
{(isMe || userStats.achievementsPointsEarnedSum !== undefined) && (
|
||||
<li className={styles.statsListItem}>
|
||||
<h3 className={styles.listItemTitle}>{t("earned_points")}</h3>
|
||||
{userStats.achievementsPointsEarnedSum !== undefined ? (
|
||||
<div
|
||||
style={{ display: "flex", justifyContent: "space-between" }}
|
||||
>
|
||||
<p className={styles.listItemDescription}>
|
||||
<HydraIcon width={20} height={20} />
|
||||
{userStats.achievementsPointsEarnedSum.value}
|
||||
{numberFormatter.format(
|
||||
userStats.achievementsPointsEarnedSum.value
|
||||
)}
|
||||
</p>
|
||||
<p title={t("ranking_updated_weekly")}>
|
||||
{t("top_percentile", {
|
||||
@ -67,25 +90,27 @@ export function UserStatsBox() {
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
<p>Unlock count: {userStats.unlockedAchievementSum}</p>
|
||||
</>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={showHydraCloudModal}
|
||||
className={styles.link}
|
||||
>
|
||||
<small>
|
||||
Saiba como exibir suas conquistas e pontos no perfil
|
||||
</small>
|
||||
</button>
|
||||
)}
|
||||
</li>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={showHydraCloudModal}
|
||||
className={styles.link}
|
||||
>
|
||||
<small style={{ color: vars.color.warning }}>
|
||||
{t("show_achievements_on_profile")}
|
||||
</small>
|
||||
</button>
|
||||
)}
|
||||
</li>
|
||||
)}
|
||||
|
||||
<li>
|
||||
<li className={styles.statsListItem}>
|
||||
<h3 className={styles.listItemTitle}>{t("total_play_time")}</h3>
|
||||
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<p>{formatPlayTime(userStats.totalPlayTimeInSeconds.value)}</p>
|
||||
<p className={styles.listItemDescription}>
|
||||
<ClockIcon />
|
||||
{formatPlayTime(userStats.totalPlayTimeInSeconds.value)}
|
||||
</p>
|
||||
<p title={t("ranking_updated_weekly")}>
|
||||
{t("top_percentile", {
|
||||
percentile: userStats.totalPlayTimeInSeconds.topPercentile,
|
||||
|
@ -99,6 +99,7 @@ export interface UserGame {
|
||||
lastTimePlayed: Date | null;
|
||||
unlockedAchievementCount: number;
|
||||
achievementCount: number;
|
||||
achievementsPointsEarnedSum: number;
|
||||
}
|
||||
|
||||
export interface DownloadQueue {
|
||||
|
Loading…
Reference in New Issue
Block a user