mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-01-23 13:34:54 +03:00
feat: removing publishers
This commit is contained in:
parent
4607665908
commit
602ee61cb6
@ -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 }
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -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) =>
|
||||||
|
@ -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");
|
||||||
|
5
src/renderer/src/declaration.d.ts
vendored
5
src/renderer/src/declaration.d.ts
vendored
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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] })
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
@ -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" }}>
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user