Merge branch 'main' into fix/game_asset_changing_path

This commit is contained in:
Moyase
2025-10-12 22:52:45 +03:00
committed by GitHub

View File

@@ -1,4 +1,11 @@
import { useContext, useEffect, useMemo, useRef, useState } from "react";
import {
useCallback,
useContext,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import {
PencilIcon,
TrashIcon,
@@ -164,6 +171,8 @@ export function GameDetailsContent() {
const [hasUserReviewed, setHasUserReviewed] = useState(false);
const [reviewCheckLoading, setReviewCheckLoading] = useState(false);
const abortControllerRef = useRef<AbortController | null>(null);
// Check if the current game is in the user's library
const isGameInLibrary = useMemo(() => {
if (!library || !shop || !objectId) return false;
@@ -225,6 +234,14 @@ export function GameDetailsContent() {
useEffect(() => {
setBackdropOpacity(1);
// Cleanup: abort any pending review requests when objectId changes
return () => {
if (abortControllerRef.current) {
abortControllerRef.current.abort();
abortControllerRef.current = null;
}
};
}, [objectId]);
const handleCloudSaveButtonClick = () => {
@@ -256,7 +273,7 @@ export function GameDetailsContent() {
const isCustomGame = game?.shop === "custom";
const checkUserReview = async () => {
const checkUserReview = useCallback(async () => {
if (!objectId || !userDetails) return;
setReviewCheckLoading(true);
@@ -265,22 +282,38 @@ export function GameDetailsContent() {
const hasReviewed = (response as any)?.hasReviewed || false;
setHasUserReviewed(hasReviewed);
const twoHoursInMilliseconds = 2 * 60 * 60 * 1000;
const hasEnoughPlaytime =
game &&
game.playTimeInMilliseconds >= twoHoursInMilliseconds &&
!game.hasManuallyUpdatedPlaytime;
if (
!hasReviewed &&
hasEnoughPlaytime &&
!sessionStorage.getItem(`reviewPromptDismissed_${objectId}`)
) {
setShowReviewPrompt(true);
setShowReviewForm(true);
}
} catch (error) {
console.error("Failed to check user review:", error);
} finally {
setReviewCheckLoading(false);
}
};
}, [objectId, userDetails, shop, game]);
const loadReviews = async (reset = false) => {
const loadReviews = useCallback(
async (reset = false) => {
if (!objectId) return;
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
const abortController = new AbortController();
abortControllerRef.current = abortController;
setReviewsLoading(true);
try {
const skip = reset ? 0 : reviewsPage * 20;
@@ -292,6 +325,10 @@ export function GameDetailsContent() {
reviewsSortBy
);
if (abortController.signal.aborted) {
return;
}
const reviewsData = (response as any)?.reviews || [];
const reviewCount = (response as any)?.totalCount || 0;
@@ -305,11 +342,17 @@ export function GameDetailsContent() {
setHasMoreReviews(reviewsData.length === 20);
} catch (error) {
if (!abortController.signal.aborted) {
console.error("Failed to load reviews:", error);
}
} finally {
if (!abortController.signal.aborted) {
setReviewsLoading(false);
}
};
}
},
[objectId, shop, reviewsPage, reviewsSortBy]
);
const handleVoteReview = async (
reviewId: string,
@@ -396,7 +439,6 @@ export function GameDetailsContent() {
const handleReviewPromptYes = () => {
setShowReviewPrompt(false);
setShowReviewForm(true);
setTimeout(() => {
const reviewFormElement = document.querySelector(
@@ -413,6 +455,7 @@ export function GameDetailsContent() {
const handleReviewPromptLater = () => {
setShowReviewPrompt(false);
setShowReviewForm(false);
if (objectId) {
sessionStorage.setItem(`reviewPromptDismissed_${objectId}`, "true");
}
@@ -451,13 +494,13 @@ export function GameDetailsContent() {
loadReviews(true);
checkUserReview();
}
}, [game, shop, objectId, reviewsSortBy, userDetails]);
}, [game, shop, objectId, loadReviews, checkUserReview]);
useEffect(() => {
if (reviewsPage > 0) {
loadReviews(false);
}
}, [reviewsPage]);
}, [reviewsPage, loadReviews]);
// Initialize previousVotesRef for new reviews
useEffect(() => {
@@ -773,8 +816,17 @@ export function GameDetailsContent() {
</div>
)}
<div
style={{
opacity: reviewsLoading && reviews.length > 0 ? 0.5 : 1,
transition: "opacity 0.2s ease",
}}
>
{reviews.map((review) => (
<div key={review.id} className="game-details__review-item">
<div
key={review.id}
className="game-details__review-item"
>
{review.isBlocked &&
!visibleBlockedReviews.has(review.id) ? (
<div className="game-details__blocked-review-simple">
@@ -899,10 +951,13 @@ export function GameDetailsContent() {
exit="exit"
transition={{ duration: 0.2 }}
onAnimationComplete={() => {
previousVotesRef.current.set(review.id, {
previousVotesRef.current.set(
review.id,
{
upvotes: review.upvotes || 0,
downvotes: review.downvotes || 0,
});
}
);
}}
>
{formatNumber(review.upvotes || 0)}
@@ -948,10 +1003,13 @@ export function GameDetailsContent() {
exit="exit"
transition={{ duration: 0.2 }}
onAnimationComplete={() => {
previousVotesRef.current.set(review.id, {
previousVotesRef.current.set(
review.id,
{
upvotes: review.upvotes || 0,
downvotes: review.downvotes || 0,
});
}
);
}}
>
{formatNumber(review.downvotes || 0)}
@@ -973,7 +1031,9 @@ export function GameDetailsContent() {
visibleBlockedReviews.has(review.id) && (
<button
className="game-details__blocked-review-hide-link"
onClick={() => toggleBlockedReview(review.id)}
onClick={() =>
toggleBlockedReview(review.id)
}
>
Hide
</button>
@@ -983,6 +1043,7 @@ export function GameDetailsContent() {
)}
</div>
))}
</div>
{hasMoreReviews && !reviewsLoading && (
<button