WIP Render plugin data from static server
This commit is contained in:
parent
6a76bfac33
commit
1fa2e3e660
@ -4,8 +4,8 @@ import Gradient from "javascript-color-gradient";
|
||||
import mapboxgl from "mapbox-gl";
|
||||
import useSWRImmutable from "swr/immutable";
|
||||
|
||||
import { useAppSelector } from "../lib/hooks";
|
||||
import { PluginFile } from "../slices/plugins";
|
||||
import { useAppDispatch, useAppSelector } from "../lib/hooks";
|
||||
import { PluginFile, setFetchedPlugin } from "../slices/plugins";
|
||||
import styles from "../styles/Map.module.css";
|
||||
import Sidebar from "./Sidebar";
|
||||
import ToggleLayersControl from "./ToggleLayersControl";
|
||||
@ -59,9 +59,11 @@ const Map: React.FC = () => {
|
||||
| null
|
||||
>(null);
|
||||
const [sidebarOpen, setSidebarOpen] = useState(true);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const plugins = useAppSelector((state) => state.plugins.plugins);
|
||||
const pluginsPending = useAppSelector((state) => state.plugins.pending);
|
||||
const fetchedPlugin = useAppSelector((state) => state.plugins.fetchedPlugin);
|
||||
|
||||
const { data: cellsData, error: cellsError } = useSWRImmutable(
|
||||
"https://cells.modmapper.com/edits.json",
|
||||
@ -246,6 +248,10 @@ const Map: React.FC = () => {
|
||||
|
||||
const clearSelectedCells = useCallback(() => {
|
||||
setSelectedCells(null);
|
||||
if (router.query.plugin !== fetchedPlugin?.hash.toString(36)) {
|
||||
console.log("clearing fetched plugin");
|
||||
dispatch(setFetchedPlugin(undefined));
|
||||
}
|
||||
if (map.current) {
|
||||
map.current.removeFeatureState({ source: "selected-cell-source" });
|
||||
map.current.removeFeatureState({ source: "conflicted-cell-source" });
|
||||
@ -253,7 +259,7 @@ const Map: React.FC = () => {
|
||||
requestAnimationFrame(() => {
|
||||
if (map.current) map.current.resize();
|
||||
});
|
||||
}, [map]);
|
||||
}, [map, fetchedPlugin, dispatch, router.query.plugin]);
|
||||
|
||||
const clearSelectedMod = useCallback(() => {
|
||||
requestAnimationFrame(() => {
|
||||
@ -305,7 +311,7 @@ const Map: React.FC = () => {
|
||||
if (plugin && plugin.parsed) {
|
||||
const cells = [];
|
||||
const cellSet = new Set<number>();
|
||||
for (const cell of plugin.parsed.cells) {
|
||||
for (const cell of plugin?.parsed?.cells) {
|
||||
if (
|
||||
cell.x !== undefined &&
|
||||
cell.y !== undefined &&
|
||||
@ -361,6 +367,25 @@ const Map: React.FC = () => {
|
||||
pluginsPending,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (router.query.plugin && typeof router.query.plugin === "string" && fetchedPlugin && fetchedPlugin.cells) {
|
||||
console.log("selecting fetchedPlugin cells");
|
||||
const cells = [];
|
||||
const cellSet = new Set<number>();
|
||||
for (const cell of fetchedPlugin.cells) {
|
||||
if (
|
||||
cell.x !== undefined &&
|
||||
cell.y !== undefined &&
|
||||
cellSet.has(cell.x + cell.y * 1000) === false
|
||||
) {
|
||||
cells.push({ x: cell.x, y: cell.y });
|
||||
cellSet.add(cell.x + cell.y * 1000);
|
||||
}
|
||||
}
|
||||
selectCells(cells);
|
||||
}
|
||||
}, [fetchedPlugin, selectCells, router.query.plugin]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!heatmapLoaded) return; // wait for all map layers to load
|
||||
if (!router.query.mod || typeof router.query.mod !== "string") {
|
||||
|
@ -1,60 +1,12 @@
|
||||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import useSWRImmutable from "swr/immutable";
|
||||
|
||||
import { useAppSelector } from "../lib/hooks";
|
||||
import { PluginFile } from "../slices/plugins";
|
||||
import { Mod } from "./ModData";
|
||||
import { Cell } from "./CellData";
|
||||
import { useAppDispatch, useAppSelector } from "../lib/hooks";
|
||||
import { setFetchedPlugin, PluginFile, PluginsByHashWithMods, Cell } from "../slices/plugins";
|
||||
import CellModList from "./CellModList";
|
||||
import PluginData, { Plugin as PluginProps } 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;
|
||||
}
|
||||
|
||||
export interface PluginsByHashWithMods {
|
||||
hash: number;
|
||||
plugins: Plugin[];
|
||||
files: File[];
|
||||
mods: Mod[];
|
||||
cells: Cell[];
|
||||
}
|
||||
|
||||
const NEXUS_MODS_URL = "https://www.nexusmods.com/skyrimspecialedition";
|
||||
|
||||
const jsonFetcher = async (url: string): Promise<PluginsByHashWithMods | null> => {
|
||||
const res = await fetch(url);
|
||||
|
||||
@ -81,7 +33,6 @@ const buildPluginProps = (data?: PluginsByHashWithMods | null, plugin?: PluginFi
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
type Props = {
|
||||
hash: string;
|
||||
counts: Record<number, [number, number, number]> | null;
|
||||
@ -93,9 +44,18 @@ const PluginDetail: React.FC<Props> = ({ hash, counts }) => {
|
||||
jsonFetcher
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const plugins = useAppSelector((state) => state.plugins.plugins);
|
||||
const fetchedPlugin = useAppSelector((state) => state.plugins.fetchedPlugin);
|
||||
const plugin = plugins.find((plugin) => plugin.hash === hash);
|
||||
|
||||
useEffect(() => {
|
||||
if (data && !fetchedPlugin) {
|
||||
console.log("setting fetched plugins from PluginDetail", data);
|
||||
dispatch(setFetchedPlugin(data));
|
||||
}
|
||||
}, [dispatch, data, fetchedPlugin])
|
||||
|
||||
if (!plugin && error && error.status === 404) {
|
||||
return <h3>Plugin could not be found.</h3>;
|
||||
} else if (!plugin && error) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit"
|
||||
|
||||
import type { AppState, AppThunk } from "../lib/store"
|
||||
import { Mod } from "../components/ModData";
|
||||
|
||||
export interface Header {
|
||||
author?: string;
|
||||
@ -41,8 +42,53 @@ export interface PluginFile {
|
||||
size: 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?: Date;
|
||||
has_download_link: boolean;
|
||||
updated_at: Date;
|
||||
created_at: Date;
|
||||
downloaded_at?: Date;
|
||||
has_plugin: boolean;
|
||||
unable_to_extract_plugins: boolean;
|
||||
}
|
||||
|
||||
export interface FetchedPlugin {
|
||||
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;
|
||||
}
|
||||
|
||||
export interface PluginsByHashWithMods {
|
||||
hash: number;
|
||||
plugins: FetchedPlugin[];
|
||||
files: File[];
|
||||
mods: Mod[];
|
||||
cells: Cell[];
|
||||
}
|
||||
|
||||
export type PluginsState = {
|
||||
plugins: PluginFile[];
|
||||
fetchedPlugin?: PluginsByHashWithMods;
|
||||
pending: number;
|
||||
}
|
||||
|
||||
@ -52,17 +98,50 @@ export const pluginsSlice = createSlice({
|
||||
name: "plugins",
|
||||
initialState,
|
||||
reducers: {
|
||||
addPlugin: (state, action: PayloadAction<PluginFile>) => ({ plugins: [...state.plugins, action.payload], pending: state.pending }),
|
||||
updatePlugin: (state, action: PayloadAction<PluginFile>) => ({ plugins: [...state.plugins.filter(plugin => plugin.hash !== action.payload.hash), action.payload], pending: state.pending }),
|
||||
setPlugins: (state, action: PayloadAction<PluginFile[]>) => ({ plugins: action.payload, pending: state.pending }),
|
||||
setPending: (state, action: PayloadAction<number>) => ({ plugins: state.plugins, pending: action.payload }),
|
||||
decrementPending: (state, action: PayloadAction<number>) => ({ plugins: state.plugins, pending: state.pending - action.payload }),
|
||||
togglePlugin: (state, action: PayloadAction<string>) => ({ plugins: state.plugins.map((plugin) => (plugin.filename === action.payload ? { ...plugin, enabled: !plugin.enabled } : plugin)), pending: state.pending }),
|
||||
clearPlugins: () => ({ plugins: [], pending: 0 }),
|
||||
addPlugin: (state, action: PayloadAction<PluginFile>) => ({
|
||||
plugins: [...state.plugins, action.payload],
|
||||
pending: state.pending,
|
||||
fetchedPlugin: state.fetchedPlugin,
|
||||
}),
|
||||
updatePlugin: (state, action: PayloadAction<PluginFile>) => ({
|
||||
plugins: [...state.plugins.filter(plugin => plugin.hash !== action.payload.hash), action.payload],
|
||||
pending: state.pending,
|
||||
fetchedPlugin: state.fetchedPlugin,
|
||||
}),
|
||||
setPlugins: (state, action: PayloadAction<PluginFile[]>) => ({
|
||||
plugins: action.payload,
|
||||
pending: state.pending,
|
||||
fetchedPlugin: state.fetchedPlugin,
|
||||
}),
|
||||
setPending: (state, action: PayloadAction<number>) => ({
|
||||
plugins: state.plugins,
|
||||
pending: action.payload,
|
||||
fetchedPlugin: state.fetchedPlugin,
|
||||
}),
|
||||
decrementPending: (state, action: PayloadAction<number>) => ({
|
||||
plugins: state.plugins,
|
||||
pending: state.pending - action.payload,
|
||||
fetchedPlugin: state.fetchedPlugin,
|
||||
}),
|
||||
togglePlugin: (state, action: PayloadAction<string>) => ({
|
||||
plugins: state.plugins.map((plugin) => (plugin.filename === action.payload ? { ...plugin, enabled: !plugin.enabled } : plugin)),
|
||||
pending: state.pending,
|
||||
fetchedPlugin: state.fetchedPlugin,
|
||||
}),
|
||||
setFetchedPlugin: (state, action: PayloadAction<PluginsByHashWithMods | undefined>) => ({
|
||||
plugins: state.plugins,
|
||||
pending: state.pending,
|
||||
fetchedPlugin: action.payload,
|
||||
}),
|
||||
clearPlugins: () => ({
|
||||
plugins: [],
|
||||
pending: 0,
|
||||
loadedPluginCells: [],
|
||||
}),
|
||||
},
|
||||
})
|
||||
|
||||
export const { addPlugin, setPlugins, setPending, decrementPending, togglePlugin, clearPlugins } = pluginsSlice.actions
|
||||
export const { addPlugin, setPlugins, setPending, decrementPending, togglePlugin, setFetchedPlugin, clearPlugins } = pluginsSlice.actions
|
||||
|
||||
export const selectPlugins = (state: AppState) => state.plugins
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user