Merge branch 'main' into fix/description-image-sizing

This commit is contained in:
Hydra 2024-04-30 03:28:57 +01:00 committed by GitHub
commit 872b7f38ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 91 additions and 71 deletions

View File

@ -1,27 +1,40 @@
import shuffle from "lodash/shuffle";
import { getRandomSteam250List } from "@main/services";
import { Steam250Game, getSteam250List } from "@main/services";
import { registerEvent } from "../register-event";
import { searchGames, searchRepacks } from "../helpers/search-games";
import { formatName } from "@main/helpers";
const state = { games: Array<Steam250Game>(), index: 0 };
const getRandomGame = async (_event: Electron.IpcMainInvokeEvent) => {
return getRandomSteam250List().then(async (games) => {
const shuffledList = shuffle(games);
if (state.games.length == 0) {
const steam250List = await getSteam250List();
for (const game of shuffledList) {
const repacks = searchRepacks(formatName(game.title));
const filteredSteam250List = steam250List.filter((game) => {
const repacks = searchRepacks(game.title);
const catalogue = searchGames({ query: game.title });
if (repacks.length) {
const results = await searchGames({ query: game.title });
return repacks.length && catalogue.length;
});
if (results.length) {
return results[0].objectID;
}
}
}
});
state.games = shuffle(filteredSteam250List);
}
if (state.games.length == 0) {
return "";
}
const resultObjectId = state.games[state.index].objectID;
state.index += 1;
if (state.index == state.games.length) {
state.index = 0;
state.games = shuffle(state.games);
}
return resultObjectId;
};
registerEvent(getRandomGame, {

View File

@ -1,11 +1,15 @@
import { registerEvent } from "../register-event";
import { searchGames } from "../helpers/search-games";
import { CatalogueEntry } from "@types";
registerEvent(
(_event: Electron.IpcMainInvokeEvent, query: string) =>
searchGames({ query, take: 12 }),
{
name: "searchGames",
memoize: true,
}
);
const searchGamesEvent = async (
_event: Electron.IpcMainInvokeEvent,
query: string
): Promise<CatalogueEntry[]> => {
return searchGames({ query, take: 12 });
};
registerEvent(searchGamesEvent, {
name: "searchGames",
memoize: true,
});

View File

@ -42,11 +42,11 @@ export interface SearchGamesArgs {
skip?: number;
}
export const searchGames = async ({
export const searchGames = ({
query,
take,
skip,
}: SearchGamesArgs): Promise<CatalogueEntry[]> => {
}: SearchGamesArgs): CatalogueEntry[] => {
const results = steamGamesIndex
.search(formatName(query || ""), { limit: take, offset: skip })
.map((index) => {
@ -61,11 +61,9 @@ export const searchGames = async ({
};
});
return Promise.all(results).then((resultsWithRepacks) =>
orderBy(
resultsWithRepacks,
[({ repacks }) => repacks.length, "repacks"],
["desc"]
)
return orderBy(
results,
[({ repacks }) => repacks.length, "repacks"],
["desc"]
);
};

View File

@ -1,24 +1,31 @@
import axios from "axios";
import { JSDOM } from "jsdom";
import shuffle from "lodash/shuffle";
export interface Steam250Game {
title: string;
objectID: string;
}
export const requestSteam250 = async (path: string) => {
return axios.get(`https://steam250.com${path}`).then((response) => {
const { window } = new JSDOM(response.data);
const { document } = window;
return axios
.get(`https://steam250.com${path}`)
.then((response) => {
const { window } = new JSDOM(response.data);
const { document } = window;
return Array.from(document.querySelectorAll(".appline .title a")).map(
($title: HTMLAnchorElement) => {
const steamGameUrl = $title.href;
if (!steamGameUrl) return null;
return Array.from(document.querySelectorAll(".appline .title a"))
.map(($title: HTMLAnchorElement) => {
const steamGameUrl = $title.href;
if (!steamGameUrl) return null;
return {
title: $title.textContent,
objectID: steamGameUrl.split("/").pop(),
};
}
);
});
return {
title: $title.textContent,
objectID: steamGameUrl.split("/").pop(),
} as Steam250Game;
})
.filter((game) => game != null);
})
.catch((_) => [] as Steam250Game[]);
};
const steam250Paths = [
@ -28,7 +35,15 @@ const steam250Paths = [
"/most_played",
];
export const getRandomSteam250List = async () => {
const [path] = shuffle(steam250Paths);
return requestSteam250(path);
export const getSteam250List = async () => {
const gamesList = (
await Promise.all(steam250Paths.map((path) => requestSteam250(path)))
).flat();
const gamesMap: Map<string, Steam250Game> = gamesList.reduce((map, item) => {
map.set(item.objectID, item);
return map;
}, new Map());
return [...gamesMap.values()];
};

View File

@ -1,6 +1,6 @@
import Color from "color";
import { average } from "color.js";
import { useCallback, useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
import type {
@ -33,6 +33,7 @@ export function GameDetails() {
const { objectID, shop } = useParams();
const [isLoading, setIsLoading] = useState(false);
const [isLoadingRandomGame, setIsLoadingRandomGame] = useState(false);
const [color, setColor] = useState("");
const [gameDetails, setGameDetails] = useState<ShopDetails | null>(null);
const [howLongToBeat, setHowLongToBeat] = useState<{
@ -53,18 +54,10 @@ export function GameDetails() {
const [showRepacksModal, setShowRepacksModal] = useState(false);
const [showSelectFolderModal, setShowSelectFolderModal] = useState(false);
const randomGameObjectID = useRef<string | null>(null);
const dispatch = useAppDispatch();
const { game: gameDownloading, startDownload, isDownloading } = useDownload();
const getRandomGame = useCallback(() => {
window.electron.getRandomGame().then((objectID) => {
randomGameObjectID.current = objectID;
});
}, []);
const handleImageSettled = useCallback((url: string) => {
average(url, { amount: 1, format: "hex" })
.then((color) => {
@ -89,8 +82,6 @@ export function GameDetails() {
setIsGamePlaying(false);
dispatch(setHeaderTitle(""));
getRandomGame();
window.electron
.getGameShopDetails(objectID, "steam", getSteamLanguage(i18n.language))
.then((result) => {
@ -107,6 +98,7 @@ export function GameDetails() {
setGameDetails(result);
dispatch(setHeaderTitle(result.name));
setIsLoadingRandomGame(false);
})
.finally(() => {
setIsLoading(false);
@ -114,7 +106,7 @@ export function GameDetails() {
getGame();
setHowLongToBeat({ isLoading: true, data: null });
}, [getGame, getRandomGame, dispatch, navigate, objectID, i18n.language]);
}, [getGame, dispatch, navigate, objectID, i18n.language]);
const isGameDownloading = isDownloading && gameDownloading?.id === game?.id;
@ -158,16 +150,15 @@ export function GameDetails() {
});
};
const handleRandomizerClick = () => {
if (!randomGameObjectID.current) return;
const handleRandomizerClick = async () => {
setIsLoadingRandomGame(true);
const randomGameObjectID = await window.electron.getRandomGame();
const searchParams = new URLSearchParams({
fromRandomizer: "1",
});
navigate(
`/game/steam/${randomGameObjectID.current}?${searchParams.toString()}`
);
navigate(`/game/steam/${randomGameObjectID}?${searchParams.toString()}`);
};
const fromRandomizer = searchParams.get("fromRandomizer");
@ -281,6 +272,7 @@ export function GameDetails() {
className={styles.randomizerButton}
onClick={handleRandomizerClick}
theme="outline"
disabled={isLoadingRandomGame}
>
<div style={{ width: 16, height: 16, position: "relative" }}>
<Lottie

View File

@ -58,14 +58,12 @@ export function Home() {
const getRandomGame = useCallback(() => {
setIsLoadingRandomGame(true);
window.electron
.getRandomGame()
.then((objectID) => {
window.electron.getRandomGame().then((objectID) => {
if (objectID) {
randomGameObjectID.current = objectID;
})
.finally(() => {
setIsLoadingRandomGame(false);
});
}
});
}, []);
const handleRandomizerClick = () => {