feat: removing publishers

This commit is contained in:
Chubby Granny Chaser 2024-12-23 00:42:08 +00:00
parent 4607665908
commit 602ee61cb6
No known key found for this signature in database
8 changed files with 65 additions and 45 deletions

View File

@ -2,13 +2,16 @@ import type { CatalogueSearchPayload } from "@types";
import { registerEvent } from "../register-event"; import { registerEvent } from "../register-event";
import { HydraApi } from "@main/services"; import { HydraApi } from "@main/services";
const PAGE_SIZE = 12;
const searchGames = async ( const searchGames = async (
_event: Electron.IpcMainInvokeEvent, _event: Electron.IpcMainInvokeEvent,
payload: CatalogueSearchPayload payload: CatalogueSearchPayload,
page: number
) => { ) => {
return HydraApi.post( return HydraApi.post(
"/catalogue/search", "/catalogue/search",
{ ...payload, take: 24, skip: 0 }, { ...payload, take: page * PAGE_SIZE, skip: (page - 1) * PAGE_SIZE },
{ needsAuth: false } { needsAuth: false }
); );
}; };

View File

@ -37,8 +37,8 @@ contextBridge.exposeInMainWorld("electron", {
}, },
/* Catalogue */ /* Catalogue */
searchGames: (payload: CatalogueSearchPayload) => searchGames: (payload: CatalogueSearchPayload, page: number) =>
ipcRenderer.invoke("searchGames", payload), ipcRenderer.invoke("searchGames", payload, page),
getCatalogue: (category: CatalogueCategory) => getCatalogue: (category: CatalogueCategory) =>
ipcRenderer.invoke("getCatalogue", category), ipcRenderer.invoke("getCatalogue", category),
getGameShopDetails: (objectId: string, shop: GameShop, language: string) => getGameShopDetails: (objectId: string, shop: GameShop, language: string) =>

View File

@ -7,7 +7,7 @@ import { useAppDispatch, useAppSelector } from "@renderer/hooks";
import * as styles from "./header.css"; import * as styles from "./header.css";
import { AutoUpdateSubHeader } from "./auto-update-sub-header"; import { AutoUpdateSubHeader } from "./auto-update-sub-header";
import { setSearch } from "@renderer/features"; import { setFilters } from "@renderer/features";
const pathTitle: Record<string, string> = { const pathTitle: Record<string, string> = {
"/": "home", "/": "home",
@ -27,7 +27,7 @@ export function Header() {
); );
const searchValue = useAppSelector( const searchValue = useAppSelector(
(state) => state.catalogueSearch.value.title (state) => state.catalogueSearch.filters.title
); );
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
@ -59,7 +59,7 @@ export function Header() {
}; };
const handleSearch = (value: string) => { const handleSearch = (value: string) => {
dispatch(setSearch({ title: value })); dispatch(setFilters({ title: value }));
if (!location.pathname.startsWith("/catalogue")) { if (!location.pathname.startsWith("/catalogue")) {
navigate("/catalogue"); navigate("/catalogue");

View File

@ -49,7 +49,10 @@ declare global {
) => () => Electron.IpcRenderer; ) => () => Electron.IpcRenderer;
/* Catalogue */ /* Catalogue */
searchGames: (payload: CatalogueSearchPayload) => Promise<any[]>; searchGames: (
payload: CatalogueSearchPayload,
page: number
) => Promise<{ edges: any[]; count: number }>;
getCatalogue: (category: CatalogueCategory) => Promise<any[]>; getCatalogue: (category: CatalogueCategory) => Promise<any[]>;
getGameShopDetails: ( getGameShopDetails: (
objectId: string, objectId: string,

View File

@ -4,11 +4,11 @@ import type { PayloadAction } from "@reduxjs/toolkit";
import type { CatalogueSearchPayload } from "@types"; import type { CatalogueSearchPayload } from "@types";
export interface CatalogueSearchState { export interface CatalogueSearchState {
value: CatalogueSearchPayload; filters: CatalogueSearchPayload;
} }
const initialState: CatalogueSearchState = { const initialState: CatalogueSearchState = {
value: { filters: {
title: "", title: "",
downloadSourceFingerprints: [], downloadSourceFingerprints: [],
tags: [], tags: [],
@ -22,16 +22,16 @@ export const catalogueSearchSlice = createSlice({
name: "catalogueSearch", name: "catalogueSearch",
initialState, initialState,
reducers: { reducers: {
setSearch: ( setFilters: (
state, state,
action: PayloadAction<Partial<CatalogueSearchPayload>> action: PayloadAction<Partial<CatalogueSearchPayload>>
) => { ) => {
state.value = { ...state.value, ...action.payload }; state.filters = { ...state.filters, ...action.payload };
}, },
clearSearch: (state) => { clearFilters: (state) => {
state.value = initialState.value; state.filters = initialState.filters;
}, },
}, },
}); });
export const { setSearch, clearSearch } = catalogueSearchSlice.actions; export const { setFilters, clearFilters } = catalogueSearchSlice.actions;

View File

@ -1,4 +1,4 @@
import { Badge } from "@renderer/components"; import { Badge, Button } from "@renderer/components";
import type { DownloadSource } from "@types"; import type { DownloadSource } from "@types";
@ -14,7 +14,7 @@ import { steamUrlBuilder } from "@shared";
import { buildGameDetailsPath } from "@renderer/helpers"; import { buildGameDetailsPath } from "@renderer/helpers";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { FilterSection } from "./filter-section"; import { FilterSection } from "./filter-section";
import { setSearch } from "@renderer/features"; import { setFilters } from "@renderer/features";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import axios from "axios"; import axios from "axios";
import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; import Skeleton, { SkeletonTheme } from "react-loading-skeleton";
@ -35,12 +35,15 @@ export default function Catalogue() {
const navigate = useNavigate(); const navigate = useNavigate();
const [downloadSources, setDownloadSources] = useState<DownloadSource[]>([]); const [downloadSources, setDownloadSources] = useState<DownloadSource[]>([]);
const [games, setGames] = useState<any[]>([]);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [publishers, setPublishers] = useState<string[]>([]); const [publishers, setPublishers] = useState<string[]>([]);
const [developers, setDevelopers] = useState<string[]>([]); const [developers, setDevelopers] = useState<string[]>([]);
const filters = useAppSelector((state) => state.catalogueSearch.value); const [results, setResults] = useState<any[]>([]);
const [page, setPage] = useState(1);
const [totalPages, setTotalPages] = useState(1);
const { filters } = useAppSelector((state) => state.catalogueSearch);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
@ -49,7 +52,7 @@ export default function Catalogue() {
const { getRepacksForObjectId } = useRepacks(); const { getRepacksForObjectId } = useRepacks();
useEffect(() => { useEffect(() => {
setGames([]); setResults([]);
setIsLoading(true); setIsLoading(true);
abortControllerRef.current?.abort(); abortControllerRef.current?.abort();
@ -57,18 +60,19 @@ export default function Catalogue() {
abortControllerRef.current = abortController; abortControllerRef.current = abortController;
window.electron window.electron
.searchGames(filters) .searchGames(filters, page)
.then((games) => { .then((response) => {
if (abortController.signal.aborted) { if (abortController.signal.aborted) {
return; return;
} }
setGames(games); setResults(response.edges);
setTotalPages(Math.ceil(response.count / 12));
}) })
.finally(() => { .finally(() => {
setIsLoading(false); setIsLoading(false);
}); });
}, [filters]); }, [filters, page, dispatch]);
useEffect(() => { useEffect(() => {
window.electron.getDevelopers().then((developers) => { window.electron.getDevelopers().then((developers) => {
@ -81,14 +85,14 @@ export default function Catalogue() {
}, []); }, []);
const gamesWithRepacks = useMemo(() => { const gamesWithRepacks = useMemo(() => {
return games.map((game) => { return results.map((game) => {
const repacks = getRepacksForObjectId(game.objectId); const repacks = getRepacksForObjectId(game.objectId);
const uniqueRepackers = Array.from( const uniqueRepackers = Array.from(
new Set(repacks.map((repack) => repack.repacker)) new Set(repacks.map((repack) => repack.repacker))
); );
return { ...game, repacks: uniqueRepackers }; return { ...game, repacks: uniqueRepackers };
}); });
}, [games, getRepacksForObjectId]); }, [results, getRepacksForObjectId]);
useEffect(() => { useEffect(() => {
downloadSourcesTable.toArray().then((sources) => { downloadSourcesTable.toArray().then((sources) => {
@ -264,27 +268,32 @@ export default function Catalogue() {
)) ))
)} )}
{/* <div style={{ display: "flex", gap: 8 }}> {totalPages > 1 && (
<Button theme="outline">1</Button> <div style={{ display: "flex", gap: 8 }}>
<Button theme="outline">2</Button> {Array.from({ length: 3 }).map((_, i) => (
</div> */} <Button theme="outline" key={i} onClick={() => setPage(i + 1)}>
{i + 1}
</Button>
))}
</div>
)}
</div> </div>
<div className="catalogue__filters-container"> <div className="catalogue__filters-container">
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}> <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
<FilterSection <FilterSection
title={t("genres")} title={t("genres")}
onClear={() => dispatch(setSearch({ genres: [] }))} onClear={() => dispatch(setFilters({ genres: [] }))}
color={filterCategoryColors.genres} color={filterCategoryColors.genres}
onSelect={(value) => { onSelect={(value) => {
if (filters.genres.includes(value)) { if (filters.genres.includes(value)) {
dispatch( dispatch(
setSearch({ setFilters({
genres: filters.genres.filter((genre) => genre !== value), genres: filters.genres.filter((genre) => genre !== value),
}) })
); );
} else { } else {
dispatch(setSearch({ genres: [...filters.genres, value] })); dispatch(setFilters({ genres: [...filters.genres, value] }));
} }
}} }}
items={[ items={[
@ -329,17 +338,17 @@ export default function Catalogue() {
<FilterSection <FilterSection
title={t("tags")} title={t("tags")}
color={filterCategoryColors.tags} color={filterCategoryColors.tags}
onClear={() => dispatch(setSearch({ tags: [] }))} onClear={() => dispatch(setFilters({ tags: [] }))}
onSelect={(value) => { onSelect={(value) => {
if (filters.tags.includes(Number(value))) { if (filters.tags.includes(Number(value))) {
dispatch( dispatch(
setSearch({ setFilters({
tags: filters.tags.filter((tag) => tag !== Number(value)), tags: filters.tags.filter((tag) => tag !== Number(value)),
}) })
); );
} else { } else {
dispatch( dispatch(
setSearch({ tags: [...filters.tags, Number(value)] }) setFilters({ tags: [...filters.tags, Number(value)] })
); );
} }
}} }}
@ -358,12 +367,12 @@ export default function Catalogue() {
title={t("download_sources")} title={t("download_sources")}
color={filterCategoryColors.downloadSourceFingerprints} color={filterCategoryColors.downloadSourceFingerprints}
onClear={() => onClear={() =>
dispatch(setSearch({ downloadSourceFingerprints: [] })) dispatch(setFilters({ downloadSourceFingerprints: [] }))
} }
onSelect={(value) => { onSelect={(value) => {
if (filters.downloadSourceFingerprints.includes(value)) { if (filters.downloadSourceFingerprints.includes(value)) {
dispatch( dispatch(
setSearch({ setFilters({
downloadSourceFingerprints: downloadSourceFingerprints:
filters.downloadSourceFingerprints.filter( filters.downloadSourceFingerprints.filter(
(fingerprint) => fingerprint !== value (fingerprint) => fingerprint !== value
@ -372,7 +381,7 @@ export default function Catalogue() {
); );
} else { } else {
dispatch( dispatch(
setSearch({ setFilters({
downloadSourceFingerprints: [ downloadSourceFingerprints: [
...filters.downloadSourceFingerprints, ...filters.downloadSourceFingerprints,
value, value,
@ -393,11 +402,11 @@ export default function Catalogue() {
<FilterSection <FilterSection
title={t("developers")} title={t("developers")}
color={filterCategoryColors.developers} color={filterCategoryColors.developers}
onClear={() => dispatch(setSearch({ developers: [] }))} onClear={() => dispatch(setFilters({ developers: [] }))}
onSelect={(value) => { onSelect={(value) => {
if (filters.developers.includes(value)) { if (filters.developers.includes(value)) {
dispatch( dispatch(
setSearch({ setFilters({
developers: filters.developers.filter( developers: filters.developers.filter(
(developer) => developer !== value (developer) => developer !== value
), ),
@ -405,7 +414,7 @@ export default function Catalogue() {
); );
} else { } else {
dispatch( dispatch(
setSearch({ developers: [...filters.developers, value] }) setFilters({ developers: [...filters.developers, value] })
); );
} }
}} }}
@ -419,11 +428,11 @@ export default function Catalogue() {
<FilterSection <FilterSection
title={t("publishers")} title={t("publishers")}
color={filterCategoryColors.publishers} color={filterCategoryColors.publishers}
onClear={() => dispatch(setSearch({ publishers: [] }))} onClear={() => dispatch(setFilters({ publishers: [] }))}
onSelect={(value) => { onSelect={(value) => {
if (filters.publishers.includes(value)) { if (filters.publishers.includes(value)) {
dispatch( dispatch(
setSearch({ setFilters({
publishers: filters.publishers.filter( publishers: filters.publishers.filter(
(publisher) => publisher !== value (publisher) => publisher !== value
), ),
@ -431,7 +440,7 @@ export default function Catalogue() {
); );
} else { } else {
dispatch( dispatch(
setSearch({ publishers: [...filters.publishers, value] }) setFilters({ publishers: [...filters.publishers, value] })
); );
} }
}} }}

View File

@ -45,6 +45,10 @@ export function FilterSection({
const { formatNumber } = useFormat(); const { formatNumber } = useFormat();
if (!items.length) {
return null;
}
return ( return (
<div> <div>
<div style={{ display: "flex", gap: 8, alignItems: "center" }}> <div style={{ display: "flex", gap: 8, alignItems: "center" }}>

View File

@ -156,6 +156,7 @@ self.onmessage = async (event: MessageEvent<Payload>) => {
const existingRepacks = await repacksTable.toArray(); const existingRepacks = await repacksTable.toArray();
for (const downloadSource of downloadSources) { for (const downloadSource of downloadSources) {
console.log(downloadSource);
if (!downloadSource.fingerprint) { if (!downloadSource.fingerprint) {
await deleteDownloadSource(downloadSource.id); await deleteDownloadSource(downloadSource.id);
await importDownloadSource(downloadSource.url); await importDownloadSource(downloadSource.url);