WIP plugin details sourced from static server

This commit is contained in:
Tyler Hallada 2022-03-10 23:41:43 -05:00
parent 559b6600ac
commit 74c8920edf
3 changed files with 153 additions and 57 deletions

View File

@ -1,63 +1,65 @@
import Head from "next/head"; import Head from "next/head";
import React from "react"; import React from "react";
import { useAppSelector } from "../lib/hooks";
import styles from "../styles/PluginData.module.css"; import styles from "../styles/PluginData.module.css";
import { Cell } from "./CellData";
export interface CellCoord { export interface Plugin {
x: number; id: number;
y: number; name: string;
hash: bigint;
file_id: number;
mod_id: number;
version: number;
size: number;
author?: string;
description?: string;
masters: string[];
file_name: string;
file_path: string;
updated_at: Date;
created_at: Date;
cells: Omit<Cell, "mods">[];
} }
const NEXUS_MODS_URL = "https://www.nexusmods.com/skyrimspecialedition";
type Props = { type Props = {
hash: string; plugin: Plugin;
counts: Record<number, [number, number, number]> | null; counts: Record<number, [number, number, number]> | null;
}; };
const PluginData: React.FC<Props> = ({ hash, counts }) => { const PluginData: React.FC<Props> = ({ plugin, counts }) => {
const plugins = useAppSelector((state) => state.plugins.plugins);
const plugin = plugins.find((plugin) => plugin.hash === hash);
if (!plugin) { if (!plugin) {
return <h3>Plugin could not be found</h3>; return <h3>Plugin could not be found.</h3>;
} }
return ( return (
<> <>
<Head> <Head>
<title key="title">{`Modmapper - ${plugin.filename}`}</title> <title key="title">{`Modmapper - ${plugin.file_name}`}</title>
<meta <meta
key="description" key="description"
name="description" name="description"
content={`Map of Skyrim showing ${ content={`Map of Skyrim showing ${plugin.cells.length} cell edits from the plugin: ${plugin.file_name}`}
plugin.parsed ? plugin.parsed.cells.length : 0
} cell edits from the plugin: ${plugin.filename}`}
/> />
<meta <meta
key="og:title" key="og:title"
property="og:title" property="og:title"
content={`Modmapper - ${plugin.filename}`} content={`Modmapper - ${plugin.file_name}`}
/> />
<meta <meta
key="og:description" key="og:description"
property="og:description" property="og:description"
content={`Map of Skyrim showing ${ content={`Map of Skyrim showing ${plugin.cells.length} cell edits from the plugin: ${plugin.file_name}`}
plugin.parsed ? plugin.parsed.cells.length : 0
} cell edits from the plugin: ${plugin.filename}`}
/> />
<meta <meta
key="twitter:title" key="twitter:title"
name="twitter:title" name="twitter:title"
content={`Modmapper - ${plugin.filename}`} content={`Modmapper - ${plugin.file_name}`}
/> />
<meta <meta
key="twitter:description" key="twitter:description"
name="twitter:description" name="twitter:description"
content={`Map of Skyrim showing ${ content={`Map of Skyrim showing ${plugin.cells.length} cell edits from the plugin: ${plugin.file_name}`}
plugin.parsed ? plugin.parsed.cells.length : 0
} cell edits from the plugin: ${plugin.filename}`}
/> />
<meta <meta
key="og:url" key="og:url"
@ -65,47 +67,27 @@ const PluginData: React.FC<Props> = ({ hash, counts }) => {
content={`https://modmapper.com/?plugin=${plugin.hash}`} content={`https://modmapper.com/?plugin=${plugin.hash}`}
/> />
</Head> </Head>
<h1 className={styles.name}>{plugin.filename}</h1> <h1 className={styles.name}>{plugin.file_name}</h1>
{plugin.parsed && ( {plugin.author && (
<div>
<strong>Version:&nbsp;</strong>
{plugin.parsed.header.version}
</div>
)}
{plugin.parsed && plugin.parsed.header.author && (
<div> <div>
<strong>Author:&nbsp;</strong> <strong>Author:&nbsp;</strong>
{plugin.parsed.header.author} {plugin.author}
</div> </div>
)} )}
{plugin.parsed && plugin.parsed.header.masters && ( {plugin.masters.length > 0 && (
<div> <div>
<strong>Master plugins:&nbsp;</strong> <strong>Master plugins:&nbsp;</strong>
{plugin.parsed.header.masters.join(", ")} {plugin.masters.join(", ")}
</div> </div>
)} )}
{plugin.parsed && ( <div>
<div> <strong>Cell edits:&nbsp;</strong>
<strong>Cell edits:&nbsp;</strong> {plugin.cells.length}
{plugin.parsed.cells.length} </div>
</div> {plugin.description && (
)}
{plugin.parsed && (
<div>
<strong>World edits:&nbsp;</strong>
{plugin.parsed.worlds.length}
</div>
)}
{plugin.parsed && plugin.parsed.header.description && (
<div> <div>
<h3>Description:</h3> <h3>Description:</h3>
<p>{plugin.parsed.header.description}</p> <p>{plugin.description}</p>
</div>
)}
{plugin.parseError && (
<div>
<h3>Failed to parse plugin:</h3>
<p>{plugin.parseError}</p>
</div> </div>
)} )}
</> </>

114
components/PluginDetail.tsx Normal file
View File

@ -0,0 +1,114 @@
import React from "react";
import useSWRImmutable from "swr/immutable";
import { useAppSelector } from "../lib/hooks";
import { Mod } from "./ModData";
import PluginData from "./PluginData";
import styles from "../styles/PluginData.module.css";
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?: Date;
has_download_link: boolean;
updated_at: Date;
created_at: Date;
downloaded_at?: Date;
has_plugin: boolean;
unable_to_extract_plugins: boolean;
}
export interface Plugin {
id: number;
name: string;
hash: bigint;
file_id: number;
mod_id: number;
version: number;
size: number;
author?: string;
description?: string;
masters: string[];
file_name: string;
file_path: string;
updated_at: Date;
created_at: Date;
file: File;
mod: Omit<Mod, "cells">;
}
const NEXUS_MODS_URL = "https://www.nexusmods.com/skyrimspecialedition";
const jsonFetcher = async (url: string): Promise<Plugin | null> => {
const res = await fetch(url);
if (!res.ok) {
if (res.status === 404) {
return null;
}
const error = new Error("An error occurred while fetching the data.");
throw error;
}
return res.json();
};
type Props = {
hash: string;
counts: Record<number, [number, number, number]> | null;
};
const PluginDetail: React.FC<Props> = ({ hash, counts }) => {
const { data, error } = useSWRImmutable(
`https://plugins.modmapper.com/${hash}.json`,
jsonFetcher
);
const plugins = useAppSelector((state) => state.plugins.plugins);
const plugin = plugins.find((plugin) => plugin.hash === hash);
if (!plugin && error && error.status === 404) {
return <h3>Plugin could not be found.</h3>;
} else if (!plugin && error) {
return <div>{`Error loading plugin data: ${error.message}`}</div>;
}
if (!plugin && data === undefined)
return <div className={styles.status}>Loading...</div>;
if (!plugin && data === null)
return <div className={styles.status}>Plugin could not be found.</div>;
return (
<>
<PluginData
plugin={
// TODO: merge into one common plugin object
data || {
id: plugin!.id,
name: plugin!.name,
hash: plugin!.hash,
file_id: plugin!.file_id,
mod_id: plugin!.mod_id,
version: plugin!.version,
size: plugin!.size,
author: plugin!.author,
description: plugin!.description,
masters: plugin!.masters,
file_name: plugin!.filename,
file_path: plugin!.filepath,
updated_at: plugin!.updated_at,
created_at: plugin!.created_at,
cells: plugin!.cells,
}
}
/>
</>
);
};
export default PluginDetail;

View File

@ -4,7 +4,7 @@ import { formatRelative } from "date-fns";
import CellData from "./CellData"; import CellData from "./CellData";
import ModData from "./ModData"; import ModData from "./ModData";
import PluginData from "./PluginData"; import PluginDetail from "./PluginDetail";
import DataDirPicker from "./DataDirPicker"; import DataDirPicker from "./DataDirPicker";
import PluginTxtEditor from "./PluginTxtEditor"; import PluginTxtEditor from "./PluginTxtEditor";
import PluginsList from "./PluginsList"; import PluginsList from "./PluginsList";
@ -63,7 +63,7 @@ const Sidebar: React.FC<Props> = ({
if (countsError) return renderLoadError(countsError); if (countsError) return renderLoadError(countsError);
if (!counts) return renderLoading(); if (!counts) return renderLoading();
return <PluginData hash={plugin} counts={counts} />; return <PluginDetail hash={plugin} counts={counts} />;
}; };
const renderOpenSidebar = () => { const renderOpenSidebar = () => {