feat: skeleton

This commit is contained in:
Zamitto 2024-10-14 15:03:50 -03:00
parent 359733fa40
commit e7a4888f54
5 changed files with 61 additions and 44 deletions

View file

@ -43,7 +43,7 @@ export const gameDetailsContext = createContext<GameDetailsContext>({
showRepacksModal: false, showRepacksModal: false,
showGameOptionsModal: false, showGameOptionsModal: false,
stats: null, stats: null,
achievements: [], achievements: null,
hasNSFWContentBlocked: false, hasNSFWContentBlocked: false,
setGameColor: () => {}, setGameColor: () => {},
selectGameExecutable: async () => null, selectGameExecutable: async () => null,
@ -70,7 +70,9 @@ export function GameDetailsContextProvider({
shop, shop,
}: GameDetailsContextProps) { }: GameDetailsContextProps) {
const [shopDetails, setShopDetails] = useState<ShopDetails | null>(null); const [shopDetails, setShopDetails] = useState<ShopDetails | null>(null);
const [achievements, setAchievements] = useState<UserAchievement[]>([]); const [achievements, setAchievements] = useState<UserAchievement[] | null>(
null
);
const [game, setGame] = useState<Game | null>(null); const [game, setGame] = useState<Game | null>(null);
const [hasNSFWContentBlocked, setHasNSFWContentBlocked] = useState(false); const [hasNSFWContentBlocked, setHasNSFWContentBlocked] = useState(false);
const abortControllerRef = useRef<AbortController | null>(null); const abortControllerRef = useRef<AbortController | null>(null);
@ -176,7 +178,7 @@ export function GameDetailsContextProvider({
setGame(null); setGame(null);
setIsLoading(true); setIsLoading(true);
setisGameRunning(false); setisGameRunning(false);
setAchievements([]); setAchievements(null);
dispatch(setHeaderTitle(gameTitle)); dispatch(setHeaderTitle(gameTitle));
}, [objectId, gameTitle, dispatch]); }, [objectId, gameTitle, dispatch]);

View file

@ -20,7 +20,7 @@ export interface GameDetailsContext {
showRepacksModal: boolean; showRepacksModal: boolean;
showGameOptionsModal: boolean; showGameOptionsModal: boolean;
stats: GameStats | null; stats: GameStats | null;
achievements: UserAchievement[]; achievements: UserAchievement[] | null;
hasNSFWContentBlocked: boolean; hasNSFWContentBlocked: boolean;
setGameColor: React.Dispatch<React.SetStateAction<string>>; setGameColor: React.Dispatch<React.SetStateAction<string>>;
selectGameExecutable: () => Promise<string | null>; selectGameExecutable: () => Promise<string | null>;

View file

@ -8,20 +8,23 @@ import { formatDownloadProgress } from "@renderer/helpers";
import { TrophyIcon } from "@primer/octicons-react"; import { TrophyIcon } from "@primer/octicons-react";
import { SPACING_UNIT, vars } from "@renderer/theme.css"; import { SPACING_UNIT, vars } from "@renderer/theme.css";
import { gameDetailsContext } from "@renderer/context"; import { gameDetailsContext } from "@renderer/context";
import { GameShop, UserAchievement } from "@types"; import { UserAchievement } from "@types";
import { average } from "color.js"; import { average } from "color.js";
import Color from "color"; import Color from "color";
const HERO_ANIMATION_THRESHOLD = 25; const HERO_ANIMATION_THRESHOLD = 25;
interface AchievementsContentProps { interface AchievementsContentProps {
otherUserId: string | null; otherUser: {
otherUserDisplayName: string | null; userId: string;
displayName: string;
achievements: UserAchievement[];
} | null;
} }
interface AchievementListProps { interface AchievementListProps {
achievements: UserAchievement[]; achievements: UserAchievement[];
otherUserAchievements: UserAchievement[]; otherUserAchievements?: UserAchievement[];
} }
interface AchievementPanelProps { interface AchievementPanelProps {
@ -100,7 +103,7 @@ function AchievementList({
const { t } = useTranslation("achievement"); const { t } = useTranslation("achievement");
const { formatDateTime } = useDate(); const { formatDateTime } = useDate();
if (otherUserAchievements.length === 0) { if (!otherUserAchievements || otherUserAchievements.length === 0) {
return ( return (
<ul className={styles.list}> <ul className={styles.list}>
{achievements.map((achievement, index) => ( {achievements.map((achievement, index) => (
@ -200,34 +203,28 @@ function AchievementList({
); );
} }
export function AchievementsContent({ export function AchievementsContent({ otherUser }: AchievementsContentProps) {
otherUserId: userId,
otherUserDisplayName: displayName,
}: AchievementsContentProps) {
const heroRef = useRef<HTMLDivElement | null>(null); const heroRef = useRef<HTMLDivElement | null>(null);
const containerRef = useRef<HTMLDivElement | null>(null); const containerRef = useRef<HTMLDivElement | null>(null);
const [isHeaderStuck, setIsHeaderStuck] = useState(false); const [isHeaderStuck, setIsHeaderStuck] = useState(false);
const [backdropOpactiy, setBackdropOpacity] = useState(1); const [backdropOpactiy, setBackdropOpacity] = useState(1);
const [otherUserAchievements, setOtherUserAchievements] = useState<
UserAchievement[]
>([]);
const { gameTitle, objectId, shop, achievements, gameColor, setGameColor } = const { gameTitle, objectId, shop, achievements, gameColor, setGameColor } =
useContext(gameDetailsContext); useContext(gameDetailsContext);
const sortedAchievements = useMemo(() => { const sortedAchievements = useMemo(() => {
if (otherUserAchievements.length === 0) return achievements; if (!otherUser || otherUser.achievements.length === 0) return achievements!;
return achievements.sort((a, b) => { return achievements!.sort((a, b) => {
const indexA = otherUserAchievements.findIndex( const indexA = otherUser.achievements.findIndex(
(achievement) => achievement.name === a.name (achievement) => achievement.name === a.name
); );
const indexB = otherUserAchievements.findIndex( const indexB = otherUser.achievements.findIndex(
(achievement) => achievement.name === b.name (achievement) => achievement.name === b.name
); );
return indexA - indexB; return indexA - indexB;
}); });
}, [achievements, otherUserAchievements]); }, [achievements, otherUser]);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
@ -250,16 +247,6 @@ export function AchievementsContent({
setGameColor(backgroundColor); setGameColor(backgroundColor);
}; };
useEffect(() => {
if (objectId && shop && userId) {
window.electron
.getGameAchievements(objectId, shop as GameShop, userId)
.then((achievements) => {
setOtherUserAchievements(achievements);
});
}
}, [objectId, shop, userId]);
const onScroll: React.UIEventHandler<HTMLElement> = (event) => { const onScroll: React.UIEventHandler<HTMLElement> = (event) => {
const heroHeight = heroRef.current?.clientHeight ?? styles.HERO_HEIGHT; const heroHeight = heroRef.current?.clientHeight ?? styles.HERO_HEIGHT;
@ -320,10 +307,10 @@ export function AchievementsContent({
</div> </div>
<div className={styles.panel({ stuck: isHeaderStuck })}> <div className={styles.panel({ stuck: isHeaderStuck })}>
{userId && ( {otherUser && (
<AchievementPanel <AchievementPanel
displayName={displayName} displayName={otherUser.displayName}
achievements={otherUserAchievements} achievements={otherUser.achievements}
/> />
)} )}
@ -342,7 +329,7 @@ export function AchievementsContent({
> >
<AchievementList <AchievementList
achievements={sortedAchievements} achievements={sortedAchievements}
otherUserAchievements={otherUserAchievements} otherUserAchievements={otherUser?.achievements}
/> />
</div> </div>
</section> </section>

View file

@ -1,7 +1,7 @@
import { setHeaderTitle } from "@renderer/features"; import { setHeaderTitle } from "@renderer/features";
import { useAppDispatch, useUserDetails } from "@renderer/hooks"; import { useAppDispatch, useUserDetails } from "@renderer/hooks";
import type { GameShop } from "@types"; import type { GameShop, UserAchievement } from "@types";
import { useEffect } from "react"; import { useEffect, useState } from "react";
import { useSearchParams } from "react-router-dom"; import { useSearchParams } from "react-router-dom";
import { vars } from "@renderer/theme.css"; import { vars } from "@renderer/theme.css";
import { import {
@ -22,6 +22,10 @@ export function Achievement() {
const { userDetails } = useUserDetails(); const { userDetails } = useUserDetails();
const [otherUserAchievements, setOtherUserAchievements] = useState<
UserAchievement[] | null
>(null);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
useEffect(() => { useEffect(() => {
@ -30,10 +34,35 @@ export function Achievement() {
} }
}, [dispatch, title]); }, [dispatch, title]);
useEffect(() => {
setOtherUserAchievements(null);
if (userDetails?.id == userId) {
setOtherUserAchievements([]);
return;
}
if (objectId && shop && userId) {
window.electron
.getGameAchievements(objectId, shop as GameShop, userId)
.then((achievements) => {
setOtherUserAchievements(achievements);
});
}
}, [objectId, shop, userId]);
if (!objectId || !shop || !title) return null; if (!objectId || !shop || !title) return null;
const otherUserId = userDetails?.id == userId ? null : userId; const otherUserId = userDetails?.id == userId ? null : userId;
const otherUser =
otherUserId != null
? {
userId: otherUserId,
displayName: displayName || "",
achievements: otherUserAchievements || [],
}
: null;
return ( return (
<GameDetailsContextProvider <GameDetailsContextProvider
gameTitle={title} gameTitle={title}
@ -41,19 +70,18 @@ export function Achievement() {
objectId={objectId} objectId={objectId}
> >
<GameDetailsContextConsumer> <GameDetailsContextConsumer>
{({ isLoading }) => { {({ isLoading, achievements }) => {
return ( return (
<SkeletonTheme <SkeletonTheme
baseColor={vars.color.background} baseColor={vars.color.background}
highlightColor="#444" highlightColor="#444"
> >
{isLoading ? ( {isLoading ||
achievements === null ||
otherUserAchievements === null ? (
<AchievementsSkeleton /> <AchievementsSkeleton />
) : ( ) : (
<AchievementsContent <AchievementsContent otherUser={otherUser} />
otherUserId={otherUserId}
otherUserDisplayName={displayName}
/>
)} )}
</SkeletonTheme> </SkeletonTheme>
); );

View file

@ -148,7 +148,7 @@ export function Sidebar() {
</ul> </ul>
</SidebarSection> </SidebarSection>
)} )}
{userDetails && achievements.length > 0 && ( {userDetails && achievements && achievements.length > 0 && (
<SidebarSection <SidebarSection
title={t("achievements_count", { title={t("achievements_count", {
unlockedCount: achievements.filter((a) => a.unlocked).length, unlockedCount: achievements.filter((a) => a.unlocked).length,