first draft
This commit is contained in:
parent
0d3f007dd4
commit
bdde054036
@ -15,6 +15,7 @@ pub mod profile_create;
|
|||||||
pub mod settings;
|
pub mod settings;
|
||||||
pub mod tags;
|
pub mod tags;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
pub mod profile_share;
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, TheseusSerializableError>;
|
pub type Result<T> = std::result::Result<T, TheseusSerializableError>;
|
||||||
|
|
||||||
|
|||||||
76
theseus_gui/src-tauri/src/api/profile_share.rs
Normal file
76
theseus_gui/src-tauri/src/api/profile_share.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
use crate::api::Result;
|
||||||
|
use theseus::{prelude::*, shared_profile::SharedProfile};
|
||||||
|
|
||||||
|
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||||
|
tauri::plugin::Builder::new("profile_share")
|
||||||
|
.invoke_handler(tauri::generate_handler![
|
||||||
|
profile_share_get_all,
|
||||||
|
profile_share_install,
|
||||||
|
profile_share_create,
|
||||||
|
profile_share_inbound_sync,
|
||||||
|
profile_share_outbound_sync,
|
||||||
|
profile_share_generate_share_link,
|
||||||
|
profile_share_accept_share_link
|
||||||
|
])
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
// invoke('plugin:profile_share|profile_share_get_all',profile)
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn profile_share_get_all(
|
||||||
|
) -> Result<Vec<SharedProfile>> {
|
||||||
|
let res = shared_profile::get_all()
|
||||||
|
.await?;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn profile_share_install(
|
||||||
|
profile: SharedProfile,
|
||||||
|
) -> Result<ProfilePathId> {
|
||||||
|
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?;
|
||||||
|
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
|
||||||
|
) -> Result<String> {
|
||||||
|
let res = shared_profile::generate_share_link(path).await?;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn profile_share_accept_share_link(
|
||||||
|
link : String
|
||||||
|
) -> Result<()> {
|
||||||
|
shared_profile::accept_share_link(link).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@ -139,6 +139,7 @@ fn main() {
|
|||||||
.plugin(api::process::init())
|
.plugin(api::process::init())
|
||||||
.plugin(api::profile::init())
|
.plugin(api::profile::init())
|
||||||
.plugin(api::profile_create::init())
|
.plugin(api::profile_create::init())
|
||||||
|
.plugin(api::profile_share::init())
|
||||||
.plugin(api::settings::init())
|
.plugin(api::settings::init())
|
||||||
.plugin(api::tags::init())
|
.plugin(api::tags::init())
|
||||||
.plugin(api::utils::init())
|
.plugin(api::utils::init())
|
||||||
|
|||||||
@ -40,12 +40,14 @@ import { TauriEvent } from '@tauri-apps/api/event'
|
|||||||
import { await_sync, check_safe_loading_bars_complete } from './helpers/state'
|
import { await_sync, check_safe_loading_bars_complete } from './helpers/state'
|
||||||
import { confirm } from '@tauri-apps/api/dialog'
|
import { confirm } from '@tauri-apps/api/dialog'
|
||||||
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
|
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
|
||||||
|
import AcceptSharedProfileModal from '@/components/ui/AcceptSharedProfileModal.vue'
|
||||||
import StickyTitleBar from '@/components/ui/tutorial/StickyTitleBar.vue'
|
import StickyTitleBar from '@/components/ui/tutorial/StickyTitleBar.vue'
|
||||||
import OnboardingScreen from '@/components/ui/tutorial/OnboardingScreen.vue'
|
import OnboardingScreen from '@/components/ui/tutorial/OnboardingScreen.vue'
|
||||||
import { install_from_file } from './helpers/pack'
|
import { install_from_file } from './helpers/pack'
|
||||||
|
|
||||||
const themeStore = useTheming()
|
const themeStore = useTheming()
|
||||||
const urlModal = ref(null)
|
const urlModal = ref(null)
|
||||||
|
const sharedProfileConfirmModal = ref(null)
|
||||||
const isLoading = ref(true)
|
const isLoading = ref(true)
|
||||||
|
|
||||||
const videoPlaying = ref(false)
|
const videoPlaying = ref(false)
|
||||||
@ -237,6 +239,9 @@ command_listener(async (e) => {
|
|||||||
source: 'CreationModalFileDrop',
|
source: 'CreationModalFileDrop',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
} else if (e.event === 'OpenSharedProfile') {
|
||||||
|
// Install a shared profile
|
||||||
|
sharedProfileConfirmModal.value.show(e)
|
||||||
} else {
|
} else {
|
||||||
// Other commands are URL-based (deep linking)
|
// Other commands are URL-based (deep linking)
|
||||||
urlModal.value.show(e)
|
urlModal.value.show(e)
|
||||||
@ -388,6 +393,7 @@ command_listener(async (e) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<URLConfirmModal ref="urlModal" />
|
<URLConfirmModal ref="urlModal" />
|
||||||
|
<AcceptSharedProfileModal ref="sharedProfileConfirmModal" />
|
||||||
<Notifications ref="notificationsWrapper" />
|
<Notifications ref="notificationsWrapper" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@ -169,7 +169,7 @@ const filteredResults = computed(() => {
|
|||||||
})
|
})
|
||||||
} else if (filters.value === 'Downloaded modpacks') {
|
} else if (filters.value === 'Downloaded modpacks') {
|
||||||
instances = instances.filter((instance) => {
|
instances = instances.filter((instance) => {
|
||||||
return instance.metadata?.linked_data
|
return instance.metadata?.linked_data?.modrinth_modpack
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -170,7 +170,7 @@ const handleOptionsClick = async (args) => {
|
|||||||
break
|
break
|
||||||
case 'install': {
|
case 'install': {
|
||||||
const versions = await useFetch(
|
const versions = await useFetch(
|
||||||
`https://api.modrinth.com/v2/project/${args.item.project_id}/version`,
|
`https://staging-api.modrinth.com/v2/project/${args.item.project_id}/version`,
|
||||||
'project versions'
|
'project versions'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
89
theseus_gui/src/components/ui/AcceptSharedProfileModal.vue
Normal file
89
theseus_gui/src/components/ui/AcceptSharedProfileModal.vue
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<script setup>
|
||||||
|
import { Modal, Button } from 'omorphia'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useFetch } from '@/helpers/fetch.js'
|
||||||
|
import SearchCard from '@/components/ui/SearchCard.vue'
|
||||||
|
import { handleError } from '@/store/notifications.js'
|
||||||
|
import { share_accept, share_install } from '@/helpers/shared_profiles.js'
|
||||||
|
|
||||||
|
const confirmModal = ref(null)
|
||||||
|
const linkId = ref(null)
|
||||||
|
const sharedProfile = ref(null)
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
async show(event) {
|
||||||
|
linkId.value = event.id
|
||||||
|
sharedProfile.value = await useFetch(
|
||||||
|
`https://staging-api.modrinth.com/_internal/share/${encodeURIComponent(event.id)}`,
|
||||||
|
'shared profile'
|
||||||
|
)
|
||||||
|
|
||||||
|
confirmModal.value.show()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
async function install() {
|
||||||
|
confirmModal.value.hide()
|
||||||
|
await share_accept(linkId.value).catch(handleError)
|
||||||
|
await share_install(sharedProfile.value.id).catch(handleError)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal ref="confirmModal" :header="`Install ${project?.title}`">
|
||||||
|
<div class="modal-body">
|
||||||
|
<SearchCard
|
||||||
|
:project="project"
|
||||||
|
class="project-card"
|
||||||
|
:categories="categories"
|
||||||
|
@open="confirmModal.hide()"
|
||||||
|
/>
|
||||||
|
<div class="button-row">
|
||||||
|
<div class="markdown-body">
|
||||||
|
<p>
|
||||||
|
Installing <code>{{ sharedProfile.id }}</code> from user {{ sharedProfile.owner_id }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="button-group">
|
||||||
|
<Button color="primary" @click="install">Install</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.modal-body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: var(--gap-md);
|
||||||
|
padding: var(--gap-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-row {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--gap-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: var(--gap-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card {
|
||||||
|
background-color: var(--color-bg);
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
:deep(.badge) {
|
||||||
|
border: 1px solid var(--color-raised-bg);
|
||||||
|
background-color: var(--color-accent-contrast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -71,7 +71,7 @@ const install = async (e) => {
|
|||||||
e?.stopPropagation()
|
e?.stopPropagation()
|
||||||
modLoading.value = true
|
modLoading.value = true
|
||||||
const versions = await useFetch(
|
const versions = await useFetch(
|
||||||
`https://api.modrinth.com/v2/project/${props.instance.project_id}/version`,
|
`https://staging-api.modrinth.com/v2/project/${props.instance.project_id}/version`,
|
||||||
'project versions'
|
'project versions'
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ const install = async (e) => {
|
|||||||
packs.length === 0 ||
|
packs.length === 0 ||
|
||||||
!packs
|
!packs
|
||||||
.map((value) => value.metadata)
|
.map((value) => value.metadata)
|
||||||
.find((pack) => pack.linked_data?.project_id === props.instance.project_id)
|
.find((pack) => pack.linked_data?.modrinth_modpack?.project_id === props.instance.project_id)
|
||||||
) {
|
) {
|
||||||
modLoading.value = true
|
modLoading.value = true
|
||||||
await pack_install(
|
await pack_install(
|
||||||
|
|||||||
@ -247,7 +247,9 @@ const check_valid = computed(() => {
|
|||||||
</Button>
|
</Button>
|
||||||
<div
|
<div
|
||||||
v-tooltip="
|
v-tooltip="
|
||||||
profile.metadata.linked_data?.locked && !profile.installedMod
|
(profile.metadata.linked_data?.modrinth_modpack.locked
|
||||||
|
|| profile.metadata.linked_data?.shared_profile
|
||||||
|
) && !profile.installedMod
|
||||||
? 'Unpair or unlock an instance to add mods.'
|
? 'Unpair or unlock an instance to add mods.'
|
||||||
: ''
|
: ''
|
||||||
"
|
"
|
||||||
@ -265,7 +267,7 @@ const check_valid = computed(() => {
|
|||||||
? 'Installing...'
|
? 'Installing...'
|
||||||
: profile.installedMod
|
: profile.installedMod
|
||||||
? 'Installed'
|
? 'Installed'
|
||||||
: profile.metadata.linked_data && profile.metadata.linked_data.locked
|
: profile.metadata.linked_data?.modrinth_modpack.locked || profile.metadata.linked_data?.shared_profile
|
||||||
? 'Paired'
|
? 'Paired'
|
||||||
: 'Install'
|
: 'Install'
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -28,7 +28,7 @@ const filteredVersions = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const modpackVersionModal = ref(null)
|
const modpackVersionModal = ref(null)
|
||||||
const installedVersion = computed(() => props.instance?.metadata?.linked_data?.version_id)
|
const installedVersion = computed(() => props.instance?.metadata?.linked_data?.modrinth_modpack?.version_id)
|
||||||
const installing = computed(() => props.instance.install_stage !== 'installed')
|
const installing = computed(() => props.instance.install_stage !== 'installed')
|
||||||
const inProgress = ref(false)
|
const inProgress = ref(false)
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ const switchVersion = async (versionId) => {
|
|||||||
:noblur="!themeStore.advancedRendering"
|
:noblur="!themeStore.advancedRendering"
|
||||||
>
|
>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<Card v-if="instance.metadata.linked_data" class="mod-card">
|
<Card v-if="instance.metadata.linked_data?.modrinth_modpack" class="mod-card">
|
||||||
<div class="table">
|
<div class="table">
|
||||||
<div class="table-row with-columns table-head">
|
<div class="table-row with-columns table-head">
|
||||||
<div class="table-cell table-text download-cell" />
|
<div class="table-cell table-text download-cell" />
|
||||||
|
|||||||
@ -73,7 +73,7 @@ const install = async (e) => {
|
|||||||
e?.stopPropagation()
|
e?.stopPropagation()
|
||||||
installing.value = true
|
installing.value = true
|
||||||
const versions = await useFetch(
|
const versions = await useFetch(
|
||||||
`https://api.modrinth.com/v2/project/${props.project.project_id}/version`,
|
`https://staging-api.modrinth.com/v2/project/${props.project.project_id}/version`,
|
||||||
'project versions'
|
'project versions'
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ const install = async (e) => {
|
|||||||
packs.length === 0 ||
|
packs.length === 0 ||
|
||||||
!packs
|
!packs
|
||||||
.map((value) => value.metadata)
|
.map((value) => value.metadata)
|
||||||
.find((pack) => pack.linked_data?.project_id === props.project.project_id)
|
.find((pack) => pack.linked_data?.modrinth_modpack?.project_id === props.project.project_id)
|
||||||
) {
|
) {
|
||||||
installing.value = true
|
installing.value = true
|
||||||
await pack_install(
|
await pack_install(
|
||||||
|
|||||||
@ -135,7 +135,7 @@ const installed = ref(props.installed)
|
|||||||
async function install() {
|
async function install() {
|
||||||
installing.value = true
|
installing.value = true
|
||||||
const versions = await useFetch(
|
const versions = await useFetch(
|
||||||
`https://api.modrinth.com/v2/project/${props.project.project_id}/version`,
|
`https://staging-api.modrinth.com/v2/project/${props.project.project_id}/version`,
|
||||||
'project versions'
|
'project versions'
|
||||||
)
|
)
|
||||||
let queuedVersionData
|
let queuedVersionData
|
||||||
@ -156,7 +156,7 @@ async function install() {
|
|||||||
packs.length === 0 ||
|
packs.length === 0 ||
|
||||||
!packs
|
!packs
|
||||||
.map((value) => value.metadata)
|
.map((value) => value.metadata)
|
||||||
.find((pack) => pack.linked_data?.project_id === props.project.project_id)
|
.find((pack) => pack.linked_data?.modrinth_modpack?.project_id === props.project.project_id)
|
||||||
) {
|
) {
|
||||||
await packInstall(
|
await packInstall(
|
||||||
props.project.project_id,
|
props.project.project_id,
|
||||||
|
|||||||
@ -20,20 +20,20 @@ defineExpose({
|
|||||||
async show(event) {
|
async show(event) {
|
||||||
if (event.event === 'InstallVersion') {
|
if (event.event === 'InstallVersion') {
|
||||||
version.value = await useFetch(
|
version.value = await useFetch(
|
||||||
`https://api.modrinth.com/v2/version/${encodeURIComponent(event.id)}`,
|
`https://staging-api.modrinth.com/v2/version/${encodeURIComponent(event.id)}`,
|
||||||
'version'
|
'version'
|
||||||
)
|
)
|
||||||
project.value = await useFetch(
|
project.value = await useFetch(
|
||||||
`https://api.modrinth.com/v2/project/${encodeURIComponent(version.value.project_id)}`,
|
`https://staging-api.modrinth.com/v2/project/${encodeURIComponent(version.value.project_id)}`,
|
||||||
'project'
|
'project'
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
project.value = await useFetch(
|
project.value = await useFetch(
|
||||||
`https://api.modrinth.com/v2/project/${encodeURIComponent(event.id)}`,
|
`https://staging-api.modrinth.com/v2/project/${encodeURIComponent(event.id)}`,
|
||||||
'project'
|
'project'
|
||||||
)
|
)
|
||||||
version.value = await useFetch(
|
version.value = await useFetch(
|
||||||
`https://api.modrinth.com/v2/version/${encodeURIComponent(project.value.versions[0])}`,
|
`https://staging-api.modrinth.com/v2/version/${encodeURIComponent(project.value.versions[0])}`,
|
||||||
'version'
|
'version'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
42
theseus_gui/src/helpers/shared_profiles.js
Normal file
42
theseus_gui/src/helpers/shared_profiles.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* All theseus API calls return serialized values (both return values and errors);
|
||||||
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
|
* and deserialized into a usable JS object.
|
||||||
|
*/
|
||||||
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
|
/// Created shared modpack from profile
|
||||||
|
export async function share_create(path) {
|
||||||
|
return await invoke('plugin:profile_share|profile_share_create', { path })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates a shared profile link
|
||||||
|
export async function share_generate(path) {
|
||||||
|
return await invoke('plugin:profile_share|profile_share_generate_share_link', { path })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Accepts a shared profile link
|
||||||
|
export async function share_accept(link) {
|
||||||
|
return await invoke('plugin:profile_share|profile_share_accept', { link })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Install a pack from a shared profile id
|
||||||
|
export async function share_install(id) {
|
||||||
|
return await invoke('plugin:profile_share|profile_share_install', { id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// get all user profiles that are available to the currentt user
|
||||||
|
export async function get_all(path) {
|
||||||
|
return await invoke('plugin:profile_share|profile_share_get_all', { path })
|
||||||
|
}
|
||||||
|
|
||||||
|
// syncs profile to match that on server
|
||||||
|
export async function inbound_sync(path) {
|
||||||
|
return await invoke('plugin:profile_share|profile_share_inbound_sync', { path })
|
||||||
|
}
|
||||||
|
|
||||||
|
// syncs profile to update server
|
||||||
|
// 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 })
|
||||||
|
}
|
||||||
@ -68,7 +68,7 @@ export const installVersionDependencies = async (profile, version) => {
|
|||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
const depVersions = await useFetch(
|
const depVersions = await useFetch(
|
||||||
`https://api.modrinth.com/v2/project/${dep.project_id}/version`,
|
`https://staging-api.modrinth.com/v2/project/${dep.project_id}/version`,
|
||||||
'dependency versions'
|
'dependency versions'
|
||||||
)
|
)
|
||||||
const latest = depVersions.find(
|
const latest = depVersions.find(
|
||||||
|
|||||||
@ -149,7 +149,7 @@ if (route.query.ai) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function refreshSearch() {
|
async function refreshSearch() {
|
||||||
const base = 'https://api.modrinth.com/v2/'
|
const base = 'https://staging-api.modrinth.com/v2/'
|
||||||
|
|
||||||
const params = [`limit=${maxResults.value}`, `index=${sortType.value.name}`]
|
const params = [`limit=${maxResults.value}`, `index=${sortType.value.name}`]
|
||||||
if (query.value.length > 0) {
|
if (query.value.length > 0) {
|
||||||
|
|||||||
@ -31,8 +31,8 @@ const getInstances = async () => {
|
|||||||
|
|
||||||
let filters = []
|
let filters = []
|
||||||
for (const instance of recentInstances.value) {
|
for (const instance of recentInstances.value) {
|
||||||
if (instance.metadata.linked_data && instance.metadata.linked_data.project_id) {
|
if (instance.metadata.linked_data?.modrinth_modpack?.project_id) {
|
||||||
filters.push(`NOT"project_id"="${instance.metadata.linked_data.project_id}"`)
|
filters.push(`NOT"project_id"="${instance.metadata.linked_data?.modrinth_modpack?.project_id}"`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filter.value = filters.join(' AND ')
|
filter.value = filters.join(' AND ')
|
||||||
@ -40,7 +40,7 @@ const getInstances = async () => {
|
|||||||
|
|
||||||
const getFeaturedModpacks = async () => {
|
const getFeaturedModpacks = async () => {
|
||||||
const response = await useFetch(
|
const response = await useFetch(
|
||||||
`https://api.modrinth.com/v2/search?facets=[["project_type:modpack"]]&limit=10&index=follows&filters=${filter.value}`,
|
`https://staging-api.modrinth.com/v2/search?facets=[["project_type:modpack"]]&limit=10&index=follows&filters=${filter.value}`,
|
||||||
'featured modpacks',
|
'featured modpacks',
|
||||||
offline.value
|
offline.value
|
||||||
)
|
)
|
||||||
@ -52,7 +52,7 @@ const getFeaturedModpacks = async () => {
|
|||||||
}
|
}
|
||||||
const getFeaturedMods = async () => {
|
const getFeaturedMods = async () => {
|
||||||
const response = await useFetch(
|
const response = await useFetch(
|
||||||
'https://api.modrinth.com/v2/search?facets=[["project_type:mod"]]&limit=10&index=follows',
|
'https://staging-api.modrinth.com/v2/search?facets=[["project_type:mod"]]&limit=10&index=follows',
|
||||||
'featured mods',
|
'featured mods',
|
||||||
offline.value
|
offline.value
|
||||||
)
|
)
|
||||||
|
|||||||
@ -209,9 +209,9 @@ const checkProcess = async () => {
|
|||||||
|
|
||||||
// Get information on associated modrinth versions, if any
|
// Get information on associated modrinth versions, if any
|
||||||
const modrinthVersions = ref([])
|
const modrinthVersions = ref([])
|
||||||
if (!(await isOffline()) && instance.value.metadata.linked_data?.project_id) {
|
if (!(await isOffline()) && instance.value.metadata.linked_data?.modrinth_modpack?.project_id) {
|
||||||
modrinthVersions.value = await useFetch(
|
modrinthVersions.value = await useFetch(
|
||||||
`https://api.modrinth.com/v2/project/${instance.value.metadata.linked_data.project_id}/version`,
|
`https://staging-api.modrinth.com/v2/project/${instance.value.metadata.linked_data?.modrinth_modpack?.project_id}/version`,
|
||||||
'project'
|
'project'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -359,7 +359,7 @@
|
|||||||
/>
|
/>
|
||||||
<ExportModal v-if="projects.length > 0" ref="exportModal" :instance="instance" />
|
<ExportModal v-if="projects.length > 0" ref="exportModal" :instance="instance" />
|
||||||
<ModpackVersionModal
|
<ModpackVersionModal
|
||||||
v-if="instance.metadata.linked_data"
|
v-if="instance.metadata.linked_data?.modrinth_modpack"
|
||||||
ref="modpackVersionModal"
|
ref="modpackVersionModal"
|
||||||
:instance="instance"
|
:instance="instance"
|
||||||
:versions="props.versions"
|
:versions="props.versions"
|
||||||
@ -443,11 +443,18 @@ const projects = ref([])
|
|||||||
const selectionMap = ref(new Map())
|
const selectionMap = ref(new Map())
|
||||||
const showingOptions = ref(false)
|
const showingOptions = ref(false)
|
||||||
const isPackLocked = computed(() => {
|
const isPackLocked = computed(() => {
|
||||||
return props.instance.metadata.linked_data && props.instance.metadata.linked_data.locked
|
if (props.instance.metadata.linked_data?.shared_profile) {
|
||||||
|
return !props.instance.metadata.linked_data.shared_profile.is_owner
|
||||||
|
}
|
||||||
|
return props.instance.metadata.linked_data?.modrinth_modpack?.locked
|
||||||
})
|
})
|
||||||
const canUpdatePack = computed(() => {
|
const canUpdatePack = computed(() => {
|
||||||
if (!props.instance.metadata.linked_data) return false
|
if (!props.instance.metadata.linked_data) return false
|
||||||
return props.instance.metadata.linked_data.version_id !== props.instance.modrinth_update_version
|
let linked_data = props.instance.metadata.linked_data
|
||||||
|
if (linked_data.modrinth_modpack) {
|
||||||
|
return linked_data.modrinth_modpack.version_id !== props.instance.sync_update_version
|
||||||
|
}
|
||||||
|
return false
|
||||||
})
|
})
|
||||||
const exportModal = ref(null)
|
const exportModal = ref(null)
|
||||||
|
|
||||||
|
|||||||
@ -358,7 +358,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<Card v-if="instance.metadata.linked_data">
|
<Card v-if="instance.metadata.linked_data?.modrinth_modpack">
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<h3>
|
<h3>
|
||||||
<span class="label__title size-card-header">Modpack</span>
|
<span class="label__title size-card-header">Modpack</span>
|
||||||
@ -413,8 +413,7 @@
|
|||||||
<XIcon /> Unpair
|
<XIcon /> Unpair
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="props.instance.metadata.linked_data?.modrinth_modpack?.project_id" class="adjacent-input">
|
||||||
<div v-if="props.instance.metadata.linked_data.project_id" class="adjacent-input">
|
|
||||||
<label for="change-modpack-version">
|
<label for="change-modpack-version">
|
||||||
<span class="label__title">Change modpack version</span>
|
<span class="label__title">Change modpack version</span>
|
||||||
<span class="label__description">
|
<span class="label__description">
|
||||||
@ -445,6 +444,90 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
<Card v-if="installedSharedProfileData">
|
||||||
|
<div class="label">
|
||||||
|
<h3>
|
||||||
|
<span class="label__title size-card-header">Shared profile management</span>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div v-if="installedSharedProfileData.is_owned" class="adjacent-input">
|
||||||
|
<label for="share-links">
|
||||||
|
<span class="label__title">Generate share link</span>
|
||||||
|
<span class="label__description">
|
||||||
|
Creates a share link to share this modpack with others. This allows them to install your instance, as well as stay up to date with any changes you make.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<Button id="share-links" @click="generateShareLink">
|
||||||
|
<GlobeIcon /> Share
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div v-if="shareLink" class="adjacent-input">
|
||||||
|
Generated link: <code>{{ shareLink }}</code>
|
||||||
|
</div>
|
||||||
|
<div v-if="installedSharedProfileData.is_owned" class="table">
|
||||||
|
<div class="table-row table-head">
|
||||||
|
<div class="table-cell table-text name-cell actions-cell">
|
||||||
|
<Button class="transparent">
|
||||||
|
Name
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="user in installedSharedProfileData.users"
|
||||||
|
:key="user"
|
||||||
|
class="table-row"
|
||||||
|
>
|
||||||
|
<div class="table-cell table-text name-cell">
|
||||||
|
<div class="user-content">
|
||||||
|
<span v-tooltip="`${user}`" class="title">{{ user }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="table-cell table-text manage">
|
||||||
|
<div v-tooltip="'Remove project'">
|
||||||
|
<Button icon-only @click="removeSharedPackUser(user)">
|
||||||
|
<TrashIcon />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="installedSharedProfileData.is_owned" class="adjacent-input">
|
||||||
|
your project
|
||||||
|
{{ props.instance.sync_update_version }}
|
||||||
|
:)
|
||||||
|
<label for="share-sync">
|
||||||
|
<span class="label__title">Sync shared profile</span>
|
||||||
|
<span class="label__description" v-if="props.instance.sync_update_version?.is_synced">
|
||||||
|
You are up to date with the shared profile.
|
||||||
|
</span>
|
||||||
|
<span class="label__description" v-else>
|
||||||
|
You have changes that have not been synced to the shared profile. Click the button to upload your changes.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<Button id="share-sync-sync" @click="outboundSyncSharedProfile" :disabled="props.instance.sync_update_version?.is_synced">
|
||||||
|
<GlobeIcon /> Sync
|
||||||
|
</Button>
|
||||||
|
<Button id="share-sync-revert" @click="inboundSyncSharedProfile" :disabled="props.instance.sync_update_version?.is_synced">
|
||||||
|
<GlobeIcon /> Revert
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
not yours
|
||||||
|
{{ props.instance.sync_update_version }}
|
||||||
|
<label for="share-sync">
|
||||||
|
<span class="label__title">Sync shared profile</span>
|
||||||
|
<span class="label__description" v-if="props.instance.sync_update_version?.is_synced">
|
||||||
|
You are up to date with the shared profile.
|
||||||
|
</span>
|
||||||
|
<span class="label__description" v-else>
|
||||||
|
You are not up to date with the shared profile. Click the button to update your instance.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<Button id="share-sync-sync" @click="inboundSyncSharedProfile">
|
||||||
|
<GlobeIcon /> Sync
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
<Card>
|
<Card>
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<h3>
|
<h3>
|
||||||
@ -502,7 +585,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<ModpackVersionModal
|
<ModpackVersionModal
|
||||||
v-if="instance.metadata.linked_data"
|
v-if="instance.metadata.linked_data?.modrinth_modpack"
|
||||||
ref="modpackVersionModal"
|
ref="modpackVersionModal"
|
||||||
:instance="instance"
|
:instance="instance"
|
||||||
:versions="props.versions"
|
:versions="props.versions"
|
||||||
@ -527,6 +610,7 @@ import {
|
|||||||
HammerIcon,
|
HammerIcon,
|
||||||
ModalConfirm,
|
ModalConfirm,
|
||||||
DownloadIcon,
|
DownloadIcon,
|
||||||
|
GlobeIcon,
|
||||||
ClipboardCopyIcon,
|
ClipboardCopyIcon,
|
||||||
Button,
|
Button,
|
||||||
Toggle,
|
Toggle,
|
||||||
@ -545,6 +629,12 @@ import {
|
|||||||
remove,
|
remove,
|
||||||
update_repair_modrinth,
|
update_repair_modrinth,
|
||||||
} from '@/helpers/profile.js'
|
} from '@/helpers/profile.js'
|
||||||
|
import {
|
||||||
|
get_all,
|
||||||
|
outbound_sync,
|
||||||
|
inbound_sync,
|
||||||
|
share_generate
|
||||||
|
} from '@/helpers/shared_profiles.js'
|
||||||
import { computed, readonly, ref, shallowRef, watch } from 'vue'
|
import { computed, readonly, ref, shallowRef, watch } from 'vue'
|
||||||
import { get_max_memory } from '@/helpers/jre.js'
|
import { get_max_memory } from '@/helpers/jre.js'
|
||||||
import { get } from '@/helpers/settings.js'
|
import { get } from '@/helpers/settings.js'
|
||||||
@ -659,12 +749,18 @@ const unlinkModpack = ref(false)
|
|||||||
|
|
||||||
const inProgress = ref(false)
|
const inProgress = ref(false)
|
||||||
const installing = computed(() => props.instance.install_stage !== 'installed')
|
const installing = computed(() => props.instance.install_stage !== 'installed')
|
||||||
const installedVersion = computed(() => props.instance?.metadata?.linked_data?.version_id)
|
const installedVersion = computed(() => props.instance?.metadata?.linked_data?.modrinth_modpack?.version_id)
|
||||||
const installedVersionData = computed(() => {
|
const installedVersionData = computed(() => {
|
||||||
if (!installedVersion.value) return null
|
if (!installedVersion.value) return null
|
||||||
return props.versions.find((version) => version.id === installedVersion.value)
|
return props.versions.find((version) => version.id === installedVersion.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
[
|
[
|
||||||
title,
|
title,
|
||||||
@ -794,13 +890,13 @@ async function unpairProfile() {
|
|||||||
|
|
||||||
async function unlockProfile() {
|
async function unlockProfile() {
|
||||||
const editProfile = props.instance
|
const editProfile = props.instance
|
||||||
editProfile.metadata.linked_data.locked = false
|
editProfile.metadata.linked_data.modrinth_modpack.locked = false
|
||||||
await edit(props.instance.path, editProfile)
|
await edit(props.instance.path, editProfile)
|
||||||
modalConfirmUnlock.value.hide()
|
modalConfirmUnlock.value.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
const isPackLocked = computed(() => {
|
const isPackLocked = computed(() => {
|
||||||
return props.instance.metadata.linked_data && props.instance.metadata.linked_data.locked
|
return props.instance.metadata.linked_data?.modrinth_modpack.locked
|
||||||
})
|
})
|
||||||
|
|
||||||
async function repairModpack() {
|
async function repairModpack() {
|
||||||
@ -932,6 +1028,20 @@ async function saveGvLoaderEdits() {
|
|||||||
editing.value = false
|
editing.value = false
|
||||||
changeVersionsModal.value.hide()
|
changeVersionsModal.value.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function outboundSyncSharedProfile() {
|
||||||
|
await outbound_sync(props.instance.path).catch(handleError)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function inboundSyncSharedProfile() {
|
||||||
|
await inbound_sync(props.instance.path).catch(handleError)
|
||||||
|
}
|
||||||
|
|
||||||
|
const shareLink = ref(null)
|
||||||
|
async function generateShareLink() {
|
||||||
|
shareLink.value = await share_generate(props.instance.path).catch(handleError)
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@ -1012,4 +1122,32 @@ async function saveGvLoaderEdits() {
|
|||||||
margin-top: 1.5rem;
|
margin-top: 1.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.table {
|
||||||
|
margin-block-start: 0;
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
border: 2px solid var(--color-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-row {
|
||||||
|
grid-template-columns: 7fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-cell {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: var(--color-contrast);
|
||||||
|
font-weight: bolder;
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -314,10 +314,10 @@ async function fetchProjectData() {
|
|||||||
categories.value,
|
categories.value,
|
||||||
instance.value,
|
instance.value,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
useFetch(`https://api.modrinth.com/v2/project/${route.params.id}`, 'project'),
|
useFetch(`https://staging-api.modrinth.com/v2/project/${route.params.id}`, 'project'),
|
||||||
useFetch(`https://api.modrinth.com/v2/project/${route.params.id}/version`, 'project'),
|
useFetch(`https://staging-api.modrinth.com/v2/project/${route.params.id}/version`, 'project'),
|
||||||
useFetch(`https://api.modrinth.com/v2/project/${route.params.id}/members`, 'project'),
|
useFetch(`https://staging-api.modrinth.com/v2/project/${route.params.id}/members`, 'project'),
|
||||||
useFetch(`https://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),
|
get_categories().catch(handleError),
|
||||||
route.query.i ? getInstance(route.query.i, false).catch(handleError) : Promise.resolve(),
|
route.query.i ? getInstance(route.query.i, false).catch(handleError) : Promise.resolve(),
|
||||||
])
|
])
|
||||||
@ -391,7 +391,7 @@ async function install(version) {
|
|||||||
packs.length === 0 ||
|
packs.length === 0 ||
|
||||||
!packs
|
!packs
|
||||||
.map((value) => value.metadata)
|
.map((value) => value.metadata)
|
||||||
.find((pack) => pack.linked_data?.project_id === data.value.id)
|
.find((pack) => pack.linked_data?.modrinth_modpack?.project_id === data.value.id)
|
||||||
) {
|
) {
|
||||||
await packInstall(
|
await packInstall(
|
||||||
data.value.id,
|
data.value.id,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user