feat: ui improvement

This commit is contained in:
Zamitto
2024-12-22 18:56:24 -03:00
parent 313ebc6055
commit c8566dd2be
12 changed files with 181 additions and 64 deletions

View File

@@ -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>
</>

View File

@@ -329,7 +329,7 @@ export function AchievementsContent({
</>
) : (
<>
<AchievementPanel isHeaderStuck={false} />
<AchievementPanel achievements={achievements!} />
<AchievementList achievements={achievements!} />
</>
)}

View File

@@ -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>

View File

@@ -14,7 +14,7 @@ export interface ComparedAchievementPanelProps {
export function ComparedAchievementPanel({
achievements,
}: ComparedAchievementPanelProps) {
const { t } = useTranslation("game_details");
const { t } = useTranslation("achievement");
const {} = useContext(gameDetailsContext);

View File

@@ -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>

View File

@@ -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",

View File

@@ -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",

View File

@@ -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,