From 1df421ba963ae0d740c2915b2e038dfb2f6b8f95 Mon Sep 17 00:00:00 2001 From: GeckoEidechse <40122905+GeckoEidechse@users.noreply.github.com> Date: Sun, 8 Jan 2023 17:50:10 +0100 Subject: feat: Delete given Thunderstore mod (#111) * feat: Expose installed NS mod directory This allows other functions to get a mod directory directly which is useful for e.g. deleting a mod. * feat: Add button to delete Northstar mod * refactor: Return vector of NorthstarMod instead of unnamed Tuples * feat: Delete given Thunderstore mod * refactor: replace information button by a dropdown menu with remove item * refactor: only display removal mod option if said mod is installed * feat: only display dropdown menu for installed mods * refactor: Remove leftover print statement * chore: Remove leftover todo comment * feat: Show confirm warning before deleting mod * refactor: Call func directly instead of proxy Removes the `func_caller` pattern * fix: Call reloading mods after attempted delete * feat: Hook up deleting mod backend function Now clicking "Remove Mod" calls the appropriate backend function that removes the corresponding mod. * refactor: Call func directly instead of proxy Removes the `func_caller` pattern * style: Autoformat * feat: Support removing outdated mods No longer include the version number in the comparison check * fix: Rephrase error message * feat: Show pop-up confirmation before deleting mod for Thunderstore mod in ThunderstoreView Co-authored-by: Alystrasz Co-authored-by: Remy Raes --- src-tauri/src/main.rs | 4 +- src-tauri/src/mod_management/mod.rs | 83 ++++++++++++++++++++++++++ src-vue/src/components/ThunderstoreModCard.vue | 83 +++++++++++++++++++++++++- 3 files changed, 167 insertions(+), 3 deletions(-) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index a9483e95..96be489f 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -23,7 +23,8 @@ use repair_and_verify::{ mod mod_management; use mod_management::{ - fc_download_mod_and_install, get_installed_mods_and_properties, set_mod_enabled_status, delete_northstar_mod, + delete_northstar_mod, delete_thunderstore_mod, fc_download_mod_and_install, + get_installed_mods_and_properties, set_mod_enabled_status, }; mod northstar; @@ -104,6 +105,7 @@ fn main() { clean_up_download_folder_caller, get_newest_flightcore_version, delete_northstar_mod, + delete_thunderstore_mod, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src-tauri/src/mod_management/mod.rs b/src-tauri/src/mod_management/mod.rs index f6897b90..05f70dcf 100644 --- a/src-tauri/src/mod_management/mod.rs +++ b/src-tauri/src/mod_management/mod.rs @@ -18,6 +18,31 @@ pub const BLACKLISTED_MODS: [&str; 3] = [ "ebkr-r2modman", ]; +#[derive(Debug, Clone)] +struct ParsedThunderstoreModString { + author_name: String, + mod_name: String, + version: Option, +} + +impl std::str::FromStr for ParsedThunderstoreModString { + type Err = (); + + fn from_str(s: &str) -> Result { + let mut parts = s.split("-"); + + let author_name = parts.next().unwrap().to_string(); + let mod_name = parts.next().unwrap().to_string(); + let version = parts.next().map(|s| s.to_string()); + + Ok(ParsedThunderstoreModString { + author_name, + mod_name, + version, + }) + } +} + /// Gets all currently installed and enabled/disabled mods to rebuild `enabledmods.json` pub fn rebuild_enabled_mods_json(game_install: GameInstall) -> Result<(), String> { let enabledmods_json_path = format!("{}/R2Northstar/enabledmods.json", game_install.game_path); @@ -430,3 +455,61 @@ pub fn delete_northstar_mod(game_install: GameInstall, nsmod_name: String) -> Re Err(format!("Mod {nsmod_name} not found to be installed")) } + +/// Deletes all NorthstarMods related to a Thunderstore mod +#[tauri::command] +pub fn delete_thunderstore_mod( + game_install: GameInstall, + thunderstore_mod_string: String, +) -> Result<(), String> { + // Prevent deleting core mod + for core_ts_mod in BLACKLISTED_MODS { + if thunderstore_mod_string == core_ts_mod { + return Err(format!("Cannot remove core mod {thunderstore_mod_string}")); + } + } + + let parsed_ts_mod_string: ParsedThunderstoreModString = + thunderstore_mod_string.parse().unwrap(); + + // Get installed mods + let installed_ns_mods = get_installed_mods_and_properties(game_install)?; + + // List of mod folders to remove + let mut mod_folders_to_remove: Vec = Vec::new(); + + // Get folder name based on Thundestore mod string + for installed_ns_mod in installed_ns_mods { + if installed_ns_mod.thunderstore_mod_string.is_none() { + // Not a Thunderstore mod + continue; + } + + let installed_ns_mod_ts_string: ParsedThunderstoreModString = installed_ns_mod + .thunderstore_mod_string + .unwrap() + .parse() + .unwrap(); + + // Installed mod matches specified Thunderstore mod string + if parsed_ts_mod_string.author_name == installed_ns_mod_ts_string.author_name + && parsed_ts_mod_string.mod_name == installed_ns_mod_ts_string.mod_name + { + // Add folder to list of folder to remove + mod_folders_to_remove.push(installed_ns_mod.directory); + } + } + + if !(mod_folders_to_remove.len() > 0) { + return Err(format!( + "No mods removed as no Northstar mods matching {thunderstore_mod_string} were found to be installed." + )); + } + + // Delete given folders + for mod_folder in mod_folders_to_remove { + delete_mod_folder(mod_folder)?; + } + + Ok(()) +} diff --git a/src-vue/src/components/ThunderstoreModCard.vue b/src-vue/src/components/ThunderstoreModCard.vue index 1e742fa2..a202aa50 100644 --- a/src-vue/src/components/ThunderstoreModCard.vue +++ b/src-vue/src/components/ThunderstoreModCard.vue @@ -35,11 +35,30 @@ > {{ modButtonText }} - + + + + + + + + + + @@ -54,6 +73,8 @@ import {ThunderstoreModStatus} from "../utils/thunderstore/ThunderstoreModStatus import {NorthstarMod} from "../utils/NorthstarMod"; import {GameInstall} from "../utils/GameInstall"; import {ElNotification} from "element-plus"; +import { NorthstarState } from "../utils/NorthstarState"; +import { ElMessageBox } from "element-plus"; export default defineComponent({ name: "ThunderstoreModCard", @@ -137,6 +158,15 @@ export default defineComponent({ } }, + /** + * Tells if a Thunderstore mod can be removed. + * This is used to tell if we should display the "Remove mod" option. + **/ + modIsRemovable(): boolean { + return [ThunderstoreModStatus.INSTALLED, ThunderstoreModStatus.OUTDATED] + .includes(this.modStatus); + }, + /** * This computes the total count of downloads of a given mod, by adding * download count of each of its releases. @@ -166,6 +196,50 @@ export default defineComponent({ return `${dependencyStringMembers[0]}-${dependencyStringMembers[1]}`; }, + async deleteMod(mod: ThunderstoreMod) { + + // Show pop-up to confirm delete + ElMessageBox.confirm( + 'Delete Thunderstore mod?', + 'Warning', + { + confirmButtonText: 'OK', + cancelButtonText: 'Cancel', + type: 'warning', + } + ) + .then(async () => { // Deletion confirmed + let game_install = { + game_path: this.$store.state.game_path, + install_type: this.$store.state.install_type + } as GameInstall; + + await invoke("delete_thunderstore_mod", { gameInstall: game_install, thunderstoreModString: this.latestVersion.full_name }) + .then((message) => { + ElNotification({ + title: `Removed ${mod.name}`, + message: message as string, + type: 'success', + position: 'bottom-right' + }); + }) + .catch((error) => { + ElNotification({ + title: 'Error', + message: error, + type: 'error', + position: 'bottom-right' + }); + }) + .finally(() => { + this.$store.commit('loadInstalledMods'); + }); + }) + .catch(() => { // Deletion cancelled + console.log("Deleting Thunderstore mod cancelled.") + }) + }, + async installMod (mod: ThunderstoreMod) { let game_install = { game_path: this.$store.state.game_path, @@ -241,8 +315,13 @@ export default defineComponent({ .infoBtn { width: 20px; - padding: 0; + padding: 0 !important; font-size: 20px; border: none; } + +.moreBtn { + margin-left: 10px; + height: auto; +} -- cgit v1.2.3