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,
showGameOptionsModal: false,
stats: null,
achievements: [],
achievements: null,
hasNSFWContentBlocked: false,
setGameColor: () => {},
selectGameExecutable: async () => null,
@ -70,7 +70,9 @@ export function GameDetailsContextProvider({
shop,
}: GameDetailsContextProps) {
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 [hasNSFWContentBlocked, setHasNSFWContentBlocked] = useState(false);
const abortControllerRef = useRef<AbortController | null>(null);
@ -176,7 +178,7 @@ export function GameDetailsContextProvider({
setGame(null);
setIsLoading(true);
setisGameRunning(false);
setAchievements([]);
setAchievements(null);
dispatch(setHeaderTitle(gameTitle));
}, [objectId, gameTitle, dispatch]);

View file

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

View file

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

View file

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

View file

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