From 811f437238f0b6ab805a0a2afec7da86574f821c Mon Sep 17 00:00:00 2001 From: SpikeHD Date: Sun, 24 Jul 2022 00:24:10 -0700 Subject: [PATCH] potentially gather installed mods --- src-tauri/src/gamebanana.rs | 64 ++++++++++++++++++++++++++++++ src-tauri/src/main.rs | 1 + src/ui/components/mods/ModList.tsx | 38 ++++++++++++++---- src/utils/gamebanana.ts | 19 +++++++++ 4 files changed, 115 insertions(+), 7 deletions(-) diff --git a/src-tauri/src/gamebanana.rs b/src-tauri/src/gamebanana.rs index 78e71e1..f3e224b 100644 --- a/src-tauri/src/gamebanana.rs +++ b/src-tauri/src/gamebanana.rs @@ -1,4 +1,9 @@ +use crate::file_helpers; use crate::web; +use std::collections::HashMap; +use std::fs::read_dir; +use std::io::Read; +use std::path::PathBuf; static API_URL: &str = "https://api.gamebanana.com"; static SITE_URL: &str = "https://gamebanana.com"; @@ -16,3 +21,62 @@ pub async fn list_submissions(mode: String) -> String { res } + +#[tauri::command] +pub async fn list_mods(path: String) -> HashMap { + let mut path_buf = PathBuf::from(path); + + // If the path includes a file, remove it + if path_buf.file_name().is_some() { + path_buf.pop(); + } + + // Ensure we are in the Mods folder + path_buf.push("Mods"); + + // Check if dir is empty + if file_helpers::dir_is_empty(path_buf.to_str().unwrap()) { + return HashMap::new(); + } + + let mut mod_info_files = vec![]; + let mut mod_info_strings = HashMap::new(); + + for entry in read_dir(path_buf).unwrap() { + let entry = entry.unwrap(); + let path = entry.path(); + + // Check each dir for a modinfo.json file + if path.is_dir() { + let mut mod_info_path = path.clone(); + mod_info_path.push("modinfo.json"); + if mod_info_path.exists() { + // Push path AND file contents into the hashmap using path as key + mod_info_files.push(mod_info_path.to_str().unwrap().to_string()); + } else { + // No modinfo, but we can still push a JSON obj with the folder name + mod_info_strings.insert( + path.to_str().unwrap().to_string(), + format!( + "{{ name: \"{}\" }}", + path.file_name().unwrap().to_str().unwrap() + ), + ); + } + } + } + + // Read each modinfo.json file + for mod_info_file in mod_info_files { + let mut mod_info_string = String::new(); + + // It is safe to unwrap here since we *know* that the file exists + let mut file = std::fs::File::open(&mod_info_file).unwrap(); + file.read_to_string(&mut mod_info_string).unwrap(); + + // Push into hashmap using path as key + mod_info_strings.insert(mod_info_file, mod_info_string); + } + + mod_info_strings +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 4ef14da..cf60233 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -61,6 +61,7 @@ fn main() { web::valid_url, web::web_get, gamebanana::list_submissions, + gamebanana::list_mods, metadata_patcher::patch_metadata ]) .run(tauri::generate_context!()) diff --git a/src/ui/components/mods/ModList.tsx b/src/ui/components/mods/ModList.tsx index 73f4b8e..7b404c5 100644 --- a/src/ui/components/mods/ModList.tsx +++ b/src/ui/components/mods/ModList.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { getMods, ModData } from '../../../utils/gamebanana' +import { getInstalledMods, getMods, ModData } from '../../../utils/gamebanana' import { LoadingCircle } from './LoadingCircle' import './ModList.css' @@ -11,7 +11,13 @@ interface IProps { } interface IState { - modList: ModData[] + modList: ModData[] | null + installedList: + | { + path: string + info: unknown + }[] + | null } export class ModList extends React.Component { @@ -20,11 +26,26 @@ export class ModList extends React.Component { console.log('Getting') + this.state = { + modList: null, + installedList: null, + } + this.downloadMod = this.downloadMod.bind(this) } async componentDidMount() { - if (this.props.mode === 'installed') return + if (this.props.mode === 'installed') { + const installedMods = await getInstalledMods() + + console.log(installedMods) + + this.setState({ + installedList: installedMods, + }) + + return + } const mods = await getMods(this.props.mode) @@ -40,11 +61,14 @@ export class ModList extends React.Component { render() { return (
- {this.state && this.state.modList ? ( + {(this.state.modList && this.props.mode !== 'installed') || + (this.state.installedList && this.props.mode === 'installed') ? (
- {this.state.modList.map((mod: ModData) => ( - - ))} + {this.props.mode === 'installed' + ? this.state.installedList?.map((mod) => <>) + : this.state.modList?.map((mod: ModData) => ( + + ))}
) : ( diff --git a/src/utils/gamebanana.ts b/src/utils/gamebanana.ts index 9b35776..4b15c9a 100644 --- a/src/utils/gamebanana.ts +++ b/src/utils/gamebanana.ts @@ -1,4 +1,5 @@ import { invoke } from '@tauri-apps/api' +import { getConfigOption } from './configuration' // Generated with https://transform.tools/json-to-typescript I'm lazy cry about it export interface GamebananaResponse { @@ -115,3 +116,21 @@ export async function formatGamebananaData(obj: GamebananaResponse[]) { }) .filter((itm) => itm.type === 'Mod') } + +export async function getInstalledMods() { + const migotoPath = await getConfigOption('migoto_path') + + if (!migotoPath) return [] + + const mods = (await invoke('list_mods', { + path: migotoPath, + })) as Record + + // These are returned as JSON strings, so we have to parse them + return Object.keys(mods).map((path) => { + return { + path, + info: JSON.parse(mods[path]), + } + }) +}