import { format } from "date-fns"; import Head from "next/head"; import React, { useCallback, useContext, useEffect, useState } from "react"; import useSWRImmutable from "swr/immutable"; import { useAppDispatch, useAppSelector } from "../lib/hooks"; import CellList from "./CellList"; import styles from "../styles/ModData.module.css"; import { jsonFetcher } from "../lib/api"; import { editionNames } from "../lib/games"; import { PluginsByHashWithMods, removeFetchedPlugin, updateFetchedPlugin, } from "../slices/plugins"; import Link from "next/link"; import { DownloadCountsContext } from "./DownloadCountsProvider"; import { GamesContext } from "./GamesProvider"; export interface CellCoord { x: number; y: number; } export interface ModFile { name: string; version: string; category: string; nexus_file_id: number; } export interface FilePlugin { hash: number; file_path: string; } export interface FileCell { x: number; y: number; } export interface File { id: number; name: string; file_name: string; nexus_file_id: number; mod_id: number; category: string; version: string; mod_version: string; size: number; uploaded_at: string; created_at: string; downloaded_at: string; has_plugin: boolean; unable_to_extract_plugins: boolean; cells: FileCell[]; plugins: FilePlugin[]; plugin_count: number; } export interface Mod { id: number; name: string; nexus_mod_id: number; author_name: string; author_id: number; category_name: string; category_id: number; description: string; thumbnail_link: string; game_id: number; is_translation: boolean; updated_at: string; created_at: string; last_update_at: string; first_upload_at: string; last_updated_files_at: string; cells: CellCoord[]; files: ModFile[]; } export const NEXUS_MODS_URL = "https://www.nexusmods.com"; type Props = { game: string; selectedMod: number; selectedFile: number; selectedPlugin: string; setSelectedCells: (cells: { x: number; y: number }[] | null) => void; onSelectFile: (fileId: number) => void; onSelectPlugin: (hash: string) => void; }; const ModData: React.FC = ({ game, selectedMod, selectedFile, selectedPlugin, setSelectedCells, onSelectFile, onSelectPlugin, }) => { const { games, getGameNameById, error: gamesError, } = useContext(GamesContext); const counts = useContext(DownloadCountsContext); const [showAddRemovePluginNotification, setShowAddRemovePluginNotification] = useState(false); const { data: modData, error: modError } = useSWRImmutable( `https://mods.modmapper.com/${game}/${selectedMod}.json`, (_) => jsonFetcher(_) ); const { data: fileData, error: fileError } = useSWRImmutable( selectedFile ? `https://files.modmapper.com/${selectedFile}.json` : null, (_) => jsonFetcher(_) ); const { data: pluginData, error: pluginError } = useSWRImmutable( selectedPlugin ? `https://plugins.modmapper.com/${selectedPlugin}.json` : null, (_) => jsonFetcher(_) ); const dispatch = useAppDispatch(); const fetchedPlugin = useAppSelector((state) => state.plugins.fetchedPlugins.find( (plugin) => plugin.hash === selectedPlugin ) ); const handleFileChange = useCallback( (event) => { onSelectFile(event.target.value); }, [onSelectFile] ); const handlePluginChange = useCallback( (event) => { onSelectPlugin(event.target.value); }, [onSelectPlugin] ); useEffect(() => { if (modData && !selectedFile) setSelectedCells(modData.cells); }, [modData, setSelectedCells, selectedFile]); useEffect(() => { if (fileData) setSelectedCells(fileData.cells); }, [fileData, setSelectedCells]); useEffect(() => { if (pluginData) setSelectedCells(pluginData.cells); }, [pluginData, setSelectedCells]); const renderDownloadCountsLoading = () => (
Loading live download counts...
); const renderDownloadCountsError = (error: Error) => (
{`Error loading live download counts: ${error.message}`}
); const renderGamesError = (error?: Error) => error ? (
{`Error loading games: ${error.message}`}
) : (
Error loading games
); if (modError && modError.status === 404) { return
Mod could not be found.
; } else if (modError) { return
{`Error loading mod modData: ${modError.message}`}
; } if (modData === undefined) return
Loading...
; if (modData === null) return
Mod could not be found.
; 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; if (selectedMod && modData) { return ( <> {`Modmapper - ${modData.name}`}

{modData.name}

Edition:  { editionNames[ getGameNameById(modData.game_id) ?? "skyrimspecialedition" ] }
Category:  {modData.category_name} {modData.is_translation &&  (translation)}
Uploaded:{" "} {format(new Date(modData.first_upload_at), "d MMM y")}
Last Update:{" "} {format(new Date(modData.last_update_at), "d MMM y")}
{(!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)}
Total Downloads: {numberFmt.format(total_downloads)}
Unique Downloads:{" "} {numberFmt.format(unique_downloads)}
{fileData && (
)} {pluginData ? ( <>
View plugin
{showAddRemovePluginNotification && ( Plugin {Boolean(fetchedPlugin) ? "added" : "removed"}.{" "} View list . )} ) : (
)} {fileError && (fileError.status === 404 ? (
File cound not be found.
) : (
{`Error loading file data: ${fileError.message}`}
))} {pluginError && (pluginError.status === 404 ? (
Plugin cound not be found.
) : (
{`Error loading plugin data: ${pluginError.message}`}
))} ); } return null; }; export default ModData;