diff --git a/theseus/src/api/handler.rs b/theseus/src/api/handler.rs index 5726f11c8..1c70eca0a 100644 --- a/theseus/src/api/handler.rs +++ b/theseus/src/api/handler.rs @@ -14,8 +14,8 @@ use crate::{ /// (Does not include modrinth://) pub async fn handle_url(sublink: &str) -> crate::Result { Ok(match sublink.split_once('/') { - Some(("shared_profile", link)) => { - CommandPayload::OpenSharedProfile { link: link.to_string() } + Some(("shared_profile", link)) => CommandPayload::OpenSharedProfile { + link: link.to_string(), }, // /mod/{id} - Installs a mod of mod id Some(("mod", id)) => CommandPayload::InstallMod { id: id.to_string() }, diff --git a/theseus/src/api/mod.rs b/theseus/src/api/mod.rs index 6b6edeeb4..767cb4ac8 100644 --- a/theseus/src/api/mod.rs +++ b/theseus/src/api/mod.rs @@ -10,8 +10,8 @@ pub mod pack; pub mod process; pub mod profile; pub mod safety; -pub mod shared_profile; pub mod settings; +pub mod shared_profile; pub mod tags; pub mod data { @@ -30,7 +30,7 @@ pub mod prelude { event::CommandPayload, jre, metadata, pack, process, profile::{self, create, Profile}, - settings, + settings, shared_profile, state::JavaGlobals, state::{Dependency, ProfilePathId, ProjectPathId}, util::{ @@ -38,6 +38,5 @@ pub mod prelude { jre::JavaVersion, }, State, - shared_profile, }; } diff --git a/theseus/src/api/pack/install_from.rs b/theseus/src/api/pack/install_from.rs index 7a5de4054..57f89f08a 100644 --- a/theseus/src/api/pack/install_from.rs +++ b/theseus/src/api/pack/install_from.rs @@ -391,29 +391,30 @@ pub async fn set_profile_information( let project_id = description.project_id.clone(); let version_id = description.version_id.clone(); - prof.metadata.linked_data = if project_id.is_some() - && version_id.is_some() - { - Some(LinkedData::ModrinthModpack { - project_id, - version_id, - locked: if !ignore_lock { - Some(true) - } else { - prof.metadata.linked_data.as_ref().and_then(|x| if let LinkedData::ModrinthModpack { - locked: Some(locked), - .. - } = x - { - Some(*locked) + prof.metadata.linked_data = + if project_id.is_some() && version_id.is_some() { + Some(LinkedData::ModrinthModpack { + project_id, + version_id, + locked: if !ignore_lock { + Some(true) } else { - None - }) - }, - }) - } else { - None - }; + prof.metadata.linked_data.as_ref().and_then(|x| { + if let LinkedData::ModrinthModpack { + locked: Some(locked), + .. + } = x + { + Some(*locked) + } else { + None + } + }) + }, + }) + } else { + None + }; prof.metadata.icon = description.icon.clone(); prof.metadata.game_version = game_version.clone(); diff --git a/theseus/src/api/profile/create.rs b/theseus/src/api/profile/create.rs index 3a2fb7a5b..18c0dcfcb 100644 --- a/theseus/src/api/profile/create.rs +++ b/theseus/src/api/profile/create.rs @@ -102,11 +102,14 @@ pub async fn profile_create( } profile.metadata.linked_data = linked_data; - if let Some(LinkedData::ModrinthModpack{ project_id, version_id, locked, .. }) = &mut profile.metadata.linked_data { - *locked = Some( - project_id.is_some() - && version_id.is_some(), - ); + if let Some(LinkedData::ModrinthModpack { + project_id, + version_id, + locked, + .. + }) = &mut profile.metadata.linked_data + { + *locked = Some(project_id.is_some() && version_id.is_some()); } emit_profile( diff --git a/theseus/src/api/profile/mod.rs b/theseus/src/api/profile/mod.rs index ee0785d21..f5c477501 100644 --- a/theseus/src/api/profile/mod.rs +++ b/theseus/src/api/profile/mod.rs @@ -3,12 +3,12 @@ use crate::event::emit::{ emit_loading, init_loading, loading_try_for_each_concurrent, }; -use crate::state::LinkedData; use crate::event::LoadingBarType; use crate::pack::install_from::{ EnvType, PackDependency, PackFile, PackFileHash, PackFormat, }; use crate::prelude::{JavaVersion, ProfilePathId, ProjectPathId}; +use crate::state::LinkedData; use crate::state::{InnerProjectPathUnix, ProjectMetadata, SideType}; use crate::util::fetch; @@ -116,15 +116,14 @@ pub async fn get_mod_full_path( project_path: &ProjectPathId, ) -> crate::Result { if get(profile_path, Some(true)).await?.is_some() { - let full_path = io::canonicalize( - project_path.get_full_path(&profile_path).await?, - )?; + let full_path = + io::canonicalize(project_path.get_full_path(profile_path).await?)?; return Ok(full_path); } Err(crate::ErrorKind::OtherError(format!( "Tried to get the full path of a nonexistent or unloaded project at path {}!", - project_path.get_full_path(&profile_path).await?.display() + project_path.get_full_path(profile_path).await?.display() )) .into()) } @@ -873,13 +872,13 @@ pub async fn try_update_playtime(path: &ProfilePathId) -> crate::Result<()> { let res = if updated_recent_playtime > 0 { // Create update struct to send to Labrinth let modrinth_pack_version_id = - profile.metadata.linked_data.and_then(|l| - if let LinkedData::ModrinthModpack { - version_id, - .. - } = l { - Some(version_id) - } else { None }); + profile.metadata.linked_data.and_then(|l| { + if let LinkedData::ModrinthModpack { version_id, .. } = l { + Some(version_id) + } else { + None + } + }); let playtime_update_json = json!({ "seconds": updated_recent_playtime, "loader": profile.metadata.loader.to_string(), diff --git a/theseus/src/api/profile/update.rs b/theseus/src/api/profile/update.rs index 35f00bbba..4469a1413 100644 --- a/theseus/src/api/profile/update.rs +++ b/theseus/src/api/profile/update.rs @@ -6,7 +6,7 @@ use crate::{ pack::{self, install_from::generate_pack_from_version_id}, prelude::{ProfilePathId, ProjectPathId}, profile::get, - state::{ProfileInstallStage, Project, LinkedData}, + state::{LinkedData, ProfileInstallStage, Project}, LoadingBarType, State, }; use futures::try_join; @@ -30,11 +30,12 @@ pub async fn update_managed_modrinth_version( }; // Extract modrinth pack information, if appropriate - let Some(LinkedData::ModrinthModpack{ + let Some(LinkedData::ModrinthModpack { project_id: Some(ref project_id), version_id: Some(ref version_id), .. - }) = profile.metadata.linked_data else { + }) = profile.metadata.linked_data + else { return Err(unmanaged_err().into()); }; @@ -105,11 +106,12 @@ pub async fn repair_managed_modrinth( .await?; // Extract modrinth pack information, if appropriate - let Some(LinkedData::ModrinthModpack{ + let Some(LinkedData::ModrinthModpack { project_id: Some(ref project_id), version_id: Some(ref version_id), .. - }) = profile.metadata.linked_data else { + }) = profile.metadata.linked_data + else { return Err(unmanaged_err().into()); }; diff --git a/theseus/src/api/settings.rs b/theseus/src/api/settings.rs index 89107ee17..99edf487f 100644 --- a/theseus/src/api/settings.rs +++ b/theseus/src/api/settings.rs @@ -1,6 +1,6 @@ //! Theseus profile management interface -use std::path::{PathBuf, Path}; +use std::path::{Path, PathBuf}; use tokio::fs; use io::IOError; @@ -10,7 +10,7 @@ use crate::{ event::emit::{emit_loading, init_loading}, prelude::DirectoryInfo, state::{self, Profiles}, - util::{io, fetch}, + util::{fetch, io}, }; pub use crate::{ state::{ @@ -109,7 +109,6 @@ pub async fn set_config_dir(new_config_dir: PathBuf) -> crate::Result<()> { let old_config_dir = state_write.directories.config_dir.read().await.clone(); - // Reset file watcher tracing::trace!("Reset file watcher"); let file_watcher = state::init_watcher().await?; @@ -125,13 +124,17 @@ pub async fn set_config_dir(new_config_dir: PathBuf) -> crate::Result<()> { .await .map_err(|e| IOError::with_path(e, &old_config_dir))? { - let entry_path = entry.path(); if let Some(file_name) = entry_path.file_name() { // We are only moving the profiles and metadata folders - if file_name == state::PROFILES_FOLDER_NAME || file_name == state::METADATA_FOLDER_NAME { + if file_name == state::PROFILES_FOLDER_NAME + || file_name == state::METADATA_FOLDER_NAME + { if across_drives { - entries.extend(crate::pack::import::get_all_subfiles(&entry_path).await?); + entries.extend( + crate::pack::import::get_all_subfiles(&entry_path) + .await?, + ); deletable_entries.push(entry_path.clone()); } else { entries.push(entry_path.clone()); @@ -151,8 +154,7 @@ pub async fn set_config_dir(new_config_dir: PathBuf) -> crate::Result<()> { } else { io::rename(entry_path.clone(), new_path.clone()).await?; } - emit_loading(&loading_bar, 80.0 * (1.0 / num_entries), None) - .await?; + emit_loading(&loading_bar, 80.0 * (1.0 / num_entries), None).await?; } tracing::trace!("Setting configuration setting"); @@ -199,7 +201,8 @@ pub async fn set_config_dir(new_config_dir: PathBuf) -> crate::Result<()> { &loading_bar, 10.0 * (1.0 / deletable_entries_len as f64), None, - ).await?; + ) + .await?; } // Reset file watcher @@ -228,7 +231,6 @@ fn is_different_drive(path1: &Path, path2: &Path) -> bool { root1 != root2 } - pub async fn is_dir_writeable(new_config_dir: PathBuf) -> crate::Result { let temp_path = new_config_dir.join(".tmp"); match fs::write(temp_path.clone(), "test").await { diff --git a/theseus/src/api/shared_profile.rs b/theseus/src/api/shared_profile.rs index fe840660c..b88b65bc4 100644 --- a/theseus/src/api/shared_profile.rs +++ b/theseus/src/api/shared_profile.rs @@ -1,9 +1,20 @@ use std::path::PathBuf; -use chrono::{DateTime,Utc}; +use crate::{ + config::MODRINTH_API_URL_INTERNAL, + prelude::{ + LinkedData, ModLoader, ProfilePathId, ProjectMetadata, ProjectPathId, + }, + profile, + state::{Profile, Profiles}, + util::{ + fetch::{fetch_advanced, REQWEST_CLIENT}, + io, + }, +}; +use chrono::{DateTime, Utc}; use reqwest::Method; use serde::{Deserialize, Serialize}; -use crate::{config::MODRINTH_API_URL_INTERNAL, prelude::{LinkedData, ModLoader, ProfilePathId, ProjectMetadata, ProjectPathId}, profile, util::{fetch::{fetch_advanced, REQWEST_CLIENT}, io}, state::{Profile, Profiles}}; #[derive(Deserialize, Serialize, Debug)] pub struct SharedProfile { @@ -23,7 +34,7 @@ pub struct SharedProfile { pub overrides: Vec, pub share_links: Option>, - pub users: Option> + pub users: Option>, } #[derive(Deserialize, Serialize, Debug)] @@ -49,23 +60,33 @@ pub struct SharedProfileOverrideHashes { // Create a new shared profile from ProfilePathId // This converts the LinkedData to a SharedProfile and uploads it to the Labrinth API #[tracing::instrument] -pub async fn create( - profile_id: ProfilePathId, -) -> crate::Result<()> { +pub async fn create(profile_id: ProfilePathId) -> crate::Result<()> { let state = crate::State::get().await?; - let profile : Profile = profile::get(&profile_id, None).await?.ok_or_else(|| crate::ErrorKind::UnmanagedProfileError(profile_id.to_string()))?; + let profile: Profile = + profile::get(&profile_id, None).await?.ok_or_else(|| { + crate::ErrorKind::UnmanagedProfileError(profile_id.to_string()) + })?; let creds = state.credentials.read().await; - let creds = creds.0.as_ref().ok_or_else(|| crate::ErrorKind::NoCredentialsError)?; + let creds = creds + .0 + .as_ref() + .ok_or_else(|| crate::ErrorKind::NoCredentialsError)?; // Currently existing linked data should fail match profile.metadata.linked_data { Some(LinkedData::SharedProfile { .. }) => { - return Err(crate::ErrorKind::OtherError("Profile already linked to a shared profile".to_string()).as_error()); - }, + return Err(crate::ErrorKind::OtherError( + "Profile already linked to a shared profile".to_string(), + ) + .as_error()); + } Some(LinkedData::ModrinthModpack { .. }) => { - return Err(crate::ErrorKind::OtherError("Profile already linked to a modrinth project".to_string()).as_error()); - }, + return Err(crate::ErrorKind::OtherError( + "Profile already linked to a modrinth project".to_string(), + ) + .as_error()); + } None => {} }; @@ -73,40 +94,59 @@ pub async fn create( let loader = profile.metadata.loader; let loader_version = profile.metadata.loader_version; let game_version = profile.metadata.game_version; - - let modrinth_projects : Vec<_> = profile.projects.iter() - .filter_map(|(_, project)|if let ProjectMetadata::Modrinth { ref version, .. } = project.metadata { - Some(&version.id) - } else { - None - }).collect(); - let override_files : Vec<_> = profile.projects.iter() - .filter_map(|(id, project)|if let ProjectMetadata::Inferred { ..} = project.metadata { - Some(id) - } else { - None - }).collect(); - + let modrinth_projects: Vec<_> = profile + .projects + .iter() + .filter_map(|(_, project)| { + if let ProjectMetadata::Modrinth { ref version, .. } = + project.metadata + { + Some(&version.id) + } else { + None + } + }) + .collect(); + + let override_files: Vec<_> = profile + .projects + .iter() + .filter_map(|(id, project)| { + if let ProjectMetadata::Inferred { .. } = project.metadata { + Some(id) + } else { + None + } + }) + .collect(); + // Create the profile on the Labrinth API let response = REQWEST_CLIENT - .post( - format!("{MODRINTH_API_URL_INTERNAL}client/profile"), - ).header("Authorization", &creds.session) - .json(&serde_json::json!({ - "name": name, - "loader": loader.as_api_str(), - "loader_version": loader_version.map(|x| x.id).unwrap_or_default(), - "game": "minecraft-java", - "game_version": game_version, - "versions": modrinth_projects, - })) - .send().await?; + .post(format!("{MODRINTH_API_URL_INTERNAL}client/profile")) + .header("Authorization", &creds.session) + .json(&serde_json::json!({ + "name": name, + "loader": loader.as_api_str(), + "loader_version": loader_version.map(|x| x.id).unwrap_or_default(), + "game": "minecraft-java", + "game_version": game_version, + "versions": modrinth_projects, + })) + .send() + .await?; let profile_response = response.json::().await?; // Extract the profile ID from the response - let shared_profile_id = profile_response["id"].as_str().ok_or_else(|| crate::ErrorKind::OtherError("Could not parse response from Labrinth API".to_string()))?.to_string(); + let shared_profile_id = profile_response["id"] + .as_str() + .ok_or_else(|| { + crate::ErrorKind::OtherError( + "Could not parse response from Labrinth API".to_string(), + ) + })? + .to_string(); // Unmanaged projects let mut data = vec![]; // 'data' field, giving installation context to labrinth @@ -114,26 +154,39 @@ pub async fn create( for override_file in override_files { let path = override_file.get_inner_path_unix(); - let Some(name) = path.0.split('/').last().map(|x| x.to_string()) else { continue }; + let Some(name) = path.0.split('/').last().map(|x| x.to_string()) else { + continue; + }; // Load override to file let full_path = &override_file.get_full_path(&profile_id).await?; let file_bytes = io::read(full_path).await?; - let ext = full_path.extension().and_then(|x| x.to_str()).unwrap_or_default(); - let mime = project_file_type(ext).ok_or_else(|| crate::ErrorKind::OtherError(format!("Could not determine file type for {}", ext)))?; + let ext = full_path + .extension() + .and_then(|x| x.to_str()) + .unwrap_or_default(); + let mime = project_file_type(ext).ok_or_else(|| { + crate::ErrorKind::OtherError(format!( + "Could not determine file type for {}", + ext + )) + })?; data.push(serde_json::json!({ - "file_name": name.clone(), + "file_name": name.clone(), "install_path": path })); - let part = reqwest::multipart::Part::bytes(file_bytes).file_name(name.clone()).mime_str(mime)?; + let part = reqwest::multipart::Part::bytes(file_bytes) + .file_name(name.clone()) + .mime_str(mime)?; parts.push((name.clone(), part)); } // Build multipart with 'data' field first let mut multipart = reqwest::multipart::Form::new().percent_encode_noop(); - let json_part = reqwest::multipart::Part::text(serde_json::to_string(&data)?);//mime_str("application/json")?; + let json_part = + reqwest::multipart::Part::text(serde_json::to_string(&data)?); //mime_str("application/json")?; multipart = multipart.part("data", json_part); for (name, part) in parts { multipart = multipart.part(name, part); @@ -154,7 +207,8 @@ pub async fn create( is_owner: true, }); async { Ok(()) } - }).await?; + }) + .await?; // Sync crate::State::sync().await?; @@ -175,7 +229,10 @@ pub fn project_file_type(ext: &str) -> Option<&str> { pub async fn get_all() -> crate::Result> { let state = crate::State::get().await?; let creds = state.credentials.read().await; - let creds = creds.0.as_ref().ok_or_else(|| crate::ErrorKind::NoCredentialsError)?; + let creds = creds + .0 + .as_ref() + .ok_or_else(|| crate::ErrorKind::NoCredentialsError)?; // First, get list of shared profiles the user has access to #[derive(Deserialize, Serialize, Debug)] @@ -186,24 +243,24 @@ pub async fn get_all() -> crate::Result> { pub created: DateTime, pub updated: DateTime, pub icon_url: Option, - + pub loader: ModLoader, - pub game : String, - + pub game: String, + pub loader_version: String, pub game_version: String, - + // Present only if we are the owner pub share_links: Option>, pub users: Option>, } let response = REQWEST_CLIENT - .get( - format!("{MODRINTH_API_URL_INTERNAL}client/user"), - ) - .header("Authorization", &creds.session) - .send().await?.error_for_status()?; + .get(format!("{MODRINTH_API_URL_INTERNAL}client/user")) + .header("Authorization", &creds.session) + .send() + .await? + .error_for_status()?; let profiles = response.json::>().await?; @@ -223,18 +280,28 @@ pub async fn get_all() -> crate::Result> { let id = profile.id; let response = REQWEST_CLIENT - .get( - format!("{MODRINTH_API_URL_INTERNAL}client/profile/{id}/files"), - ) - .header("Authorization", &creds.session) - .send().await?.error_for_status()?; + .get(format!( + "{MODRINTH_API_URL_INTERNAL}client/profile/{id}/files" + )) + .header("Authorization", &creds.session) + .send() + .await? + .error_for_status()?; let files = response.json::().await?; - + shared_profiles.push(SharedProfile { id, name: profile.name, - is_owned: profile.owner_id == state.credentials.read().await.0.as_ref().map(|x| x.user.id.as_str()).unwrap_or_default(), + is_owned: profile.owner_id + == state + .credentials + .read() + .await + .0 + .as_ref() + .map(|x| x.user.id.as_str()) + .unwrap_or_default(), owner_id: profile.owner_id, loader: profile.loader, loader_version: profile.loader_version, @@ -253,7 +320,9 @@ pub async fn get_all() -> crate::Result> { } #[tracing::instrument] -pub async fn install(shared_profile : SharedProfile) -> crate::Result { +pub async fn install( + shared_profile: SharedProfile, +) -> crate::Result { let state = crate::State::get().await?; let linked_data = LinkedData::SharedProfile { @@ -261,7 +330,7 @@ pub async fn install(shared_profile : SharedProfile) -> crate::Result crate::Result crate::Result crate::Result { - let profile : Profile = profile::get(&profile_id, None).await?.ok_or_else(|| crate::ErrorKind::UnmanagedProfileError(profile_id.to_string()))?; +pub async fn check_updated( + profile_id: &ProfilePathId, + shared_profile: &SharedProfile, +) -> crate::Result { + let profile: Profile = + profile::get(profile_id, None).await?.ok_or_else(|| { + crate::ErrorKind::UnmanagedProfileError(profile_id.to_string()) + })?; // Check if the metadata is the same- if different, we return false with no file updates - if profile.metadata.name != shared_profile.name || - profile.metadata.loader != shared_profile.loader || - profile.metadata.loader_version.map(|x| x.id).unwrap_or_default() != shared_profile.loader_version || - profile.metadata.game_version != shared_profile.game_version { + if profile.metadata.name != shared_profile.name + || profile.metadata.loader != shared_profile.loader + || profile + .metadata + .loader_version + .map(|x| x.id) + .unwrap_or_default() + != shared_profile.loader_version + || profile.metadata.game_version != shared_profile.game_version + { return Ok(SharedModpackFileUpdate::default()); } - + // Check if the projects are the same- we check each override by hash and each modrinth project by version id let mut modrinth_projects = shared_profile.versions.clone(); let mut overrides = shared_profile.overrides.clone(); - let unsynced_projects : Vec<_> = profile.projects.into_iter().filter_map(|(id, project)|{ - match project.metadata { - ProjectMetadata::Modrinth { ref version, .. } => { - if modrinth_projects.contains(&version.id) { - modrinth_projects.retain(|x| x != &version.id); - } - else { - return Some(id); - } - }, - ProjectMetadata::Inferred { .. } => { - let Some(matching_override) = overrides.iter().position(|o| o.install_path.to_string_lossy().to_string() == id.get_inner_path_unix().0) - else { - return Some(id); - }; - - if let Some(o) = overrides.get(matching_override) { - if o.hashes.sha512 != project.sha512 { + let unsynced_projects: Vec<_> = profile + .projects + .into_iter() + .filter_map(|(id, project)| { + match project.metadata { + ProjectMetadata::Modrinth { ref version, .. } => { + if modrinth_projects.contains(&version.id) { + modrinth_projects.retain(|x| x != &version.id); + } else { return Some(id); - } - } else { + } + } + ProjectMetadata::Inferred { .. } => { + let Some(matching_override) = + overrides.iter().position(|o| { + o.install_path.to_string_lossy() + == id.get_inner_path_unix().0 + }) + else { + return Some(id); + }; + + if let Some(o) = overrides.get(matching_override) { + if o.hashes.sha512 != project.sha512 { + return Some(id); + } + } else { + return Some(id); + } + overrides.remove(matching_override); + } + ProjectMetadata::Unknown => { + // TODO: What to do for unknown projects? return Some(id); } - overrides.remove(matching_override); } - ProjectMetadata::Unknown => { - // TODO: What to do for unknown projects? - return Some(id) - } - } - None - }).collect(); + None + }) + .collect(); Ok(SharedModpackFileUpdate { - is_synced: modrinth_projects.is_empty() && overrides.is_empty() && unsynced_projects.is_empty(), + is_synced: modrinth_projects.is_empty() + && overrides.is_empty() + && unsynced_projects.is_empty(), unsynced_projects, missing_versions: modrinth_projects, missing_overrides: overrides, }) - } // Updates projects for a given ProfilePathId from a SharedProfile // This updates the local profile to match the shared profile on the Labrinth API #[tracing::instrument] -pub async fn inbound_sync( - profile_id: ProfilePathId, -) -> crate::Result<()> { +pub async fn inbound_sync(profile_id: ProfilePathId) -> crate::Result<()> { let state = crate::State::get().await?; - let profile : Profile = profile::get(&profile_id, None).await?.ok_or_else(|| crate::ErrorKind::UnmanagedProfileError(profile_id.to_string()))?; + let profile: Profile = + profile::get(&profile_id, None).await?.ok_or_else(|| { + crate::ErrorKind::UnmanagedProfileError(profile_id.to_string()) + })?; let creds = state.credentials.read().await; // Get linked let shared_profile = match profile.metadata.linked_data { Some(LinkedData::SharedProfile { ref profile_id, .. }) => profile_id, - _ => return Err(crate::ErrorKind::OtherError("Profile is not linked to a shared profile".to_string()).as_error()), + _ => { + return Err(crate::ErrorKind::OtherError( + "Profile is not linked to a shared profile".to_string(), + ) + .as_error()) + } }; // Get updated shared profile - let shared_profile = get_all().await?.into_iter().find(|x| &x.id == shared_profile).ok_or_else(|| crate::ErrorKind::OtherError("Profile is not linked to a shared profile".to_string()))?; - + let shared_profile = get_all() + .await? + .into_iter() + .find(|x| &x.id == shared_profile) + .ok_or_else(|| { + crate::ErrorKind::OtherError( + "Profile is not linked to a shared profile".to_string(), + ) + })?; + let update_data = check_updated(&profile_id, &shared_profile).await?; if update_data.is_synced { return Ok(()); @@ -425,8 +532,10 @@ pub async fn inbound_sync( &creds, ) .await?; - - profile.add_project_bytes_directly(&file_override.install_path, file).await?; + + profile + .add_project_bytes_directly(&file_override.install_path, file) + .await?; } Ok(()) @@ -435,27 +544,47 @@ pub async fn inbound_sync( // Updates metadata for a given ProfilePathId to the Labrinth API // Must be an owner of the shared profile #[tracing::instrument] -pub async fn outbound_sync( - profile_id: ProfilePathId, -) -> crate::Result<()> { +pub async fn outbound_sync(profile_id: ProfilePathId) -> crate::Result<()> { let state = crate::State::get().await?; - let profile : Profile = profile::get(&profile_id, None).await?.ok_or_else(|| crate::ErrorKind::UnmanagedProfileError(profile_id.to_string()))?; + let profile: Profile = + profile::get(&profile_id, None).await?.ok_or_else(|| { + crate::ErrorKind::UnmanagedProfileError(profile_id.to_string()) + })?; let creds = state.credentials.read().await; - let creds = creds.0.as_ref().ok_or_else(|| crate::ErrorKind::NoCredentialsError)?; + let creds = creds + .0 + .as_ref() + .ok_or_else(|| crate::ErrorKind::NoCredentialsError)?; // Get linked let shared_profile = match profile.metadata.linked_data { Some(LinkedData::SharedProfile { profile_id, .. }) => profile_id, - _ => return Err(crate::ErrorKind::OtherError("Profile is not linked to a shared profile".to_string()).as_error()), + _ => { + return Err(crate::ErrorKind::OtherError( + "Profile is not linked to a shared profile".to_string(), + ) + .as_error()) + } }; // Get updated shared profile - let shared_profile = get_all().await?.into_iter().find(|x| x.id == shared_profile).ok_or_else(|| crate::ErrorKind::OtherError("Profile is not linked to a shared profile".to_string()))?; + let shared_profile = get_all() + .await? + .into_iter() + .find(|x| x.id == shared_profile) + .ok_or_else(|| { + crate::ErrorKind::OtherError( + "Profile is not linked to a shared profile".to_string(), + ) + })?; // Check owner if !shared_profile.is_owned { - return Err(crate::ErrorKind::OtherError("Profile is not owned by the current user".to_string()).as_error()); + return Err(crate::ErrorKind::OtherError( + "Profile is not owned by the current user".to_string(), + ) + .as_error()); } // Check if we are synced @@ -466,20 +595,35 @@ pub async fn outbound_sync( } let unsynced = update_data.unsynced_projects; - let projects : Vec<_> = profile.projects.clone().into_iter().filter(|(id, _)| unsynced.contains(id)).collect(); - let unsynced_modrinth_projects : Vec<_> = projects.iter() - .filter_map(|(_, project)|if let ProjectMetadata::Modrinth { ref version, .. } = project.metadata { - Some(&version.id) - } else { - None - }).collect(); + let projects: Vec<_> = profile + .projects + .clone() + .into_iter() + .filter(|(id, _)| unsynced.contains(id)) + .collect(); + let unsynced_modrinth_projects: Vec<_> = projects + .iter() + .filter_map(|(_, project)| { + if let ProjectMetadata::Modrinth { ref version, .. } = + project.metadata + { + Some(&version.id) + } else { + None + } + }) + .collect(); - let unsynced_override_files : Vec<_> = projects.iter() - .filter_map(|(id, project)|if let ProjectMetadata::Inferred { ..} = project.metadata { - Some(id) - } else { - None - }).collect(); + let unsynced_override_files: Vec<_> = projects + .iter() + .filter_map(|(id, project)| { + if let ProjectMetadata::Inferred { .. } = project.metadata { + Some(id) + } else { + None + } + }) + .collect(); // Generate new version set let mut new_version_set = shared_profile.versions; @@ -511,35 +655,49 @@ pub async fn outbound_sync( let mut data = vec![]; // 'data' field, giving installation context to labrinth for override_file in unsynced_override_files { let path = override_file.get_inner_path_unix(); - let Some(name) = path.0.split('/').last().map(|x| x.to_string()) else { continue }; + let Some(name) = path.0.split('/').last().map(|x| x.to_string()) else { + continue; + }; // Load override to file let full_path = &override_file.get_full_path(&profile_id).await?; let file_bytes = io::read(full_path).await?; - let ext = full_path.extension().and_then(|x| x.to_str()).unwrap_or_default(); - let mime = project_file_type(ext).ok_or_else(|| crate::ErrorKind::OtherError(format!("Could not determine file type for {}", ext)))?; + let ext = full_path + .extension() + .and_then(|x| x.to_str()) + .unwrap_or_default(); + let mime = project_file_type(ext).ok_or_else(|| { + crate::ErrorKind::OtherError(format!( + "Could not determine file type for {}", + ext + )) + })?; data.push(serde_json::json!({ - "file_name": name.clone(), + "file_name": name.clone(), "install_path": path })); - let part = reqwest::multipart::Part::bytes(file_bytes).file_name(name.clone()).mime_str(mime)?; + let part = reqwest::multipart::Part::bytes(file_bytes) + .file_name(name.clone()) + .mime_str(mime)?; parts.push((name.clone(), part)); } // Build multipart with 'data' field first let mut multipart = reqwest::multipart::Form::new().percent_encode_noop(); - let json_part = reqwest::multipart::Part::text(serde_json::to_string(&data)?);//mime_str("application/json")?; + let json_part = + reqwest::multipart::Part::text(serde_json::to_string(&data)?); //mime_str("application/json")?; multipart = multipart.part("data", json_part); for (name, part) in parts { multipart = multipart.part(name, part); } - let response = REQWEST_CLIENT.post( - format!("{MODRINTH_API_URL_INTERNAL}client/profile/{id}/override"), - ) - .header("Authorization", &creds.session) - .multipart(multipart); + let response = REQWEST_CLIENT + .post(format!( + "{MODRINTH_API_URL_INTERNAL}client/profile/{id}/override" + )) + .header("Authorization", &creds.session) + .multipart(multipart); response.send().await?.error_for_status()?; @@ -555,25 +713,38 @@ pub async fn remove_shared_profile_users( ) -> crate::Result<()> { let state = crate::State::get().await?; - let profile : Profile = profile::get(&profile_id, None).await?.ok_or_else(|| crate::ErrorKind::UnmanagedProfileError(profile_id.to_string()))?; + let profile: Profile = + profile::get(&profile_id, None).await?.ok_or_else(|| { + crate::ErrorKind::UnmanagedProfileError(profile_id.to_string()) + })?; let creds = state.credentials.read().await; - let creds = creds.0.as_ref().ok_or_else(|| crate::ErrorKind::NoCredentialsError)?; + let creds = creds + .0 + .as_ref() + .ok_or_else(|| crate::ErrorKind::NoCredentialsError)?; let shared_profile = match profile.metadata.linked_data { Some(LinkedData::SharedProfile { profile_id, .. }) => profile_id, - _ => return Err(crate::ErrorKind::OtherError("Profile is not linked to a shared profile".to_string()).as_error()), + _ => { + return Err(crate::ErrorKind::OtherError( + "Profile is not linked to a shared profile".to_string(), + ) + .as_error()) + } }; REQWEST_CLIENT - .patch( - format!("{MODRINTH_API_URL_INTERNAL}client/profile/{shared_profile}"), - ) - .header("Authorization", &creds.session) - .json(&serde_json::json!({ - "remove_users": users, - })) - .send().await?.error_for_status()?; - + .patch(format!( + "{MODRINTH_API_URL_INTERNAL}client/profile/{shared_profile}" + )) + .header("Authorization", &creds.session) + .json(&serde_json::json!({ + "remove_users": users, + })) + .send() + .await? + .error_for_status()?; + Ok(()) } @@ -583,24 +754,37 @@ pub async fn remove_shared_profile_links( ) -> crate::Result<()> { let state = crate::State::get().await?; - let profile : Profile = profile::get(&profile_id, None).await?.ok_or_else(|| crate::ErrorKind::UnmanagedProfileError(profile_id.to_string()))?; + let profile: Profile = + profile::get(&profile_id, None).await?.ok_or_else(|| { + crate::ErrorKind::UnmanagedProfileError(profile_id.to_string()) + })?; let creds = state.credentials.read().await; - let creds = creds.0.as_ref().ok_or_else(|| crate::ErrorKind::NoCredentialsError)?; + let creds = creds + .0 + .as_ref() + .ok_or_else(|| crate::ErrorKind::NoCredentialsError)?; let shared_profile = match profile.metadata.linked_data { Some(LinkedData::SharedProfile { profile_id, .. }) => profile_id, - _ => return Err(crate::ErrorKind::OtherError("Profile is not linked to a shared profile".to_string()).as_error()), + _ => { + return Err(crate::ErrorKind::OtherError( + "Profile is not linked to a shared profile".to_string(), + ) + .as_error()) + } }; REQWEST_CLIENT - .patch( - format!("{MODRINTH_API_URL_INTERNAL}client/profile/{shared_profile}"), - ) - .header("Authorization", &creds.session) - .json(&serde_json::json!({ - "remove_links": links, - })) - .send().await?.error_for_status()?; + .patch(format!( + "{MODRINTH_API_URL_INTERNAL}client/profile/{shared_profile}" + )) + .header("Authorization", &creds.session) + .json(&serde_json::json!({ + "remove_links": links, + })) + .send() + .await? + .error_for_status()?; Ok(()) } @@ -610,47 +794,61 @@ pub async fn generate_share_link( ) -> crate::Result { let state = crate::State::get().await?; - let profile : Profile = profile::get(&profile_id, None).await?.ok_or_else(|| crate::ErrorKind::UnmanagedProfileError(profile_id.to_string()))?; + let profile: Profile = + profile::get(&profile_id, None).await?.ok_or_else(|| { + crate::ErrorKind::UnmanagedProfileError(profile_id.to_string()) + })?; let creds = state.credentials.read().await; - let creds = creds.0.as_ref().ok_or_else(|| crate::ErrorKind::NoCredentialsError)?; + let creds = creds + .0 + .as_ref() + .ok_or_else(|| crate::ErrorKind::NoCredentialsError)?; let shared_profile = match profile.metadata.linked_data { Some(LinkedData::SharedProfile { profile_id, .. }) => profile_id, - _ => return Err(crate::ErrorKind::OtherError("Profile is not linked to a shared profile".to_string()).as_error()), + _ => { + return Err(crate::ErrorKind::OtherError( + "Profile is not linked to a shared profile".to_string(), + ) + .as_error()) + } }; let response = REQWEST_CLIENT - .post( - format!("{MODRINTH_API_URL_INTERNAL}client/profile/{shared_profile}/share"), - ) - .header("Authorization", &creds.session) - .send().await?.error_for_status()?; + .post(format!( + "{MODRINTH_API_URL_INTERNAL}client/profile/{shared_profile}/share" + )) + .header("Authorization", &creds.session) + .send() + .await? + .error_for_status()?; let link = response.json::().await?; Ok(generate_deep_link(&link)) } -fn generate_deep_link( - link: &SharedProfileLink -) -> String { +fn generate_deep_link(link: &SharedProfileLink) -> String { format!("modrinth://shared_profile/{}", link.id) } -pub async fn accept_share_link( - link: String, -) -> crate::Result<()> { +pub async fn accept_share_link(link: String) -> crate::Result<()> { let state = crate::State::get().await?; let creds = state.credentials.read().await; - let creds = creds.0.as_ref().ok_or_else(|| crate::ErrorKind::NoCredentialsError)?; + let creds = creds + .0 + .as_ref() + .ok_or_else(|| crate::ErrorKind::NoCredentialsError)?; REQWEST_CLIENT - .post( - format!("{MODRINTH_API_URL_INTERNAL}client/profile/share/{link}/accept"), - ) - .header("Authorization", &creds.session) - .send().await?.error_for_status()?; + .post(format!( + "{MODRINTH_API_URL_INTERNAL}client/profile/share/{link}/accept" + )) + .header("Authorization", &creds.session) + .send() + .await? + .error_for_status()?; Ok(()) -} \ No newline at end of file +} diff --git a/theseus/src/config.rs b/theseus/src/config.rs index 5dab778dd..7aeba0438 100644 --- a/theseus/src/config.rs +++ b/theseus/src/config.rs @@ -1,4 +1,5 @@ //! Configuration structs pub const MODRINTH_API_URL: &str = "https://staging-api.modrinth.com/v2/"; -pub const MODRINTH_API_URL_INTERNAL: &str = "https://staging-api.modrinth.com/_internal/"; +pub const MODRINTH_API_URL_INTERNAL: &str = + "https://staging-api.modrinth.com/_internal/"; diff --git a/theseus/src/state/profiles.rs b/theseus/src/state/profiles.rs index a40ac01d1..1ebd5f1b3 100644 --- a/theseus/src/state/profiles.rs +++ b/theseus/src/state/profiles.rs @@ -606,21 +606,18 @@ impl Profile { relative_path: &Path, bytes: bytes::Bytes, ) -> crate::Result { - let state = State::get().await?; - let file_path = self - .get_profile_full_path() - .await? - .join(relative_path); - let project_path_id = ProjectPathId::new(&relative_path); + let file_path = self.get_profile_full_path().await?.join(relative_path); + let project_path_id = ProjectPathId::new(relative_path); write(&file_path, &bytes, &state.io_semaphore).await?; let file_name = relative_path .file_name() .ok_or_else(|| { - crate::ErrorKind::InputError( - format!("Could not find file name for {:?}", relative_path), - ) + crate::ErrorKind::InputError(format!( + "Could not find file name for {:?}", + relative_path + )) })? .to_string_lossy(); @@ -945,13 +942,15 @@ impl Profiles { { let profiles = state.profiles.read().await; for (profile_path, profile) in profiles.0.iter() { - if let Some(LinkedData::ModrinthModpack { project_id, .. }) = &profile.metadata.linked_data { - if let Some(linked_project) = &project_id { - modrinth_updatables.push(( - profile_path.clone(), - linked_project.clone(), - )); - } + if let Some(LinkedData::ModrinthModpack { + project_id: Some(ref linked_project), + .. + }) = &profile.metadata.linked_data + { + modrinth_updatables.push(( + profile_path.clone(), + linked_project.clone(), + )); } } } @@ -990,7 +989,9 @@ impl Profiles { }); if let Some(recent_version) = recent_version { profile.sync_update_version = - Some(ProfileUpdateData::ModrinthModpack(recent_version.id.clone())); + Some(ProfileUpdateData::ModrinthModpack( + recent_version.id.clone(), + )); } else { profile.sync_update_version = None; } @@ -1022,7 +1023,7 @@ impl Profiles { #[tracing::instrument] #[theseus_macros::debug_pin] - pub async fn update_shared_projects() { + pub async fn update_shared_projects() { let res = async { let state = State::get().await?; // Next, get updates for all shared profiles @@ -1032,20 +1033,33 @@ impl Profiles { { let profiles = state.profiles.read().await; for (profile_path, profile) in profiles.0.iter() { - if let Some(LinkedData::SharedProfile { profile_id, .. }) = &profile.metadata.linked_data { - if let Some(shared_profile) = shared_profiles.iter().find(|x| x.id == *profile_id) { + if let Some(LinkedData::SharedProfile { + profile_id, .. + }) = &profile.metadata.linked_data + { + if let Some(shared_profile) = + shared_profiles.iter().find(|x| x.id == *profile_id) + { // Check for update - let update = shared_profile::check_updated(profile_path, shared_profile).await?; - update_profiles.insert(profile_path.clone(), update); + let update = shared_profile::check_updated( + profile_path, + shared_profile, + ) + .await?; + update_profiles + .insert(profile_path.clone(), update); } } - } + } } { let mut new_profiles = state.profiles.write().await; for (profile_path, update) in update_profiles.iter() { - if let Some(profile) = new_profiles.0.get_mut(&profile_path) { - profile.sync_update_version = Some(ProfileUpdateData::SharedProfile(update.clone())); + if let Some(profile) = new_profiles.0.get_mut(profile_path) + { + profile.sync_update_version = Some( + ProfileUpdateData::SharedProfile(update.clone()), + ); } } } @@ -1054,10 +1068,12 @@ impl Profiles { profiles.sync().await?; for (profile_path, _) in update_profiles.iter() { - let Some(profile) = profiles.0.get(&profile_path) else { continue; }; + let Some(profile) = profiles.0.get(profile_path) else { + continue; + }; emit_profile( profile.uuid, - &profile_path, + profile_path, &profile.metadata.name, ProfilePayloadType::Edited, ) @@ -1065,13 +1081,14 @@ impl Profiles { } } Ok::<(), crate::Error>(()) - }.await; + } + .await; match res { Ok(()) => {} Err(err) => { tracing::warn!("Unable to update modrinth versions: {err}") } - }; + }; } #[tracing::instrument(skip(self, profile))] diff --git a/theseus/src/util/io.rs b/theseus/src/util/io.rs index 03484e987..3a7ac4e53 100644 --- a/theseus/src/util/io.rs +++ b/theseus/src/util/io.rs @@ -1,10 +1,10 @@ // IO error // A wrapper around the tokio IO functions that adds the path to the error message, instead of the uninformative std::io::Error. -use std::{path::Path, io::Write}; +use std::{io::Write, path::Path}; -use tempfile::NamedTempFile; use tauri::async_runtime::spawn_blocking; +use tempfile::NamedTempFile; #[derive(Debug, thiserror::Error)] pub enum IOError { @@ -137,12 +137,13 @@ fn sync_write( data: impl AsRef<[u8]>, path: impl AsRef, ) -> Result<(), std::io::Error> { - let mut tempfile = NamedTempFile::new_in(path.as_ref().parent().ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::Other, - "could not get parent directory for temporary file", - ) - })?)?; + let mut tempfile = + NamedTempFile::new_in(path.as_ref().parent().ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::Other, + "could not get parent directory for temporary file", + ) + })?)?; tempfile.write_all(data.as_ref())?; let tmp_path = tempfile.into_temp_path(); let path = path.as_ref(); diff --git a/theseus_gui/src-tauri/src/api/mod.rs b/theseus_gui/src-tauri/src/api/mod.rs index 9c2f324d0..ea9e6098a 100644 --- a/theseus_gui/src-tauri/src/api/mod.rs +++ b/theseus_gui/src-tauri/src/api/mod.rs @@ -12,10 +12,10 @@ pub mod pack; pub mod process; pub mod profile; pub mod profile_create; +pub mod profile_share; pub mod settings; pub mod tags; pub mod utils; -pub mod profile_share; pub type Result = std::result::Result; diff --git a/theseus_gui/src-tauri/src/api/profile_share.rs b/theseus_gui/src-tauri/src/api/profile_share.rs index f077374cf..3f256c65a 100644 --- a/theseus_gui/src-tauri/src/api/profile_share.rs +++ b/theseus_gui/src-tauri/src/api/profile_share.rs @@ -19,10 +19,8 @@ pub fn init() -> tauri::plugin::TauriPlugin { // invoke('plugin:profile_share|profile_share_get_all',profile) #[tauri::command] -pub async fn profile_share_get_all( -) -> Result> { - let res = shared_profile::get_all() - .await?; +pub async fn profile_share_get_all() -> Result> { + let res = shared_profile::get_all().await?; Ok(res) } @@ -30,57 +28,46 @@ pub async fn profile_share_get_all( pub async fn profile_share_install( profile: SharedProfile, ) -> Result { - let res = shared_profile::install(profile) - .await?; + let res = shared_profile::install(profile).await?; Ok(res) } #[tauri::command] -pub async fn profile_share_create( - path: ProfilePathId -) -> Result<()> { - shared_profile::create(path) - .await?; - Ok(()) -} - -#[tauri::command] -pub async fn profile_share_inbound_sync( - path: ProfilePathId -) -> Result<()> { - shared_profile::inbound_sync(path) - .await?; +pub async fn profile_share_create(path: ProfilePathId) -> Result<()> { + shared_profile::create(path).await?; Ok(()) } #[tauri::command] -pub async fn profile_share_outbound_sync( - path : ProfilePathId -) -> Result<()> { +pub async fn profile_share_inbound_sync(path: ProfilePathId) -> Result<()> { + shared_profile::inbound_sync(path).await?; + Ok(()) +} + +#[tauri::command] +pub async fn profile_share_outbound_sync(path: ProfilePathId) -> Result<()> { shared_profile::outbound_sync(path).await?; Ok(()) } #[tauri::command] pub async fn profile_share_generate_share_link( - path : ProfilePathId + path: ProfilePathId, ) -> Result { let res = shared_profile::generate_share_link(path).await?; Ok(res) } #[tauri::command] -pub async fn profile_share_accept_share_link( - link : String -) -> Result<()> { +pub async fn profile_share_accept_share_link(link: String) -> Result<()> { shared_profile::accept_share_link(link).await?; Ok(()) } #[tauri::command] pub async fn profile_share_remove_users( - path : ProfilePathId, - users: Vec + path: ProfilePathId, + users: Vec, ) -> Result<()> { shared_profile::remove_shared_profile_users(path, users).await?; Ok(()) @@ -88,9 +75,9 @@ pub async fn profile_share_remove_users( #[tauri::command] pub async fn profile_share_remove_links( - path : ProfilePathId, - links : Vec + path: ProfilePathId, + links: Vec, ) -> Result<()> { shared_profile::remove_shared_profile_links(path, links).await?; Ok(()) -} \ No newline at end of file +} diff --git a/theseus_gui/src/components/ui/AcceptSharedProfileModal.vue b/theseus_gui/src/components/ui/AcceptSharedProfileModal.vue index 3dda939db..af570d782 100644 --- a/theseus_gui/src/components/ui/AcceptSharedProfileModal.vue +++ b/theseus_gui/src/components/ui/AcceptSharedProfileModal.vue @@ -14,9 +14,9 @@ defineExpose({ async show(event) { linkId.value = event.id sharedProfile.value = await useFetch( - `https://staging-api.modrinth.com/_internal/share/${encodeURIComponent(event.id)}`, - 'shared profile' - ) + `https://staging-api.modrinth.com/_internal/share/${encodeURIComponent(event.id)}`, + 'shared profile' + ) confirmModal.value.show() }, diff --git a/theseus_gui/src/components/ui/Instance.vue b/theseus_gui/src/components/ui/Instance.vue index fee2581d5..22da88b02 100644 --- a/theseus_gui/src/components/ui/Instance.vue +++ b/theseus_gui/src/components/ui/Instance.vue @@ -82,7 +82,9 @@ const install = async (e) => { packs.length === 0 || !packs .map((value) => value.metadata) - .find((pack) => pack.linked_data?.modrinth_modpack?.project_id === props.instance.project_id) + .find( + (pack) => pack.linked_data?.modrinth_modpack?.project_id === props.instance.project_id + ) ) { modLoading.value = true await pack_install( diff --git a/theseus_gui/src/components/ui/ModInstallModal.vue b/theseus_gui/src/components/ui/ModInstallModal.vue index 8219a0e38..d1864d9f6 100644 --- a/theseus_gui/src/components/ui/ModInstallModal.vue +++ b/theseus_gui/src/components/ui/ModInstallModal.vue @@ -247,9 +247,9 @@ const check_valid = computed(() => {
{ ? 'Installing...' : profile.installedMod ? 'Installed' - : profile.metadata.linked_data?.modrinth_modpack.locked || profile.metadata.linked_data?.shared_profile + : profile.metadata.linked_data?.modrinth_modpack.locked || + profile.metadata.linked_data?.shared_profile ? 'Paired' : 'Install' }} diff --git a/theseus_gui/src/components/ui/ModpackVersionModal.vue b/theseus_gui/src/components/ui/ModpackVersionModal.vue index 0f7797cd6..6f9bfdce0 100644 --- a/theseus_gui/src/components/ui/ModpackVersionModal.vue +++ b/theseus_gui/src/components/ui/ModpackVersionModal.vue @@ -28,7 +28,9 @@ const filteredVersions = computed(() => { }) const modpackVersionModal = ref(null) -const installedVersion = computed(() => props.instance?.metadata?.linked_data?.modrinth_modpack?.version_id) +const installedVersion = computed( + () => props.instance?.metadata?.linked_data?.modrinth_modpack?.version_id +) const installing = computed(() => props.instance.install_stage !== 'installed') const inProgress = ref(false) diff --git a/theseus_gui/src/components/ui/URLConfirmModal.vue b/theseus_gui/src/components/ui/URLConfirmModal.vue index d87b66399..663b404a2 100644 --- a/theseus_gui/src/components/ui/URLConfirmModal.vue +++ b/theseus_gui/src/components/ui/URLConfirmModal.vue @@ -24,7 +24,9 @@ defineExpose({ 'version' ) project.value = await useFetch( - `https://staging-api.modrinth.com/v2/project/${encodeURIComponent(version.value.project_id)}`, + `https://staging-api.modrinth.com/v2/project/${encodeURIComponent( + version.value.project_id + )}`, 'project' ) } else { @@ -33,7 +35,9 @@ defineExpose({ 'project' ) version.value = await useFetch( - `https://staging-api.modrinth.com/v2/version/${encodeURIComponent(project.value.versions[0])}`, + `https://staging-api.modrinth.com/v2/version/${encodeURIComponent( + project.value.versions[0] + )}`, 'version' ) } diff --git a/theseus_gui/src/helpers/shared_profiles.js b/theseus_gui/src/helpers/shared_profiles.js index ef4034400..f16dc7d76 100644 --- a/theseus_gui/src/helpers/shared_profiles.js +++ b/theseus_gui/src/helpers/shared_profiles.js @@ -49,4 +49,4 @@ export async function inbound_sync(path) { // only allowed if profile is owned by user export async function outbound_sync(path) { return await invoke('plugin:profile_share|profile_share_outbound_sync', { path }) -} \ No newline at end of file +} diff --git a/theseus_gui/src/pages/Index.vue b/theseus_gui/src/pages/Index.vue index eb851a437..985450850 100644 --- a/theseus_gui/src/pages/Index.vue +++ b/theseus_gui/src/pages/Index.vue @@ -32,7 +32,9 @@ const getInstances = async () => { let filters = [] for (const instance of recentInstances.value) { if (instance.metadata.linked_data?.modrinth_modpack?.project_id) { - filters.push(`NOT"project_id"="${instance.metadata.linked_data?.modrinth_modpack?.project_id}"`) + filters.push( + `NOT"project_id"="${instance.metadata.linked_data?.modrinth_modpack?.project_id}"` + ) } } filter.value = filters.join(' AND ') diff --git a/theseus_gui/src/pages/instance/Options.vue b/theseus_gui/src/pages/instance/Options.vue index e1f55e511..7e6ddf75b 100644 --- a/theseus_gui/src/pages/instance/Options.vue +++ b/theseus_gui/src/pages/instance/Options.vue @@ -413,7 +413,10 @@ Unpair
-
+
- +
Generated link: {{ shareLink }} @@ -467,16 +469,10 @@
- +
-
+
{{ user }} @@ -484,7 +480,11 @@
-
@@ -494,38 +494,45 @@
your project {{ props.instance.sync_update_version }} - :) + :) - - + +
not yours {{ props.instance.sync_update_version }} - +
@@ -750,16 +757,20 @@ const unlinkModpack = ref(false) const inProgress = ref(false) const installing = computed(() => props.instance.install_stage !== 'installed') -const installedVersion = computed(() => props.instance?.metadata?.linked_data?.modrinth_modpack?.version_id) +const installedVersion = computed( + () => props.instance?.metadata?.linked_data?.modrinth_modpack?.version_id +) const installedVersionData = computed(() => { if (!installedVersion.value) return null return props.versions.find((version) => version.id === installedVersion.value) }) -const sharedProfiles = await get_all(); +const sharedProfiles = await get_all() const installedSharedProfileData = computed(() => { if (!props.instance.metadata.linked_data?.shared_profile) return null - return sharedProfiles.find((profile) => profile.id === props.instance.metadata.linked_data?.shared_profile?.profile_id) + return sharedProfiles.find( + (profile) => profile.id === props.instance.metadata.linked_data?.shared_profile?.profile_id + ) }) watch( @@ -1046,7 +1057,6 @@ async function generateShareLink() { async function removeSharedPackUser(user) { await remove_users(props.instance.path, [user]).catch(handleError) } - diff --git a/theseus_gui/src/pages/project/Index.vue b/theseus_gui/src/pages/project/Index.vue index 8a1e27729..326d727ce 100644 --- a/theseus_gui/src/pages/project/Index.vue +++ b/theseus_gui/src/pages/project/Index.vue @@ -317,7 +317,10 @@ async function fetchProjectData() { useFetch(`https://staging-api.modrinth.com/v2/project/${route.params.id}`, 'project'), useFetch(`https://staging-api.modrinth.com/v2/project/${route.params.id}/version`, 'project'), useFetch(`https://staging-api.modrinth.com/v2/project/${route.params.id}/members`, 'project'), - useFetch(`https://staging-api.modrinth.com/v2/project/${route.params.id}/dependencies`, 'project'), + useFetch( + `https://staging-api.modrinth.com/v2/project/${route.params.id}/dependencies`, + 'project' + ), get_categories().catch(handleError), route.query.i ? getInstance(route.query.i, false).catch(handleError) : Promise.resolve(), ])