working acceptance + download

This commit is contained in:
thesuzerain 2024-01-30 19:14:47 -08:00
parent 719aded698
commit 1018d05e36
7 changed files with 97 additions and 55 deletions

View File

@ -46,6 +46,30 @@ pub struct SharedProfileOverrideHashes {
pub sha512: String, pub sha512: String,
} }
// Simplified version of SharedProfile- this is what is returned from the Labrinth API
// This is not used, except for requests where we are not a member of the shared profile
// (ie: previewing a shared profile from a link, before accepting it)
#[derive(Deserialize, Serialize, Debug)]
pub struct SharedProfileResponse {
pub id: String,
pub name: String,
pub owner_id: String,
pub created: DateTime<Utc>,
pub updated: DateTime<Utc>,
pub icon_url: Option<String>,
pub loader: ModLoader,
pub game : String,
pub loader_version: String,
pub game_version: String,
// Present only if we are the owner
pub share_links: Option<Vec<SharedProfileLink>>,
pub users: Option<Vec<String>>,
}
// Create a new shared profile from ProfilePathId // Create a new shared profile from ProfilePathId
// This converts the LinkedData to a SharedProfile and uploads it to the Labrinth API // This converts the LinkedData to a SharedProfile and uploads it to the Labrinth API
#[tracing::instrument] #[tracing::instrument]
@ -177,27 +201,6 @@ pub async fn get_all() -> crate::Result<Vec<SharedProfile>> {
let creds = state.credentials.read().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)]
pub struct SharedProfileResponse {
pub id: String,
pub name: String,
pub owner_id: String,
pub created: DateTime<Utc>,
pub updated: DateTime<Utc>,
pub icon_url: Option<String>,
pub loader: ModLoader,
pub game : String,
pub loader_version: String,
pub game_version: String,
// Present only if we are the owner
pub share_links: Option<Vec<SharedProfileLink>>,
pub users: Option<Vec<String>>,
}
let response = REQWEST_CLIENT let response = REQWEST_CLIENT
.get( .get(
format!("{MODRINTH_API_URL_INTERNAL}client/user"), format!("{MODRINTH_API_URL_INTERNAL}client/user"),
@ -205,6 +208,7 @@ pub async fn get_all() -> crate::Result<Vec<SharedProfile>> {
.header("Authorization", &creds.session) .header("Authorization", &creds.session)
.send().await?.error_for_status()?; .send().await?.error_for_status()?;
// First, get list of shared profiles the user has access to
let profiles = response.json::<Vec<SharedProfileResponse>>().await?; let profiles = response.json::<Vec<SharedProfileResponse>>().await?;
// Next, get files for each shared profile // Next, get files for each shared profile
@ -253,8 +257,9 @@ pub async fn get_all() -> crate::Result<Vec<SharedProfile>> {
} }
#[tracing::instrument] #[tracing::instrument]
pub async fn install(shared_profile : SharedProfile) -> crate::Result<ProfilePathId> { pub async fn install(shared_profile_id : String) -> crate::Result<ProfilePathId> {
let state = crate::State::get().await?; let state = crate::State::get().await?;
let shared_profile = get_all().await?.into_iter().find(|x| x.id == shared_profile_id).ok_or_else(|| crate::ErrorKind::OtherError("Profile not found".to_string()))?;
let linked_data = LinkedData::SharedProfile { let linked_data = LinkedData::SharedProfile {
profile_id: shared_profile.id, profile_id: shared_profile.id,
@ -647,10 +652,32 @@ pub async fn accept_share_link(
REQWEST_CLIENT REQWEST_CLIENT
.post( .post(
format!("{MODRINTH_API_URL_INTERNAL}client/profile/share/{link}/accept"), format!("{MODRINTH_API_URL_INTERNAL}client/share/{link}/accept"),
) )
.header("Authorization", &creds.session) .header("Authorization", &creds.session)
.send().await?.error_for_status()?; .send().await?.error_for_status()?;
Ok(()) Ok(())
}
// Gets a shared profile from a share link
// This is done without accepting it- so would not include any link information, and is only usable for basic info
pub async fn get_from_link(
link: String
) -> crate::Result<SharedProfileResponse> {
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 response = REQWEST_CLIENT
.get(
format!("{MODRINTH_API_URL_INTERNAL}client/share/{link}"),
)
.header("Authorization", &creds.session)
.send().await?.error_for_status()?;
let profile = response.json::<SharedProfileResponse>().await?;
Ok(profile)
} }

View File

@ -1,5 +1,5 @@
use crate::api::Result; use crate::api::Result;
use theseus::{prelude::*, shared_profile::SharedProfile}; use theseus::{prelude::*, shared_profile::{SharedProfile,SharedProfileResponse}};
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> { pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
tauri::plugin::Builder::new("profile_share") tauri::plugin::Builder::new("profile_share")
@ -12,7 +12,8 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
profile_share_generate_share_link, profile_share_generate_share_link,
profile_share_accept_share_link, profile_share_accept_share_link,
profile_share_remove_users, profile_share_remove_users,
profile_share_remove_links profile_share_remove_links,
profile_share_get_link_id,
]) ])
.build() .build()
} }
@ -28,9 +29,9 @@ pub async fn profile_share_get_all(
#[tauri::command] #[tauri::command]
pub async fn profile_share_install( pub async fn profile_share_install(
profile: SharedProfile, shared_profile_id: String,
) -> Result<ProfilePathId> { ) -> Result<ProfilePathId> {
let res = shared_profile::install(profile) let res = shared_profile::install(shared_profile_id)
.await?; .await?;
Ok(res) Ok(res)
} }
@ -77,6 +78,17 @@ pub async fn profile_share_accept_share_link(
Ok(()) Ok(())
} }
// Gets a shared profile from a share link
// This is done without accepting it- so would not include any link information, and is only usable for basic info
#[tauri::command]
pub async fn profile_share_get_link_id(
link : String
) -> Result<SharedProfileResponse> {
let res = shared_profile::get_from_link(link).await?;
Ok(res)
}
#[tauri::command] #[tauri::command]
pub async fn profile_share_remove_users( pub async fn profile_share_remove_users(
path : ProfilePathId, path : ProfilePathId,

View File

@ -20,7 +20,8 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
get_opening_command, get_opening_command,
await_sync, await_sync,
is_offline, is_offline,
refresh_offline refresh_offline,
test_command,
]) ])
.build() .build()
} }
@ -159,6 +160,11 @@ pub async fn get_opening_command() -> Result<Option<CommandPayload>> {
Ok(None) Ok(None)
} }
#[tauri::command]
pub async fn test_command(command: String) -> Result<()> {
Ok(handle_command(command).await?)
}
// helper function called when redirected by a weblink (ie: modrith://do-something) or when redirected by a .mrpack file (in which case its a filepath) // helper function called when redirected by a weblink (ie: modrith://do-something) or when redirected by a .mrpack file (in which case its a filepath)
// We hijack the deep link library (which also contains functionality for instance-checking) // We hijack the deep link library (which also contains functionality for instance-checking)
pub async fn handle_command(command: String) -> Result<()> { pub async fn handle_command(command: String) -> Result<()> {

View File

@ -1,10 +1,8 @@
<script setup> <script setup>
import { Modal, Button } from 'omorphia' import { Modal, Button } from 'omorphia'
import { ref } from 'vue' import { ref } from 'vue'
import { useFetch } from '@/helpers/fetch.js'
import SearchCard from '@/components/ui/SearchCard.vue'
import { handleError } from '@/store/notifications.js' import { handleError } from '@/store/notifications.js'
import { share_accept, share_install } from '@/helpers/shared_profiles.js' import { share_accept, share_install, share_get_link_id } from '@/helpers/shared_profiles.js'
const confirmModal = ref(null) const confirmModal = ref(null)
const linkId = ref(null) const linkId = ref(null)
@ -12,13 +10,11 @@ const sharedProfile = ref(null)
defineExpose({ defineExpose({
async show(event) { async show(event) {
linkId.value = event.id console.log('showing accept shared profile modal', event)
sharedProfile.value = await useFetch( linkId.value = event.link
`https://staging-api.modrinth.com/_internal/share/${encodeURIComponent(event.id)}`, sharedProfile.value = await share_get_link_id(linkId.value).catch(handleError)
'shared profile'
)
confirmModal.value.show() confirmModal.value.show()
console.log('sharedProfile')
}, },
}) })
@ -30,18 +26,12 @@ async function install() {
</script> </script>
<template> <template>
<Modal ref="confirmModal" :header="`Install ${project?.title}`"> <Modal ref="confirmModal" :header="`Install ${sharedProfile?.name}`">
<div class="modal-body"> <div class="modal-body">
<SearchCard
:project="project"
class="project-card"
:categories="categories"
@open="confirmModal.hide()"
/>
<div class="button-row"> <div class="button-row">
<div class="markdown-body"> <div class="markdown-body">
<p> <p>
Installing <code>{{ sharedProfile.id }}</code> from user {{ sharedProfile.owner_id }} Installing <code>{{ sharedProfile.name }}</code> from user {{ sharedProfile.owner_id }}
</p> </p>
</div> </div>
<div class="button-group"> <div class="button-group">

View File

@ -15,9 +15,15 @@ export async function share_generate(path) {
return await invoke('plugin:profile_share|profile_share_generate_share_link', { path }) return await invoke('plugin:profile_share|profile_share_generate_share_link', { path })
} }
/// Gets the shared profile from the link id
// This is done without accepting it- so would not include any link information, and is only usable for basic info
export async function share_get_link_id(link) {
return await invoke('plugin:profile_share|profile_share_get_link_id', { link })
}
/// Accepts a shared profile link /// Accepts a shared profile link
export async function share_accept(link) { export async function share_accept(link) {
return await invoke('plugin:profile_share|profile_share_accept', { link }) return await invoke('plugin:profile_share|profile_share_accept_share_link', { link })
} }
/// Removes users from a shared profile /// Removes users from a shared profile
@ -31,8 +37,8 @@ export async function remove_links(path, links) {
} }
/// Install a pack from a shared profile id /// Install a pack from a shared profile id
export async function share_install(id) { export async function share_install(sharedProfileId) {
return await invoke('plugin:profile_share|profile_share_install', { id }) return await invoke('plugin:profile_share|profile_share_install', { sharedProfileId })
} }
// get all user profiles that are available to the currentt user // get all user profiles that are available to the currentt user

View File

@ -29,10 +29,15 @@ const raw_invoke = async (plugin, fn, args) => {
return await invoke('plugin:' + plugin + '|' + fn, args) return await invoke('plugin:' + plugin + '|' + fn, args)
} }
} }
const test_command = async (command) => {
return await raw_invoke('utils', 'test_command', {command })
}
isDev() isDev()
.then((dev) => { .then((dev) => {
if (dev) { if (dev) {
window.raw_invoke = raw_invoke window.raw_invoke = raw_invoke
window.test_command = test_command
} }
}) })
.catch((err) => { .catch((err) => {

View File

@ -492,9 +492,6 @@
</div> </div>
</div> </div>
<div v-if="installedSharedProfileData.is_owned" class="adjacent-input"> <div v-if="installedSharedProfileData.is_owned" class="adjacent-input">
your project
{{ props.instance.sync_update_version }}
:)
<label for="share-sync"> <label for="share-sync">
<span class="label__title">Sync shared profile</span> <span class="label__title">Sync shared profile</span>
<span class="label__description" v-if="props.instance.sync_update_version?.is_synced"> <span class="label__description" v-if="props.instance.sync_update_version?.is_synced">
@ -511,9 +508,7 @@
<GlobeIcon /> Revert <GlobeIcon /> Revert
</Button> </Button>
</div> </div>
<div v-else> <div v-else class="adjacent-input">
not yours
{{ props.instance.sync_update_version }}
<label for="share-sync"> <label for="share-sync">
<span class="label__title">Sync shared profile</span> <span class="label__title">Sync shared profile</span>
<span class="label__description" v-if="props.instance.sync_update_version?.is_synced"> <span class="label__description" v-if="props.instance.sync_update_version?.is_synced">
@ -523,10 +518,11 @@
You are not up to date with the shared profile. Click the button to update your instance. You are not up to date with the shared profile. Click the button to update your instance.
</span> </span>
</label> </label>
<Button id="share-sync-sync" @click="inboundSyncSharedProfile"> <Button id="share-sync-sync" @click="inboundSyncSharedProfile" :disabled="props.instance.sync_update_version?.is_synced">
<GlobeIcon /> Sync <GlobeIcon /> Sync
</Button> </Button>
</div> </div>
{{ props.instance.sync_update_version }}
</Card> </Card>
<Card> <Card>
<div class="label"> <div class="label">