Misc bugs 4 (#381)
* bug fixes * fixed jres being undetected * cleanup * prettier * fixed folders not displaying windows exporting * fixes, more bugs * missed function * clippy, fmt * prettier
This commit is contained in:
parent
744d11f09e
commit
87449f91c3
@ -94,6 +94,19 @@ pub async fn get_by_uuid(
|
||||
Ok(profile)
|
||||
}
|
||||
|
||||
/// Get profile's full path in the filesystem
|
||||
#[tracing::instrument]
|
||||
pub async fn get_full_path(path: &ProfilePathId) -> crate::Result<PathBuf> {
|
||||
let _ = get(path, Some(true)).await?.ok_or_else(|| {
|
||||
crate::ErrorKind::OtherError(format!(
|
||||
"Tried to get the full path of a nonexistent or unloaded profile at path {}!",
|
||||
path
|
||||
))
|
||||
})?;
|
||||
let full_path = io::canonicalize(path.get_full_path().await?)?;
|
||||
Ok(full_path)
|
||||
}
|
||||
|
||||
/// Edit a profile using a given asynchronous closure
|
||||
pub async fn edit<Fut>(
|
||||
path: &ProfilePathId,
|
||||
@ -373,6 +386,7 @@ pub async fn update_project(
|
||||
profile.projects.insert(path.clone(), project);
|
||||
}
|
||||
}
|
||||
drop(profiles);
|
||||
|
||||
if !skip_send_event.unwrap_or(false) {
|
||||
emit_profile(
|
||||
@ -409,7 +423,7 @@ pub async fn add_project_from_version(
|
||||
version_id: String,
|
||||
) -> crate::Result<ProjectPathId> {
|
||||
if let Some(profile) = get(profile_path, None).await? {
|
||||
let (path, _) = profile.add_project_version(version_id).await?;
|
||||
let (project_path, _) = profile.add_project_version(version_id).await?;
|
||||
|
||||
emit_profile(
|
||||
profile.uuid,
|
||||
@ -418,9 +432,7 @@ pub async fn add_project_from_version(
|
||||
ProfilePayloadType::Edited,
|
||||
)
|
||||
.await?;
|
||||
State::sync().await?;
|
||||
|
||||
Ok(path)
|
||||
Ok(project_path)
|
||||
} else {
|
||||
Err(
|
||||
crate::ErrorKind::UnmanagedProfileError(profile_path.to_string())
|
||||
|
@ -143,6 +143,8 @@ impl Children {
|
||||
mc_exit_status = t;
|
||||
break;
|
||||
}
|
||||
// sleep for 10ms
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
|
||||
}
|
||||
|
||||
{
|
||||
@ -204,6 +206,9 @@ impl Children {
|
||||
mc_exit_status = t;
|
||||
break;
|
||||
}
|
||||
// sleep for 10ms
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(10))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,7 +270,7 @@ pub async fn init_watcher() -> crate::Result<Debouncer<RecommendedWatcher>> {
|
||||
let (mut tx, mut rx) = channel(1);
|
||||
|
||||
let file_watcher = new_debouncer(
|
||||
Duration::from_secs(2),
|
||||
Duration::from_secs_f32(0.25),
|
||||
None,
|
||||
move |res: DebounceEventResult| {
|
||||
futures::executor::block_on(async {
|
||||
|
@ -107,7 +107,8 @@ impl ProjectPathId {
|
||||
let profiles_dir: PathBuf = io::canonicalize(
|
||||
State::get().await?.directories.profiles_dir().await,
|
||||
)?;
|
||||
path.strip_prefix(profiles_dir)
|
||||
let path = path
|
||||
.strip_prefix(profiles_dir)
|
||||
.ok()
|
||||
.map(|p| p.components().skip(1).collect::<PathBuf>())
|
||||
.ok_or_else(|| {
|
||||
@ -458,7 +459,6 @@ impl Profile {
|
||||
version_id: String,
|
||||
) -> crate::Result<(ProjectPathId, ModrinthVersion)> {
|
||||
let state = State::get().await?;
|
||||
|
||||
let version = fetch_json::<ModrinthVersion>(
|
||||
Method::GET,
|
||||
&format!("{MODRINTH_API_URL}version/{version_id}"),
|
||||
@ -467,7 +467,6 @@ impl Profile {
|
||||
&state.fetch_semaphore,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let file = if let Some(file) = version.files.iter().find(|x| x.primary)
|
||||
{
|
||||
file
|
||||
@ -486,7 +485,6 @@ impl Profile {
|
||||
&state.fetch_semaphore,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let path = self
|
||||
.add_project_bytes(
|
||||
&file.filename,
|
||||
@ -494,7 +492,6 @@ impl Profile {
|
||||
ProjectType::get_from_loaders(version.loaders.clone()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok((path, version))
|
||||
}
|
||||
|
||||
@ -569,7 +566,6 @@ impl Profile {
|
||||
}
|
||||
|
||||
/// Toggle a project's disabled state.
|
||||
/// 'path' should be relative to the profile's path.
|
||||
#[tracing::instrument(skip(self))]
|
||||
#[theseus_macros::debug_pin]
|
||||
pub async fn toggle_disable_project(
|
||||
@ -662,11 +658,11 @@ impl Profile {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(crate::ErrorKind::InputError(format!(
|
||||
"Project path does not exist: {:?}",
|
||||
// If we are removing a project that doesn't exist, allow it to pass through without error, but warn
|
||||
tracing::warn!(
|
||||
"Attempted to remove non-existent project: {:?}",
|
||||
relative_path
|
||||
))
|
||||
.into());
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -272,6 +272,16 @@ pub async fn infer_data_from_files(
|
||||
|
||||
// TODO: Make this concurrent and use progressive hashing to avoid loading each JAR in memory
|
||||
for path in paths {
|
||||
if !path.exists() {
|
||||
continue;
|
||||
}
|
||||
if let Some(ext) = path.extension() {
|
||||
// Ignore txt configuration files
|
||||
if ext == "txt" {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let mut file = tokio::fs::File::open(path.clone())
|
||||
.await
|
||||
.map_err(|e| IOError::with_path(e, &path))?;
|
||||
@ -460,9 +470,7 @@ pub async fn infer_data_from_files(
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
if let Ok(pack) =
|
||||
serde_json::from_str::<ForgeModInfo>(&file_str)
|
||||
{
|
||||
if let Ok(pack) = toml::from_str::<ForgeModInfo>(&file_str) {
|
||||
if let Some(pack) = pack.mods.first() {
|
||||
let icon = read_icon_from_file(
|
||||
pack.logo_file.clone(),
|
||||
|
@ -2,11 +2,9 @@ use super::io;
|
||||
use futures::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::env;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::{collections::HashSet, path::Path};
|
||||
use tempfile::NamedTempFile;
|
||||
use tokio::task::JoinError;
|
||||
|
||||
use crate::State;
|
||||
@ -280,20 +278,17 @@ pub async fn check_java_at_filepath(path: &Path) -> Option<JavaVersion> {
|
||||
return None;
|
||||
};
|
||||
|
||||
let mut file = NamedTempFile::new().ok()?;
|
||||
file.write_all(include_bytes!("../../library/JavaInfo.class"))
|
||||
.ok()?;
|
||||
let bytes = include_bytes!("../../library/JavaInfo.class");
|
||||
let tempdir: PathBuf = tempfile::tempdir().ok()?.into_path();
|
||||
if !tempdir.exists() {
|
||||
return None;
|
||||
}
|
||||
let file_path = tempdir.join("JavaInfo.class");
|
||||
io::write(&file_path, bytes).await.ok()?;
|
||||
|
||||
let original_path = file.path().to_path_buf();
|
||||
let mut new_path = original_path.clone();
|
||||
new_path.set_file_name("JavaInfo");
|
||||
new_path.set_extension("class");
|
||||
tokio::fs::rename(&original_path, &new_path).await.ok()?;
|
||||
|
||||
// Run java checker on java binary
|
||||
let output = Command::new(&java)
|
||||
.arg("-cp")
|
||||
.arg(file.path().parent().unwrap())
|
||||
.arg(file_path.parent().unwrap())
|
||||
.arg("JavaInfo")
|
||||
.output()
|
||||
.ok()?;
|
||||
|
@ -12,6 +12,7 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
profile_remove,
|
||||
profile_get,
|
||||
profile_get_optimal_jre_key,
|
||||
profile_get_full_path,
|
||||
profile_list,
|
||||
profile_check_installed,
|
||||
profile_install,
|
||||
@ -55,6 +56,14 @@ pub async fn profile_get(
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
// Get a profile's full path
|
||||
// invoke('plugin:profile|profile_get_full_path',path)
|
||||
#[tauri::command]
|
||||
pub async fn profile_get_full_path(path: ProfilePathId) -> Result<PathBuf> {
|
||||
let res = profile::get_full_path(&path).await?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
// Get optimal java version from profile
|
||||
#[tauri::command]
|
||||
pub async fn profile_get_optimal_jre_key(
|
||||
|
@ -59,7 +59,7 @@ pub fn show_in_folder(path: String) -> Result<()> {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
Command::new("explorer")
|
||||
.args(["/select,", &path]) // The comma after select is not a typo
|
||||
.args([&path]) // The comma after select is not a typo
|
||||
.spawn()?;
|
||||
}
|
||||
|
||||
@ -86,7 +86,7 @@ pub fn show_in_folder(path: String) -> Result<()> {
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
Command::new("open").args(["-R", &path]).spawn()?;
|
||||
Command::new("open").args([&path]).spawn()?;
|
||||
}
|
||||
|
||||
Ok::<(), theseus::Error>(())
|
||||
|
@ -26,6 +26,19 @@ fn is_dev() -> bool {
|
||||
cfg!(debug_assertions)
|
||||
}
|
||||
|
||||
// Toggles decorations
|
||||
#[tauri::command]
|
||||
async fn toggle_decorations(b: bool, window: tauri::Window) -> api::Result<()> {
|
||||
window.set_decorations(b).map_err(|e| {
|
||||
theseus::Error::from(theseus::ErrorKind::OtherError(format!(
|
||||
"Failed to toggle decorations: {}",
|
||||
e
|
||||
)))
|
||||
})?;
|
||||
println!("Toggled decorations!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Clone, serde::Serialize)]
|
||||
struct Payload {
|
||||
args: Vec<String>,
|
||||
@ -84,16 +97,12 @@ fn main() {
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
win.set_decorations(true).unwrap();
|
||||
|
||||
use macos::window_ext::WindowExt;
|
||||
win.set_transparent_titlebar(true);
|
||||
win.position_traffic_lights(9.0, 16.0);
|
||||
}
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
{
|
||||
win.set_decorations(false).unwrap();
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
|
||||
macos::delegate::register_open_file(|filename| {
|
||||
tauri::async_runtime::spawn(api::utils::handle_command(
|
||||
filename,
|
||||
@ -102,6 +111,9 @@ fn main() {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Show app now that we are setup
|
||||
win.show().unwrap();
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
@ -129,7 +141,11 @@ fn main() {
|
||||
.plugin(api::settings::init())
|
||||
.plugin(api::tags::init())
|
||||
.plugin(api::utils::init())
|
||||
.invoke_handler(tauri::generate_handler![initialize_state, is_dev]);
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
initialize_state,
|
||||
is_dev,
|
||||
toggle_decorations
|
||||
]);
|
||||
|
||||
builder
|
||||
.run(tauri::generate_context!())
|
||||
|
@ -101,7 +101,9 @@
|
||||
"title": "Modrinth App",
|
||||
"width": 1280,
|
||||
"minHeight": 630,
|
||||
"minWidth": 1100
|
||||
"minWidth": 1100,
|
||||
"visible": false,
|
||||
"decorations": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -308,7 +308,7 @@ const accounts = ref(null)
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
background: var(--color-raised-bg);
|
||||
box-shadow: inset 0px -3px 0px black;
|
||||
box-shadow: var(--shadow-inset-sm), var(--shadow-floating);
|
||||
text-align: center;
|
||||
padding: var(--gap-md);
|
||||
height: 3.25rem;
|
||||
|
@ -27,7 +27,7 @@ import {
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { remove, run } from '@/helpers/profile.js'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { showInFolder } from '@/helpers/utils.js'
|
||||
import { showProfileInFolder } from '@/helpers/utils.js'
|
||||
import { useFetch } from '@/helpers/fetch.js'
|
||||
import { install as pack_install } from '@/helpers/pack.js'
|
||||
import { useTheming } from '@/store/state.js'
|
||||
@ -155,7 +155,7 @@ const handleOptionsClick = async (args) => {
|
||||
deleteConfirmModal.value.show()
|
||||
break
|
||||
case 'open_folder':
|
||||
await showInFolder(args.item.path)
|
||||
await showProfileInFolder(args.item.path)
|
||||
break
|
||||
case 'copy_path':
|
||||
await navigator.clipboard.writeText(args.item.path)
|
||||
|
@ -5,6 +5,7 @@ import { ref } from 'vue'
|
||||
import { export_profile_mrpack, get_potential_override_folders } from '@/helpers/profile.js'
|
||||
import { open } from '@tauri-apps/api/dialog'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { sep } from '@tauri-apps/api/path'
|
||||
|
||||
const props = defineProps({
|
||||
instance: {
|
||||
@ -33,11 +34,11 @@ const initFiles = async () => {
|
||||
filePaths
|
||||
.map((folder) => ({
|
||||
path: folder,
|
||||
name: folder.split('/').pop(),
|
||||
name: folder.split(sep).pop(),
|
||||
selected: false,
|
||||
}))
|
||||
.forEach((pathData) => {
|
||||
const parent = pathData.path.split('/').slice(0, -1).join('/')
|
||||
const parent = pathData.path.split(sep).slice(0, -1).join(sep)
|
||||
if (parent !== '') {
|
||||
if (newFolders.has(parent)) {
|
||||
newFolders.get(parent).push(pathData)
|
||||
|
@ -31,14 +31,13 @@ defineExpose({
|
||||
async function install() {
|
||||
installing.value = true
|
||||
console.log(`Installing ${projectId.value} ${version.value} ${title.value} ${icon.value}`)
|
||||
confirmModal.value.hide()
|
||||
await pack_install(
|
||||
projectId.value,
|
||||
version.value,
|
||||
title.value,
|
||||
icon.value ? icon.value : null
|
||||
).catch(handleError)
|
||||
confirmModal.value.hide()
|
||||
|
||||
mixpanel.track('PackInstall', {
|
||||
id: projectId.value,
|
||||
version_id: version.value,
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
import { process_listener } from '@/helpers/events'
|
||||
import { useFetch } from '@/helpers/fetch.js'
|
||||
import { handleError } from '@/store/state.js'
|
||||
import { showInFolder } from '@/helpers/utils.js'
|
||||
import { showProfileInFolder } from '@/helpers/utils.js'
|
||||
import InstanceInstallModal from '@/components/ui/InstanceInstallModal.vue'
|
||||
import mixpanel from 'mixpanel-browser'
|
||||
|
||||
@ -155,7 +155,7 @@ const stop = async (e, context) => {
|
||||
}
|
||||
|
||||
const openFolder = async () => {
|
||||
await showInFolder(props.instance.path)
|
||||
await showProfileInFolder(props.instance.path)
|
||||
}
|
||||
|
||||
const addContent = async () => {
|
||||
|
@ -37,6 +37,12 @@ export async function get(path, clearProjects) {
|
||||
return await invoke('plugin:profile|profile_get', { path, clearProjects })
|
||||
}
|
||||
|
||||
// Get a profile's full fs path
|
||||
// Returns a path
|
||||
export async function get_full_path(path) {
|
||||
return await invoke('plugin:profile|profile_get_full_path', { path })
|
||||
}
|
||||
|
||||
// Get optimal java version from profile
|
||||
// Returns a java version
|
||||
export async function get_optimal_jre_key(path) {
|
||||
|
@ -1,4 +1,8 @@
|
||||
import { add_project_from_version as installMod, check_installed } from '@/helpers/profile'
|
||||
import {
|
||||
add_project_from_version as installMod,
|
||||
check_installed,
|
||||
get_full_path,
|
||||
} from '@/helpers/profile'
|
||||
import { useFetch } from '@/helpers/fetch.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
@ -11,6 +15,12 @@ export async function showInFolder(path) {
|
||||
return await invoke('plugin:utils|show_in_folder', { path })
|
||||
}
|
||||
|
||||
// Opens a profile's folder in the OS file explorer
|
||||
export async function showProfileInFolder(path) {
|
||||
const fullPath = await get_full_path(path)
|
||||
return await showInFolder(fullPath)
|
||||
}
|
||||
|
||||
export const releaseColor = (releaseType) => {
|
||||
switch (releaseType) {
|
||||
case 'release':
|
||||
|
@ -23,7 +23,11 @@ app.mixin(loadCssMixin)
|
||||
const mountedApp = app.mount('#app')
|
||||
|
||||
const raw_invoke = async (plugin, fn, args) => {
|
||||
return await invoke('plugin:' + plugin + '|' + fn, args)
|
||||
if (plugin == '') {
|
||||
return await invoke(fn, args)
|
||||
} else {
|
||||
return await invoke('plugin:' + plugin + '|' + fn, args)
|
||||
}
|
||||
}
|
||||
isDev()
|
||||
.then((dev) => {
|
||||
|
@ -654,7 +654,7 @@ const showLoaders = computed(
|
||||
</Card>
|
||||
</aside>
|
||||
<div class="search">
|
||||
<Promotion class="promotion" />
|
||||
<Promotion class="promotion" :external="false" />
|
||||
<Card class="project-type-container">
|
||||
<NavRow :links="selectableProjectTypes" />
|
||||
</Card>
|
||||
|
@ -51,7 +51,7 @@
|
||||
<Button
|
||||
v-tooltip="'Open instance folder'"
|
||||
class="instance-button"
|
||||
@click="showInFolder(instance.path)"
|
||||
@click="showProfileInFolder(instance.path)"
|
||||
>
|
||||
<FolderOpenIcon />
|
||||
Folder
|
||||
@ -148,7 +148,7 @@ import { process_listener, profile_listener } from '@/helpers/events'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ref, onUnmounted } from 'vue'
|
||||
import { handleError, useBreadcrumbs, useLoading } from '@/store/state'
|
||||
import { showInFolder } from '@/helpers/utils.js'
|
||||
import { showProfileInFolder } from '@/helpers/utils.js'
|
||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||
import mixpanel from 'mixpanel-browser'
|
||||
import { PackageIcon } from '@/assets/icons/index.js'
|
||||
@ -268,7 +268,7 @@ const handleOptionsClick = async (args) => {
|
||||
})
|
||||
break
|
||||
case 'open_folder':
|
||||
await showInFolder(instance.value.path)
|
||||
await showProfileInFolder(instance.value.path)
|
||||
break
|
||||
case 'copy_path':
|
||||
await navigator.clipboard.writeText(instance.value.path)
|
||||
|
@ -223,7 +223,11 @@
|
||||
:checked="!mod.disabled"
|
||||
@change="toggleDisableMod(mod)"
|
||||
/>
|
||||
<Button v-tooltip="`Show ${mod.file_name}`" icon-only @click="showInFolder(mod.path)">
|
||||
<Button
|
||||
v-tooltip="`Show ${mod.file_name}`"
|
||||
icon-only
|
||||
@click="showProfileInFolder(mod.path)"
|
||||
>
|
||||
<FolderOpenIcon />
|
||||
</Button>
|
||||
</div>
|
||||
@ -345,7 +349,7 @@ import mixpanel from 'mixpanel-browser'
|
||||
import { open } from '@tauri-apps/api/dialog'
|
||||
import { listen } from '@tauri-apps/api/event'
|
||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||
import { showInFolder } from '@/helpers/utils.js'
|
||||
import { showProfileInFolder } from '@/helpers/utils.js'
|
||||
import { MenuIcon, ToggleIcon, TextInputIcon, AddProjectImage } from '@/assets/icons'
|
||||
|
||||
const router = useRouter()
|
||||
@ -605,17 +609,39 @@ const updateProject = async (mod) => {
|
||||
})
|
||||
}
|
||||
|
||||
let locks = {}
|
||||
|
||||
const toggleDisableMod = async (mod) => {
|
||||
mod.path = await toggle_disable_project(props.instance.path, mod.path).catch(handleError)
|
||||
mod.disabled = !mod.disabled
|
||||
mixpanel.track('InstanceProjectDisable', {
|
||||
loader: props.instance.metadata.loader,
|
||||
game_version: props.instance.metadata.game_version,
|
||||
id: mod.id,
|
||||
name: mod.name,
|
||||
project_type: mod.project_type,
|
||||
disabled: mod.disabled,
|
||||
})
|
||||
// Use mod's id as the key for the lock. If mod doesn't have a unique id, replace `mod.id` with some unique property.
|
||||
if (!locks[mod.id]) {
|
||||
locks[mod.id] = ref(null)
|
||||
}
|
||||
|
||||
let lock = locks[mod.id]
|
||||
|
||||
while (lock.value) {
|
||||
await lock.value
|
||||
}
|
||||
|
||||
lock.value = toggle_disable_project(props.instance.path, mod.path)
|
||||
.then((newPath) => {
|
||||
mod.path = newPath
|
||||
mod.disabled = !mod.disabled
|
||||
mixpanel.track('InstanceProjectDisable', {
|
||||
loader: props.instance.metadata.loader,
|
||||
game_version: props.instance.metadata.game_version,
|
||||
id: mod.id,
|
||||
name: mod.name,
|
||||
project_type: mod.project_type,
|
||||
disabled: mod.disabled,
|
||||
})
|
||||
})
|
||||
.catch(handleError)
|
||||
.finally(() => {
|
||||
lock.value = null
|
||||
})
|
||||
|
||||
await lock.value
|
||||
}
|
||||
|
||||
const removeMod = async (mod) => {
|
||||
|
@ -350,15 +350,19 @@ async function install(version) {
|
||||
}
|
||||
|
||||
if (installed.value) {
|
||||
await remove_project(
|
||||
instance.value.path,
|
||||
Object.entries(instance.value.projects)
|
||||
.map(([key, value]) => ({
|
||||
key,
|
||||
value,
|
||||
}))
|
||||
.find((p) => p.value.metadata?.version?.project_id === data.value.id).key
|
||||
)
|
||||
const old_project = Object.entries(instance.value.projects)
|
||||
.map(([key, value]) => ({
|
||||
key,
|
||||
value,
|
||||
}))
|
||||
.find((p) => p.value.metadata?.version?.project_id === data.value.id)
|
||||
if (!old_project) {
|
||||
// Switching too fast, old project is not recognized as a Modrinth project yet
|
||||
installing.value = false
|
||||
return
|
||||
}
|
||||
|
||||
await remove_project(instance.value.path, old_project.key)
|
||||
}
|
||||
|
||||
if (version) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user