Tyler Hallada
8f254ef761
* Adds game filter to ModList. * Prefixes search results with game * Adds `&game=X` to URL for mods * Loads json from new directories on static server
187 lines
5.9 KiB
TypeScript
187 lines
5.9 KiB
TypeScript
import { format } from "date-fns";
|
|
import React, { useCallback, useContext, useState } from "react";
|
|
import useSWRImmutable from "swr/immutable";
|
|
|
|
import { Mod, File, NEXUS_MODS_URL } from "./ModData";
|
|
import styles from "../styles/AddModData.module.css";
|
|
import { jsonFetcher } from "../lib/api";
|
|
import { DownloadCountsContext } from "./DownloadCountsProvider";
|
|
import { GamesContext } from "./GamesProvider";
|
|
import type { SelectedMod } from "./AddModDialog";
|
|
|
|
type Props = {
|
|
selectedMod: SelectedMod;
|
|
selectedPlugin: string | null;
|
|
setSelectedPlugin: (plugin: string) => void;
|
|
};
|
|
|
|
const AddModData: React.FC<Props> = ({
|
|
selectedMod,
|
|
selectedPlugin,
|
|
setSelectedPlugin,
|
|
}) => {
|
|
const {
|
|
games,
|
|
getGameNameById,
|
|
error: gamesError,
|
|
} = useContext(GamesContext);
|
|
const counts = useContext(DownloadCountsContext);
|
|
const [selectedFile, setSelectedFile] = useState<number | null>(null);
|
|
|
|
const { data: modData, error: modError } = useSWRImmutable(
|
|
selectedMod
|
|
? `https://mods.modmapper.com/${selectedMod.game}/${selectedMod.id}.json`
|
|
: null,
|
|
(_) => jsonFetcher<Mod>(_)
|
|
);
|
|
const { data: fileData, error: fileError } = useSWRImmutable(
|
|
selectedFile ? `https://files.modmapper.com/${selectedFile}.json` : null,
|
|
(_) => jsonFetcher<File>(_)
|
|
);
|
|
|
|
const handleFileChange = useCallback(
|
|
(event) => {
|
|
setSelectedFile(event.target.value);
|
|
},
|
|
[setSelectedFile]
|
|
);
|
|
const handlePluginChange = useCallback(
|
|
(event) => {
|
|
setSelectedPlugin(event.target.value);
|
|
},
|
|
[setSelectedPlugin]
|
|
);
|
|
|
|
if (modError && modError.status === 404) {
|
|
return <div>Mod could not be found.</div>;
|
|
} else if (modError) {
|
|
return <div>{`Error loading mod data: ${modError.message}`}</div>;
|
|
}
|
|
if (modData === undefined)
|
|
return <div className={styles.status}>Loading...</div>;
|
|
if (modData === null)
|
|
return <div className={styles.status}>Mod could not be found.</div>;
|
|
|
|
let numberFmt = new Intl.NumberFormat("en-US");
|
|
const gameName = getGameNameById(modData.game_id);
|
|
const gameDownloadCounts = gameName && counts[gameName].counts;
|
|
const modCounts =
|
|
gameDownloadCounts && gameDownloadCounts[modData.nexus_mod_id];
|
|
const total_downloads = modCounts ? modCounts[0] : 0;
|
|
const unique_downloads = modCounts ? modCounts[1] : 0;
|
|
const views = modCounts ? modCounts[2] : 0;
|
|
|
|
const renderDownloadCountsLoading = () => (
|
|
<div>Loading live download counts...</div>
|
|
);
|
|
const renderDownloadCountsError = (error: Error) => (
|
|
<div>{`Error loading live download counts: ${error.message}`}</div>
|
|
);
|
|
const renderGamesError = (error?: Error) =>
|
|
error ? (
|
|
<div>{`Error loading games: ${error.message}`}</div>
|
|
) : (
|
|
<div>Error loading games</div>
|
|
);
|
|
|
|
if (selectedMod && modData) {
|
|
return (
|
|
<div className={styles.wrapper}>
|
|
<h3>
|
|
<a
|
|
href={`${NEXUS_MODS_URL}/mods/${modData.nexus_mod_id}`}
|
|
target="_blank"
|
|
rel="noreferrer noopener"
|
|
className={styles.name}
|
|
>
|
|
{modData.name}
|
|
</a>
|
|
</h3>
|
|
<div>
|
|
<strong>Category: </strong>
|
|
<a
|
|
href={`${NEXUS_MODS_URL}/mods/categories/${modData.category_id}`}
|
|
target="_blank"
|
|
rel="noreferrer noopener"
|
|
>
|
|
{modData.category_name}
|
|
</a>
|
|
{modData.is_translation && <strong> (translation)</strong>}
|
|
</div>
|
|
<div>
|
|
<strong>Author: </strong>
|
|
<a
|
|
href={`${NEXUS_MODS_URL}/users/${modData.author_id}`}
|
|
target="_blank"
|
|
rel="noreferrer noopener"
|
|
>
|
|
{modData.author_name}
|
|
</a>
|
|
</div>
|
|
<div>
|
|
<strong>Uploaded:</strong>{" "}
|
|
{format(new Date(modData.first_upload_at), "d MMM y")}
|
|
</div>
|
|
{(!counts.skyrim.counts || !counts.skyrimspecialedition.counts) &&
|
|
renderDownloadCountsLoading()}
|
|
{(!games || gamesError) && renderGamesError(gamesError)}
|
|
{counts.skyrim.error && renderDownloadCountsError(counts.skyrim.error)}
|
|
{counts.skyrimspecialedition.error &&
|
|
renderDownloadCountsError(counts.skyrimspecialedition.error)}
|
|
<div>
|
|
<strong>Last Update:</strong>{" "}
|
|
{format(new Date(modData.last_update_at), "d MMM y")}
|
|
</div>
|
|
<div>
|
|
<strong>Total Downloads:</strong> {numberFmt.format(total_downloads)}
|
|
</div>
|
|
<div>
|
|
<strong>Unique Downloads:</strong>{" "}
|
|
{numberFmt.format(unique_downloads)}
|
|
</div>
|
|
<div className={styles["select-container"]}>
|
|
<label htmlFor="mod-file-select" className={styles.label}>
|
|
Select file:
|
|
</label>
|
|
<select
|
|
name="file"
|
|
id="mod-file-select"
|
|
className={styles.select}
|
|
onChange={handleFileChange}
|
|
>
|
|
<option value="">--Select file--</option>
|
|
{[...modData.files].reverse().map((file) => (
|
|
<option key={file.nexus_file_id} value={file.nexus_file_id}>
|
|
{file.name} (v{file.version}) ({file.category})
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
{fileData && (
|
|
<div className={styles["select-container"]}>
|
|
<label htmlFor="file-plugin-select" className={styles.label}>
|
|
Select plugin:
|
|
</label>
|
|
<select
|
|
name="plugin"
|
|
id="file-plugin-select"
|
|
className={styles.select}
|
|
onChange={handlePluginChange}
|
|
>
|
|
<option value="">--Select plugin--</option>
|
|
{fileData.plugins.map((plugin) => (
|
|
<option key={plugin.hash} value={plugin.hash}>
|
|
{plugin.file_path}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
return null;
|
|
};
|
|
|
|
export default AddModData;
|