You've already forked pages
forked from didirus/AstralRinth
Migrate to SQLite for Internal Launcher Data (#1300)
* initial migration * barebones profiles * Finish profiles * Add back file watcher * UI support progress * Finish most of cache * Fix options page * Fix forge, finish modrinth auth * Accounts, process cache * Run SQLX prepare * Finish * Run lint + actions * Fix version to be compat with windows * fix lint * actually fix lint * actually fix lint again
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -54,3 +54,6 @@ apps/frontend/src/generated
|
||||
.turbo
|
||||
target
|
||||
generated
|
||||
|
||||
# app testing dir
|
||||
app-playground-data/*
|
||||
736
Cargo.lock
generated
736
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,6 @@
|
||||
resolver = '2'
|
||||
members = [
|
||||
'./packages/app-lib',
|
||||
'./packages/app-macros',
|
||||
'./apps/app-playground',
|
||||
'./apps/app'
|
||||
]
|
||||
@@ -14,3 +13,6 @@ codegen-units = 1 # Compile crates one after another so the compiler can optimiz
|
||||
lto = true # Enables link to optimizations
|
||||
opt-level = "s" # Optimize for binary size
|
||||
strip = true # Remove debug symbols
|
||||
|
||||
[profile.dev.package.sqlx-macros]
|
||||
opt-level = 3
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@modrinth/app-frontend",
|
||||
"private": true,
|
||||
"version": "0.7.2",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { computed, ref, onMounted } from 'vue'
|
||||
import { RouterView, RouterLink, useRouter, useRoute } from 'vue-router'
|
||||
import {
|
||||
HomeIcon,
|
||||
@@ -21,11 +21,11 @@ import SplashScreen from '@/components/ui/SplashScreen.vue'
|
||||
import ErrorModal from '@/components/ui/ErrorModal.vue'
|
||||
import ModrinthLoadingIndicator from '@/components/modrinth-loading-indicator'
|
||||
import { handleError, useNotifications } from '@/store/notifications.js'
|
||||
import { offline_listener, command_listener, warning_listener } from '@/helpers/events.js'
|
||||
import { command_listener, warning_listener } from '@/helpers/events.js'
|
||||
import { MinimizeIcon, MaximizeIcon, ChatIcon } from '@/assets/icons'
|
||||
import { type } from '@tauri-apps/api/os'
|
||||
import { appWindow } from '@tauri-apps/api/window'
|
||||
import { isDev, getOS, isOffline, showLauncherLogsFolder } from '@/helpers/utils.js'
|
||||
import { isDev, getOS, showLauncherLogsFolder } from '@/helpers/utils.js'
|
||||
import {
|
||||
mixpanel_track,
|
||||
mixpanel_init,
|
||||
@@ -36,18 +36,27 @@ import { saveWindowState, StateFlags } from 'tauri-plugin-window-state-api'
|
||||
import { getVersion } from '@tauri-apps/api/app'
|
||||
import { window as TauriWindow } from '@tauri-apps/api'
|
||||
import { TauriEvent } from '@tauri-apps/api/event'
|
||||
import { await_sync, check_safe_loading_bars_complete } from './helpers/state'
|
||||
import { confirm } from '@tauri-apps/api/dialog'
|
||||
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
|
||||
import OnboardingScreen from '@/components/ui/tutorial/OnboardingScreen.vue'
|
||||
import { install_from_file } from './helpers/pack'
|
||||
import { useError } from '@/store/error.js'
|
||||
import ModInstallModal from '@/components/ui/install_flow/ModInstallModal.vue'
|
||||
import IncompatibilityWarningModal from '@/components/ui/install_flow/IncompatibilityWarningModal.vue'
|
||||
import InstallConfirmModal from '@/components/ui/install_flow/InstallConfirmModal.vue'
|
||||
import { useInstall } from '@/store/install.js'
|
||||
|
||||
const themeStore = useTheming()
|
||||
const urlModal = ref(null)
|
||||
const isLoading = ref(true)
|
||||
|
||||
const offline = ref(false)
|
||||
const offline = ref(!navigator.onLine)
|
||||
window.addEventListener('offline', () => {
|
||||
offline.value = true
|
||||
})
|
||||
window.addEventListener('online', () => {
|
||||
offline.value = false
|
||||
})
|
||||
|
||||
const showOnboarding = ref(false)
|
||||
const nativeDecorations = ref(false)
|
||||
|
||||
@@ -62,16 +71,16 @@ defineExpose({
|
||||
const {
|
||||
native_decorations,
|
||||
theme,
|
||||
opt_out_analytics,
|
||||
telemetry,
|
||||
collapsed_navigation,
|
||||
advanced_rendering,
|
||||
fully_onboarded,
|
||||
onboarded,
|
||||
} = await get()
|
||||
// video should play if the user is not on linux, and has not onboarded
|
||||
os.value = await getOS()
|
||||
const dev = await isDev()
|
||||
const version = await getVersion()
|
||||
showOnboarding.value = !fully_onboarded
|
||||
showOnboarding.value = !onboarded
|
||||
|
||||
nativeDecorations.value = native_decorations
|
||||
if (os.value !== 'MacOS') appWindow.setDecorations(native_decorations)
|
||||
@@ -81,10 +90,10 @@ defineExpose({
|
||||
themeStore.advancedRendering = advanced_rendering
|
||||
|
||||
mixpanel_init('014c7d6a336d0efaefe3aca91063748d', { debug: dev, persistence: 'localStorage' })
|
||||
if (opt_out_analytics) {
|
||||
if (telemetry) {
|
||||
mixpanel_opt_out_tracking()
|
||||
}
|
||||
mixpanel_track('Launched', { version, dev, fully_onboarded })
|
||||
mixpanel_track('Launched', { version, dev, onboarded })
|
||||
|
||||
if (!dev) document.addEventListener('contextmenu', (event) => event.preventDefault())
|
||||
|
||||
@@ -94,11 +103,6 @@ defineExpose({
|
||||
document.getElementsByTagName('html')[0].classList.add('windows')
|
||||
}
|
||||
|
||||
offline.value = await isOffline()
|
||||
await offline_listener((b) => {
|
||||
offline.value = b
|
||||
})
|
||||
|
||||
await warning_listener((e) =>
|
||||
notificationsWrapper.value.addNotification({
|
||||
title: 'Warning',
|
||||
@@ -118,49 +122,10 @@ defineExpose({
|
||||
},
|
||||
})
|
||||
|
||||
const confirmClose = async () => {
|
||||
const confirmed = await confirm(
|
||||
'An action is currently in progress. Are you sure you want to exit?',
|
||||
{
|
||||
title: 'Modrinth',
|
||||
type: 'warning',
|
||||
},
|
||||
)
|
||||
return confirmed
|
||||
}
|
||||
|
||||
const handleClose = async () => {
|
||||
if (failureText.value != null) {
|
||||
await TauriWindow.getCurrent().close()
|
||||
return
|
||||
}
|
||||
// State should respond immeiately if it's safe to close
|
||||
// If not, code is deadlocked or worse, so wait 2 seconds and then ask the user to confirm closing
|
||||
// (Exception: if the user is changing config directory, which takes control of the state, and it's taking a significant amount of time for some reason)
|
||||
const isSafe = await Promise.race([
|
||||
check_safe_loading_bars_complete(),
|
||||
new Promise((r) => setTimeout(r, 2000)),
|
||||
])
|
||||
if (!isSafe) {
|
||||
const response = await confirmClose()
|
||||
if (!response) {
|
||||
return
|
||||
}
|
||||
}
|
||||
await await_sync()
|
||||
await TauriWindow.getCurrent().close()
|
||||
}
|
||||
|
||||
const openSupport = async () => {
|
||||
window.__TAURI_INVOKE__('tauri', {
|
||||
__tauriModule: 'Shell',
|
||||
message: {
|
||||
cmd: 'open',
|
||||
path: 'https://discord.gg/modrinth',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
TauriWindow.getCurrent().listen(TauriEvent.WINDOW_CLOSE_REQUESTED, async () => {
|
||||
await handleClose()
|
||||
})
|
||||
@@ -179,15 +144,22 @@ const loading = useLoading()
|
||||
const notifications = useNotifications()
|
||||
const notificationsWrapper = ref()
|
||||
|
||||
watch(notificationsWrapper, () => {
|
||||
notifications.setNotifs(notificationsWrapper.value)
|
||||
})
|
||||
|
||||
const error = useError()
|
||||
const errorModal = ref()
|
||||
|
||||
watch(errorModal, () => {
|
||||
const install = useInstall()
|
||||
const modInstallModal = ref()
|
||||
const installConfirmModal = ref()
|
||||
const incompatibilityWarningModal = ref()
|
||||
|
||||
onMounted(() => {
|
||||
notifications.setNotifs(notificationsWrapper.value)
|
||||
|
||||
error.setErrorModal(errorModal.value)
|
||||
|
||||
install.setIncompatibilityWarningModal(incompatibilityWarningModal)
|
||||
install.setInstallConfirmModal(installConfirmModal)
|
||||
install.setModInstallModal(modInstallModal)
|
||||
})
|
||||
|
||||
document.querySelector('body').addEventListener('click', function (e) {
|
||||
@@ -284,7 +256,7 @@ command_listener(async (e) => {
|
||||
<div class="button-row push-right">
|
||||
<Button @click="showLauncherLogsFolder"><FileIcon />Open launcher logs</Button>
|
||||
|
||||
<Button @click="openSupport"><ChatIcon />Get support</Button>
|
||||
<a class="btn" href="https://support.modrinth.com"> <ChatIcon /> Get support </a>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
@@ -385,6 +357,9 @@ command_listener(async (e) => {
|
||||
<URLConfirmModal ref="urlModal" />
|
||||
<Notifications ref="notificationsWrapper" />
|
||||
<ErrorModal ref="errorModal" />
|
||||
<ModInstallModal ref="modInstallModal" />
|
||||
<IncompatibilityWarningModal ref="incompatibilityWarningModal" />
|
||||
<InstallConfirmModal ref="installConfirmModal" />
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -584,55 +559,6 @@ command_listener(async (e) => {
|
||||
}
|
||||
}
|
||||
|
||||
.instance-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
width: 70%;
|
||||
margin: 0.4rem;
|
||||
|
||||
p:nth-child(1) {
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
|
||||
& > p {
|
||||
color: var(--color-base);
|
||||
margin: 0.8rem 0;
|
||||
font-size: 0.7rem;
|
||||
line-height: 0.8125rem;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
.user-section {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 4.375rem;
|
||||
|
||||
section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
text-align: left;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.username {
|
||||
margin-bottom: 0.3rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.25rem;
|
||||
color: var(--color-contrast);
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 400;
|
||||
color: var(--color-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.nav-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -643,14 +569,6 @@ command_listener(async (e) => {
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.video {
|
||||
margin-top: 2.25rem;
|
||||
width: 100vw;
|
||||
height: calc(100vh - 2.25rem);
|
||||
object-fit: cover;
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.button-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
@@ -127,46 +127,46 @@ const sortBy = ref('Name')
|
||||
|
||||
const filteredResults = computed(() => {
|
||||
let instances = props.instances.filter((instance) => {
|
||||
return instance.metadata.name.toLowerCase().includes(search.value.toLowerCase())
|
||||
return instance.name.toLowerCase().includes(search.value.toLowerCase())
|
||||
})
|
||||
|
||||
if (sortBy.value === 'Name') {
|
||||
instances.sort((a, b) => {
|
||||
return a.metadata.name.localeCompare(b.metadata.name)
|
||||
return a.name.localeCompare(b.name)
|
||||
})
|
||||
}
|
||||
|
||||
if (sortBy.value === 'Game version') {
|
||||
instances.sort((a, b) => {
|
||||
return a.metadata.game_version.localeCompare(b.metadata.game_version)
|
||||
return a.game_version.localeCompare(b.game_version)
|
||||
})
|
||||
}
|
||||
|
||||
if (sortBy.value === 'Last played') {
|
||||
instances.sort((a, b) => {
|
||||
return dayjs(b.metadata.last_played ?? 0).diff(dayjs(a.metadata.last_played ?? 0))
|
||||
return dayjs(b.last_played ?? 0).diff(dayjs(a.last_played ?? 0))
|
||||
})
|
||||
}
|
||||
|
||||
if (sortBy.value === 'Date created') {
|
||||
instances.sort((a, b) => {
|
||||
return dayjs(b.metadata.date_created).diff(dayjs(a.metadata.date_created))
|
||||
return dayjs(b.date_created).diff(dayjs(a.date_created))
|
||||
})
|
||||
}
|
||||
|
||||
if (sortBy.value === 'Date modified') {
|
||||
instances.sort((a, b) => {
|
||||
return dayjs(b.metadata.date_modified).diff(dayjs(a.metadata.date_modified))
|
||||
return dayjs(b.date_modified).diff(dayjs(a.date_modified))
|
||||
})
|
||||
}
|
||||
|
||||
if (filters.value === 'Custom instances') {
|
||||
instances = instances.filter((instance) => {
|
||||
return !instance.metadata?.linked_data
|
||||
return !instance.linked_data
|
||||
})
|
||||
} else if (filters.value === 'Downloaded modpacks') {
|
||||
instances = instances.filter((instance) => {
|
||||
return instance.metadata?.linked_data
|
||||
return instance.linked_data
|
||||
})
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ const filteredResults = computed(() => {
|
||||
|
||||
if (group.value === 'Loader') {
|
||||
instances.forEach((instance) => {
|
||||
const loader = formatCategoryHeader(instance.metadata.loader)
|
||||
const loader = formatCategoryHeader(instance.loader)
|
||||
if (!instanceMap.has(loader)) {
|
||||
instanceMap.set(loader, [])
|
||||
}
|
||||
@@ -183,19 +183,19 @@ const filteredResults = computed(() => {
|
||||
})
|
||||
} else if (group.value === 'Game version') {
|
||||
instances.forEach((instance) => {
|
||||
if (!instanceMap.has(instance.metadata.game_version)) {
|
||||
instanceMap.set(instance.metadata.game_version, [])
|
||||
if (!instanceMap.has(instance.game_version)) {
|
||||
instanceMap.set(instance.game_version, [])
|
||||
}
|
||||
|
||||
instanceMap.get(instance.metadata.game_version).push(instance)
|
||||
instanceMap.get(instance.game_version).push(instance)
|
||||
})
|
||||
} else if (group.value === 'Category') {
|
||||
instances.forEach((instance) => {
|
||||
if (instance.metadata.groups.length === 0) {
|
||||
instance.metadata.groups.push('None')
|
||||
if (instance.groups.length === 0) {
|
||||
instance.groups.push('None')
|
||||
}
|
||||
|
||||
for (const category of instance.metadata.groups) {
|
||||
for (const category of instance.groups) {
|
||||
if (!instanceMap.has(category)) {
|
||||
instanceMap.set(category, [])
|
||||
}
|
||||
|
||||
@@ -17,22 +17,15 @@ import Instance from '@/components/ui/Instance.vue'
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||
import ProjectCard from '@/components/ui/ProjectCard.vue'
|
||||
import InstallConfirmModal from '@/components/ui/InstallConfirmModal.vue'
|
||||
import ModInstallModal from '@/components/ui/ModInstallModal.vue'
|
||||
import {
|
||||
get_all_running_profile_paths,
|
||||
get_uuids_by_profile_path,
|
||||
kill_by_uuid,
|
||||
} from '@/helpers/process.js'
|
||||
import { get_by_profile_path } from '@/helpers/process.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { duplicate, remove, run } from '@/helpers/profile.js'
|
||||
import { duplicate, kill, remove, run } from '@/helpers/profile.js'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { showProfileInFolder } from '@/helpers/utils.js'
|
||||
import { useFetch } from '@/helpers/fetch.js'
|
||||
import { install as pack_install } from '@/helpers/pack.js'
|
||||
import { useTheming } from '@/store/state.js'
|
||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||
import { handleSevereError } from '@/store/error.js'
|
||||
import { install as installVersion } from '@/store/install.js'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
@@ -58,9 +51,7 @@ const modsRow = ref(null)
|
||||
const instanceOptions = ref(null)
|
||||
const instanceComponents = ref(null)
|
||||
const rows = ref(null)
|
||||
const confirmModal = ref(null)
|
||||
const deleteConfirmModal = ref(null)
|
||||
const modInstallModal = ref(null)
|
||||
|
||||
const themeStore = useTheming()
|
||||
const currentDeleteInstance = ref(null)
|
||||
@@ -90,23 +81,24 @@ const handleInstanceRightClick = async (event, passedInstance) => {
|
||||
},
|
||||
]
|
||||
|
||||
const running = await get_all_running_profile_paths().catch(handleError)
|
||||
const runningProcesses = await get_by_profile_path(passedInstance.path).catch(handleError)
|
||||
|
||||
const options = running.includes(passedInstance.path)
|
||||
? [
|
||||
{
|
||||
name: 'stop',
|
||||
color: 'danger',
|
||||
},
|
||||
...baseOptions,
|
||||
]
|
||||
: [
|
||||
{
|
||||
name: 'play',
|
||||
color: 'primary',
|
||||
},
|
||||
...baseOptions,
|
||||
]
|
||||
const options =
|
||||
runningProcesses.length > 0
|
||||
? [
|
||||
{
|
||||
name: 'stop',
|
||||
color: 'danger',
|
||||
},
|
||||
...baseOptions,
|
||||
]
|
||||
: [
|
||||
{
|
||||
name: 'play',
|
||||
color: 'primary',
|
||||
},
|
||||
...baseOptions,
|
||||
]
|
||||
|
||||
instanceOptions.value.showMenu(event, passedInstance, options)
|
||||
}
|
||||
@@ -132,22 +124,20 @@ const handleOptionsClick = async (args) => {
|
||||
case 'play':
|
||||
await run(args.item.path).catch(handleSevereError)
|
||||
mixpanel_track('InstanceStart', {
|
||||
loader: args.item.metadata.loader,
|
||||
game_version: args.item.metadata.game_version,
|
||||
loader: args.item.loader,
|
||||
game_version: args.item.game_version,
|
||||
})
|
||||
break
|
||||
case 'stop':
|
||||
for (const u of await get_uuids_by_profile_path(args.item.path).catch(handleError)) {
|
||||
await kill_by_uuid(u).catch(handleError)
|
||||
}
|
||||
await kill(args.item.path).catch(handleError)
|
||||
mixpanel_track('InstanceStop', {
|
||||
loader: args.item.metadata.loader,
|
||||
game_version: args.item.metadata.game_version,
|
||||
loader: args.item.loader,
|
||||
game_version: args.item.game_version,
|
||||
})
|
||||
break
|
||||
case 'add_content':
|
||||
await router.push({
|
||||
path: `/browse/${args.item.metadata.loader === 'vanilla' ? 'datapack' : 'mod'}`,
|
||||
path: `/browse/${args.item.loader === 'vanilla' ? 'datapack' : 'mod'}`,
|
||||
query: { i: args.item.path },
|
||||
})
|
||||
break
|
||||
@@ -170,21 +160,8 @@ const handleOptionsClick = async (args) => {
|
||||
await navigator.clipboard.writeText(args.item.path)
|
||||
break
|
||||
case 'install': {
|
||||
const versions = await useFetch(
|
||||
`https://api.modrinth.com/v2/project/${args.item.project_id}/version`,
|
||||
'project versions',
|
||||
)
|
||||
await installVersion(args.item.project_id, null, null, 'ProjectCardContextMenu')
|
||||
|
||||
if (args.item.project_type === 'modpack') {
|
||||
await pack_install(
|
||||
args.item.project_id,
|
||||
versions[0].id,
|
||||
args.item.title,
|
||||
args.item.icon_url,
|
||||
)
|
||||
} else {
|
||||
modInstallModal.value.show(args.item.project_id, versions)
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'open_link':
|
||||
@@ -243,7 +220,7 @@ onUnmounted(() => {
|
||||
<router-link :to="row.route">{{ row.label }}</router-link>
|
||||
<ChevronRightIcon />
|
||||
</div>
|
||||
<section v-if="row.instances[0].metadata" ref="modsRow" class="instances">
|
||||
<section v-if="row.instance" ref="modsRow" class="instances">
|
||||
<Instance
|
||||
v-for="instance in row.instances.slice(0, maxInstancesPerRow)"
|
||||
:key="(instance?.project_id || instance?.id) + instance.install_stage"
|
||||
@@ -258,8 +235,6 @@ onUnmounted(() => {
|
||||
ref="instanceComponents"
|
||||
class="item"
|
||||
:project="project"
|
||||
:confirm-modal="confirmModal"
|
||||
:mod-install-modal="modInstallModal"
|
||||
@contextmenu.prevent.stop="(event) => handleProjectClick(event, project)"
|
||||
/>
|
||||
</section>
|
||||
@@ -278,8 +253,6 @@ onUnmounted(() => {
|
||||
<template #open_link> <GlobeIcon /> Open in Modrinth <ExternalIcon /> </template>
|
||||
<template #copy_link> <ClipboardCopyIcon /> Copy link </template>
|
||||
</ContextMenu>
|
||||
<InstallConfirmModal ref="confirmModal" />
|
||||
<ModInstallModal ref="modInstallModal" />
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { DropdownIcon, FolderOpenIcon, SearchIcon } from '@modrinth/assets'
|
||||
import { Button, OverflowMenu } from '@modrinth/ui'
|
||||
import { open } from '@tauri-apps/api/dialog'
|
||||
import { add_project_from_path, get } from '@/helpers/profile.js'
|
||||
import { add_project_from_path } from '@/helpers/profile.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
@@ -20,14 +20,13 @@ const handleAddContentFromFile = async () => {
|
||||
if (!newProject) return
|
||||
|
||||
for (const project of newProject) {
|
||||
await add_project_from_path(props.instance.path, project, 'mod').catch(handleError)
|
||||
await add_project_from_path(props.instance.path, project).catch(handleError)
|
||||
}
|
||||
props.instance.initProjects(await get(props.instance.path).catch(handleError))
|
||||
}
|
||||
|
||||
const handleSearchContent = async () => {
|
||||
await router.push({
|
||||
path: `/browse/${props.instance.metadata.loader === 'vanilla' ? 'datapack' : 'mod'}`,
|
||||
path: `/browse/${props.instance.loader === 'vanilla' ? 'datapack' : 'mod'}`,
|
||||
query: { i: props.instance.path },
|
||||
})
|
||||
}
|
||||
|
||||
@@ -60,9 +60,9 @@ defineExpose({
|
||||
})
|
||||
|
||||
const isLinkedData = (item) => {
|
||||
if (item.instance != undefined && item.instance.metadata.linked_data) {
|
||||
if (item.instance != undefined && item.instance.linked_data) {
|
||||
return true
|
||||
} else if (item.metadata != undefined && item.metadata.linked_data) {
|
||||
} else if (item != undefined && item.linked_data) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -23,7 +23,7 @@ defineExpose({
|
||||
})
|
||||
|
||||
const exportModal = ref(null)
|
||||
const nameInput = ref(props.instance.metadata.name)
|
||||
const nameInput = ref(props.instance.name)
|
||||
const exportDescription = ref('')
|
||||
const versionInput = ref('1.0.0')
|
||||
const files = ref([])
|
||||
|
||||
@@ -1,22 +1,14 @@
|
||||
<script setup>
|
||||
import { onUnmounted, ref, watch } from 'vue'
|
||||
import { onUnmounted, ref, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { DownloadIcon, StopCircleIcon, PlayIcon } from '@modrinth/assets'
|
||||
import { StopCircleIcon, PlayIcon } from '@modrinth/assets'
|
||||
import { Card, Avatar, AnimatedLogo } from '@modrinth/ui'
|
||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||
import InstallConfirmModal from '@/components/ui/InstallConfirmModal.vue'
|
||||
import { install as pack_install } from '@/helpers/pack'
|
||||
import { list, run } from '@/helpers/profile'
|
||||
import {
|
||||
get_all_running_profile_paths,
|
||||
get_uuids_by_profile_path,
|
||||
kill_by_uuid,
|
||||
} from '@/helpers/process'
|
||||
import { kill, run } from '@/helpers/profile'
|
||||
import { get_by_profile_path } from '@/helpers/process'
|
||||
import { process_listener } from '@/helpers/events'
|
||||
import { useFetch } from '@/helpers/fetch.js'
|
||||
import { handleError } from '@/store/state.js'
|
||||
import { showProfileInFolder } from '@/helpers/utils.js'
|
||||
import ModInstallModal from '@/components/ui/ModInstallModal.vue'
|
||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||
import { handleSevereError } from '@/store/error.js'
|
||||
|
||||
@@ -29,107 +21,31 @@ const props = defineProps({
|
||||
},
|
||||
})
|
||||
|
||||
const confirmModal = ref(null)
|
||||
const modInstallModal = ref(null)
|
||||
const playing = ref(false)
|
||||
|
||||
const uuid = ref(null)
|
||||
const modLoading = ref(
|
||||
props.instance.install_stage ? props.instance.install_stage !== 'installed' : false,
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.instance,
|
||||
() => {
|
||||
modLoading.value = props.instance.install_stage
|
||||
? props.instance.install_stage !== 'installed'
|
||||
: false
|
||||
},
|
||||
)
|
||||
const modLoading = computed(() => props.instance.install_stage !== 'installed')
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const seeInstance = async () => {
|
||||
const instancePath = props.instance.metadata
|
||||
? `/instance/${encodeURIComponent(props.instance.path)}/`
|
||||
: `/project/${encodeURIComponent(props.instance.project_id)}/`
|
||||
|
||||
await router.push(instancePath)
|
||||
await router.push(`/instance/${encodeURIComponent(props.instance.path)}/`)
|
||||
}
|
||||
|
||||
const checkProcess = async () => {
|
||||
const runningPaths = await get_all_running_profile_paths().catch(handleError)
|
||||
const runningProcesses = await get_by_profile_path(props.instance.path).catch(handleError)
|
||||
|
||||
if (runningPaths.includes(props.instance.path)) {
|
||||
playing.value = true
|
||||
return
|
||||
}
|
||||
|
||||
playing.value = false
|
||||
uuid.value = null
|
||||
}
|
||||
|
||||
const install = async (e) => {
|
||||
e?.stopPropagation()
|
||||
modLoading.value = true
|
||||
const versions = await useFetch(
|
||||
`https://api.modrinth.com/v2/project/${props.instance.project_id}/version`,
|
||||
'project versions',
|
||||
)
|
||||
|
||||
if (props.instance.project_type === 'modpack') {
|
||||
const packs = Object.values(await list(true).catch(handleError))
|
||||
|
||||
if (
|
||||
packs.length === 0 ||
|
||||
!packs
|
||||
.map((value) => value.metadata)
|
||||
.find((pack) => pack.linked_data?.project_id === props.instance.project_id)
|
||||
) {
|
||||
modLoading.value = true
|
||||
await pack_install(
|
||||
props.instance.project_id,
|
||||
versions[0].id,
|
||||
props.instance.title,
|
||||
props.instance.icon_url,
|
||||
).catch(handleError)
|
||||
modLoading.value = false
|
||||
|
||||
mixpanel_track('PackInstall', {
|
||||
id: props.instance.project_id,
|
||||
version_id: versions[0].id,
|
||||
title: props.instance.title,
|
||||
source: 'InstanceCard',
|
||||
})
|
||||
} else
|
||||
confirmModal.value.show(
|
||||
props.instance.project_id,
|
||||
versions[0].id,
|
||||
props.instance.title,
|
||||
props.instance.icon_url,
|
||||
)
|
||||
} else {
|
||||
modInstallModal.value.show(
|
||||
props.instance.project_id,
|
||||
versions,
|
||||
props.instance.title,
|
||||
props.instance.project_type,
|
||||
)
|
||||
}
|
||||
|
||||
modLoading.value = false
|
||||
playing.value = runningProcesses.length > 0
|
||||
}
|
||||
|
||||
const play = async (e, context) => {
|
||||
e?.stopPropagation()
|
||||
modLoading.value = true
|
||||
uuid.value = await run(props.instance.path).catch(handleSevereError)
|
||||
await run(props.instance.path).catch(handleSevereError)
|
||||
modLoading.value = false
|
||||
playing.value = true
|
||||
|
||||
mixpanel_track('InstancePlay', {
|
||||
loader: props.instance.metadata.loader,
|
||||
game_version: props.instance.metadata.game_version,
|
||||
loader: props.instance.loader,
|
||||
game_version: props.instance.game_version,
|
||||
source: context,
|
||||
})
|
||||
}
|
||||
@@ -138,22 +54,13 @@ const stop = async (e, context) => {
|
||||
e?.stopPropagation()
|
||||
playing.value = false
|
||||
|
||||
// If we lost the uuid for some reason, such as a user navigating
|
||||
// from-then-back to this page, we will get all uuids by the instance path.
|
||||
// For-each uuid, kill the process.
|
||||
if (!uuid.value) {
|
||||
const uuids = await get_uuids_by_profile_path(props.instance.path).catch(handleError)
|
||||
uuid.value = uuids[0]
|
||||
uuids.forEach(async (u) => await kill_by_uuid(u).catch(handleError))
|
||||
} else await kill_by_uuid(uuid.value).catch(handleError) // If we still have the uuid, just kill it
|
||||
await kill(props.instance.path).catch(handleError)
|
||||
|
||||
mixpanel_track('InstanceStop', {
|
||||
loader: props.instance.metadata.loader,
|
||||
game_version: props.instance.metadata.game_version,
|
||||
loader: props.instance.loader,
|
||||
game_version: props.instance.game_version,
|
||||
source: context,
|
||||
})
|
||||
|
||||
uuid.value = null
|
||||
}
|
||||
|
||||
const openFolder = async () => {
|
||||
@@ -162,14 +69,12 @@ const openFolder = async () => {
|
||||
|
||||
const addContent = async () => {
|
||||
await router.push({
|
||||
path: `/browse/${props.instance.metadata.loader === 'vanilla' ? 'datapack' : 'mod'}`,
|
||||
path: `/browse/${props.instance.loader === 'vanilla' ? 'datapack' : 'mod'}`,
|
||||
query: { i: props.instance.path },
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
install,
|
||||
playing,
|
||||
play,
|
||||
stop,
|
||||
seeInstance,
|
||||
@@ -179,7 +84,7 @@ defineExpose({
|
||||
})
|
||||
|
||||
const unlisten = await process_listener((e) => {
|
||||
if (e.event === 'finished' && e.uuid === uuid.value) playing.value = false
|
||||
if (e.event === 'finished' && e.profile_path_id === props.instance.path) playing.value = false
|
||||
})
|
||||
|
||||
onUnmounted(() => unlisten())
|
||||
@@ -190,46 +95,32 @@ onUnmounted(() => unlisten())
|
||||
<Card class="instance-card-item button-base" @click="seeInstance" @mouseenter="checkProcess">
|
||||
<Avatar
|
||||
size="lg"
|
||||
:src="
|
||||
props.instance.metadata
|
||||
? !props.instance.metadata.icon ||
|
||||
(props.instance.metadata.icon && props.instance.metadata.icon.startsWith('http'))
|
||||
? props.instance.metadata.icon
|
||||
: convertFileSrc(props.instance.metadata?.icon)
|
||||
: props.instance.icon_url
|
||||
"
|
||||
:src="props.instance.icon_path ? convertFileSrc(props.instance.icon_path) : null"
|
||||
alt="Mod card"
|
||||
class="mod-image"
|
||||
/>
|
||||
<div class="project-info">
|
||||
<p class="title">{{ props.instance.metadata?.name || props.instance.title }}</p>
|
||||
<p class="title">{{ props.instance.name }}</p>
|
||||
<p class="description">
|
||||
{{ props.instance.metadata?.loader }}
|
||||
{{ props.instance.metadata?.game_version || props.instance.latest_version }}
|
||||
{{ props.instance.loader }}
|
||||
{{ props.instance.game_version }}
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
<div
|
||||
v-if="props.instance.metadata && playing === false && modLoading === false"
|
||||
class="install cta button-base"
|
||||
@click="(e) => play(e, 'InstanceCard')"
|
||||
>
|
||||
<PlayIcon />
|
||||
</div>
|
||||
<div v-else-if="modLoading === true && playing === false" class="cta loading-cta">
|
||||
<AnimatedLogo class="loading-indicator" />
|
||||
</div>
|
||||
<div
|
||||
v-else-if="playing === true"
|
||||
v-if="playing === true"
|
||||
class="stop cta button-base"
|
||||
@click="(e) => stop(e, 'InstanceCard')"
|
||||
@mousehover="checkProcess"
|
||||
>
|
||||
<StopCircleIcon />
|
||||
</div>
|
||||
<div v-else class="install cta button-base" @click="install"><DownloadIcon /></div>
|
||||
<InstallConfirmModal ref="confirmModal" />
|
||||
<ModInstallModal ref="modInstallModal" />
|
||||
<div v-else-if="modLoading === true && playing === false" class="cta loading-cta">
|
||||
<AnimatedLogo class="loading-indicator" />
|
||||
</div>
|
||||
<div v-else class="install cta button-base" @click="(e) => play(e, 'InstanceCard')">
|
||||
<PlayIcon />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -213,13 +213,7 @@ import { get_loaders } from '@/helpers/tags'
|
||||
import { create } from '@/helpers/profile'
|
||||
import { open } from '@tauri-apps/api/dialog'
|
||||
import { tauri } from '@tauri-apps/api'
|
||||
import {
|
||||
get_game_versions,
|
||||
get_fabric_versions,
|
||||
get_forge_versions,
|
||||
get_quilt_versions,
|
||||
get_neoforge_versions,
|
||||
} from '@/helpers/metadata'
|
||||
import { get_game_versions, get_loader_versions } from '@/helpers/metadata'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import Multiselect from 'vue-multiselect'
|
||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||
@@ -304,10 +298,10 @@ const [
|
||||
all_game_versions,
|
||||
loaders,
|
||||
] = await Promise.all([
|
||||
get_fabric_versions().then(shallowRef).catch(handleError),
|
||||
get_forge_versions().then(shallowRef).catch(handleError),
|
||||
get_quilt_versions().then(shallowRef).catch(handleError),
|
||||
get_neoforge_versions().then(shallowRef).catch(handleError),
|
||||
get_loader_versions('fabric').then(shallowRef).catch(handleError),
|
||||
get_loader_versions('forge').then(shallowRef).catch(handleError),
|
||||
get_loader_versions('quilt').then(shallowRef).catch(handleError),
|
||||
get_loader_versions('neo').then(shallowRef).catch(handleError),
|
||||
get_game_versions().then(shallowRef).catch(handleError),
|
||||
get_loaders()
|
||||
.then((value) =>
|
||||
|
||||
@@ -53,9 +53,6 @@ defineExpose({
|
||||
show: async (version, currentSelectedJava) => {
|
||||
chosenInstallOptions.value = await find_filtered_jres(version).catch(handleError)
|
||||
|
||||
console.log(chosenInstallOptions.value)
|
||||
console.log(version)
|
||||
|
||||
currentSelected.value = currentSelectedJava
|
||||
if (!currentSelected.value) {
|
||||
currentSelected.value = { path: '', version: '' }
|
||||
|
||||
@@ -162,7 +162,6 @@ async function reinstallJava() {
|
||||
const path = await auto_install_java(props.version).catch(handleError)
|
||||
let result = await get_jre(path)
|
||||
|
||||
console.log('java result ' + result)
|
||||
if (!result) {
|
||||
result = {
|
||||
path: path,
|
||||
@@ -205,6 +204,10 @@ async function reinstallJava() {
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin: 0;
|
||||
|
||||
.btn {
|
||||
width: max-content;
|
||||
}
|
||||
}
|
||||
|
||||
.test-success {
|
||||
|
||||
@@ -29,7 +29,7 @@ const filteredVersions = computed(() => {
|
||||
})
|
||||
|
||||
const modpackVersionModal = ref(null)
|
||||
const installedVersion = computed(() => props.instance?.metadata?.linked_data?.version_id)
|
||||
const installedVersion = computed(() => props.instance?.linked_data?.version_id)
|
||||
const installing = computed(() => props.instance.install_stage !== 'installed')
|
||||
const inProgress = ref(false)
|
||||
|
||||
@@ -50,7 +50,7 @@ const switchVersion = async (versionId) => {
|
||||
:noblur="!themeStore.advancedRendering"
|
||||
>
|
||||
<div class="modal-body">
|
||||
<Card v-if="instance.metadata.linked_data" class="mod-card">
|
||||
<Card v-if="instance.linked_data" class="mod-card">
|
||||
<div class="table">
|
||||
<div class="table-row with-columns table-head">
|
||||
<div class="table-cell table-text download-cell" />
|
||||
|
||||
@@ -6,10 +6,8 @@ import { computed, ref } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useFetch } from '@/helpers/fetch.js'
|
||||
import { list } from '@/helpers/profile.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { install as pack_install } from '@/helpers/pack.js'
|
||||
import { install as installVersion } from '@/store/install.js'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
const router = useRouter()
|
||||
@@ -22,18 +20,6 @@ const props = defineProps({
|
||||
return {}
|
||||
},
|
||||
},
|
||||
confirmModal: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
modInstallModal: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const toColor = computed(() => {
|
||||
@@ -65,40 +51,15 @@ const toTransparent = computed(() => {
|
||||
const install = async (e) => {
|
||||
e?.stopPropagation()
|
||||
installing.value = true
|
||||
const versions = await useFetch(
|
||||
`https://api.modrinth.com/v2/project/${props.project.project_id}/version`,
|
||||
'project versions',
|
||||
)
|
||||
|
||||
if (props.project.project_type === 'modpack') {
|
||||
const packs = Object.values(await list(true).catch(handleError))
|
||||
|
||||
if (
|
||||
packs.length === 0 ||
|
||||
!packs
|
||||
.map((value) => value.metadata)
|
||||
.find((pack) => pack.linked_data?.project_id === props.project.project_id)
|
||||
) {
|
||||
installing.value = true
|
||||
await pack_install(
|
||||
props.project.project_id,
|
||||
versions[0].id,
|
||||
props.project.title,
|
||||
props.project.icon_url,
|
||||
).catch(handleError)
|
||||
await installVersion(
|
||||
props.project.project_id,
|
||||
null,
|
||||
props.instance ? props.instance.path : null,
|
||||
'ProjectCard',
|
||||
() => {
|
||||
installing.value = false
|
||||
} else
|
||||
props.confirmModal.show(
|
||||
props.project.project_id,
|
||||
versions[0].id,
|
||||
props.project.title,
|
||||
props.project.icon_url,
|
||||
)
|
||||
} else {
|
||||
props.modInstallModal.show(props.project.project_id, versions)
|
||||
}
|
||||
|
||||
installing.value = false
|
||||
},
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
</Button>
|
||||
<div v-if="offline" class="status">
|
||||
<span class="circle stopped" />
|
||||
<div class="running-text clickable" @click="refreshInternet()">
|
||||
<div class="running-text">
|
||||
<span> Offline </span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="selectedProfile" class="status">
|
||||
<div v-if="selectedProcess" class="status">
|
||||
<span class="circle running" />
|
||||
<div ref="profileButton" class="running-text">
|
||||
<router-link :to="`/instance/${encodeURIComponent(selectedProfile.path)}`">
|
||||
{{ selectedProfile.metadata.name }}
|
||||
<router-link :to="`/instance/${encodeURIComponent(selectedProcess.profile.path)}`">
|
||||
{{ selectedProcess.profile.name }}
|
||||
</router-link>
|
||||
<div
|
||||
v-if="currentProcesses.length > 1"
|
||||
@@ -34,7 +34,12 @@
|
||||
<DropdownIcon />
|
||||
</div>
|
||||
</div>
|
||||
<Button v-tooltip="'Stop instance'" icon-only class="icon-button stop" @click="stop()">
|
||||
<Button
|
||||
v-tooltip="'Stop instance'"
|
||||
icon-only
|
||||
class="icon-button stop"
|
||||
@click="stop(selectedProcess)"
|
||||
>
|
||||
<StopCircleIcon />
|
||||
</Button>
|
||||
<Button v-tooltip="'View logs'" icon-only class="icon-button" @click="goToTerminal()">
|
||||
@@ -75,17 +80,17 @@
|
||||
class="profile-card"
|
||||
>
|
||||
<Button
|
||||
v-for="profile in currentProcesses"
|
||||
:key="profile.id"
|
||||
v-for="process in currentProcesses"
|
||||
:key="process.pid"
|
||||
class="profile-button"
|
||||
@click="selectProfile(profile)"
|
||||
@click="selectedProcess(process)"
|
||||
>
|
||||
<div class="text"><span class="circle running" /> {{ profile.metadata.name }}</div>
|
||||
<div class="text"><span class="circle running" /> {{ process.profile.name }}</div>
|
||||
<Button
|
||||
v-tooltip="'Stop instance'"
|
||||
icon-only
|
||||
class="icon-button stop"
|
||||
@click.stop="stop(profile.path)"
|
||||
@click.stop="stop(process)"
|
||||
>
|
||||
<StopCircleIcon />
|
||||
</Button>
|
||||
@@ -93,7 +98,7 @@
|
||||
v-tooltip="'View logs'"
|
||||
icon-only
|
||||
class="icon-button"
|
||||
@click.stop="goToTerminal(profile.path)"
|
||||
@click.stop="goToTerminal(process.profile.path)"
|
||||
>
|
||||
<TerminalSquareIcon />
|
||||
</Button>
|
||||
@@ -106,19 +111,15 @@
|
||||
import { DownloadIcon, StopCircleIcon, TerminalSquareIcon, DropdownIcon } from '@modrinth/assets'
|
||||
import { Button, Card } from '@modrinth/ui'
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import {
|
||||
get_all_running_profiles as getRunningProfiles,
|
||||
kill_by_uuid as killProfile,
|
||||
get_uuids_by_profile_path as getProfileProcesses,
|
||||
} from '@/helpers/process'
|
||||
import { loading_listener, process_listener, offline_listener } from '@/helpers/events'
|
||||
import { get_all as getRunningProcesses, kill as killProcess } from '@/helpers/process'
|
||||
import { loading_listener, process_listener } from '@/helpers/events'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { progress_bars_list } from '@/helpers/state.js'
|
||||
import { refreshOffline, isOffline } from '@/helpers/utils.js'
|
||||
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||
import { ChatIcon } from '@/assets/icons'
|
||||
import { get_many } from '@/helpers/profile.js'
|
||||
|
||||
const router = useRouter()
|
||||
const card = ref(null)
|
||||
@@ -129,38 +130,44 @@ const showCard = ref(false)
|
||||
|
||||
const showProfiles = ref(false)
|
||||
|
||||
const currentProcesses = ref(await getRunningProfiles().catch(handleError))
|
||||
const selectedProfile = ref(currentProcesses.value[0])
|
||||
const currentProcesses = ref([])
|
||||
const selectedProcess = ref()
|
||||
|
||||
const offline = ref(await isOffline().catch(handleError))
|
||||
const refreshInternet = async () => {
|
||||
offline.value = await refreshOffline().catch(handleError)
|
||||
const refresh = async () => {
|
||||
const processes = await getRunningProcesses().catch(handleError)
|
||||
const profiles = await get_many(processes.map((x) => x.profile_path)).catch(handleError)
|
||||
|
||||
currentProcesses.value = processes.map((x) => ({
|
||||
profile: profiles.find((prof) => x.profile_path === prof.path),
|
||||
...x,
|
||||
}))
|
||||
if (!selectedProcess.value || !currentProcesses.value.includes(selectedProcess.value)) {
|
||||
selectedProcess.value = currentProcesses.value[0]
|
||||
}
|
||||
}
|
||||
|
||||
await refresh()
|
||||
|
||||
const offline = ref(!navigator.onLine)
|
||||
window.addEventListener('offline', () => {
|
||||
offline.value = true
|
||||
})
|
||||
window.addEventListener('online', () => {
|
||||
offline.value = false
|
||||
})
|
||||
|
||||
const unlistenProcess = await process_listener(async () => {
|
||||
await refresh()
|
||||
})
|
||||
|
||||
const unlistenRefresh = await offline_listener(async (b) => {
|
||||
offline.value = b
|
||||
await refresh()
|
||||
})
|
||||
|
||||
const refresh = async () => {
|
||||
currentProcesses.value = await getRunningProfiles().catch(handleError)
|
||||
if (!currentProcesses.value.includes(selectedProfile.value)) {
|
||||
selectedProfile.value = currentProcesses.value[0]
|
||||
}
|
||||
}
|
||||
|
||||
const stop = async (path) => {
|
||||
const stop = async (process) => {
|
||||
try {
|
||||
const processes = await getProfileProcesses(path ?? selectedProfile.value.path)
|
||||
await killProfile(processes[0])
|
||||
console.log(process.pid)
|
||||
await killProcess(process.pid).catch(handleError)
|
||||
|
||||
mixpanel_track('InstanceStop', {
|
||||
loader: currentProcesses.value[0].metadata.loader,
|
||||
game_version: currentProcesses.value[0].metadata.game_version,
|
||||
loader: process.profile.loader,
|
||||
game_version: process.profile.game_version,
|
||||
source: 'AppBar',
|
||||
})
|
||||
} catch (e) {
|
||||
@@ -170,7 +177,7 @@ const stop = async (path) => {
|
||||
}
|
||||
|
||||
const goToTerminal = (path) => {
|
||||
router.push(`/instance/${encodeURIComponent(path ?? selectedProfile.value.path)}/logs`)
|
||||
router.push(`/instance/${encodeURIComponent(path ?? selectedProcess.value.profile.path)}/logs`)
|
||||
}
|
||||
|
||||
const currentLoadingBars = ref([])
|
||||
@@ -182,8 +189,8 @@ const refreshInfo = async () => {
|
||||
if (x.bar_type.type === 'java_download') {
|
||||
x.title = 'Downloading Java ' + x.bar_type.version
|
||||
}
|
||||
if (x.bar_type.profile_name) {
|
||||
x.title = x.bar_type.profile_name
|
||||
if (x.bar_type.profile_path) {
|
||||
x.title = x.bar_type.profile_path
|
||||
}
|
||||
if (x.bar_type.pack_name) {
|
||||
x.title = x.bar_type.pack_name
|
||||
@@ -215,8 +222,8 @@ const unlistenLoading = await loading_listener(async () => {
|
||||
await refreshInfo()
|
||||
})
|
||||
|
||||
const selectProfile = (profile) => {
|
||||
selectedProfile.value = profile
|
||||
const selectProcess = (process) => {
|
||||
selectedProcess.value = process
|
||||
showProfiles.value = false
|
||||
}
|
||||
|
||||
@@ -267,7 +274,6 @@ onBeforeUnmount(() => {
|
||||
window.removeEventListener('click', handleClickOutsideProfile)
|
||||
unlistenProcess()
|
||||
unlistenLoading()
|
||||
unlistenRefresh()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -69,12 +69,7 @@ import { formatNumber, formatCategory } from '@modrinth/utils'
|
||||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
import { ref } from 'vue'
|
||||
import { add_project_from_version as installMod, list } from '@/helpers/profile.js'
|
||||
import { install as packInstall } from '@/helpers/pack.js'
|
||||
import { installVersionDependencies } from '@/helpers/utils.js'
|
||||
import { useFetch } from '@/helpers/fetch.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||
import { install as installVersion } from '@/store/install.js'
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
const props = defineProps({
|
||||
@@ -94,18 +89,6 @@ const props = defineProps({
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
confirmModal: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
modInstallModal: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
incompatibilityWarningModal: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
featured: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
@@ -123,93 +106,19 @@ const installed = ref(props.installed)
|
||||
|
||||
async function install() {
|
||||
installing.value = true
|
||||
const versions = await useFetch(
|
||||
`https://api.modrinth.com/v2/project/${props.project.project_id}/version`,
|
||||
'project versions',
|
||||
)
|
||||
let queuedVersionData
|
||||
|
||||
if (!props.instance) {
|
||||
queuedVersionData = versions[0]
|
||||
} else {
|
||||
queuedVersionData = versions.find(
|
||||
(v) =>
|
||||
v.game_versions.includes(props.instance.metadata.game_version) &&
|
||||
(props.project.project_type !== 'mod' ||
|
||||
v.loaders.includes(props.instance.metadata.loader)),
|
||||
)
|
||||
}
|
||||
|
||||
if (props.project.project_type === 'modpack') {
|
||||
const packs = Object.values(await list().catch(handleError))
|
||||
if (
|
||||
packs.length === 0 ||
|
||||
!packs
|
||||
.map((value) => value.metadata)
|
||||
.find((pack) => pack.linked_data?.project_id === props.project.project_id)
|
||||
) {
|
||||
await packInstall(
|
||||
props.project.project_id,
|
||||
queuedVersionData.id,
|
||||
props.project.title,
|
||||
props.project.icon_url,
|
||||
).catch(handleError)
|
||||
|
||||
mixpanel_track('PackInstall', {
|
||||
id: props.project.project_id,
|
||||
version_id: queuedVersionData.id,
|
||||
title: props.project.title,
|
||||
source: 'SearchCard',
|
||||
})
|
||||
} else {
|
||||
props.confirmModal.show(
|
||||
props.project.project_id,
|
||||
queuedVersionData.id,
|
||||
props.project.title,
|
||||
props.project.icon_url,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (props.instance) {
|
||||
if (!queuedVersionData) {
|
||||
props.incompatibilityWarningModal.show(
|
||||
props.instance,
|
||||
props.project.title,
|
||||
versions,
|
||||
() => (installed.value = true),
|
||||
props.project.project_id,
|
||||
props.project.project_type,
|
||||
)
|
||||
installing.value = false
|
||||
return
|
||||
} else {
|
||||
await installMod(props.instance.path, queuedVersionData.id).catch(handleError)
|
||||
await installVersionDependencies(props.instance, queuedVersionData)
|
||||
|
||||
mixpanel_track('ProjectInstall', {
|
||||
loader: props.instance.metadata.loader,
|
||||
game_version: props.instance.metadata.game_version,
|
||||
id: props.project.project_id,
|
||||
project_type: props.project.project_type,
|
||||
version_id: queuedVersionData.id,
|
||||
title: props.project.title,
|
||||
source: 'SearchCard',
|
||||
})
|
||||
}
|
||||
} else {
|
||||
props.modInstallModal.show(
|
||||
props.project.project_id,
|
||||
versions,
|
||||
props.project.title,
|
||||
props.project.project_type,
|
||||
)
|
||||
await installVersion(
|
||||
props.project.project_id,
|
||||
null,
|
||||
props.instance ? props.instance.path : null,
|
||||
'SearchCard',
|
||||
(version) => {
|
||||
installing.value = false
|
||||
return
|
||||
}
|
||||
if (props.instance) installed.value = true
|
||||
}
|
||||
|
||||
installing.value = false
|
||||
if (props.instance && version) {
|
||||
installed.value = true
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,77 +1,37 @@
|
||||
<script setup>
|
||||
import { Modal, Button } from '@modrinth/ui'
|
||||
import { ref } from 'vue'
|
||||
import { useFetch } from '@/helpers/fetch.js'
|
||||
import SearchCard from '@/components/ui/SearchCard.vue'
|
||||
import { get_categories } from '@/helpers/tags.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { install as packInstall } from '@/helpers/pack.js'
|
||||
import mixpanel from 'mixpanel-browser'
|
||||
import ModInstallModal from '@/components/ui/ModInstallModal.vue'
|
||||
import { get_version, get_project } from '@/helpers/cache.js'
|
||||
import { install as installVersion } from '@/store/install.js'
|
||||
|
||||
const confirmModal = ref(null)
|
||||
const project = ref(null)
|
||||
const version = ref(null)
|
||||
const categories = ref(null)
|
||||
const installing = ref(false)
|
||||
const modInstallModal = ref(null)
|
||||
|
||||
defineExpose({
|
||||
async show(event) {
|
||||
if (event.event === 'InstallVersion') {
|
||||
version.value = await useFetch(
|
||||
`https://api.modrinth.com/v2/version/${encodeURIComponent(event.id)}`,
|
||||
'version',
|
||||
)
|
||||
project.value = await useFetch(
|
||||
`https://api.modrinth.com/v2/project/${encodeURIComponent(version.value.project_id)}`,
|
||||
'project',
|
||||
)
|
||||
version.value = await get_version(event.id).catch(handleError)
|
||||
project.value = await get_project(version.value.project_id).catch(handleError)
|
||||
} else {
|
||||
project.value = await useFetch(
|
||||
`https://api.modrinth.com/v2/project/${encodeURIComponent(event.id)}`,
|
||||
'project',
|
||||
)
|
||||
version.value = await useFetch(
|
||||
`https://api.modrinth.com/v2/version/${encodeURIComponent(project.value.versions[0])}`,
|
||||
'version',
|
||||
)
|
||||
project.value = await get_project(event.id).catch(handleError)
|
||||
version.value = await get_version(project.value.versions[0]).catch(handleError)
|
||||
}
|
||||
categories.value = (await get_categories().catch(handleError)).filter(
|
||||
(cat) => project.value.categories.includes(cat.name) && cat.project_type === 'mod',
|
||||
)
|
||||
confirmModal.value.show()
|
||||
categories.value = (await get_categories().catch(handleError)).filter(
|
||||
(cat) => project.value.categories.includes(cat.name) && cat.project_type === 'mod',
|
||||
)
|
||||
confirmModal.value.show()
|
||||
},
|
||||
})
|
||||
|
||||
async function install() {
|
||||
confirmModal.value.hide()
|
||||
if (project.value.project_type === 'modpack') {
|
||||
await packInstall(
|
||||
project.value.id,
|
||||
version.value.id,
|
||||
project.value.title,
|
||||
project.value.icon_url,
|
||||
).catch(handleError)
|
||||
|
||||
mixpanel.track('PackInstall', {
|
||||
id: project.value.id,
|
||||
version_id: version.value.id,
|
||||
title: project.value.title,
|
||||
source: 'ProjectPage',
|
||||
})
|
||||
} else {
|
||||
modInstallModal.value.show(
|
||||
project.value.id,
|
||||
[version.value],
|
||||
project.value.title,
|
||||
project.value.project_type,
|
||||
)
|
||||
}
|
||||
await installVersion(project.value.id, version.value.id, null, 'URLConfirmModal')
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -96,7 +56,6 @@ async function install() {
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
<ModInstallModal ref="modInstallModal" />
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
ref="incompatibleModal"
|
||||
header="Incompatibility warning"
|
||||
:noblur="!themeStore.advancedRendering"
|
||||
:on-hide="onInstall"
|
||||
>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
@@ -12,13 +13,11 @@
|
||||
</p>
|
||||
<table>
|
||||
<tr class="header">
|
||||
<th>{{ instance?.metadata.name }}</th>
|
||||
<th>{{ projectTitle }}</th>
|
||||
<th>{{ instance?.name }}</th>
|
||||
<th>{{ project.title }}</th>
|
||||
</tr>
|
||||
<tr class="content">
|
||||
<td class="data">
|
||||
{{ instance?.metadata.loader }} {{ instance?.metadata.game_version }}
|
||||
</td>
|
||||
<td class="data">{{ instance?.loader }} {{ instance?.game_version }}</td>
|
||||
<td>
|
||||
<DropdownSelect
|
||||
v-if="versions?.length > 1"
|
||||
@@ -68,34 +67,25 @@ const themeStore = useTheming()
|
||||
|
||||
const instance = ref(null)
|
||||
const project = ref(null)
|
||||
const projectType = ref(null)
|
||||
const projectTitle = ref(null)
|
||||
const versions = ref(null)
|
||||
const selectedVersion = ref(null)
|
||||
const incompatibleModal = ref(null)
|
||||
const installing = ref(false)
|
||||
|
||||
let markInstalled = () => {}
|
||||
let onInstall = ref(() => {})
|
||||
|
||||
defineExpose({
|
||||
show: (
|
||||
instanceVal,
|
||||
projectTitleVal,
|
||||
selectedVersions,
|
||||
extMarkInstalled,
|
||||
projectIdVal,
|
||||
projectTypeVal,
|
||||
) => {
|
||||
show: (instanceVal, projectVal, projectVersions, callback) => {
|
||||
instance.value = instanceVal
|
||||
projectTitle.value = projectTitleVal
|
||||
versions.value = selectedVersions
|
||||
selectedVersion.value = selectedVersions[0]
|
||||
versions.value = projectVersions
|
||||
selectedVersion.value = projectVersions[0]
|
||||
|
||||
project.value = projectIdVal
|
||||
projectType.value = projectTypeVal
|
||||
project.value = projectVal
|
||||
|
||||
onInstall.value = callback
|
||||
installing.value = false
|
||||
|
||||
incompatibleModal.value.show()
|
||||
markInstalled = extMarkInstalled
|
||||
|
||||
mixpanel_track('ProjectInstallStart', { source: 'ProjectIncompatibilityWarningModal' })
|
||||
},
|
||||
@@ -105,16 +95,16 @@ const install = async () => {
|
||||
installing.value = true
|
||||
await installMod(instance.value.path, selectedVersion.value.id).catch(handleError)
|
||||
installing.value = false
|
||||
markInstalled()
|
||||
onInstall.value(selectedVersion.value.id)
|
||||
incompatibleModal.value.hide()
|
||||
|
||||
mixpanel_track('ProjectInstall', {
|
||||
loader: instance.value.metadata.loader,
|
||||
game_version: instance.value.metadata.game_version,
|
||||
loader: instance.value.loader,
|
||||
game_version: instance.value.game_version,
|
||||
id: project.value,
|
||||
version_id: selectedVersion.value.id,
|
||||
project_type: projectType.value,
|
||||
title: projectTitle.value,
|
||||
project_type: project.value.project_type,
|
||||
title: project.value.title,
|
||||
source: 'ProjectIncompatibilityWarningModal',
|
||||
})
|
||||
}
|
||||
@@ -9,47 +9,55 @@ import { handleError } from '@/store/state.js'
|
||||
|
||||
const themeStore = useTheming()
|
||||
|
||||
const version = ref('')
|
||||
const title = ref('')
|
||||
const projectId = ref('')
|
||||
const icon = ref('')
|
||||
const versionId = ref()
|
||||
const project = ref()
|
||||
const confirmModal = ref(null)
|
||||
const installing = ref(false)
|
||||
|
||||
let onInstall = ref(() => {})
|
||||
|
||||
defineExpose({
|
||||
show: (projectIdVal, versionId, projectTitle, projectIcon) => {
|
||||
projectId.value = projectIdVal
|
||||
version.value = versionId
|
||||
title.value = projectTitle
|
||||
icon.value = projectIcon
|
||||
show: (projectVal, versionIdVal, callback) => {
|
||||
project.value = projectVal
|
||||
versionId.value = versionIdVal
|
||||
installing.value = false
|
||||
confirmModal.value.show()
|
||||
|
||||
onInstall.value = callback
|
||||
|
||||
mixpanel_track('PackInstallStart')
|
||||
},
|
||||
})
|
||||
|
||||
async function install() {
|
||||
installing.value = true
|
||||
console.log(`Installing ${projectId.value} ${version.value} ${title.value} ${icon.value}`)
|
||||
confirmModal.value.hide()
|
||||
|
||||
await pack_install(
|
||||
projectId.value,
|
||||
version.value,
|
||||
title.value,
|
||||
icon.value ? icon.value : null,
|
||||
project.value.id,
|
||||
versionId.value,
|
||||
project.value.title,
|
||||
project.value.icon_url,
|
||||
).catch(handleError)
|
||||
mixpanel_track('PackInstall', {
|
||||
id: projectId.value,
|
||||
version_id: version.value,
|
||||
title: title.value,
|
||||
id: project.value.id,
|
||||
version_id: versionId.value,
|
||||
title: project.value.title,
|
||||
source: 'ConfirmModal',
|
||||
})
|
||||
|
||||
onInstall.value(versionId.value)
|
||||
installing.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal ref="confirmModal" header="Are you sure?" :noblur="!themeStore.advancedRendering">
|
||||
<Modal
|
||||
ref="confirmModal"
|
||||
header="Are you sure?"
|
||||
:noblur="!themeStore.advancedRendering"
|
||||
:on-hide="onInstall"
|
||||
>
|
||||
<div class="modal-body">
|
||||
<p>You already have this modpack installed. Are you sure you want to install it again?</p>
|
||||
<div class="input-group push-right">
|
||||
@@ -14,10 +14,10 @@ import {
|
||||
check_installed,
|
||||
get,
|
||||
list,
|
||||
create,
|
||||
} from '@/helpers/profile'
|
||||
import { open } from '@tauri-apps/api/dialog'
|
||||
import { create } from '@/helpers/profile'
|
||||
import { installVersionDependencies } from '@/helpers/utils'
|
||||
import { installVersionDependencies } from '@/store/install.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||
import { useTheming } from '@/store/theme.js'
|
||||
@@ -27,13 +27,12 @@ import { tauri } from '@tauri-apps/api'
|
||||
const themeStore = useTheming()
|
||||
const router = useRouter()
|
||||
|
||||
const versions = ref([])
|
||||
const project = ref('')
|
||||
const projectTitle = ref('')
|
||||
const projectType = ref('')
|
||||
const versions = ref()
|
||||
const project = ref()
|
||||
|
||||
const installModal = ref(null)
|
||||
const installModal = ref()
|
||||
const searchFilter = ref('')
|
||||
|
||||
const showCreation = ref(false)
|
||||
const icon = ref(null)
|
||||
const name = ref(null)
|
||||
@@ -42,33 +41,65 @@ const loader = ref(null)
|
||||
const gameVersion = ref(null)
|
||||
const creatingInstance = ref(false)
|
||||
|
||||
defineExpose({
|
||||
show: async (projectId, selectedVersions, title, type) => {
|
||||
project.value = projectId
|
||||
versions.value = selectedVersions
|
||||
projectTitle.value = title
|
||||
projectType.value = type
|
||||
const profiles = ref([])
|
||||
|
||||
installModal.value.show()
|
||||
const shownProfiles = computed(() =>
|
||||
profiles.value
|
||||
.filter((profile) => {
|
||||
return profile.name.toLowerCase().includes(searchFilter.value.toLowerCase())
|
||||
})
|
||||
.filter((profile) => {
|
||||
let loaders = versions.value.flatMap((v) => v.loaders)
|
||||
|
||||
return (
|
||||
versions.value.flatMap((v) => v.game_versions).includes(profile.game_version) &&
|
||||
(project.value.project_type === 'mod'
|
||||
? loaders.includes(profile.loader) || loaders.includes('minecraft')
|
||||
: true)
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
let onInstall = ref(() => {})
|
||||
|
||||
defineExpose({
|
||||
show: async (projectVal, versionsVal, callback) => {
|
||||
project.value = projectVal
|
||||
versions.value = versionsVal
|
||||
searchFilter.value = ''
|
||||
|
||||
profiles.value = await getData()
|
||||
showCreation.value = false
|
||||
name.value = null
|
||||
icon.value = null
|
||||
display_icon.value = null
|
||||
gameVersion.value = null
|
||||
loader.value = null
|
||||
|
||||
onInstall.value = callback
|
||||
|
||||
const profilesVal = await list().catch(handleError)
|
||||
for (let profile of profilesVal) {
|
||||
profile.installing = false
|
||||
profile.installedMod = await check_installed(profile.path, project.value.id).catch(
|
||||
handleError,
|
||||
)
|
||||
}
|
||||
profiles.value = profilesVal
|
||||
|
||||
installModal.value.show()
|
||||
|
||||
mixpanel_track('ProjectInstallStart', { source: 'ProjectInstallModal' })
|
||||
},
|
||||
})
|
||||
|
||||
const profiles = ref([])
|
||||
|
||||
async function install(instance) {
|
||||
instance.installing = true
|
||||
const version = versions.value.find((v) => {
|
||||
return (
|
||||
v.game_versions.includes(instance.metadata.game_version) &&
|
||||
(v.loaders.includes(instance.metadata.loader) ||
|
||||
v.loaders.includes('minecraft') ||
|
||||
v.loaders.includes('iris') ||
|
||||
v.loaders.includes('optifine'))
|
||||
v.game_versions.includes(instance.game_version) &&
|
||||
(project.value.project_type === 'mod'
|
||||
? v.loaders.includes(instance.loader) || v.loaders.includes('minecraft')
|
||||
: true)
|
||||
)
|
||||
})
|
||||
|
||||
@@ -85,45 +116,18 @@ async function install(instance) {
|
||||
instance.installing = false
|
||||
|
||||
mixpanel_track('ProjectInstall', {
|
||||
loader: instance.metadata.loader,
|
||||
game_version: instance.metadata.game_version,
|
||||
id: project.value,
|
||||
loader: instance.loader,
|
||||
game_version: instance.game_version,
|
||||
id: project.value.id,
|
||||
version_id: version.id,
|
||||
project_type: projectType.value,
|
||||
title: projectTitle.value,
|
||||
project_type: project.value.project_type,
|
||||
title: project.value.title,
|
||||
source: 'ProjectInstallModal',
|
||||
})
|
||||
|
||||
onInstall.value(version.id)
|
||||
}
|
||||
|
||||
async function getData() {
|
||||
const projects = await list(true).then(Object.values).catch(handleError)
|
||||
|
||||
const filtered = projects
|
||||
.filter((profile) => {
|
||||
return profile.metadata.name.toLowerCase().includes(searchFilter.value.toLowerCase())
|
||||
})
|
||||
.filter((profile) => {
|
||||
return (
|
||||
versions.value.flatMap((v) => v.game_versions).includes(profile.metadata.game_version) &&
|
||||
versions.value
|
||||
.flatMap((v) => v.loaders)
|
||||
.some(
|
||||
(value) =>
|
||||
value === profile.metadata.loader ||
|
||||
['minecraft', 'iris', 'optifine'].includes(value),
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
for (let profile of filtered) {
|
||||
profile.installing = false
|
||||
profile.installedMod = await check_installed(profile.path, project.value).catch(handleError)
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
const alreadySentCreation = ref(false)
|
||||
const toggleCreation = () => {
|
||||
showCreation.value = !showCreation.value
|
||||
name.value = null
|
||||
@@ -132,8 +136,7 @@ const toggleCreation = () => {
|
||||
gameVersion.value = null
|
||||
loader.value = null
|
||||
|
||||
if (!alreadySentCreation.value) {
|
||||
alreadySentCreation.value = false
|
||||
if (showCreation.value) {
|
||||
mixpanel_track('InstanceCreateStart', { source: 'ProjectInstallModal' })
|
||||
}
|
||||
}
|
||||
@@ -197,18 +200,16 @@ const createInstance = async () => {
|
||||
game_version: versions.value[0].game_versions[0],
|
||||
id: project.value,
|
||||
version_id: versions.value[0].id,
|
||||
project_type: projectType.value,
|
||||
title: projectTitle.value,
|
||||
project_type: project.value.project_type,
|
||||
title: project.value.title,
|
||||
source: 'ProjectInstallModal',
|
||||
})
|
||||
|
||||
onInstall.value(versions.value[0].id)
|
||||
|
||||
if (installModal.value) installModal.value.hide()
|
||||
creatingInstance.value = false
|
||||
}
|
||||
|
||||
const check_valid = computed(() => {
|
||||
return name.value
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -216,6 +217,7 @@ const check_valid = computed(() => {
|
||||
ref="installModal"
|
||||
header="Install project to instance"
|
||||
:noblur="!themeStore.advancedRendering"
|
||||
:on-hide="onInstall"
|
||||
>
|
||||
<div class="modal-body">
|
||||
<input
|
||||
@@ -226,34 +228,27 @@ const check_valid = computed(() => {
|
||||
placeholder="Search for an instance"
|
||||
/>
|
||||
<div class="profiles" :class="{ 'hide-creation': !showCreation }">
|
||||
<div v-for="profile in profiles" :key="profile.metadata.name" class="option">
|
||||
<Button
|
||||
transparent
|
||||
class="profile-button"
|
||||
@click="$router.push(`/instance/${encodeURIComponent(profile.path)}`)"
|
||||
<div v-for="profile in shownProfiles" :key="profile.name" class="option">
|
||||
<router-link
|
||||
class="btn btn-transparent profile-button"
|
||||
:to="`/instance/${encodeURIComponent(profile.path)}`"
|
||||
@click="installModal.hide()"
|
||||
>
|
||||
<Avatar
|
||||
:src="
|
||||
!profile.metadata.icon ||
|
||||
(profile.metadata.icon && profile.metadata.icon.startsWith('http'))
|
||||
? profile.metadata.icon
|
||||
: tauri.convertFileSrc(profile.metadata?.icon)
|
||||
"
|
||||
:src="profile.icon_path ? tauri.convertFileSrc(profile.icon_path) : null"
|
||||
class="profile-image"
|
||||
/>
|
||||
{{ profile.metadata.name }}
|
||||
</Button>
|
||||
{{ profile.name }}
|
||||
</router-link>
|
||||
<div
|
||||
v-tooltip="
|
||||
profile.metadata.linked_data?.locked && !profile.installedMod
|
||||
profile.linked_data?.locked && !profile.installedMod
|
||||
? 'Unpair or unlock an instance to add mods.'
|
||||
: ''
|
||||
"
|
||||
>
|
||||
<Button
|
||||
:disabled="
|
||||
profile.installedMod || profile.installing || profile.metadata.linked_data?.locked
|
||||
"
|
||||
:disabled="profile.installedMod || profile.installing || profile.linked_data?.locked"
|
||||
@click="install(profile)"
|
||||
>
|
||||
<DownloadIcon v-if="!profile.installedMod && !profile.installing" />
|
||||
@@ -263,7 +258,7 @@ const check_valid = computed(() => {
|
||||
? 'Installing...'
|
||||
: profile.installedMod
|
||||
? 'Installed'
|
||||
: profile.metadata.linked_data && profile.metadata.linked_data.locked
|
||||
: profile.linked_data && profile.linked_data.locked
|
||||
? 'Paired'
|
||||
: 'Install'
|
||||
}}
|
||||
@@ -294,7 +289,7 @@ const check_valid = computed(() => {
|
||||
placeholder="Name"
|
||||
class="creation-input"
|
||||
/>
|
||||
<Button :disabled="creatingInstance === true || !check_valid" @click="createInstance()">
|
||||
<Button :disabled="creatingInstance === true || !name" @click="createInstance()">
|
||||
<RightArrowIcon />
|
||||
{{ creatingInstance ? 'Creating...' : 'Create' }}
|
||||
</Button>
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import { UserIcon, LockIcon, MailIcon } from '@modrinth/assets'
|
||||
import { Button, Card, Checkbox } from '@modrinth/ui'
|
||||
import { Button, Card, Checkbox, Modal } from '@modrinth/ui'
|
||||
import {
|
||||
DiscordIcon,
|
||||
GithubIcon,
|
||||
@@ -9,31 +9,57 @@ import {
|
||||
SteamIcon,
|
||||
GitLabIcon,
|
||||
} from '@/assets/external'
|
||||
import {
|
||||
authenticate_begin_flow,
|
||||
authenticate_await_completion,
|
||||
login_2fa,
|
||||
create_account,
|
||||
login_pass,
|
||||
} from '@/helpers/mr_auth.js'
|
||||
import { login, login_2fa, create_account, login_pass } from '@/helpers/mr_auth.js'
|
||||
import { handleError, useNotifications } from '@/store/state.js'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { ref } from 'vue'
|
||||
import { handleSevereError } from '@/store/error.js'
|
||||
|
||||
const props = defineProps({
|
||||
nextPage: {
|
||||
callback: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
prevPage: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
modal: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const modal = ref()
|
||||
const turnstileToken = ref()
|
||||
const widgetId = ref()
|
||||
|
||||
defineExpose({
|
||||
show: () => {
|
||||
modal.value.show()
|
||||
|
||||
if (window.turnstile === null || !window.turnstile) {
|
||||
const script = document.createElement('script')
|
||||
script.src =
|
||||
'https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback'
|
||||
script.async = true
|
||||
script.defer = true
|
||||
document.head.appendChild(script)
|
||||
|
||||
window.onloadTurnstileCallback = loadWidget
|
||||
} else {
|
||||
loadWidget()
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
function loadWidget() {
|
||||
widgetId.value = window.turnstile.render('#turnstile-container', {
|
||||
sitekey: '0x4AAAAAAAW3guHM6Eunbgwu',
|
||||
callback: (token) => (turnstileToken.value = token),
|
||||
expiredCallback: () => (turnstileToken.value = null),
|
||||
})
|
||||
}
|
||||
|
||||
function removeWidget() {
|
||||
if (widgetId.value) {
|
||||
window.turnstile.remove(widgetId.value)
|
||||
widgetId.value = null
|
||||
turnstileToken.value = null
|
||||
}
|
||||
}
|
||||
|
||||
const loggingIn = ref(true)
|
||||
const twoFactorFlow = ref(null)
|
||||
const twoFactorCode = ref('')
|
||||
@@ -45,22 +71,13 @@ const confirmPassword = ref('')
|
||||
const subscribe = ref(true)
|
||||
|
||||
async function signInOauth(provider) {
|
||||
const url = await authenticate_begin_flow(provider).catch(handleError)
|
||||
|
||||
await window.__TAURI_INVOKE__('tauri', {
|
||||
__tauriModule: 'Shell',
|
||||
message: {
|
||||
cmd: 'open',
|
||||
path: url,
|
||||
},
|
||||
})
|
||||
|
||||
const creds = await authenticate_await_completion().catch(handleError)
|
||||
const creds = await login(provider).catch(handleSevereError)
|
||||
|
||||
if (creds && creds.type === 'two_factor_required') {
|
||||
twoFactorFlow.value = creds.flow
|
||||
} else if (creds && creds.session) {
|
||||
props.nextPage()
|
||||
props.callback()
|
||||
modal.value.hide()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,22 +85,22 @@ async function signIn2fa() {
|
||||
const creds = await login_2fa(twoFactorCode.value, twoFactorFlow.value).catch(handleError)
|
||||
|
||||
if (creds && creds.session) {
|
||||
props.nextPage()
|
||||
props.callback()
|
||||
modal.value.hide()
|
||||
}
|
||||
}
|
||||
|
||||
async function signIn() {
|
||||
const creds = await login_pass(
|
||||
username.value,
|
||||
password.value,
|
||||
window.turnstile.getResponse(),
|
||||
).catch(handleError)
|
||||
window.turnstile.reset()
|
||||
const creds = await login_pass(username.value, password.value, turnstileToken.value).catch(
|
||||
handleError,
|
||||
)
|
||||
window.turnstile.reset(widgetId.value)
|
||||
|
||||
if (creds && creds.type === 'two_factor_required') {
|
||||
twoFactorFlow.value = creds.flow
|
||||
} else if (creds && creds.session) {
|
||||
props.nextPage()
|
||||
props.callback()
|
||||
modal.value.hide()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,117 +119,128 @@ async function createAccount() {
|
||||
username.value,
|
||||
email.value,
|
||||
password.value,
|
||||
window.turnstile.getResponse(),
|
||||
turnstileToken.value,
|
||||
subscribe.value,
|
||||
).catch(handleError)
|
||||
window.turnstile.reset()
|
||||
window.turnstile.reset(widgetId.value)
|
||||
|
||||
if (creds && creds.session) {
|
||||
props.nextPage()
|
||||
props.callback()
|
||||
modal.value.hide()
|
||||
}
|
||||
}
|
||||
|
||||
async function goToNextPage() {
|
||||
props.nextPage()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (window.turnstile === null || !window.turnstile) {
|
||||
const script = document.createElement('script')
|
||||
script.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js'
|
||||
script.async = true
|
||||
script.defer = true
|
||||
document.head.appendChild(script)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card>
|
||||
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAHWfmKCm7cUG869"></div>
|
||||
<template v-if="twoFactorFlow">
|
||||
<h1>Enter two-factor code</h1>
|
||||
<p>Please enter a two-factor code to proceed.</p>
|
||||
<input v-model="twoFactorCode" maxlength="11" type="text" placeholder="Enter code..." />
|
||||
</template>
|
||||
<template v-else>
|
||||
<h1 v-if="loggingIn">Login to Modrinth</h1>
|
||||
<h1 v-else>Create an account</h1>
|
||||
<div class="button-grid">
|
||||
<Button class="discord" large @click="signInOauth('discord')">
|
||||
<DiscordIcon />
|
||||
Discord
|
||||
</Button>
|
||||
<Button class="github" large @click="signInOauth('github')">
|
||||
<GithubIcon />
|
||||
Github
|
||||
</Button>
|
||||
<Button class="white" large @click="signInOauth('microsoft')">
|
||||
<MicrosoftIcon />
|
||||
Microsoft
|
||||
</Button>
|
||||
<Button class="google" large @click="signInOauth('google')">
|
||||
<GoogleIcon />
|
||||
Google
|
||||
</Button>
|
||||
<Button class="white" large @click="signInOauth('steam')">
|
||||
<SteamIcon />
|
||||
Steam
|
||||
</Button>
|
||||
<Button class="gitlab" large @click="signInOauth('gitlab')">
|
||||
<GitLabIcon />
|
||||
GitLab
|
||||
</Button>
|
||||
</div>
|
||||
<div class="divider">
|
||||
<hr />
|
||||
<p>Or</p>
|
||||
</div>
|
||||
<div v-if="!loggingIn" class="iconified-input username">
|
||||
<MailIcon />
|
||||
<input v-model="email" type="text" placeholder="Email" />
|
||||
</div>
|
||||
<div class="iconified-input username">
|
||||
<UserIcon />
|
||||
<input
|
||||
v-model="username"
|
||||
type="text"
|
||||
:placeholder="loggingIn ? 'Email or username' : 'Username'"
|
||||
<Modal ref="modal" :on-hide="removeWidget">
|
||||
<Card>
|
||||
<template v-if="twoFactorFlow">
|
||||
<h1>Enter two-factor code</h1>
|
||||
<p>Please enter a two-factor code to proceed.</p>
|
||||
<input v-model="twoFactorCode" maxlength="11" type="text" placeholder="Enter code..." />
|
||||
</template>
|
||||
<template v-else>
|
||||
<h1 v-if="loggingIn">Login to Modrinth</h1>
|
||||
<h1 v-else>Create an account</h1>
|
||||
<div class="button-grid">
|
||||
<Button class="discord" large @click="signInOauth('discord')">
|
||||
<DiscordIcon />
|
||||
Discord
|
||||
</Button>
|
||||
<Button class="github" large @click="signInOauth('github')">
|
||||
<GithubIcon />
|
||||
Github
|
||||
</Button>
|
||||
<Button class="white" large @click="signInOauth('microsoft')">
|
||||
<MicrosoftIcon />
|
||||
Microsoft
|
||||
</Button>
|
||||
<Button class="google" large @click="signInOauth('google')">
|
||||
<GoogleIcon />
|
||||
Google
|
||||
</Button>
|
||||
<Button class="white" large @click="signInOauth('steam')">
|
||||
<SteamIcon />
|
||||
Steam
|
||||
</Button>
|
||||
<Button class="gitlab" large @click="signInOauth('gitlab')">
|
||||
<GitLabIcon />
|
||||
GitLab
|
||||
</Button>
|
||||
</div>
|
||||
<div class="divider">
|
||||
<hr />
|
||||
<p>Or</p>
|
||||
</div>
|
||||
<div v-if="!loggingIn" class="iconified-input username">
|
||||
<MailIcon />
|
||||
<input v-model="email" type="text" placeholder="Email" />
|
||||
</div>
|
||||
<div class="iconified-input username">
|
||||
<UserIcon />
|
||||
<input
|
||||
v-model="username"
|
||||
type="text"
|
||||
:placeholder="loggingIn ? 'Email or username' : 'Username'"
|
||||
/>
|
||||
</div>
|
||||
<div class="iconified-input" :class="{ username: !loggingIn }">
|
||||
<LockIcon />
|
||||
<input v-model="password" type="password" placeholder="Password" />
|
||||
</div>
|
||||
<div v-if="!loggingIn" class="iconified-input username">
|
||||
<LockIcon />
|
||||
<input v-model="confirmPassword" type="password" placeholder="Confirm password" />
|
||||
</div>
|
||||
<div class="turnstile">
|
||||
<div id="turnstile-container"></div>
|
||||
<div id="turnstile-container-2"></div>
|
||||
</div>
|
||||
<Checkbox
|
||||
v-if="!loggingIn"
|
||||
v-model="subscribe"
|
||||
class="subscribe-btn"
|
||||
label="Subscribe to updates about Modrinth"
|
||||
/>
|
||||
<div class="link-row">
|
||||
<a v-if="loggingIn" class="button-base" @click="loggingIn = false"> Create account </a>
|
||||
<a v-else class="button-base" @click="loggingIn = true">Sign in</a>
|
||||
<a class="button-base" href="https://modrinth.com/auth/reset-password">
|
||||
Forgot password?
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<div class="button-row">
|
||||
<Button class="transparent" large>Close</Button>
|
||||
<Button v-if="twoFactorCode" color="primary" large @click="signIn2fa"> Login </Button>
|
||||
<Button
|
||||
v-else-if="loggingIn"
|
||||
color="primary"
|
||||
large
|
||||
@click="signIn"
|
||||
:disabled="!turnstileToken"
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
<Button v-else color="primary" large @click="createAccount" :disabled="!turnstileToken">
|
||||
Create account
|
||||
</Button>
|
||||
</div>
|
||||
<div class="iconified-input" :class="{ username: !loggingIn }">
|
||||
<LockIcon />
|
||||
<input v-model="password" type="password" placeholder="Password" />
|
||||
</div>
|
||||
<div v-if="!loggingIn" class="iconified-input username">
|
||||
<LockIcon />
|
||||
<input v-model="confirmPassword" type="password" placeholder="Confirm password" />
|
||||
</div>
|
||||
<Checkbox
|
||||
v-if="!loggingIn"
|
||||
v-model="subscribe"
|
||||
class="subscribe-btn"
|
||||
label="Subscribe to updates about Modrinth"
|
||||
/>
|
||||
<div class="link-row">
|
||||
<a v-if="loggingIn" class="button-base" @click="loggingIn = false"> Create account </a>
|
||||
<a v-else class="button-base" @click="loggingIn = true">Sign in</a>
|
||||
<a class="button-base" href="https://modrinth.com/auth/reset-password">
|
||||
Forgot password?
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<div class="button-row">
|
||||
<Button class="transparent" large @click="prevPage"> {{ modal ? 'Close' : 'Back' }} </Button>
|
||||
<Button v-if="twoFactorCode" color="primary" large @click="signIn2fa"> Login </Button>
|
||||
<Button v-else-if="loggingIn" color="primary" large @click="signIn"> Login </Button>
|
||||
<Button v-else color="primary" large @click="createAccount"> Create account </Button>
|
||||
<Button v-if="!modal" class="transparent" large @click="goToNextPage"> Next </Button>
|
||||
</div>
|
||||
</Card>
|
||||
</Card>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
:deep(.modal-container) {
|
||||
.modal-body {
|
||||
width: auto;
|
||||
|
||||
.content {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
width: 25rem;
|
||||
}
|
||||
@@ -321,4 +349,19 @@ onMounted(() => {
|
||||
:deep(.checkbox) {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.turnstile {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius-md);
|
||||
border: 2px solid var(--color-button-bg);
|
||||
height: 66px;
|
||||
margin-top: var(--gap-md);
|
||||
|
||||
iframe {
|
||||
margin: -1px;
|
||||
min-width: calc(100% + 2px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -34,7 +34,7 @@ const prevPage = () => {
|
||||
const finishOnboarding = async () => {
|
||||
mixpanel.track('OnboardingFinish')
|
||||
const settings = await get()
|
||||
settings.fully_onboarded = true
|
||||
settings.onboarded = true
|
||||
await set(settings)
|
||||
props.finish()
|
||||
}
|
||||
|
||||
@@ -45,11 +45,3 @@ export async function remove_user(user) {
|
||||
export async function users() {
|
||||
return await invoke('plugin:auth|auth_users')
|
||||
}
|
||||
|
||||
// Get a user by UUID
|
||||
// Prefer to use refresh() instead of this because it will refresh the credentials
|
||||
// user is UUID
|
||||
// Returns Credentials (of user)
|
||||
export async function get_user(user) {
|
||||
return await invoke('plugin:auth|auth_get_user', { user })
|
||||
}
|
||||
|
||||
49
apps/app-frontend/src/helpers/cache.js
Normal file
49
apps/app-frontend/src/helpers/cache.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
|
||||
export async function get_project(id) {
|
||||
return await invoke('plugin:cache|get_project', { id })
|
||||
}
|
||||
|
||||
export async function get_project_many(ids) {
|
||||
return await invoke('plugin:cache|get_project_many', { ids })
|
||||
}
|
||||
|
||||
export async function get_version(id) {
|
||||
return await invoke('plugin:cache|get_version', { id })
|
||||
}
|
||||
|
||||
export async function get_version_many(ids) {
|
||||
return await invoke('plugin:cache|get_version_many', { ids })
|
||||
}
|
||||
|
||||
export async function get_user(id) {
|
||||
return await invoke('plugin:cache|get_user', { id })
|
||||
}
|
||||
|
||||
export async function get_user_many(ids) {
|
||||
return await invoke('plugin:cache|get_user_many', { ids })
|
||||
}
|
||||
|
||||
export async function get_team(id) {
|
||||
return await invoke('plugin:cache|get_team', { id })
|
||||
}
|
||||
|
||||
export async function get_team_many(ids) {
|
||||
return await invoke('plugin:cache|get_team_many', { ids })
|
||||
}
|
||||
|
||||
export async function get_organization(id) {
|
||||
return await invoke('plugin:cache|get_organization', { id })
|
||||
}
|
||||
|
||||
export async function get_organization_many(ids) {
|
||||
return await invoke('plugin:cache|get_organization_many', { ids })
|
||||
}
|
||||
|
||||
export async function get_search_results(id) {
|
||||
return await invoke('plugin:cache|get_search_results', { id })
|
||||
}
|
||||
|
||||
export async function get_search_results_many(ids) {
|
||||
return await invoke('plugin:cache|get_search_results_many', { ids })
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
// event.payload is the payload object
|
||||
console.log(event)
|
||||
})
|
||||
|
||||
|
||||
Putting that in a script will print any emitted signal from rust
|
||||
*/
|
||||
import { listen } from '@tauri-apps/api/event'
|
||||
@@ -93,15 +93,3 @@ export async function command_listener(callback) {
|
||||
export async function warning_listener(callback) {
|
||||
return await listen('warning', (event) => callback(event.payload))
|
||||
}
|
||||
|
||||
/// Payload for the 'offline' event
|
||||
/*
|
||||
OfflinePayload {
|
||||
offline: bool, true or false
|
||||
}
|
||||
*/
|
||||
export async function offline_listener(callback) {
|
||||
return await listen('offline', (event) => {
|
||||
return callback(event.payload)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,6 +14,14 @@ JavaVersion {
|
||||
|
||||
*/
|
||||
|
||||
export async function get_java_versions() {
|
||||
return await invoke('plugin:jre|get_java_versions')
|
||||
}
|
||||
|
||||
export async function set_java_version(javaVersion) {
|
||||
return await invoke('plugin:jre|set_java_version', { javaVersion })
|
||||
}
|
||||
|
||||
// Finds all the installation of Java 7, if it exists
|
||||
// Returns [JavaVersion]
|
||||
export async function find_filtered_jres(version) {
|
||||
|
||||
@@ -6,34 +6,8 @@ export async function get_game_versions() {
|
||||
return await invoke('plugin:metadata|metadata_get_game_versions')
|
||||
}
|
||||
|
||||
// Gets the fabric versions from daedalus
|
||||
// Gets the given loader versions from daedalus
|
||||
// Returns Manifest
|
||||
export async function get_fabric_versions() {
|
||||
const c = await invoke('plugin:metadata|metadata_get_fabric_versions')
|
||||
console.log('Getting fabric versions', c)
|
||||
return c
|
||||
}
|
||||
|
||||
// Gets the forge versions from daedalus
|
||||
// Returns Manifest
|
||||
export async function get_forge_versions() {
|
||||
const c = await invoke('plugin:metadata|metadata_get_forge_versions')
|
||||
console.log('Getting forge versions', c)
|
||||
return c
|
||||
}
|
||||
|
||||
// Gets the quilt versions from daedalus
|
||||
// Returns Manifest
|
||||
export async function get_quilt_versions() {
|
||||
const c = await invoke('plugin:metadata|metadata_get_quilt_versions')
|
||||
console.log('Getting quilt versions', c)
|
||||
return c
|
||||
}
|
||||
|
||||
// Gets the neoforge versions from daedalus
|
||||
// Returns Manifest
|
||||
export async function get_neoforge_versions() {
|
||||
const c = await invoke('plugin:metadata|metadata_get_neoforge_versions')
|
||||
console.log('Getting neoforge versions', c)
|
||||
return c
|
||||
export async function get_loader_versions(loader) {
|
||||
return await invoke('plugin:metadata|metadata_get_loader_versions', { loader })
|
||||
}
|
||||
|
||||
@@ -5,17 +5,10 @@
|
||||
*/
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
|
||||
export async function authenticate_begin_flow(provider) {
|
||||
return await invoke('plugin:mr_auth|authenticate_begin_flow', { provider })
|
||||
export async function login(provider) {
|
||||
return await invoke('modrinth_auth_login', { provider })
|
||||
}
|
||||
|
||||
export async function authenticate_await_completion() {
|
||||
return await invoke('plugin:mr_auth|authenticate_await_completion')
|
||||
}
|
||||
|
||||
export async function cancel_flow() {
|
||||
return await invoke('plugin:mr_auth|cancel_flow')
|
||||
}
|
||||
export async function login_pass(username, password, challenge) {
|
||||
return await invoke('plugin:mr_auth|login_pass', { username, password, challenge })
|
||||
}
|
||||
@@ -34,10 +27,6 @@ export async function create_account(username, email, password, challenge, signU
|
||||
})
|
||||
}
|
||||
|
||||
export async function refresh() {
|
||||
return await invoke('plugin:mr_auth|refresh')
|
||||
}
|
||||
|
||||
export async function logout() {
|
||||
return await invoke('plugin:mr_auth|logout')
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ export async function install(projectId, versionId, packTitle, iconUrl) {
|
||||
profile_creator.gameVersion,
|
||||
profile_creator.modloader,
|
||||
profile_creator.loaderVersion,
|
||||
profile_creator.icon,
|
||||
null,
|
||||
true,
|
||||
)
|
||||
|
||||
return await invoke('plugin:pack|pack_install', { location, profile })
|
||||
@@ -39,7 +40,8 @@ export async function install_from_file(path) {
|
||||
profile_creator.gameVersion,
|
||||
profile_creator.modloader,
|
||||
profile_creator.loaderVersion,
|
||||
profile_creator.icon,
|
||||
null,
|
||||
true,
|
||||
)
|
||||
return await invoke('plugin:pack|pack_install', { location, profile })
|
||||
}
|
||||
|
||||
@@ -5,49 +5,19 @@
|
||||
*/
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
|
||||
/// Gets if a process has finished by UUID
|
||||
/// Returns bool
|
||||
export async function has_finished_by_uuid(uuid) {
|
||||
return await invoke('plugin:process|process_has_finished_by_uuid', { uuid })
|
||||
}
|
||||
|
||||
/// Gets process exit status by UUID
|
||||
/// Returns u32
|
||||
export async function get_exit_status_by_uuid(uuid) {
|
||||
return await invoke('plugin:process|process_get_exit_status_by_uuid', { uuid })
|
||||
}
|
||||
|
||||
/// Gets all process IDs
|
||||
/// Gets all running process IDs with a given profile path
|
||||
/// Returns [u32]
|
||||
export async function get_all_uuids() {
|
||||
return await invoke('plugin:process|process_get_all_uuids')
|
||||
}
|
||||
|
||||
/// Gets all running process IDs
|
||||
/// Returns [u32]
|
||||
export async function get_all_running_uuids() {
|
||||
return await invoke('plugin:process|process_get_all_running_uuids')
|
||||
export async function get_by_profile_path(path) {
|
||||
return await invoke('plugin:process|process_get_by_profile_path', { path })
|
||||
}
|
||||
|
||||
/// Gets all running process IDs with a given profile path
|
||||
/// Returns [u32]
|
||||
export async function get_uuids_by_profile_path(profilePath) {
|
||||
return await invoke('plugin:process|process_get_uuids_by_profile_path', { profilePath })
|
||||
}
|
||||
|
||||
/// Gets all running process IDs with a given profile path
|
||||
/// Returns [u32]
|
||||
export async function get_all_running_profile_paths(profilePath) {
|
||||
return await invoke('plugin:process|process_get_all_running_profile_paths', { profilePath })
|
||||
}
|
||||
|
||||
/// Gets all running process IDs with a given profile path
|
||||
/// Returns [u32]
|
||||
export async function get_all_running_profiles() {
|
||||
return await invoke('plugin:process|process_get_all_running_profiles')
|
||||
export async function get_all() {
|
||||
return await invoke('plugin:process|process_get_all')
|
||||
}
|
||||
|
||||
/// Kills a process by UUID
|
||||
export async function kill_by_uuid(uuid) {
|
||||
return await invoke('plugin:process|process_kill_by_uuid', { uuid })
|
||||
export async function kill(pid) {
|
||||
return await invoke('plugin:process|process_kill', { pid })
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import { invoke } from '@tauri-apps/api/tauri'
|
||||
- icon is a path to an image file, which will be copied into the profile directory
|
||||
*/
|
||||
|
||||
export async function create(name, gameVersion, modloader, loaderVersion, icon, noWatch) {
|
||||
export async function create(name, gameVersion, modloader, loaderVersion, iconPath, skipInstall) {
|
||||
//Trim string name to avoid "Unable to find directory"
|
||||
name = name.trim()
|
||||
return await invoke('plugin:profile_create|profile_create', {
|
||||
@@ -24,8 +24,8 @@ export async function create(name, gameVersion, modloader, loaderVersion, icon,
|
||||
gameVersion,
|
||||
modloader,
|
||||
loaderVersion,
|
||||
icon,
|
||||
noWatch,
|
||||
iconPath,
|
||||
skipInstall,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -41,8 +41,18 @@ export async function remove(path) {
|
||||
|
||||
// Get a profile by path
|
||||
// Returns a Profile
|
||||
export async function get(path, clearProjects) {
|
||||
return await invoke('plugin:profile|profile_get', { path, clearProjects })
|
||||
export async function get(path) {
|
||||
return await invoke('plugin:profile|profile_get', { path })
|
||||
}
|
||||
|
||||
export async function get_many(paths) {
|
||||
return await invoke('plugin:profile|profile_get_many', { paths })
|
||||
}
|
||||
|
||||
// Get a profile's projects
|
||||
// Returns a map of a path to profile file
|
||||
export async function get_projects(path) {
|
||||
return await invoke('plugin:profile|profile_get_projects', { path })
|
||||
}
|
||||
|
||||
// Get a profile's full fs path
|
||||
@@ -65,8 +75,8 @@ export async function get_optimal_jre_key(path) {
|
||||
|
||||
// Get a copy of the profile set
|
||||
// Returns hashmap of path -> Profile
|
||||
export async function list(clearProjects) {
|
||||
return await invoke('plugin:profile|profile_list', { clearProjects })
|
||||
export async function list() {
|
||||
return await invoke('plugin:profile|profile_list')
|
||||
}
|
||||
|
||||
export async function check_installed(path, projectId) {
|
||||
@@ -163,10 +173,8 @@ export async function run(path) {
|
||||
return await invoke('plugin:profile|profile_run', { path })
|
||||
}
|
||||
|
||||
// Run Minecraft using a pathed profile
|
||||
// Waits for end
|
||||
export async function run_wait(path) {
|
||||
return await invoke('plugin:profile|profile_run_wait', { path })
|
||||
export async function kill(path) {
|
||||
return await invoke('plugin:profile|profile_kill', { path })
|
||||
}
|
||||
|
||||
// Edits a profile
|
||||
|
||||
@@ -16,11 +16,6 @@ export async function progress_bars_list() {
|
||||
return await invoke('plugin:utils|progress_bars_list')
|
||||
}
|
||||
|
||||
// Check if any safe loading bars are active
|
||||
export async function check_safe_loading_bars_complete() {
|
||||
return await invoke('plugin:utils|safety_check_safe_loading_bars')
|
||||
}
|
||||
|
||||
// Get opening command
|
||||
// For example, if a user clicks on an .mrpack to open the app.
|
||||
// This should be called once and only when the app is done booting up and ready to receive a command
|
||||
@@ -28,8 +23,3 @@ export async function check_safe_loading_bars_complete() {
|
||||
export async function get_opening_command() {
|
||||
return await invoke('plugin:utils|get_opening_command')
|
||||
}
|
||||
|
||||
// Wait for settings to sync
|
||||
export async function await_sync() {
|
||||
return await invoke('plugin:utils|await_sync')
|
||||
}
|
||||
|
||||
@@ -5,11 +5,6 @@
|
||||
*/
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
|
||||
// Gets tag bundle of all tags
|
||||
export async function get_tag_bundle() {
|
||||
return await invoke('plugin:tags|tags_get_tag_bundle')
|
||||
}
|
||||
|
||||
// Gets cached category tags
|
||||
export async function get_categories() {
|
||||
return await invoke('plugin:tags|tags_get_categories')
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import {
|
||||
add_project_from_version as installMod,
|
||||
check_installed,
|
||||
get_full_path,
|
||||
get_mod_full_path,
|
||||
} from '@/helpers/profile'
|
||||
import { useFetch } from '@/helpers/fetch.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { get_full_path, get_mod_full_path } from '@/helpers/profile'
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
|
||||
export async function isDev() {
|
||||
@@ -49,55 +42,16 @@ export const releaseColor = (releaseType) => {
|
||||
}
|
||||
}
|
||||
|
||||
export const installVersionDependencies = async (profile, version) => {
|
||||
for (const dep of version.dependencies) {
|
||||
if (dep.dependency_type !== 'required') continue
|
||||
// disallow fabric api install on quilt
|
||||
if (dep.project_id === 'P7dR8mSH' && profile.metadata.loader === 'quilt') continue
|
||||
if (dep.version_id) {
|
||||
if (
|
||||
dep.project_id &&
|
||||
(await check_installed(profile.path, dep.project_id).catch(handleError))
|
||||
)
|
||||
continue
|
||||
await installMod(profile.path, dep.version_id)
|
||||
} else {
|
||||
if (
|
||||
dep.project_id &&
|
||||
(await check_installed(profile.path, dep.project_id).catch(handleError))
|
||||
)
|
||||
continue
|
||||
const depVersions = await useFetch(
|
||||
`https://api.modrinth.com/v2/project/${dep.project_id}/version`,
|
||||
'dependency versions',
|
||||
)
|
||||
const latest = depVersions.find(
|
||||
(v) =>
|
||||
v.game_versions.includes(profile.metadata.game_version) &&
|
||||
v.loaders.includes(profile.metadata.loader),
|
||||
)
|
||||
if (latest) {
|
||||
await installMod(profile.path, latest.id).catch(handleError)
|
||||
}
|
||||
export function debounce(fn, wait) {
|
||||
let timer
|
||||
return function (...args) {
|
||||
if (timer) {
|
||||
clearTimeout(timer) // clear any pre-existing timer
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const context = this // get the current context
|
||||
timer = setTimeout(() => {
|
||||
fn.apply(context, args) // call the function if time expires
|
||||
}, wait)
|
||||
}
|
||||
}
|
||||
|
||||
export const openLink = (url) => {
|
||||
window.__TAURI_INVOKE__('tauri', {
|
||||
__tauriModule: 'Shell',
|
||||
message: {
|
||||
cmd: 'open',
|
||||
path: url,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const refreshOffline = async () => {
|
||||
return await invoke('plugin:utils|refresh_offline', {})
|
||||
}
|
||||
|
||||
// returns true/false
|
||||
export const isOffline = async () => {
|
||||
return await invoke('plugin:utils|is_offline', {})
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
NavRow,
|
||||
Card,
|
||||
SearchFilter,
|
||||
Avatar,
|
||||
} from '@modrinth/ui'
|
||||
import { formatCategoryHeader, formatCategory } from '@modrinth/utils'
|
||||
import Multiselect from 'vue-multiselect'
|
||||
@@ -17,29 +18,22 @@ import { handleError } from '@/store/state'
|
||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||
import { get_categories, get_loaders, get_game_versions } from '@/helpers/tags'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { Avatar } from '@modrinth/ui'
|
||||
import SearchCard from '@/components/ui/SearchCard.vue'
|
||||
import InstallConfirmModal from '@/components/ui/InstallConfirmModal.vue'
|
||||
import ModInstallModal from '@/components/ui/ModInstallModal.vue'
|
||||
import SplashScreen from '@/components/ui/SplashScreen.vue'
|
||||
import IncompatibilityWarningModal from '@/components/ui/IncompatibilityWarningModal.vue'
|
||||
import { useFetch } from '@/helpers/fetch.js'
|
||||
import { check_installed, get, get as getInstance } from '@/helpers/profile.js'
|
||||
import { get as getInstance, get_projects as getInstanceProjects } from '@/helpers/profile.js'
|
||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||
import { isOffline } from '@/helpers/utils'
|
||||
import { offline_listener } from '@/helpers/events'
|
||||
|
||||
import { get_search_results } from '@/helpers/cache.js'
|
||||
import { debounce } from '@/helpers/utils.js'
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const offline = ref(await isOffline())
|
||||
const unlistenOffline = await offline_listener((b) => {
|
||||
offline.value = b
|
||||
const offline = ref(!navigator.onLine)
|
||||
window.addEventListener('offline', () => {
|
||||
offline.value = true
|
||||
})
|
||||
window.addEventListener('online', () => {
|
||||
offline.value = false
|
||||
})
|
||||
|
||||
const confirmModal = ref(null)
|
||||
const modInstallModal = ref(null)
|
||||
const incompatibilityWarningModal = ref(null)
|
||||
|
||||
const breadcrumbs = useBreadcrumbs()
|
||||
breadcrumbs.setContext({ name: 'Browse', link: route.path, query: route.query })
|
||||
@@ -65,6 +59,7 @@ const maxResults = ref(20)
|
||||
const currentPage = ref(1)
|
||||
const projectType = ref(route.params.projectType)
|
||||
const instanceContext = ref(null)
|
||||
const instanceProjects = ref(null)
|
||||
const ignoreInstanceLoaders = ref(false)
|
||||
const ignoreInstanceGameVersions = ref(false)
|
||||
|
||||
@@ -88,7 +83,10 @@ if (route.query.il) {
|
||||
ignoreInstanceLoaders.value = route.query.il === 'true'
|
||||
}
|
||||
if (route.query.i) {
|
||||
instanceContext.value = await getInstance(route.query.i, true)
|
||||
;[instanceContext.value, instanceProjects.value] = await Promise.all([
|
||||
getInstance(route.query.i).catch(handleError),
|
||||
getInstanceProjects(route.query.i).catch(handleError),
|
||||
])
|
||||
}
|
||||
if (route.query.q) {
|
||||
query.value = route.query.q
|
||||
@@ -144,18 +142,16 @@ if (route.query.ai) {
|
||||
}
|
||||
|
||||
async function refreshSearch() {
|
||||
const base = 'https://api.modrinth.com/v2/'
|
||||
|
||||
const params = [`limit=${maxResults.value}`, `index=${sortType.value.name}`]
|
||||
if (query.value.length > 0) {
|
||||
params.push(`query=${query.value.replace(/ /g, '+')}`)
|
||||
}
|
||||
if (instanceContext.value) {
|
||||
if (!ignoreInstanceLoaders.value && projectType.value === 'mod') {
|
||||
orFacets.value = [`categories:${encodeURIComponent(instanceContext.value.metadata.loader)}`]
|
||||
orFacets.value = [`categories:${encodeURIComponent(instanceContext.value.loader)}`]
|
||||
}
|
||||
if (!ignoreInstanceGameVersions.value) {
|
||||
selectedVersions.value = [instanceContext.value.metadata.game_version]
|
||||
selectedVersions.value = [instanceContext.value.game_version]
|
||||
}
|
||||
}
|
||||
if (
|
||||
@@ -224,13 +220,11 @@ async function refreshSearch() {
|
||||
}
|
||||
|
||||
if (hideAlreadyInstalled.value) {
|
||||
const installedMods = await get(instanceContext.value.path, false).then((x) =>
|
||||
Object.values(x.projects)
|
||||
.filter((x) => x.metadata.project)
|
||||
.map((x) => x.metadata.project.id),
|
||||
)
|
||||
const installedMods = Object.values(instanceProjects.value)
|
||||
.filter((x) => x.metadata)
|
||||
.map((x) => x.metadata.project_id)
|
||||
|
||||
installedMods.map((x) => [`project_id != ${x}`]).forEach((x) => formattedFacets.push(x))
|
||||
console.log(`facets=${JSON.stringify(formattedFacets)}`)
|
||||
}
|
||||
|
||||
params.push(`facets=${JSON.stringify(formattedFacets)}`)
|
||||
@@ -246,24 +240,24 @@ async function refreshSearch() {
|
||||
}
|
||||
}
|
||||
|
||||
let val = `${base}${url}`
|
||||
|
||||
let rawResults = await useFetch(val, 'search results', offline.value)
|
||||
let rawResults = await get_search_results(`?${url}`)
|
||||
if (!rawResults) {
|
||||
rawResults = {
|
||||
hits: [],
|
||||
total_hits: 0,
|
||||
limit: 1,
|
||||
result: {
|
||||
hits: [],
|
||||
total_hits: 0,
|
||||
limit: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
if (instanceContext.value) {
|
||||
for (val of rawResults.hits) {
|
||||
val.installed = await check_installed(instanceContext.value.path, val.project_id).then(
|
||||
(x) => (val.installed = x),
|
||||
for (const val of rawResults.result.hits) {
|
||||
val.installed = Object.values(instanceProjects.value).some(
|
||||
(x) => x.metadata && x.metadata.project_id === val.project_id,
|
||||
)
|
||||
}
|
||||
}
|
||||
results.value = rawResults
|
||||
results.value = rawResults.result
|
||||
}
|
||||
|
||||
async function onSearchChange(newPageNumber) {
|
||||
@@ -282,6 +276,8 @@ async function onSearchChange(newPageNumber) {
|
||||
}
|
||||
}
|
||||
|
||||
const debouncedSearchChange = debounce(() => onSearchChange(1), 200)
|
||||
|
||||
const searchWrapper = ref(null)
|
||||
async function onSearchChangeToTop(newPageNumber) {
|
||||
await onSearchChange(newPageNumber)
|
||||
@@ -505,13 +501,13 @@ const selectableProjectTypes = computed(() => {
|
||||
if (instanceContext.value) {
|
||||
if (
|
||||
availableGameVersions.value.findIndex(
|
||||
(x) => x.version === instanceContext.value.metadata.game_version,
|
||||
(x) => x.version === instanceContext.value.game_version,
|
||||
) <= availableGameVersions.value.findIndex((x) => x.version === '1.13')
|
||||
) {
|
||||
values.unshift({ label: 'Data Packs', href: `/browse/datapack` })
|
||||
}
|
||||
|
||||
if (instanceContext.value.metadata.loader !== 'vanilla') {
|
||||
if (instanceContext.value.loader !== 'vanilla') {
|
||||
values.unshift({ label: 'Mods', href: '/browse/mod' })
|
||||
}
|
||||
} else {
|
||||
@@ -528,8 +524,6 @@ const showVersions = computed(
|
||||
)
|
||||
|
||||
const isModProject = computed(() => ['modpack', 'mod'].includes(projectType.value))
|
||||
|
||||
onUnmounted(() => unlistenOffline())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -538,27 +532,19 @@ onUnmounted(() => unlistenOffline())
|
||||
<Card v-if="instanceContext" class="small-instance">
|
||||
<router-link :to="`/instance/${encodeURIComponent(instanceContext.path)}`" class="instance">
|
||||
<Avatar
|
||||
:src="
|
||||
!instanceContext.metadata.icon ||
|
||||
(instanceContext.metadata.icon && instanceContext.metadata.icon.startsWith('http'))
|
||||
? instanceContext.metadata.icon
|
||||
: convertFileSrc(instanceContext.metadata.icon)
|
||||
"
|
||||
:alt="instanceContext.metadata.name"
|
||||
:src="instanceContext.icon_path ? convertFileSrc(instanceContext.icon_path) : null"
|
||||
:alt="instanceContext.name"
|
||||
size="sm"
|
||||
/>
|
||||
<div class="small-instance_info">
|
||||
<span class="title">{{
|
||||
instanceContext.metadata.name.length > 20
|
||||
? instanceContext.metadata.name.substring(0, 20) + '...'
|
||||
: instanceContext.metadata.name
|
||||
instanceContext.name.length > 20
|
||||
? instanceContext.name.substring(0, 20) + '...'
|
||||
: instanceContext.name
|
||||
}}</span>
|
||||
<span>
|
||||
{{
|
||||
instanceContext.metadata.loader.charAt(0).toUpperCase() +
|
||||
instanceContext.metadata.loader.slice(1)
|
||||
}}
|
||||
{{ instanceContext.metadata.game_version }}
|
||||
{{ instanceContext.loader.charAt(0).toUpperCase() + instanceContext.loader.slice(1) }}
|
||||
{{ instanceContext.game_version }}
|
||||
</span>
|
||||
</div>
|
||||
</router-link>
|
||||
@@ -598,7 +584,10 @@ onUnmounted(() => unlistenOffline())
|
||||
>
|
||||
<ClearIcon /> Clear filters
|
||||
</Button>
|
||||
<div v-if="isModProject || projectType === 'shader'" class="loaders">
|
||||
<div
|
||||
v-if="(isModProject && ignoreInstanceLoaders) || projectType === 'shader'"
|
||||
class="loaders"
|
||||
>
|
||||
<h2>Loaders</h2>
|
||||
<div v-for="loader in filteredLoaders" :key="loader">
|
||||
<SearchFilter
|
||||
@@ -693,9 +682,10 @@ onUnmounted(() => unlistenOffline())
|
||||
<input
|
||||
v-model="query"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
type="text"
|
||||
:placeholder="`Search ${projectType}s...`"
|
||||
@input="onSearchChange(1)"
|
||||
@input="debouncedSearchChange()"
|
||||
/>
|
||||
<Button class="r-btn" @click="() => clearSearch()">
|
||||
<XIcon />
|
||||
@@ -752,9 +742,6 @@ onUnmounted(() => unlistenOffline())
|
||||
loader.supported_project_types?.includes(projectType),
|
||||
),
|
||||
]"
|
||||
:confirm-modal="confirmModal"
|
||||
:mod-install-modal="modInstallModal"
|
||||
:incompatibility-warning-modal="incompatibilityWarningModal"
|
||||
:installed="result.installed"
|
||||
/>
|
||||
</section>
|
||||
@@ -768,9 +755,6 @@ onUnmounted(() => unlistenOffline())
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
<InstallConfirmModal ref="confirmModal" />
|
||||
<ModInstallModal ref="modInstallModal" />
|
||||
<IncompatibilityWarningModal ref="incompatibilityWarningModal" />
|
||||
</template>
|
||||
|
||||
<style src="vue-multiselect/dist/vue-multiselect.css"></style>
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
<script setup>
|
||||
import { ref, onUnmounted, shallowRef, computed } from 'vue'
|
||||
import { ref, onUnmounted, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import RowDisplay from '@/components/RowDisplay.vue'
|
||||
import { list } from '@/helpers/profile.js'
|
||||
import { offline_listener, profile_listener } from '@/helpers/events'
|
||||
import { profile_listener } from '@/helpers/events'
|
||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||
import { useFetch } from '@/helpers/fetch.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import dayjs from 'dayjs'
|
||||
import { isOffline } from '@/helpers/utils'
|
||||
import { get_search_results } from '@/helpers/cache.js'
|
||||
|
||||
const featuredModpacks = ref({})
|
||||
const featuredMods = ref({})
|
||||
@@ -19,45 +18,55 @@ const breadcrumbs = useBreadcrumbs()
|
||||
|
||||
breadcrumbs.setRootContext({ name: 'Home', link: route.path })
|
||||
|
||||
const recentInstances = shallowRef([])
|
||||
const recentInstances = ref([])
|
||||
|
||||
const offline = ref(await isOffline())
|
||||
const offline = ref(!navigator.onLine)
|
||||
window.addEventListener('offline', () => {
|
||||
offline.value = true
|
||||
})
|
||||
window.addEventListener('online', () => {
|
||||
offline.value = false
|
||||
})
|
||||
|
||||
const getInstances = async () => {
|
||||
const profiles = await list(true).catch(handleError)
|
||||
recentInstances.value = Object.values(profiles).sort((a, b) => {
|
||||
return dayjs(b.metadata.last_played ?? 0).diff(dayjs(a.metadata.last_played ?? 0))
|
||||
const profiles = await list().catch(handleError)
|
||||
|
||||
recentInstances.value = profiles.sort((a, b) => {
|
||||
const dateA = dayjs(a.last_played ?? 0)
|
||||
const dateB = dayjs(b.last_played ?? 0)
|
||||
|
||||
if (dateA.isSame(dateB)) {
|
||||
return a.name.localeCompare(b.name)
|
||||
}
|
||||
|
||||
return dateB - dateA
|
||||
})
|
||||
|
||||
let filters = []
|
||||
for (const instance of recentInstances.value) {
|
||||
if (instance.metadata.linked_data && instance.metadata.linked_data.project_id) {
|
||||
filters.push(`NOT"project_id"="${instance.metadata.linked_data.project_id}"`)
|
||||
if (instance.linked_data && instance.linked_data.project_id) {
|
||||
filters.push(`NOT"project_id"="${instance.linked_data.project_id}"`)
|
||||
}
|
||||
}
|
||||
filter.value = filters.join(' AND ')
|
||||
}
|
||||
|
||||
const getFeaturedModpacks = async () => {
|
||||
const response = await useFetch(
|
||||
`https://api.modrinth.com/v2/search?facets=[["project_type:modpack"]]&limit=10&index=follows&filters=${filter.value}`,
|
||||
'featured modpacks',
|
||||
offline.value,
|
||||
const response = await get_search_results(
|
||||
`?facets=[["project_type:modpack"]]&limit=10&index=follows&filters=${filter.value}`,
|
||||
)
|
||||
|
||||
if (response) {
|
||||
featuredModpacks.value = response.hits
|
||||
featuredModpacks.value = response.result.hits
|
||||
} else {
|
||||
featuredModpacks.value = []
|
||||
}
|
||||
}
|
||||
const getFeaturedMods = async () => {
|
||||
const response = await useFetch(
|
||||
'https://api.modrinth.com/v2/search?facets=[["project_type:mod"]]&limit=10&index=follows',
|
||||
'featured mods',
|
||||
offline.value,
|
||||
)
|
||||
const response = await get_search_results('?facets=[["project_type:mod"]]&limit=10&index=follows')
|
||||
|
||||
if (response) {
|
||||
featuredMods.value = response.hits
|
||||
featuredMods.value = response.result.hits
|
||||
} else {
|
||||
featuredModpacks.value = []
|
||||
}
|
||||
@@ -69,14 +78,8 @@ await Promise.all([getFeaturedModpacks(), getFeaturedMods()])
|
||||
|
||||
const unlistenProfile = await profile_listener(async (e) => {
|
||||
await getInstances()
|
||||
if (e.event === 'created' || e.event === 'removed') {
|
||||
await Promise.all([getFeaturedModpacks(), getFeaturedMods()])
|
||||
}
|
||||
})
|
||||
|
||||
const unlistenOffline = await offline_listener(async (b) => {
|
||||
offline.value = b
|
||||
if (!b) {
|
||||
if (e.event === 'added' || e.event === 'created' || e.event === 'removed') {
|
||||
await Promise.all([getFeaturedModpacks(), getFeaturedMods()])
|
||||
}
|
||||
})
|
||||
@@ -92,7 +95,6 @@ const total = computed(() => {
|
||||
|
||||
onUnmounted(() => {
|
||||
unlistenProfile()
|
||||
unlistenOffline()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -105,6 +107,7 @@ onUnmounted(() => {
|
||||
label: 'Jump back in',
|
||||
route: '/library',
|
||||
instances: recentInstances,
|
||||
instance: true,
|
||||
downloaded: true,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -4,34 +4,33 @@ import GridDisplay from '@/components/GridDisplay.vue'
|
||||
import { list } from '@/helpers/profile.js'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||
import { offline_listener, profile_listener } from '@/helpers/events.js'
|
||||
import { profile_listener } from '@/helpers/events.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { PlusIcon } from '@modrinth/assets'
|
||||
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
|
||||
import { NewInstanceImage } from '@/assets/icons'
|
||||
import { isOffline } from '@/helpers/utils'
|
||||
|
||||
const route = useRoute()
|
||||
const breadcrumbs = useBreadcrumbs()
|
||||
|
||||
breadcrumbs.setRootContext({ name: 'Library', link: route.path })
|
||||
|
||||
const profiles = await list(true).catch(handleError)
|
||||
const instances = shallowRef(Object.values(profiles))
|
||||
const instances = shallowRef(await list().catch(handleError))
|
||||
|
||||
const offline = ref(await isOffline())
|
||||
const unlistenOffline = await offline_listener((b) => {
|
||||
offline.value = b
|
||||
const offline = ref(!navigator.onLine)
|
||||
window.addEventListener('offline', () => {
|
||||
offline.value = true
|
||||
})
|
||||
window.addEventListener('online', () => {
|
||||
offline.value = false
|
||||
})
|
||||
|
||||
const unlistenProfile = await profile_listener(async () => {
|
||||
const profiles = await list(true).catch(handleError)
|
||||
instances.value = Object.values(profiles)
|
||||
instances.value = await list().catch(handleError)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
unlistenProfile()
|
||||
unlistenOffline()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { LogOutIcon, LogInIcon, BoxIcon, FolderSearchIcon, UpdatedIcon } from '@
|
||||
import { Card, Slider, DropdownSelect, Toggle, Modal, Button } from '@modrinth/ui'
|
||||
import { handleError, useTheming } from '@/store/state'
|
||||
import { is_dir_writeable, change_config_dir, get, set } from '@/helpers/settings'
|
||||
import { get_max_memory } from '@/helpers/jre'
|
||||
import { get_java_versions, get_max_memory, set_java_version } from '@/helpers/jre'
|
||||
import { get as getCreds, logout } from '@/helpers/mr_auth.js'
|
||||
import JavaSelector from '@/components/ui/JavaSelector.vue'
|
||||
import ModrinthLoginScreen from '@/components/ui/tutorial/ModrinthLoginScreen.vue'
|
||||
@@ -12,6 +12,7 @@ import { mixpanel_opt_out_tracking, mixpanel_opt_in_tracking } from '@/helpers/m
|
||||
import { open } from '@tauri-apps/api/dialog'
|
||||
import { getOS } from '@/helpers/utils.js'
|
||||
import { getVersion } from '@tauri-apps/api/app'
|
||||
import { get_user } from '@/helpers/cache.js'
|
||||
|
||||
const pageOptions = ['Home', 'Library']
|
||||
|
||||
@@ -22,8 +23,8 @@ const version = await getVersion()
|
||||
const accessSettings = async () => {
|
||||
const settings = await get()
|
||||
|
||||
settings.javaArgs = settings.custom_java_args.join(' ')
|
||||
settings.envArgs = settings.custom_env_args.map((x) => x.join('=')).join(' ')
|
||||
settings.launchArgs = settings.extra_launch_args.join(' ')
|
||||
settings.envVars = settings.custom_env_vars.map((x) => x.join('=')).join(' ')
|
||||
|
||||
return settings
|
||||
}
|
||||
@@ -31,7 +32,8 @@ const accessSettings = async () => {
|
||||
const fetchSettings = await accessSettings().catch(handleError)
|
||||
|
||||
const settings = ref(fetchSettings)
|
||||
const settingsDir = ref(settings.value.loaded_config_dir)
|
||||
// const settingsDir = ref(settings.value.loaded_config_dir)
|
||||
|
||||
const maxMemory = ref(Math.floor((await get_max_memory().catch(handleError)) / 1024))
|
||||
|
||||
watch(
|
||||
@@ -43,26 +45,14 @@ watch(
|
||||
|
||||
const setSettings = JSON.parse(JSON.stringify(newSettings))
|
||||
|
||||
if (setSettings.opt_out_analytics) {
|
||||
if (setSettings.telemetry) {
|
||||
mixpanel_opt_out_tracking()
|
||||
} else {
|
||||
mixpanel_opt_in_tracking()
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(setSettings.java_globals)) {
|
||||
if (value?.path === '') {
|
||||
value.path = undefined
|
||||
}
|
||||
|
||||
if (value?.path) {
|
||||
value.path = value.path.replace('java.exe', 'javaw.exe')
|
||||
}
|
||||
|
||||
console.log(`${key}: ${value}`)
|
||||
}
|
||||
|
||||
setSettings.custom_java_args = setSettings.javaArgs.trim().split(/\s+/).filter(Boolean)
|
||||
setSettings.custom_env_args = setSettings.envArgs
|
||||
setSettings.extra_launch_args = setSettings.launchArgs.trim().split(/\s+/).filter(Boolean)
|
||||
setSettings.custom_env_vars = setSettings.envVars
|
||||
.trim()
|
||||
.split(/\s+/)
|
||||
.filter(Boolean)
|
||||
@@ -78,22 +68,49 @@ watch(
|
||||
setSettings.hooks.post_exit = null
|
||||
}
|
||||
|
||||
if (!setSettings.custom_dir) {
|
||||
setSettings.custom_dir = null
|
||||
}
|
||||
|
||||
await set(setSettings)
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
const credentials = ref(await getCreds().catch(handleError))
|
||||
const javaVersions = ref(await get_java_versions().catch(handleError))
|
||||
async function updateJavaVersion(version) {
|
||||
if (version?.path === '') {
|
||||
version.path = undefined
|
||||
}
|
||||
|
||||
if (version?.path) {
|
||||
version.path = version.path.replace('java.exe', 'javaw.exe')
|
||||
}
|
||||
|
||||
await set_java_version(version).catch(handleError)
|
||||
}
|
||||
|
||||
async function fetchCredentials() {
|
||||
const creds = await getCreds().catch(handleError)
|
||||
console.log(creds)
|
||||
if (creds && creds.user_id) {
|
||||
creds.user = await get_user(creds.user_id).catch(handleError)
|
||||
}
|
||||
credentials.value = creds
|
||||
}
|
||||
|
||||
const credentials = ref()
|
||||
await fetchCredentials()
|
||||
|
||||
const loginScreenModal = ref()
|
||||
|
||||
async function logOut() {
|
||||
await logout().catch(handleError)
|
||||
credentials.value = await getCreds().catch(handleError)
|
||||
await fetchCredentials()
|
||||
}
|
||||
|
||||
async function signInAfter() {
|
||||
loginScreenModal.value.hide()
|
||||
credentials.value = await getCreds().catch(handleError)
|
||||
await fetchCredentials()
|
||||
}
|
||||
|
||||
async function findLauncherDir() {
|
||||
@@ -103,24 +120,10 @@ async function findLauncherDir() {
|
||||
title: 'Select a new app directory',
|
||||
})
|
||||
|
||||
const writeable = await is_dir_writeable(newDir)
|
||||
|
||||
if (!writeable) {
|
||||
handleError('The selected directory does not have proper permissions for write access.')
|
||||
return
|
||||
}
|
||||
|
||||
if (newDir) {
|
||||
settingsDir.value = newDir
|
||||
await refreshDir()
|
||||
settings.value.custom_dir = newDir
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshDir() {
|
||||
await change_config_dir(settingsDir.value).catch(handleError)
|
||||
settings.value = await accessSettings().catch(handleError)
|
||||
settingsDir.value = settings.value.loaded_config_dir
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -131,13 +134,7 @@ async function refreshDir() {
|
||||
<span class="label__title size-card-header">General settings</span>
|
||||
</h3>
|
||||
</div>
|
||||
<Modal
|
||||
ref="loginScreenModal"
|
||||
class="login-screen-modal"
|
||||
:noblur="!themeStore.advancedRendering"
|
||||
>
|
||||
<ModrinthLoginScreen :modal="true" :prev-page="signInAfter" :next-page="signInAfter" />
|
||||
</Modal>
|
||||
<ModrinthLoginScreen ref="loginScreenModal" :callback="signInAfter" />
|
||||
<div class="adjacent-input">
|
||||
<label for="theme">
|
||||
<span class="label__title">Manage account</span>
|
||||
@@ -164,15 +161,11 @@ async function refreshDir() {
|
||||
<div class="app-directory">
|
||||
<div class="iconified-input">
|
||||
<BoxIcon />
|
||||
<input id="appDir" v-model="settingsDir" type="text" class="input" />
|
||||
<input id="appDir" v-model="settings.custom_dir" type="text" class="input" />
|
||||
<Button class="r-btn" @click="findLauncherDir">
|
||||
<FolderSearchIcon />
|
||||
</Button>
|
||||
</div>
|
||||
<Button large @click="refreshDir">
|
||||
<UpdatedIcon />
|
||||
Refresh
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
<Card>
|
||||
@@ -230,11 +223,11 @@ async function refreshDir() {
|
||||
</label>
|
||||
<Toggle
|
||||
id="minimize-launcher"
|
||||
:model-value="settings.hide_on_process"
|
||||
:checked="settings.hide_on_process"
|
||||
:model-value="settings.hide_on_process_start"
|
||||
:checked="settings.hide_on_process_start"
|
||||
@update:model-value="
|
||||
(e) => {
|
||||
settings.hide_on_process = e
|
||||
settings.hide_on_process_start = e
|
||||
}
|
||||
"
|
||||
/>
|
||||
@@ -285,10 +278,11 @@ async function refreshDir() {
|
||||
<div class="adjacent-input">
|
||||
<label for="max-downloads">
|
||||
<span class="label__title">Maximum concurrent downloads</span>
|
||||
<span class="label__description"
|
||||
>The maximum amount of files the launcher can download at the same time. Set this to a
|
||||
lower value if you have a poor internet connection.</span
|
||||
>
|
||||
<span class="label__description">
|
||||
The maximum amount of files the launcher can download at the same time. Set this to a
|
||||
lower value if you have a poor internet connection. (app restart required to take
|
||||
effect)
|
||||
</span>
|
||||
</label>
|
||||
<Slider
|
||||
id="max-downloads"
|
||||
@@ -302,10 +296,11 @@ async function refreshDir() {
|
||||
<div class="adjacent-input">
|
||||
<label for="max-writes">
|
||||
<span class="label__title">Maximum concurrent writes</span>
|
||||
<span class="label__description"
|
||||
>The maximum amount of files the launcher can write to the disk at once. Set this to a
|
||||
lower value if you are frequently getting I/O errors.</span
|
||||
>
|
||||
<span class="label__description">
|
||||
The maximum amount of files the launcher can write to the disk at once. Set this to a
|
||||
lower value if you are frequently getting I/O errors. (app restart required to take
|
||||
effect)
|
||||
</span>
|
||||
</label>
|
||||
<Slider
|
||||
id="max-writes"
|
||||
@@ -324,37 +319,38 @@ async function refreshDir() {
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<label for="opt-out-analytics">
|
||||
<span class="label__title">Disable analytics</span>
|
||||
<span class="label__title">Telemetry</span>
|
||||
<span class="label__description">
|
||||
Modrinth collects anonymized analytics and usage data to improve our user experience and
|
||||
customize your experience. By enabling this option, you opt out and your data will no
|
||||
customize your experience. By disabling this option, you opt out and your data will no
|
||||
longer be collected.
|
||||
</span>
|
||||
</label>
|
||||
<Toggle
|
||||
id="opt-out-analytics"
|
||||
:model-value="settings.opt_out_analytics"
|
||||
:checked="settings.opt_out_analytics"
|
||||
:model-value="settings.telemetry"
|
||||
:checked="settings.telemetry"
|
||||
@update:model-value="
|
||||
(e) => {
|
||||
settings.opt_out_analytics = e
|
||||
settings.telemetry = e
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<label for="disable-discord-rpc">
|
||||
<span class="label__title">Disable Discord RPC</span>
|
||||
<span class="label__title">Discord RPC</span>
|
||||
<span class="label__description">
|
||||
Disables the Discord Rich Presence integration. 'Modrinth' will no longer show up as a
|
||||
game or app you are using on your Discord profile. This does not disable any
|
||||
instance-specific Discord Rich Presence integrations, such as those added by mods.
|
||||
Manages the Discord Rich Presence integration. Disabling this will cause 'Modrinth' to
|
||||
no longer show up as a game or app you are using on your Discord profile. This does not
|
||||
disable any instance-specific Discord Rich Presence integrations, such as those added by
|
||||
mods. (app restart required to take effect)
|
||||
</span>
|
||||
</label>
|
||||
<Toggle
|
||||
id="disable-discord-rpc"
|
||||
v-model="settings.disable_discord_rpc"
|
||||
:checked="settings.disable_discord_rpc"
|
||||
v-model="settings.discord_rpc"
|
||||
:checked="settings.discord_rpc"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -364,25 +360,24 @@ async function refreshDir() {
|
||||
<span class="label__title size-card-header">Java settings</span>
|
||||
</h3>
|
||||
</div>
|
||||
<label for="java-21">
|
||||
<span class="label__title">Java 21 location</span>
|
||||
</label>
|
||||
<JavaSelector id="java-17" v-model="settings.java_globals.JAVA_21" :version="21" />
|
||||
<label for="java-17">
|
||||
<span class="label__title">Java 17 location</span>
|
||||
</label>
|
||||
<JavaSelector id="java-17" v-model="settings.java_globals.JAVA_17" :version="17" />
|
||||
<label for="java-8">
|
||||
<span class="label__title">Java 8 location</span>
|
||||
</label>
|
||||
<JavaSelector id="java-8" v-model="settings.java_globals.JAVA_8" :version="8" />
|
||||
<template v-for="version in [21, 17, 8]">
|
||||
<label :for="'java-' + version">
|
||||
<span class="label__title">Java {{ version }} location</span>
|
||||
</label>
|
||||
<JavaSelector
|
||||
:id="'java-selector-' + version"
|
||||
v-model="javaVersions[version]"
|
||||
:version="version"
|
||||
@update:model-value="updateJavaVersion"
|
||||
/>
|
||||
</template>
|
||||
<hr class="card-divider" />
|
||||
<label for="java-args">
|
||||
<span class="label__title">Java arguments</span>
|
||||
</label>
|
||||
<input
|
||||
id="java-args"
|
||||
v-model="settings.javaArgs"
|
||||
v-model="settings.launchArgs"
|
||||
autocomplete="off"
|
||||
type="text"
|
||||
class="installation-input"
|
||||
@@ -393,7 +388,7 @@ async function refreshDir() {
|
||||
</label>
|
||||
<input
|
||||
id="env-vars"
|
||||
v-model="settings.envArgs"
|
||||
v-model="settings.envVars"
|
||||
autocomplete="off"
|
||||
type="text"
|
||||
class="installation-input"
|
||||
@@ -526,7 +521,7 @@ async function refreshDir() {
|
||||
<div>
|
||||
<label>
|
||||
<span class="label__title">App version</span>
|
||||
<span class="label__description">Theseus v{{ version }} </span>
|
||||
<span class="label__description">Modrinth App v{{ version }} </span>
|
||||
</label>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -551,16 +546,6 @@ async function refreshDir() {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
:deep(.login-screen-modal) {
|
||||
.modal-container .modal-body {
|
||||
width: auto;
|
||||
|
||||
.content {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.app-directory {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
@@ -2,20 +2,10 @@
|
||||
<div class="instance-container">
|
||||
<div class="side-cards">
|
||||
<Card class="instance-card" @contextmenu.prevent.stop="handleRightClick">
|
||||
<Avatar
|
||||
size="lg"
|
||||
:src="
|
||||
!instance.metadata.icon ||
|
||||
(instance.metadata.icon && instance.metadata.icon.startsWith('http'))
|
||||
? instance.metadata.icon
|
||||
: convertFileSrc(instance.metadata?.icon)
|
||||
"
|
||||
/>
|
||||
<Avatar size="lg" :src="instance.icon_path ? convertFileSrc(instance.icon_path) : null" />
|
||||
<div class="instance-info">
|
||||
<h2 class="name">{{ instance.metadata.name }}</h2>
|
||||
<span class="metadata">
|
||||
{{ instance.metadata.loader }} {{ instance.metadata.game_version }}
|
||||
</span>
|
||||
<h2 class="name">{{ instance.name }}</h2>
|
||||
<span class="metadata"> {{ instance.loader }} {{ instance.game_version }} </span>
|
||||
</div>
|
||||
<span class="button-group">
|
||||
<Button v-if="instance.install_stage !== 'installed'" disabled class="instance-button">
|
||||
@@ -26,7 +16,6 @@
|
||||
color="danger"
|
||||
class="instance-button"
|
||||
@click="stopInstance('InstancePage')"
|
||||
@mouseover="checkProcess"
|
||||
>
|
||||
<StopCircleIcon />
|
||||
Stop
|
||||
@@ -36,7 +25,6 @@
|
||||
color="primary"
|
||||
class="instance-button"
|
||||
@click="startInstance('InstancePage')"
|
||||
@mouseover="checkProcess"
|
||||
>
|
||||
<PlayIcon />
|
||||
Play
|
||||
@@ -135,22 +123,20 @@ import {
|
||||
CheckCircleIcon,
|
||||
UpdatedIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { get, run } from '@/helpers/profile'
|
||||
import {
|
||||
get_all_running_profile_paths,
|
||||
get_uuids_by_profile_path,
|
||||
kill_by_uuid,
|
||||
} from '@/helpers/process'
|
||||
import { offline_listener, process_listener, profile_listener } from '@/helpers/events'
|
||||
import { get, kill, run } from '@/helpers/profile'
|
||||
import { get_by_profile_path } from '@/helpers/process'
|
||||
import { process_listener, profile_listener } from '@/helpers/events'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ref, onUnmounted } from 'vue'
|
||||
import { handleError, useBreadcrumbs, useLoading } from '@/store/state'
|
||||
import { isOffline, showProfileInFolder } from '@/helpers/utils.js'
|
||||
import { showProfileInFolder } from '@/helpers/utils.js'
|
||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||
import { useFetch } from '@/helpers/fetch'
|
||||
import { handleSevereError } from '@/store/error.js'
|
||||
import { get_project, get_version_many } from '@/helpers/cache.js'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
@@ -161,72 +147,71 @@ const instance = ref(await get(route.params.id).catch(handleError))
|
||||
|
||||
breadcrumbs.setName(
|
||||
'Instance',
|
||||
instance.value.metadata.name.length > 40
|
||||
? instance.value.metadata.name.substring(0, 40) + '...'
|
||||
: instance.value.metadata.name,
|
||||
instance.value.name.length > 40
|
||||
? instance.value.name.substring(0, 40) + '...'
|
||||
: instance.value.name,
|
||||
)
|
||||
|
||||
breadcrumbs.setContext({
|
||||
name: instance.value.metadata.name,
|
||||
name: instance.value.name,
|
||||
link: route.path,
|
||||
query: route.query,
|
||||
})
|
||||
|
||||
const offline = ref(await isOffline())
|
||||
const offline = ref(!navigator.onLine)
|
||||
window.addEventListener('offline', () => {
|
||||
offline.value = true
|
||||
})
|
||||
window.addEventListener('online', () => {
|
||||
offline.value = false
|
||||
})
|
||||
|
||||
const loadingBar = useLoading()
|
||||
|
||||
const uuid = ref(null)
|
||||
const playing = ref(false)
|
||||
const loading = ref(false)
|
||||
const options = ref(null)
|
||||
|
||||
const startInstance = async (context) => {
|
||||
loading.value = true
|
||||
uuid.value = await run(route.params.id).catch(handleSevereError)
|
||||
run(route.params.id).catch(handleSevereError)
|
||||
loading.value = false
|
||||
playing.value = true
|
||||
|
||||
mixpanel_track('InstanceStart', {
|
||||
loader: instance.value.metadata.loader,
|
||||
game_version: instance.value.metadata.game_version,
|
||||
loader: instance.value.loader,
|
||||
game_version: instance.value.game_version,
|
||||
source: context,
|
||||
})
|
||||
}
|
||||
|
||||
const checkProcess = async () => {
|
||||
const runningPaths = await get_all_running_profile_paths().catch(handleError)
|
||||
if (runningPaths.includes(instance.value.path)) {
|
||||
playing.value = true
|
||||
return
|
||||
}
|
||||
const runningProcesses = await get_by_profile_path(route.params.id).catch(handleError)
|
||||
|
||||
playing.value = false
|
||||
uuid.value = null
|
||||
playing.value = runningProcesses.length > 0
|
||||
}
|
||||
|
||||
// Get information on associated modrinth versions, if any
|
||||
const modrinthVersions = ref([])
|
||||
if (!(await isOffline()) && instance.value.metadata.linked_data?.project_id) {
|
||||
modrinthVersions.value = await useFetch(
|
||||
`https://api.modrinth.com/v2/project/${instance.value.metadata.linked_data.project_id}/version`,
|
||||
'project',
|
||||
)
|
||||
if (!offline.value && instance.value.linked_data && instance.value.linked_data.project_id) {
|
||||
const project = await get_project(instance.value.linked_data.project_id).catch(handleError)
|
||||
|
||||
if (project && project.versions) {
|
||||
modrinthVersions.value = (await get_version_many(project.versions).catch(handleError)).sort(
|
||||
(a, b) => dayjs(b.date_published) - dayjs(a.date_published),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
await checkProcess()
|
||||
|
||||
const stopInstance = async (context) => {
|
||||
playing.value = false
|
||||
if (!uuid.value) {
|
||||
const uuids = await get_uuids_by_profile_path(instance.value.path).catch(handleError)
|
||||
uuid.value = uuids[0] // populate Uuid to listen for in the process_listener
|
||||
uuids.forEach(async (u) => await kill_by_uuid(u).catch(handleError))
|
||||
} else await kill_by_uuid(uuid.value).catch(handleError)
|
||||
await kill(route.params.id).catch(handleError)
|
||||
|
||||
mixpanel_track('InstanceStop', {
|
||||
loader: instance.value.metadata.loader,
|
||||
game_version: instance.value.metadata.game_version,
|
||||
loader: instance.value.loader,
|
||||
game_version: instance.value.game_version,
|
||||
source: context,
|
||||
})
|
||||
}
|
||||
@@ -271,7 +256,7 @@ const handleOptionsClick = async (args) => {
|
||||
break
|
||||
case 'add_content':
|
||||
await router.push({
|
||||
path: `/browse/${instance.value.metadata.loader === 'vanilla' ? 'datapack' : 'mod'}`,
|
||||
path: `/browse/${instance.value.loader === 'vanilla' ? 'datapack' : 'mod'}`,
|
||||
query: { i: route.params.id },
|
||||
})
|
||||
break
|
||||
@@ -302,17 +287,12 @@ const unlistenProfiles = await profile_listener(async (event) => {
|
||||
})
|
||||
|
||||
const unlistenProcesses = await process_listener((e) => {
|
||||
if (e.event === 'finished' && uuid.value === e.uuid) playing.value = false
|
||||
})
|
||||
|
||||
const unlistenOffline = await offline_listener((b) => {
|
||||
offline.value = b
|
||||
if (e.event === 'finished' && e.profile_path_id === route.params.id) playing.value = false
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
unlistenProcesses()
|
||||
unlistenProfiles()
|
||||
unlistenOffline()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ import { computed, nextTick, onBeforeUnmount, onMounted, onUnmounted, ref, watch
|
||||
import dayjs from 'dayjs'
|
||||
import isToday from 'dayjs/plugin/isToday'
|
||||
import isYesterday from 'dayjs/plugin/isYesterday'
|
||||
import { get_uuids_by_profile_path } from '@/helpers/process.js'
|
||||
import { get_by_profile_path } from '@/helpers/process.js'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { process_listener } from '@/helpers/events.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
@@ -209,9 +209,9 @@ const processedLogs = computed(() => {
|
||||
|
||||
async function getLiveStdLog() {
|
||||
if (route.params.id) {
|
||||
const uuids = await get_uuids_by_profile_path(route.params.id).catch(handleError)
|
||||
const processes = await get_by_profile_path(route.params.id).catch(handleError)
|
||||
let returnValue
|
||||
if (uuids.length === 0) {
|
||||
if (processes.length === 0) {
|
||||
returnValue = emptyText.join('\n')
|
||||
} else {
|
||||
const logCursor = await get_latest_log_cursor(
|
||||
|
||||
@@ -281,7 +281,7 @@
|
||||
<div class="markdown-body">
|
||||
<p>
|
||||
Are you sure you want to remove
|
||||
<strong>{{ functionValues.length }} project(s)</strong> from {{ instance.metadata.name }}?
|
||||
<strong>{{ functionValues.length }} project(s)</strong> from {{ instance.name }}?
|
||||
<br />
|
||||
This action <strong>cannot</strong> be undone.
|
||||
</p>
|
||||
@@ -304,7 +304,7 @@
|
||||
>{{ Array.from(projects.values()).filter((x) => x.disabled).length }} disabled
|
||||
project(s)</strong
|
||||
>
|
||||
from {{ instance.metadata.name }}?
|
||||
from {{ instance.name }}?
|
||||
<br />
|
||||
This action <strong>cannot</strong> be undone.
|
||||
</p>
|
||||
@@ -326,7 +326,7 @@
|
||||
/>
|
||||
<ExportModal v-if="projects.length > 0" ref="exportModal" :instance="instance" />
|
||||
<ModpackVersionModal
|
||||
v-if="instance.metadata.linked_data"
|
||||
v-if="instance.linked_data"
|
||||
ref="modpackVersionModal"
|
||||
:instance="instance"
|
||||
:versions="props.versions"
|
||||
@@ -364,6 +364,7 @@ import { computed, onUnmounted, ref, watch } from 'vue'
|
||||
import {
|
||||
add_project_from_path,
|
||||
get,
|
||||
get_projects,
|
||||
remove_project,
|
||||
toggle_disable_project,
|
||||
update_all,
|
||||
@@ -372,12 +373,18 @@ import {
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||
import { listen } from '@tauri-apps/api/event'
|
||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||
import { highlightModInProfile } from '@/helpers/utils.js'
|
||||
import { MenuIcon, ToggleIcon, TextInputIcon, AddProjectImage, PackageIcon } from '@/assets/icons'
|
||||
import ExportModal from '@/components/ui/ExportModal.vue'
|
||||
import ModpackVersionModal from '@/components/ui/ModpackVersionModal.vue'
|
||||
import AddContentButton from '@/components/ui/AddContentButton.vue'
|
||||
import {
|
||||
get_organization_many,
|
||||
get_project_many,
|
||||
get_team_many,
|
||||
get_version_many,
|
||||
} from '@/helpers/cache.js'
|
||||
import { profile_listener } from '@/helpers/events.js'
|
||||
|
||||
const props = defineProps({
|
||||
instance: {
|
||||
@@ -404,65 +411,102 @@ const props = defineProps({
|
||||
},
|
||||
})
|
||||
|
||||
const projects = ref([])
|
||||
const selectionMap = ref(new Map())
|
||||
const unlistenProfiles = await profile_listener(async (event) => {
|
||||
if (
|
||||
event.profile_path_id === props.instance.path &&
|
||||
event.event === 'synced' &&
|
||||
props.instance.install_stage !== 'pack_installing'
|
||||
) {
|
||||
await initProjects()
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
unlistenProfiles()
|
||||
})
|
||||
|
||||
const showingOptions = ref(false)
|
||||
const isPackLocked = computed(() => {
|
||||
return props.instance.metadata.linked_data && props.instance.metadata.linked_data.locked
|
||||
return props.instance.linked_data && props.instance.linked_data.locked
|
||||
})
|
||||
const canUpdatePack = computed(() => {
|
||||
if (!props.instance.metadata.linked_data) return false
|
||||
return props.instance.metadata.linked_data.version_id !== props.instance.modrinth_update_version
|
||||
if (!props.instance.linked_data || !props.versions || !props.versions[0]) return false
|
||||
return props.instance.linked_data.version_id !== props.versions[0].id
|
||||
})
|
||||
const exportModal = ref(null)
|
||||
|
||||
const initProjects = (initInstance) => {
|
||||
projects.value = []
|
||||
if (!initInstance || !initInstance.projects) return
|
||||
for (const [path, project] of Object.entries(initInstance.projects)) {
|
||||
if (project.metadata.type === 'modrinth' && !props.offline) {
|
||||
let owner = project.metadata.members.find((x) => x.role === 'Owner')
|
||||
projects.value.push({
|
||||
const projects = ref([])
|
||||
const selectionMap = ref(new Map())
|
||||
|
||||
const initProjects = async () => {
|
||||
const newProjects = []
|
||||
|
||||
const profileProjects = await get_projects(props.instance.path)
|
||||
const fetchProjects = []
|
||||
const fetchVersions = []
|
||||
|
||||
for (const value of Object.values(profileProjects)) {
|
||||
if (value.metadata) {
|
||||
fetchProjects.push(value.metadata.project_id)
|
||||
fetchVersions.push(value.metadata.version_id)
|
||||
}
|
||||
}
|
||||
|
||||
const [modrinthProjects, modrinthVersions] = await Promise.all([
|
||||
await get_project_many(fetchProjects).catch(handleError),
|
||||
await get_version_many(fetchVersions).catch(handleError),
|
||||
])
|
||||
|
||||
const [modrinthTeams, modrinthOrganizations] = await Promise.all([
|
||||
await get_team_many(modrinthProjects.map((x) => x.team)).catch(handleError),
|
||||
await get_organization_many(
|
||||
modrinthProjects.map((x) => x.organization).filter((x) => !!x),
|
||||
).catch(handleError),
|
||||
])
|
||||
|
||||
for (const [path, file] of Object.entries(profileProjects)) {
|
||||
if (file.metadata) {
|
||||
const project = modrinthProjects.find((x) => file.metadata.project_id === x.id)
|
||||
const version = modrinthVersions.find((x) => file.metadata.version_id === x.id)
|
||||
const org = project.organization
|
||||
? modrinthOrganizations.find((x) => x.id === project.organization)
|
||||
: null
|
||||
|
||||
const team = modrinthTeams.find((x) => x[0].team_id === project.team)
|
||||
|
||||
let owner = org ? org.name : team.find((x) => x.is_owner).user.username
|
||||
|
||||
newProjects.push({
|
||||
path,
|
||||
name: project.metadata.project.title,
|
||||
slug: project.metadata.project.slug,
|
||||
author: owner ? owner.user.username : null,
|
||||
version: project.metadata.version.version_number,
|
||||
file_name: project.file_name,
|
||||
icon: project.metadata.project.icon_url,
|
||||
disabled: project.disabled,
|
||||
updateVersion: project.metadata.update_version,
|
||||
outdated: !!project.metadata.update_version,
|
||||
project_type: project.metadata.project.project_type,
|
||||
id: project.metadata.project.id,
|
||||
})
|
||||
} else if (project.metadata.type === 'inferred') {
|
||||
projects.value.push({
|
||||
path,
|
||||
name: project.metadata.title ?? project.file_name,
|
||||
author: project.metadata.authors[0],
|
||||
version: project.metadata.version,
|
||||
file_name: project.file_name,
|
||||
icon: project.metadata.icon ? convertFileSrc(project.metadata.icon) : null,
|
||||
disabled: project.disabled,
|
||||
outdated: false,
|
||||
project_type: project.metadata.project_type,
|
||||
name: project.title,
|
||||
slug: project.slug,
|
||||
author: owner,
|
||||
version: version.version_number,
|
||||
file_name: file.file_name,
|
||||
icon: project.icon_url,
|
||||
disabled: file.file_name.endsWith('.disabled'),
|
||||
updateVersion: file.update_version_id,
|
||||
outdated: !!file.update_version_id,
|
||||
project_type: project.project_type,
|
||||
id: project.id,
|
||||
})
|
||||
} else {
|
||||
projects.value.push({
|
||||
newProjects.push({
|
||||
path,
|
||||
name: project.file_name,
|
||||
name: file.file_name.replace('.disabled', ''),
|
||||
author: '',
|
||||
version: null,
|
||||
file_name: project.file_name,
|
||||
file_name: file.file_name,
|
||||
icon: null,
|
||||
disabled: project.disabled,
|
||||
disabled: file.file_name.endsWith('.disabled'),
|
||||
outdated: false,
|
||||
project_type: null,
|
||||
project_type: file.project_type,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
projects.value = newProjects
|
||||
|
||||
const newSelectionMap = new Map()
|
||||
for (const project of projects.value) {
|
||||
newSelectionMap.set(
|
||||
@@ -475,22 +519,7 @@ const initProjects = (initInstance) => {
|
||||
}
|
||||
selectionMap.value = newSelectionMap
|
||||
}
|
||||
|
||||
initProjects(props.instance)
|
||||
|
||||
watch(
|
||||
() => props.instance.projects,
|
||||
() => {
|
||||
initProjects(props.instance)
|
||||
},
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.offline,
|
||||
() => {
|
||||
if (props.instance) initProjects(props.instance)
|
||||
},
|
||||
)
|
||||
await initProjects()
|
||||
|
||||
const modpackVersionModal = ref(null)
|
||||
const installing = computed(() => props.instance.install_stage !== 'installed')
|
||||
@@ -633,8 +662,8 @@ const updateAll = async () => {
|
||||
}
|
||||
|
||||
mixpanel_track('InstanceUpdateAll', {
|
||||
loader: props.instance.metadata.loader,
|
||||
game_version: props.instance.metadata.game_version,
|
||||
loader: props.instance.loader,
|
||||
game_version: props.instance.game_version,
|
||||
count: setProjects.length,
|
||||
selected: selected.value.length > 1,
|
||||
})
|
||||
@@ -659,8 +688,8 @@ const updateProject = async (mod) => {
|
||||
mod.updateVersion = null
|
||||
|
||||
mixpanel_track('InstanceProjectUpdate', {
|
||||
loader: props.instance.metadata.loader,
|
||||
game_version: props.instance.metadata.game_version,
|
||||
loader: props.instance.loader,
|
||||
game_version: props.instance.game_version,
|
||||
id: mod.id,
|
||||
name: mod.name,
|
||||
project_type: mod.project_type,
|
||||
@@ -686,8 +715,8 @@ const toggleDisableMod = async (mod) => {
|
||||
mod.path = newPath
|
||||
mod.disabled = !mod.disabled
|
||||
mixpanel_track('InstanceProjectDisable', {
|
||||
loader: props.instance.metadata.loader,
|
||||
game_version: props.instance.metadata.game_version,
|
||||
loader: props.instance.loader,
|
||||
game_version: props.instance.game_version,
|
||||
id: mod.id,
|
||||
name: mod.name,
|
||||
project_type: mod.project_type,
|
||||
@@ -707,8 +736,8 @@ const removeMod = async (mod) => {
|
||||
projects.value = projects.value.filter((x) => mod.path !== x.path)
|
||||
|
||||
mixpanel_track('InstanceProjectRemove', {
|
||||
loader: props.instance.metadata.loader,
|
||||
game_version: props.instance.metadata.game_version,
|
||||
loader: props.instance.loader,
|
||||
game_version: props.instance.game_version,
|
||||
id: mod.id,
|
||||
name: mod.name,
|
||||
project_type: mod.project_type,
|
||||
@@ -820,7 +849,7 @@ watch(selectAll, () => {
|
||||
const unlisten = await listen('tauri://file-drop', async (event) => {
|
||||
for (const file of event.payload) {
|
||||
if (file.endsWith('.mrpack')) continue
|
||||
await add_project_from_path(props.instance.path, file, 'mod').catch(handleError)
|
||||
await add_project_from_path(props.instance.path, file).catch(handleError)
|
||||
}
|
||||
initProjects(await get(props.instance.path).catch(handleError))
|
||||
})
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<XIcon />
|
||||
Cancel
|
||||
</button>
|
||||
<button class="btn btn-danger" :disabled="action_disabled" @click="unlockProfile">
|
||||
<button class="btn btn-danger" @click="unlockProfile">
|
||||
<LockIcon />
|
||||
Unlock
|
||||
</button>
|
||||
@@ -50,7 +50,7 @@
|
||||
<XIcon />
|
||||
Cancel
|
||||
</button>
|
||||
<button class="btn btn-danger" :disabled="action_disabled" @click="unpairProfile">
|
||||
<button class="btn btn-danger" @click="unpairProfile">
|
||||
<XIcon />
|
||||
Unpair
|
||||
</button>
|
||||
@@ -117,21 +117,13 @@
|
||||
<span class="label__title">Icon</span>
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<Avatar
|
||||
:src="!icon || (icon && icon.startsWith('http')) ? icon : convertFileSrc(icon)"
|
||||
size="md"
|
||||
class="project__icon"
|
||||
/>
|
||||
<Avatar :src="icon ? convertFileSrc(icon) : icon" size="md" class="project__icon" />
|
||||
<div class="input-stack">
|
||||
<button id="instance-icon" class="btn" @click="setIcon">
|
||||
<UploadIcon />
|
||||
Select icon
|
||||
</button>
|
||||
<button
|
||||
:disabled="!(!icon || (icon && icon.startsWith('http')) ? icon : convertFileSrc(icon))"
|
||||
class="btn"
|
||||
@click="resetIcon"
|
||||
>
|
||||
<button :disabled="!icon" class="btn" @click="resetIcon">
|
||||
<TrashIcon />
|
||||
Remove icon
|
||||
</button>
|
||||
@@ -147,7 +139,7 @@
|
||||
autocomplete="off"
|
||||
maxlength="80"
|
||||
type="text"
|
||||
:disabled="instance.metadata.linked_data"
|
||||
:disabled="instance.linked_data"
|
||||
/>
|
||||
|
||||
<div class="adjacent-input">
|
||||
@@ -358,7 +350,7 @@
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
<Card v-if="instance.metadata.linked_data">
|
||||
<Card v-if="instance.linked_data">
|
||||
<div class="label">
|
||||
<h3>
|
||||
<span class="label__title size-card-header">Modpack</span>
|
||||
@@ -366,9 +358,7 @@
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<label for="general-modpack-info">
|
||||
<span class="label__description">
|
||||
<strong>Modpack: </strong> {{ instance.metadata.name }}
|
||||
</span>
|
||||
<span class="label__description"> <strong>Modpack: </strong> {{ instance.name }} </span>
|
||||
<span class="label__description">
|
||||
<strong>Version: </strong>
|
||||
{{
|
||||
@@ -414,7 +404,7 @@
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div v-if="props.instance.metadata.linked_data.project_id" class="adjacent-input">
|
||||
<div v-if="instance.linked_data.project_id" class="adjacent-input">
|
||||
<label for="change-modpack-version">
|
||||
<span class="label__title">Change modpack version</span>
|
||||
<span class="label__description">
|
||||
@@ -502,7 +492,7 @@
|
||||
</div>
|
||||
</Card>
|
||||
<ModpackVersionModal
|
||||
v-if="instance.metadata.linked_data"
|
||||
v-if="instance.linked_data"
|
||||
ref="modpackVersionModal"
|
||||
:instance="instance"
|
||||
:versions="props.versions"
|
||||
@@ -553,12 +543,7 @@ import { get } from '@/helpers/settings.js'
|
||||
import JavaSelector from '@/components/ui/JavaSelector.vue'
|
||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||
import { open } from '@tauri-apps/api/dialog'
|
||||
import {
|
||||
get_fabric_versions,
|
||||
get_forge_versions,
|
||||
get_neoforge_versions,
|
||||
get_quilt_versions,
|
||||
} from '@/helpers/metadata.js'
|
||||
import { get_loader_versions } from '@/helpers/metadata.js'
|
||||
import { get_game_versions, get_loaders } from '@/helpers/tags.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||
@@ -587,17 +572,17 @@ const props = defineProps({
|
||||
|
||||
const themeStore = useTheming()
|
||||
|
||||
const title = ref(props.instance.metadata.name)
|
||||
const icon = ref(props.instance.metadata.icon)
|
||||
const groups = ref(props.instance.metadata.groups)
|
||||
const title = ref(props.instance.name)
|
||||
const icon = ref(props.instance.icon_path)
|
||||
const groups = ref(props.instance.groups)
|
||||
|
||||
const modpackVersionModal = ref(null)
|
||||
|
||||
const instancesList = Object.values(await list(true))
|
||||
const instancesList = await list()
|
||||
const availableGroups = ref([
|
||||
...new Set(
|
||||
instancesList.reduce((acc, obj) => {
|
||||
return acc.concat(obj.metadata.groups)
|
||||
return acc.concat(obj.groups)
|
||||
}, []),
|
||||
),
|
||||
])
|
||||
@@ -632,18 +617,18 @@ const globalSettings = await get().catch(handleError)
|
||||
const modalConfirmUnlock = ref(null)
|
||||
const modalConfirmUnpair = ref(null)
|
||||
|
||||
const javaSettings = props.instance.java ?? {}
|
||||
|
||||
const overrideJavaInstall = ref(!!javaSettings.override_version)
|
||||
const overrideJavaInstall = ref(!!props.instance.java_path)
|
||||
const optimalJava = readonly(await get_optimal_jre_key(props.instance.path).catch(handleError))
|
||||
const javaInstall = ref(optimalJava ?? javaSettings.override_version ?? { path: '', version: '' })
|
||||
const javaInstall = ref({ path: optimalJava.path ?? props.instance.java_path })
|
||||
|
||||
const overrideJavaArgs = ref(!!javaSettings.extra_arguments)
|
||||
const javaArgs = ref((javaSettings.extra_arguments ?? globalSettings.custom_java_args).join(' '))
|
||||
const overrideJavaArgs = ref(!!props.instance.extra_launch_args)
|
||||
const javaArgs = ref(
|
||||
(props.instance.extra_launch_args ?? globalSettings.extra_launch_args).join(' '),
|
||||
)
|
||||
|
||||
const overrideEnvVars = ref(!!javaSettings.custom_env_args)
|
||||
const overrideEnvVars = ref(!!props.instance.custom_env_vars)
|
||||
const envVars = ref(
|
||||
(javaSettings.custom_env_args ?? globalSettings.custom_env_args)
|
||||
(props.instance.custom_env_vars ?? globalSettings.custom_env_vars)
|
||||
.map((x) => x.join('='))
|
||||
.join(' '),
|
||||
)
|
||||
@@ -652,18 +637,22 @@ const overrideMemorySettings = ref(!!props.instance.memory)
|
||||
const memory = ref(props.instance.memory ?? globalSettings.memory)
|
||||
const maxMemory = Math.floor((await get_max_memory().catch(handleError)) / 1024)
|
||||
|
||||
const overrideWindowSettings = ref(!!props.instance.resolution || !!props.instance.fullscreen)
|
||||
const resolution = ref(props.instance.resolution ?? globalSettings.game_resolution)
|
||||
const overrideHooks = ref(!!props.instance.hooks)
|
||||
const overrideWindowSettings = ref(
|
||||
!!props.instance.game_resolution || !!props.instance.force_fullscreen,
|
||||
)
|
||||
const resolution = ref(props.instance.game_resolution ?? globalSettings.game_resolution)
|
||||
const overrideHooks = ref(
|
||||
props.instance.hooks.pre_launch || props.instance.hooks.wrapper || props.instance.hooks.post_exit,
|
||||
)
|
||||
const hooks = ref(props.instance.hooks ?? globalSettings.hooks)
|
||||
|
||||
const fullscreenSetting = ref(!!props.instance.fullscreen)
|
||||
const fullscreenSetting = ref(!!props.instance.force_fullscreen)
|
||||
|
||||
const unlinkModpack = ref(false)
|
||||
|
||||
const inProgress = ref(false)
|
||||
const installing = computed(() => props.instance.install_stage !== 'installed')
|
||||
const installedVersion = computed(() => props.instance?.metadata?.linked_data?.version_id)
|
||||
const installedVersion = computed(() => props.instance?.linked_data?.version_id)
|
||||
const installedVersionData = computed(() => {
|
||||
if (!installedVersion.value) return null
|
||||
return props.versions.find((version) => version.id === installedVersion.value)
|
||||
@@ -706,34 +695,29 @@ const getLocalVersion = (path) => {
|
||||
|
||||
const editProfileObject = computed(() => {
|
||||
const editProfile = {
|
||||
metadata: {
|
||||
name: title.value.trim().substring(0, 32) ?? 'Instance',
|
||||
groups: groups.value.map((x) => x.trim().substring(0, 32)).filter((x) => x.length > 0),
|
||||
loader_version: props.instance.metadata.loader_version,
|
||||
linked_data: props.instance.metadata.linked_data,
|
||||
},
|
||||
name: title.value.trim().substring(0, 32) ?? 'Instance',
|
||||
groups: groups.value.map((x) => x.trim().substring(0, 32)).filter((x) => x.length > 0),
|
||||
loader_version: props.instance.loader_version,
|
||||
linked_data: props.instance.linked_data,
|
||||
java: {},
|
||||
hooks: {},
|
||||
}
|
||||
|
||||
if (overrideJavaInstall.value) {
|
||||
if (javaInstall.value.path !== '') {
|
||||
editProfile.java.override_version = javaInstall.value
|
||||
editProfile.java.override_version.path = editProfile.java.override_version.path.replace(
|
||||
'java.exe',
|
||||
'javaw.exe',
|
||||
)
|
||||
editProfile.java_path = javaInstall.value.path.replace('java.exe', 'javaw.exe')
|
||||
}
|
||||
}
|
||||
|
||||
if (overrideJavaArgs.value) {
|
||||
if (javaArgs.value !== '') {
|
||||
editProfile.java.extra_arguments = javaArgs.value.trim().split(/\s+/).filter(Boolean)
|
||||
editProfile.extra_launch_args = javaArgs.value.trim().split(/\s+/).filter(Boolean)
|
||||
}
|
||||
}
|
||||
|
||||
if (overrideEnvVars.value) {
|
||||
if (envVars.value !== '') {
|
||||
editProfile.java.custom_env_args = envVars.value
|
||||
editProfile.custom_env_vars = envVars.value
|
||||
.trim()
|
||||
.split(/\s+/)
|
||||
.filter(Boolean)
|
||||
@@ -746,10 +730,10 @@ const editProfileObject = computed(() => {
|
||||
}
|
||||
|
||||
if (overrideWindowSettings.value) {
|
||||
editProfile.fullscreen = fullscreenSetting.value
|
||||
editProfile.force_fullscreen = fullscreenSetting.value
|
||||
|
||||
if (!fullscreenSetting.value) {
|
||||
editProfile.resolution = resolution.value
|
||||
editProfile.game_resolution = resolution.value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -758,10 +742,10 @@ const editProfileObject = computed(() => {
|
||||
}
|
||||
|
||||
if (unlinkModpack.value) {
|
||||
editProfile.metadata.linked_data = null
|
||||
editProfile.linked_data = null
|
||||
}
|
||||
|
||||
breadcrumbs.setName('Instance', editProfile.metadata.name)
|
||||
breadcrumbs.setName('Instance', editProfile.name)
|
||||
|
||||
return editProfile
|
||||
})
|
||||
@@ -771,8 +755,8 @@ const repairing = ref(false)
|
||||
async function duplicateProfile() {
|
||||
await duplicate(props.instance.path).catch(handleError)
|
||||
mixpanel_track('InstanceDuplicate', {
|
||||
loader: props.instance.metadata.loader,
|
||||
game_version: props.instance.metadata.game_version,
|
||||
loader: props.instance.loader,
|
||||
game_version: props.instance.game_version,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -782,14 +766,14 @@ async function repairProfile(force) {
|
||||
repairing.value = false
|
||||
|
||||
mixpanel_track('InstanceRepair', {
|
||||
loader: props.instance.metadata.loader,
|
||||
game_version: props.instance.metadata.game_version,
|
||||
loader: props.instance.loader,
|
||||
game_version: props.instance.game_version,
|
||||
})
|
||||
}
|
||||
|
||||
async function unpairProfile() {
|
||||
const editProfile = props.instance
|
||||
editProfile.metadata.linked_data = null
|
||||
editProfile.linked_data = null
|
||||
await edit(props.instance.path, editProfile)
|
||||
installedVersion.value = null
|
||||
installedVersionData.value = null
|
||||
@@ -798,13 +782,13 @@ async function unpairProfile() {
|
||||
|
||||
async function unlockProfile() {
|
||||
const editProfile = props.instance
|
||||
editProfile.metadata.linked_data.locked = false
|
||||
editProfile.linked_data.locked = false
|
||||
await edit(props.instance.path, editProfile)
|
||||
modalConfirmUnlock.value.hide()
|
||||
}
|
||||
|
||||
const isPackLocked = computed(() => {
|
||||
return props.instance.metadata.linked_data && props.instance.metadata.linked_data.locked
|
||||
return props.instance.linked_data && props.instance.linked_data.locked
|
||||
})
|
||||
|
||||
async function repairModpack() {
|
||||
@@ -813,8 +797,8 @@ async function repairModpack() {
|
||||
inProgress.value = false
|
||||
|
||||
mixpanel_track('InstanceRepair', {
|
||||
loader: props.instance.metadata.loader,
|
||||
game_version: props.instance.metadata.game_version,
|
||||
loader: props.instance.loader,
|
||||
game_version: props.instance.game_version,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -825,8 +809,8 @@ async function removeProfile() {
|
||||
removing.value = false
|
||||
|
||||
mixpanel_track('InstanceRemove', {
|
||||
loader: props.instance.metadata.loader,
|
||||
game_version: props.instance.metadata.game_version,
|
||||
loader: props.instance.loader,
|
||||
game_version: props.instance.game_version,
|
||||
})
|
||||
|
||||
await router.push({ path: '/' })
|
||||
@@ -843,10 +827,10 @@ const [
|
||||
all_game_versions,
|
||||
loaders,
|
||||
] = await Promise.all([
|
||||
get_fabric_versions().then(shallowRef).catch(handleError),
|
||||
get_forge_versions().then(shallowRef).catch(handleError),
|
||||
get_quilt_versions().then(shallowRef).catch(handleError),
|
||||
get_neoforge_versions().then(shallowRef).catch(handleError),
|
||||
get_loader_versions('fabric').then(shallowRef).catch(handleError),
|
||||
get_loader_versions('forge').then(shallowRef).catch(handleError),
|
||||
get_loader_versions('quilt').then(shallowRef).catch(handleError),
|
||||
get_loader_versions('neo').then(shallowRef).catch(handleError),
|
||||
get_game_versions().then(shallowRef).catch(handleError),
|
||||
get_loaders()
|
||||
.then((value) =>
|
||||
@@ -859,8 +843,8 @@ const [
|
||||
])
|
||||
loaders.value.unshift('vanilla')
|
||||
|
||||
const loader = ref(props.instance.metadata.loader)
|
||||
const gameVersion = ref(props.instance.metadata.game_version)
|
||||
const loader = ref(props.instance.loader)
|
||||
const gameVersion = ref(props.instance.game_version)
|
||||
const selectableGameVersions = computed(() => {
|
||||
return all_game_versions.value
|
||||
.filter((item) => {
|
||||
@@ -896,9 +880,7 @@ const selectableLoaderVersions = computed(() => {
|
||||
return []
|
||||
})
|
||||
const loaderVersionIndex = ref(
|
||||
selectableLoaderVersions.value.findIndex(
|
||||
(x) => x.id === props.instance.metadata.loader_version?.id,
|
||||
),
|
||||
selectableLoaderVersions.value.findIndex((x) => x.id === props.instance.loader_version),
|
||||
)
|
||||
|
||||
const isValid = computed(() => {
|
||||
@@ -910,10 +892,9 @@ const isValid = computed(() => {
|
||||
|
||||
const isChanged = computed(() => {
|
||||
return (
|
||||
loader.value != props.instance.metadata.loader ||
|
||||
gameVersion.value != props.instance.metadata.game_version ||
|
||||
JSON.stringify(selectableLoaderVersions.value[loaderVersionIndex.value]) !==
|
||||
JSON.stringify(props.instance.metadata.loader_version)
|
||||
loader.value !== props.instance.loader ||
|
||||
gameVersion.value !== props.instance.game_version ||
|
||||
selectableLoaderVersions.value[loaderVersionIndex.value].id !== props.instance.loader_version
|
||||
)
|
||||
})
|
||||
|
||||
@@ -924,11 +905,11 @@ async function saveGvLoaderEdits() {
|
||||
editing.value = true
|
||||
|
||||
let editProfile = editProfileObject.value
|
||||
editProfile.metadata.loader = loader.value
|
||||
editProfile.metadata.game_version = gameVersion.value
|
||||
editProfile.loader = loader.value
|
||||
editProfile.game_version = gameVersion.value
|
||||
|
||||
if (loader.value !== 'vanilla') {
|
||||
editProfile.metadata.loader_version = selectableLoaderVersions.value[loaderVersionIndex.value]
|
||||
editProfile.loader_version = selectableLoaderVersions.value[loaderVersionIndex.value].id
|
||||
}
|
||||
await edit(props.instance.path, editProfile).catch(handleError)
|
||||
await repairProfile(false)
|
||||
|
||||
@@ -4,26 +4,17 @@
|
||||
<Card v-if="instance" class="small-instance">
|
||||
<router-link class="instance" :to="`/instance/${encodeURIComponent(instance.path)}`">
|
||||
<Avatar
|
||||
:src="
|
||||
!instance.metadata.icon ||
|
||||
(instance.metadata.icon && instance.metadata.icon.startsWith('http'))
|
||||
? instance.metadata.icon
|
||||
: convertFileSrc(instance.metadata?.icon)
|
||||
"
|
||||
:alt="instance.metadata.name"
|
||||
:src="instance.icon_path ? convertFileSrc(instance.icon_path) : null"
|
||||
:alt="instance.name"
|
||||
size="sm"
|
||||
/>
|
||||
<div class="small-instance_info">
|
||||
<span class="title">{{
|
||||
instance.metadata.name.length > 20
|
||||
? instance.metadata.name.substring(0, 20) + '...'
|
||||
: instance.metadata.name
|
||||
instance.name.length > 20 ? instance.name.substring(0, 20) + '...' : instance.name
|
||||
}}</span>
|
||||
<span>
|
||||
{{
|
||||
instance.metadata.loader.charAt(0).toUpperCase() + instance.metadata.loader.slice(1)
|
||||
}}
|
||||
{{ instance.metadata.game_version }}
|
||||
{{ instance.loader.charAt(0).toUpperCase() + instance.loader.slice(1) }}
|
||||
{{ instance.game_version }}
|
||||
</span>
|
||||
</div>
|
||||
</router-link>
|
||||
@@ -209,7 +200,6 @@
|
||||
:project="data"
|
||||
:versions="versions"
|
||||
:members="members"
|
||||
:dependencies="dependencies"
|
||||
:instance="instance"
|
||||
:install="install"
|
||||
:installed="installed"
|
||||
@@ -218,9 +208,6 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<InstallConfirmModal ref="confirmModal" />
|
||||
<ModInstallModal ref="modInstallModal" />
|
||||
<IncompatibilityWarningModal ref="incompatibilityWarning" />
|
||||
<ContextMenu ref="options" @option-clicked="handleOptionsClick">
|
||||
<template #install> <DownloadIcon /> Install </template>
|
||||
<template #open_link> <GlobeIcon /> Open in Modrinth <ExternalIcon /> </template>
|
||||
@@ -263,79 +250,63 @@ import {
|
||||
OpenCollectiveIcon,
|
||||
} from '@/assets/external'
|
||||
import { get_categories } from '@/helpers/tags'
|
||||
import { install as packInstall } from '@/helpers/pack'
|
||||
import {
|
||||
list,
|
||||
add_project_from_version as installMod,
|
||||
check_installed,
|
||||
get as getInstance,
|
||||
remove_project,
|
||||
} from '@/helpers/profile'
|
||||
import { get as getInstance, get_projects as getInstanceProjects } from '@/helpers/profile'
|
||||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { ref, shallowRef, watch } from 'vue'
|
||||
import { installVersionDependencies, isOffline } from '@/helpers/utils'
|
||||
import InstallConfirmModal from '@/components/ui/InstallConfirmModal.vue'
|
||||
import ModInstallModal from '@/components/ui/ModInstallModal.vue'
|
||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||
import IncompatibilityWarningModal from '@/components/ui/IncompatibilityWarningModal.vue'
|
||||
import { useFetch } from '@/helpers/fetch.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||
import { install as installVersion } from '@/store/install.js'
|
||||
import { get_project, get_project_many, get_team, get_version_many } from '@/helpers/cache.js'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
const route = useRoute()
|
||||
const breadcrumbs = useBreadcrumbs()
|
||||
|
||||
const confirmModal = ref(null)
|
||||
const modInstallModal = ref(null)
|
||||
const incompatibilityWarning = ref(null)
|
||||
|
||||
const options = ref(null)
|
||||
const installing = ref(false)
|
||||
const data = shallowRef(null)
|
||||
const versions = shallowRef([])
|
||||
const members = shallowRef([])
|
||||
const dependencies = shallowRef([])
|
||||
const categories = shallowRef([])
|
||||
const instance = ref(null)
|
||||
const instanceProjects = ref(null)
|
||||
|
||||
const installed = ref(false)
|
||||
const installedVersion = ref(null)
|
||||
|
||||
const offline = ref(await isOffline())
|
||||
|
||||
async function fetchProjectData() {
|
||||
;[
|
||||
data.value,
|
||||
versions.value,
|
||||
members.value,
|
||||
dependencies.value,
|
||||
categories.value,
|
||||
instance.value,
|
||||
] = await Promise.all([
|
||||
useFetch(`https://api.modrinth.com/v2/project/${route.params.id}`, 'project'),
|
||||
useFetch(`https://api.modrinth.com/v2/project/${route.params.id}/version`, 'project'),
|
||||
useFetch(`https://api.modrinth.com/v2/project/${route.params.id}/members`, 'project'),
|
||||
useFetch(`https://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(),
|
||||
])
|
||||
const project = await get_project(route.params.id).catch(handleError)
|
||||
|
||||
installed.value =
|
||||
instance.value?.path &&
|
||||
(await check_installed(instance.value.path, data.value.id).catch(handleError))
|
||||
data.value = project
|
||||
;[versions.value, members.value, categories.value, instance.value, instanceProjects.value] =
|
||||
await Promise.all([
|
||||
get_version_many(project.versions).catch(handleError),
|
||||
get_team(project.team).catch(handleError),
|
||||
get_categories().catch(handleError),
|
||||
route.query.i ? getInstance(route.query.i).catch(handleError) : Promise.resolve(),
|
||||
route.query.i ? getInstanceProjects(route.query.i).catch(handleError) : Promise.resolve(),
|
||||
])
|
||||
|
||||
versions.value = versions.value.sort((a, b) => dayjs(b.date_published) - dayjs(a.date_published))
|
||||
|
||||
if (instanceProjects.value) {
|
||||
const installedFile = Object.values(instanceProjects.value).find(
|
||||
(x) => x.metadata && x.metadata.project_id === data.value.id,
|
||||
)
|
||||
if (installedFile) {
|
||||
installed.value = true
|
||||
installedVersion.value = installedFile.metadata.version_id
|
||||
}
|
||||
}
|
||||
breadcrumbs.setName('Project', data.value.title)
|
||||
installedVersion.value = instance.value
|
||||
? Object.values(instance.value.projects).find(
|
||||
(p) => p?.metadata?.version?.project_id === data.value.id,
|
||||
)?.metadata?.version?.id
|
||||
: null
|
||||
}
|
||||
|
||||
if (!offline.value) await fetchProjectData()
|
||||
await fetchProjectData()
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
@@ -346,162 +317,22 @@ watch(
|
||||
},
|
||||
)
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
const markInstalled = () => {
|
||||
installed.value = true
|
||||
}
|
||||
|
||||
async function install(version) {
|
||||
installing.value = true
|
||||
let queuedVersionData
|
||||
if (instance.value) {
|
||||
instance.value = await getInstance(instance.value.path, false).catch(handleError)
|
||||
}
|
||||
|
||||
if (installed.value) {
|
||||
const old_project = Object.entries(instance.value.projects)
|
||||
.map(([key, value]) => ({
|
||||
key,
|
||||
value,
|
||||
}))
|
||||
.find((p) => p.value.metadata?.version?.project_id === data.value.id)
|
||||
if (!old_project) {
|
||||
// Switching too fast, old project is not recognized as a Modrinth project yet
|
||||
await installVersion(
|
||||
data.value.id,
|
||||
version,
|
||||
instance.value ? instance.value.path : null,
|
||||
'ProjectPage',
|
||||
(version) => {
|
||||
installing.value = false
|
||||
return
|
||||
}
|
||||
|
||||
await remove_project(instance.value.path, old_project.key)
|
||||
}
|
||||
|
||||
if (version) {
|
||||
queuedVersionData = versions.value.find((v) => v.id === version)
|
||||
} else {
|
||||
if (data.value.project_type === 'modpack' || !instance.value) {
|
||||
queuedVersionData = versions.value[0]
|
||||
} else {
|
||||
queuedVersionData = versions.value.find((v) =>
|
||||
v.game_versions.includes(data.value.game_versions[0]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (data.value.project_type === 'modpack') {
|
||||
const packs = Object.values(await list(true).catch(handleError))
|
||||
if (
|
||||
packs.length === 0 ||
|
||||
!packs
|
||||
.map((value) => value.metadata)
|
||||
.find((pack) => pack.linked_data?.project_id === data.value.id)
|
||||
) {
|
||||
await packInstall(
|
||||
data.value.id,
|
||||
queuedVersionData.id,
|
||||
data.value.title,
|
||||
data.value.icon_url,
|
||||
).catch(handleError)
|
||||
|
||||
mixpanel_track('PackInstall', {
|
||||
id: data.value.id,
|
||||
version_id: queuedVersionData.id,
|
||||
title: data.value.title,
|
||||
source: 'ProjectPage',
|
||||
})
|
||||
} else {
|
||||
confirmModal.value.show(
|
||||
data.value.id,
|
||||
queuedVersionData.id,
|
||||
data.value.title,
|
||||
data.value.icon_url,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (instance.value) {
|
||||
if (!version) {
|
||||
const gameVersion = instance.value.metadata.game_version
|
||||
const loader = instance.value.metadata.loader
|
||||
const selectedVersion = versions.value.find(
|
||||
(v) =>
|
||||
v.game_versions.includes(gameVersion) &&
|
||||
(data.value.project_type === 'mod'
|
||||
? v.loaders.includes(loader) || v.loaders.includes('minecraft')
|
||||
: true),
|
||||
)
|
||||
if (!selectedVersion) {
|
||||
incompatibilityWarning.value.show(
|
||||
instance.value,
|
||||
data.value.title,
|
||||
versions.value,
|
||||
markInstalled,
|
||||
data.value.id,
|
||||
data.value.project_type,
|
||||
)
|
||||
installing.value = false
|
||||
return
|
||||
} else {
|
||||
queuedVersionData = selectedVersion
|
||||
await installMod(instance.value.path, selectedVersion.id).catch(handleError)
|
||||
await installVersionDependencies(instance.value, queuedVersionData)
|
||||
installedVersion.value = selectedVersion.id
|
||||
mixpanel_track('ProjectInstall', {
|
||||
loader: instance.value.metadata.loader,
|
||||
game_version: instance.value.metadata.game_version,
|
||||
id: data.value.id,
|
||||
project_type: data.value.project_type,
|
||||
version_id: queuedVersionData.id,
|
||||
title: data.value.title,
|
||||
source: 'ProjectPage',
|
||||
})
|
||||
}
|
||||
} else {
|
||||
const gameVersion = instance.value.metadata.game_version
|
||||
const loader = instance.value.metadata.loader
|
||||
const compatible = versions.value.some(
|
||||
(v) =>
|
||||
v.game_versions.includes(gameVersion) &&
|
||||
(data.value.project_type === 'mod'
|
||||
? v.loaders.includes(loader) || v.loaders.includes('minecraft')
|
||||
: true),
|
||||
)
|
||||
if (compatible) {
|
||||
await installMod(instance.value.path, queuedVersionData.id).catch(handleError)
|
||||
await installVersionDependencies(instance.value, queuedVersionData)
|
||||
installedVersion.value = queuedVersionData.id
|
||||
mixpanel_track('ProjectInstall', {
|
||||
loader: instance.value.metadata.loader,
|
||||
game_version: instance.value.metadata.game_version,
|
||||
id: data.value.id,
|
||||
project_type: data.value.project_type,
|
||||
version_id: queuedVersionData.id,
|
||||
title: data.value.title,
|
||||
source: 'ProjectPage',
|
||||
})
|
||||
} else {
|
||||
incompatibilityWarning.value.show(
|
||||
instance.value,
|
||||
data.value.title,
|
||||
[queuedVersionData],
|
||||
markInstalled,
|
||||
data.value.id,
|
||||
data.value.project_type,
|
||||
)
|
||||
installing.value = false
|
||||
return
|
||||
}
|
||||
if (instance.value && version) {
|
||||
installed.value = true
|
||||
installedVersion.value = version
|
||||
}
|
||||
installed.value = true
|
||||
} else {
|
||||
modInstallModal.value.show(
|
||||
data.value.id,
|
||||
version ? [versions.value.find((v) => v.id === queuedVersionData.id)] : versions.value,
|
||||
data.value.title,
|
||||
data.value.project_type,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
installing.value = false
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
const handleRightClick = (e) => {
|
||||
|
||||
@@ -190,6 +190,7 @@ import { ref, watch, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||
import { SwapIcon } from '@/assets/icons'
|
||||
import { get_project_many, get_version_many } from '@/helpers/cache.js'
|
||||
|
||||
const breadcrumbs = useBreadcrumbs()
|
||||
|
||||
@@ -204,10 +205,6 @@ const props = defineProps({
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
dependencies: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
members: {
|
||||
type: Array,
|
||||
required: true,
|
||||
@@ -238,20 +235,45 @@ watch(
|
||||
async () => {
|
||||
if (route.params.version) {
|
||||
version.value = props.versions.find((version) => version.id === route.params.version)
|
||||
await refreshDisplayDependencies()
|
||||
breadcrumbs.setName('Version', version.value.name)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
const author = computed(() =>
|
||||
props.members.find((member) => member.user.id === version.value.author_id),
|
||||
props.members ? props.members.find((member) => member.user.id === version.value.author_id) : null,
|
||||
)
|
||||
|
||||
const displayDependencies = computed(() =>
|
||||
version.value.dependencies.map((dependency) => {
|
||||
const version = props.dependencies.versions.find((obj) => obj.id === dependency.version_id)
|
||||
const displayDependencies = ref({})
|
||||
|
||||
async function refreshDisplayDependencies() {
|
||||
const projectIds = new Set()
|
||||
const versionIds = new Set()
|
||||
if (version.value.dependencies) {
|
||||
for (const dependency of version.value.dependencies) {
|
||||
if (dependency.project_id) {
|
||||
projectIds.add(dependency.project_id)
|
||||
}
|
||||
if (dependency.version_id) {
|
||||
versionIds.add(dependency.version_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
const [projectDeps, versionDeps] = await Promise.all([
|
||||
get_project_many([...projectIds]),
|
||||
get_version_many([...versionIds]),
|
||||
])
|
||||
|
||||
const dependencies = {
|
||||
projects: projectDeps,
|
||||
versions: versionDeps,
|
||||
}
|
||||
|
||||
displayDependencies.value = version.value.dependencies.map((dependency) => {
|
||||
const version = dependencies.versions.find((obj) => obj.id === dependency.version_id)
|
||||
if (version) {
|
||||
const project = props.dependencies.projects.find(
|
||||
const project = dependencies.projects.find(
|
||||
(obj) => obj.id === version.project_id || obj.id === dependency.project_id,
|
||||
)
|
||||
return {
|
||||
@@ -261,7 +283,7 @@ const displayDependencies = computed(() =>
|
||||
link: `/project/${project.slug}/version/${version.id}`,
|
||||
}
|
||||
} else {
|
||||
const project = props.dependencies.projects.find((obj) => obj.id === dependency.project_id)
|
||||
const project = dependencies.projects.find((obj) => obj.id === dependency.project_id)
|
||||
|
||||
if (project) {
|
||||
return {
|
||||
@@ -279,8 +301,9 @@ const displayDependencies = computed(() =>
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
)
|
||||
})
|
||||
}
|
||||
await refreshDisplayDependencies()
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -187,8 +187,8 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
const filterVersions = ref([])
|
||||
const filterLoader = ref(props.instance ? [props.instance?.metadata?.loader] : [])
|
||||
const filterGameVersions = ref(props.instance ? [props.instance?.metadata?.game_version] : [])
|
||||
const filterLoader = ref(props.instance ? [props.instance?.loader] : [])
|
||||
const filterGameVersions = ref(props.instance ? [props.instance?.game_version] : [])
|
||||
|
||||
const currentPage = ref(1)
|
||||
|
||||
|
||||
182
apps/app-frontend/src/store/install.js
Normal file
182
apps/app-frontend/src/store/install.js
Normal file
@@ -0,0 +1,182 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import {
|
||||
add_project_from_version,
|
||||
check_installed,
|
||||
list,
|
||||
get,
|
||||
get_projects,
|
||||
remove_project,
|
||||
} from '@/helpers/profile.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { get_project, get_version_many } from '@/helpers/cache.js'
|
||||
import { install as packInstall } from '@/helpers/pack.js'
|
||||
import { mixpanel_track } from '@/helpers/mixpanel.js'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
export const useInstall = defineStore('installStore', {
|
||||
state: () => ({
|
||||
installConfirmModal: null,
|
||||
modInstallModal: null,
|
||||
incompatibilityWarningModal: null,
|
||||
}),
|
||||
actions: {
|
||||
setInstallConfirmModal(ref) {
|
||||
this.installConfirmModal = ref
|
||||
},
|
||||
showInstallConfirmModal(project, version_id, onInstall) {
|
||||
this.installConfirmModal.show(project, version_id, onInstall)
|
||||
},
|
||||
setIncompatibilityWarningModal(ref) {
|
||||
this.incompatibilityWarningModal = ref
|
||||
},
|
||||
showIncompatibilityWarningModal(instance, project, versions, onInstall) {
|
||||
this.incompatibilityWarningModal.show(instance, project, versions, onInstall)
|
||||
},
|
||||
setModInstallModal(ref) {
|
||||
this.modInstallModal = ref
|
||||
},
|
||||
showModInstallModal(project, versions, onInstall) {
|
||||
this.modInstallModal.show(project, versions, onInstall)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export const install = async (projectId, versionId, instancePath, source, callback = () => {}) => {
|
||||
const project = await get_project(projectId).catch(handleError)
|
||||
|
||||
if (project.project_type === 'modpack') {
|
||||
const version = versionId ?? project.versions[project.versions.length - 1]
|
||||
const packs = await list().catch(handleError)
|
||||
|
||||
if (packs.length === 0 || !packs.find((pack) => pack.linked_data?.project_id === project.id)) {
|
||||
await packInstall(project.id, version, project.title, project.icon_url).catch(handleError)
|
||||
|
||||
mixpanel_track('PackInstall', {
|
||||
id: project.id,
|
||||
version_id: version,
|
||||
title: project.title,
|
||||
source,
|
||||
})
|
||||
|
||||
callback(version)
|
||||
} else {
|
||||
const install = useInstall()
|
||||
install.showInstallConfirmModal(project, version, callback)
|
||||
}
|
||||
} else {
|
||||
if (instancePath) {
|
||||
const [instance, instanceProjects, versions] = await Promise.all([
|
||||
await get(instancePath).catch(handleError),
|
||||
await get_projects(instancePath).catch(handleError),
|
||||
await get_version_many(project.versions),
|
||||
])
|
||||
|
||||
const projectVersions = versions.sort(
|
||||
(a, b) => dayjs(b.date_published) - dayjs(a.date_published),
|
||||
)
|
||||
|
||||
let version
|
||||
if (versionId) {
|
||||
version = projectVersions.find((x) => x.id === versionId)
|
||||
} else {
|
||||
version = projectVersions.find(
|
||||
(v) =>
|
||||
v.game_versions.includes(instance.game_version) &&
|
||||
(project.project_type === 'mod'
|
||||
? v.loaders.includes(instance.loader) || v.loaders.includes('minecraft')
|
||||
: true),
|
||||
)
|
||||
}
|
||||
|
||||
if (!version) {
|
||||
version = projectVersions[0]
|
||||
}
|
||||
|
||||
if (
|
||||
version.game_versions.includes(instance.game_version) &&
|
||||
(project.project_type === 'mod'
|
||||
? version.loaders.includes(instance.loader) || version.loaders.includes('minecraft')
|
||||
: true)
|
||||
) {
|
||||
for (const [path, file] of Object.entries(instanceProjects)) {
|
||||
if (file.metadata && file.metadata.project_id === project.id) {
|
||||
await remove_project(instance.path, path)
|
||||
}
|
||||
}
|
||||
|
||||
await add_project_from_version(instance.path, version.id).catch(handleError)
|
||||
await installVersionDependencies(instance, version)
|
||||
|
||||
mixpanel_track('ProjectInstall', {
|
||||
loader: instance.loader,
|
||||
game_version: instance.game_version,
|
||||
id: project.id,
|
||||
project_type: project.project_type,
|
||||
version_id: version.id,
|
||||
title: project.title,
|
||||
source,
|
||||
})
|
||||
|
||||
callback(version.id)
|
||||
} else {
|
||||
const install = useInstall()
|
||||
install.showIncompatibilityWarningModal(instance, project, projectVersions, callback)
|
||||
}
|
||||
} else {
|
||||
const versions = (await get_version_many(project.versions).catch(handleError)).sort(
|
||||
(a, b) => dayjs(b.date_published) - dayjs(a.date_published),
|
||||
)
|
||||
|
||||
const install = useInstall()
|
||||
install.showModInstallModal(project, versions, callback)
|
||||
}
|
||||
}
|
||||
|
||||
// If project is modpack:
|
||||
// - We check all available instances if modpack is already installed
|
||||
// If true: show confirmation modal
|
||||
// If false: install it (latest version if passed version is null)
|
||||
// If project is mod:
|
||||
// - If instance is selected:
|
||||
// - If project is already installed
|
||||
// We first uninstall the project
|
||||
// - If no version is selected, we look check the instance for versions to select based on the versions
|
||||
// - If there are no versions, we show the incompat modal
|
||||
// - If a version is selected, and the version is incompatible, we show the incompat modal
|
||||
// - Version is inarlled, as well as version dependencies
|
||||
}
|
||||
|
||||
export const installVersionDependencies = async (profile, version) => {
|
||||
for (const dep of version.dependencies) {
|
||||
if (dep.dependency_type !== 'required') continue
|
||||
// disallow fabric api install on quilt
|
||||
if (dep.project_id === 'P7dR8mSH' && profile.loader === 'quilt') continue
|
||||
if (dep.version_id) {
|
||||
if (
|
||||
dep.project_id &&
|
||||
(await check_installed(profile.path, dep.project_id).catch(handleError))
|
||||
)
|
||||
continue
|
||||
await add_project_from_version(profile.path, dep.version_id)
|
||||
} else {
|
||||
if (
|
||||
dep.project_id &&
|
||||
(await check_installed(profile.path, dep.project_id).catch(handleError))
|
||||
)
|
||||
continue
|
||||
|
||||
const depProject = await get_project(dep.project_id).catch(handleError)
|
||||
|
||||
const depVersions = (await get_version_many(depProject.versions).catch(handleError)).sort(
|
||||
(a, b) => dayjs(b.date_published) - dayjs(a.date_published),
|
||||
)
|
||||
|
||||
const latest = depVersions.find(
|
||||
(v) => v.game_versions.includes(profile.game_version) && v.loaders.includes(profile.loader),
|
||||
)
|
||||
if (latest) {
|
||||
await add_project_from_version(profile.path, latest.id).catch(handleError)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,5 +2,6 @@ import { useTheming } from './theme'
|
||||
import { useBreadcrumbs } from './breadcrumbs'
|
||||
import { useLoading } from './loading'
|
||||
import { useNotifications, handleError } from './notifications'
|
||||
import { useInstall } from './install'
|
||||
|
||||
export { useTheming, useBreadcrumbs, useLoading, useNotifications, handleError }
|
||||
export { useTheming, useBreadcrumbs, useLoading, useNotifications, handleError, useInstall }
|
||||
|
||||
@@ -19,7 +19,6 @@ dunce = "1.0.3"
|
||||
|
||||
tokio-stream = { version = "0.1", features = ["fs"] }
|
||||
futures = "0.3"
|
||||
daedalus = {version = "0.1.15", features = ["bincode"] }
|
||||
uuid = { version = "1.1", features = ["serde", "v4"] }
|
||||
|
||||
tracing = "0.1.37"
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
windows_subsystem = "windows"
|
||||
)]
|
||||
|
||||
use theseus::pack::install_from::{get_profile_from_pack, CreatePackLocation};
|
||||
use theseus::pack::install_mrpack::install_zipped_mrpack;
|
||||
use theseus::prelude::*;
|
||||
|
||||
use theseus::profile::create::profile_create;
|
||||
@@ -39,21 +41,12 @@ async fn main() -> theseus::Result<()> {
|
||||
let _log_guard = theseus::start_logger();
|
||||
|
||||
// Initialize state
|
||||
let st = State::get().await?;
|
||||
//State::update();
|
||||
State::init().await?;
|
||||
|
||||
if minecraft_auth::users().await?.is_empty() {
|
||||
println!("No users found, authenticating.");
|
||||
authenticate_run().await?; // could take credentials from here direct, but also deposited in state users
|
||||
}
|
||||
|
||||
// Autodetect java globals
|
||||
st.settings.write().await.max_concurrent_downloads = 50;
|
||||
st.settings.write().await.hooks.post_exit =
|
||||
Some("echo This is after Minecraft runs- global setting!".to_string());
|
||||
// Changed the settings, so need to reset the semaphore
|
||||
st.reset_fetch_semaphore().await;
|
||||
|
||||
//
|
||||
// st.settings
|
||||
// .write()
|
||||
@@ -63,56 +56,59 @@ async fn main() -> theseus::Result<()> {
|
||||
// Clear profiles
|
||||
println!("Clearing profiles.");
|
||||
{
|
||||
let h = profile::list(None).await?;
|
||||
for (path, _) in h.into_iter() {
|
||||
profile::remove(&path).await?;
|
||||
let h = profile::list().await?;
|
||||
for profile in h.into_iter() {
|
||||
profile::remove(&profile.path).await?;
|
||||
}
|
||||
}
|
||||
|
||||
println!("Creating/adding profile.");
|
||||
|
||||
let name = "Example".to_string();
|
||||
let game_version = "1.19.2".to_string();
|
||||
let modloader = ModLoader::Vanilla;
|
||||
let loader_version = "stable".to_string();
|
||||
// let name = "Example".to_string();
|
||||
// let game_version = "1.21".to_string();
|
||||
// let modloader = ModLoader::Fabric;
|
||||
// let loader_version = "stable".to_string();
|
||||
|
||||
let pack = CreatePackLocation::FromVersionId {
|
||||
project_id: "1KVo5zza".to_string(),
|
||||
version_id: "lKloE8SA".to_string(),
|
||||
title: "Fabulously Optimized".to_string(),
|
||||
icon_url: Some("https://cdn.modrinth.com/data/1KVo5zza/d8152911f8fd5d7e9a8c499fe89045af81fe816e.png".to_string()),
|
||||
};
|
||||
|
||||
let profile = get_profile_from_pack(pack.clone());
|
||||
let profile_path = profile_create(
|
||||
name.clone(),
|
||||
game_version,
|
||||
modloader,
|
||||
Some(loader_version),
|
||||
None,
|
||||
None,
|
||||
profile.name,
|
||||
profile.game_version,
|
||||
profile.modloader,
|
||||
profile.loader_version,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
install_zipped_mrpack(pack, profile_path.to_string()).await?;
|
||||
|
||||
State::sync().await?;
|
||||
let projects = profile::get_projects(&profile_path).await?;
|
||||
|
||||
for (path, file) in projects {
|
||||
println!(
|
||||
"{path} {} {:?} {:?}",
|
||||
file.file_name, file.update_version_id, file.metadata
|
||||
)
|
||||
}
|
||||
|
||||
println!("running");
|
||||
// Run a profile, running minecraft and store the RwLock to the process
|
||||
let proc_lock = profile::run(&profile_path).await?;
|
||||
let uuid = proc_lock.read().await.uuid;
|
||||
let pid = proc_lock.read().await.current_child.read().await.id();
|
||||
let process = profile::run(&profile_path).await?;
|
||||
|
||||
println!("Minecraft UUID: {}", uuid);
|
||||
println!("Minecraft PID: {:?}", pid);
|
||||
println!("Minecraft PID: {}", process.pid);
|
||||
|
||||
println!(
|
||||
"All running process UUID {:?}",
|
||||
process::get_all_running_uuids().await?
|
||||
);
|
||||
println!(
|
||||
"All running process paths {:?}",
|
||||
process::get_all_running_profile_paths().await?
|
||||
);
|
||||
println!("All running process UUID {:?}", process::get_all().await?);
|
||||
|
||||
// hold the lock to the process until it ends
|
||||
println!("Waiting for process to end...");
|
||||
let mut proc = proc_lock.write().await;
|
||||
process::wait_for(&mut proc).await?;
|
||||
process.wait_for().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "theseus_gui"
|
||||
version = "0.7.2"
|
||||
version = "0.0.0"
|
||||
description = "A Tauri App"
|
||||
authors = ["you"]
|
||||
license = ""
|
||||
@@ -28,7 +28,7 @@ tokio = { version = "1", features = ["full"] }
|
||||
thiserror = "1.0"
|
||||
tokio-stream = { version = "0.1", features = ["fs"] }
|
||||
futures = "0.3"
|
||||
daedalus = {version = "0.1.15", features = ["bincode"] }
|
||||
daedalus = "0.2.2"
|
||||
chrono = "0.4.26"
|
||||
|
||||
dirs = "5.0.1"
|
||||
@@ -46,6 +46,9 @@ sentry-rust-minidump = "0.7.0"
|
||||
lazy_static = "1"
|
||||
once_cell = "1"
|
||||
|
||||
dashmap = "6.0.1"
|
||||
paste = "1.0.15"
|
||||
|
||||
[target.'cfg(not(target_os = "linux"))'.dependencies]
|
||||
window-shadows = "0.2.1"
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<!-- Obviously needs to be replaced with your app's bundle identifier -->
|
||||
<string>com.modrinth.theseus</string>
|
||||
<string>ModrinthApp</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<!-- register the myapp:// and myscheme:// schemes -->
|
||||
@@ -27,7 +27,7 @@
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.modrinth.theseus-type</string>
|
||||
<string>ModrinthApp-type</string>
|
||||
</array>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>NSDocument</string>
|
||||
@@ -45,7 +45,7 @@
|
||||
<key>UTTypeIcons</key>
|
||||
<dict/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>com.modrinth.theseus-type</string>
|
||||
<string>ModrinthApp-type</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
|
||||
@@ -11,7 +11,6 @@ pub fn init<R: tauri::Runtime>() -> TauriPlugin<R> {
|
||||
auth_set_default_user,
|
||||
auth_remove_user,
|
||||
auth_users,
|
||||
auth_get_user,
|
||||
])
|
||||
.build()
|
||||
}
|
||||
@@ -96,11 +95,3 @@ pub async fn auth_set_default_user(user: uuid::Uuid) -> Result<()> {
|
||||
pub async fn auth_users() -> Result<Vec<Credentials>> {
|
||||
Ok(minecraft_auth::users().await?)
|
||||
}
|
||||
|
||||
/// Get a user from the UUID
|
||||
/// Prefer to use refresh instead, as it will refresh the credentials as well
|
||||
// invoke('plugin:auth|auth_users',user)
|
||||
#[tauri::command]
|
||||
pub async fn auth_get_user(user: uuid::Uuid) -> Result<Credentials> {
|
||||
Ok(minecraft_auth::get_user(user).await?)
|
||||
}
|
||||
|
||||
56
apps/app/src/api/cache.rs
Normal file
56
apps/app/src/api/cache.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use crate::api::Result;
|
||||
use theseus::prelude::*;
|
||||
|
||||
macro_rules! impl_cache_methods {
|
||||
($(($variant:ident, $type:ty)),*) => {
|
||||
$(
|
||||
paste::paste! {
|
||||
#[tauri::command]
|
||||
pub async fn [<get_ $variant:snake>](id: &str) -> Result<Option<$type>>
|
||||
{
|
||||
Ok(theseus::cache::[<get_ $variant:snake>](id).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn [<get_ $variant:snake _many>](
|
||||
ids: Vec<String>,
|
||||
) -> Result<Vec<$type>>
|
||||
{
|
||||
let ids = ids.iter().map(|x| &**x).collect::<Vec<&str>>();
|
||||
let entries =
|
||||
theseus::cache::[<get_ $variant:snake _many>](&*ids).await?;
|
||||
|
||||
Ok(entries)
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
impl_cache_methods!(
|
||||
(Project, Project),
|
||||
(Version, Version),
|
||||
(User, User),
|
||||
(Team, Vec<TeamMember>),
|
||||
(Organization, Organization),
|
||||
(SearchResults, SearchResults)
|
||||
);
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
tauri::plugin::Builder::new("cache")
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
get_project,
|
||||
get_project_many,
|
||||
get_version,
|
||||
get_version_many,
|
||||
get_user,
|
||||
get_user_many,
|
||||
get_team,
|
||||
get_team_many,
|
||||
get_organization,
|
||||
get_organization_many,
|
||||
get_search_results,
|
||||
get_search_results_many,
|
||||
])
|
||||
.build()
|
||||
}
|
||||
@@ -4,7 +4,6 @@ use crate::api::Result;
|
||||
use theseus::pack::import::ImportLauncherType;
|
||||
|
||||
use theseus::pack::import;
|
||||
use theseus::prelude::ProfilePathId;
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
tauri::plugin::Builder::new("import")
|
||||
@@ -33,7 +32,7 @@ pub async fn import_get_importable_instances(
|
||||
/// eg: import_instance(ImportLauncherType::MultiMC, PathBuf::from("C:/MultiMC"), "Instance 1")
|
||||
#[tauri::command]
|
||||
pub async fn import_import_instance(
|
||||
profile_path: ProfilePathId,
|
||||
profile_path: &str,
|
||||
launcher_type: ImportLauncherType,
|
||||
base_path: PathBuf,
|
||||
instance_folder: String,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::api::Result;
|
||||
use dashmap::DashMap;
|
||||
use std::path::PathBuf;
|
||||
use tauri::plugin::TauriPlugin;
|
||||
use theseus::prelude::JavaVersion;
|
||||
use theseus::prelude::*;
|
||||
@@ -8,6 +8,8 @@ use theseus::prelude::*;
|
||||
pub fn init<R: tauri::Runtime>() -> TauriPlugin<R> {
|
||||
tauri::plugin::Builder::new("jre")
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
get_java_versions,
|
||||
set_java_version,
|
||||
jre_find_filtered_jres,
|
||||
jre_get_jre,
|
||||
jre_test_jre,
|
||||
@@ -17,6 +19,17 @@ pub fn init<R: tauri::Runtime>() -> TauriPlugin<R> {
|
||||
.build()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_java_versions() -> Result<DashMap<u32, JavaVersion>> {
|
||||
Ok(jre::get_java_versions().await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn set_java_version(java_version: JavaVersion) -> Result<()> {
|
||||
jre::set_java_version(java_version).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Finds the installation of Java 8, if it exists
|
||||
#[tauri::command]
|
||||
pub async fn jre_find_filtered_jres(
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use crate::api::Result;
|
||||
use theseus::logs::LogType;
|
||||
use theseus::{
|
||||
logs::{self, CensoredString, LatestLogCursor, Logs},
|
||||
prelude::ProfilePathId,
|
||||
};
|
||||
use theseus::logs::{self, CensoredString, LatestLogCursor, Logs};
|
||||
|
||||
/*
|
||||
A log is a struct containing the filename string, stdout, and stderr, as follows:
|
||||
@@ -31,7 +28,7 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
/// Get all Logs for a profile, sorted by filename
|
||||
#[tauri::command]
|
||||
pub async fn logs_get_logs(
|
||||
profile_path: ProfilePathId,
|
||||
profile_path: &str,
|
||||
clear_contents: Option<bool>,
|
||||
) -> Result<Vec<Logs>> {
|
||||
let val = logs::get_logs(profile_path, clear_contents).await?;
|
||||
@@ -42,7 +39,7 @@ pub async fn logs_get_logs(
|
||||
/// Get a Log struct for a profile by profile id and filename string
|
||||
#[tauri::command]
|
||||
pub async fn logs_get_logs_by_filename(
|
||||
profile_path: ProfilePathId,
|
||||
profile_path: &str,
|
||||
log_type: LogType,
|
||||
filename: String,
|
||||
) -> Result<Logs> {
|
||||
@@ -52,37 +49,23 @@ pub async fn logs_get_logs_by_filename(
|
||||
/// Get the stdout for a profile by profile id and filename string
|
||||
#[tauri::command]
|
||||
pub async fn logs_get_output_by_filename(
|
||||
profile_path: ProfilePathId,
|
||||
profile_path: &str,
|
||||
log_type: LogType,
|
||||
filename: String,
|
||||
) -> Result<CensoredString> {
|
||||
let profile_path = if let Some(p) =
|
||||
crate::profile::get(&profile_path, None).await?
|
||||
{
|
||||
p.profile_id()
|
||||
} else {
|
||||
return Err(theseus::Error::from(
|
||||
theseus::ErrorKind::UnmanagedProfileError(profile_path.to_string()),
|
||||
)
|
||||
.into());
|
||||
};
|
||||
|
||||
Ok(
|
||||
logs::get_output_by_filename(&profile_path, log_type, &filename)
|
||||
.await?,
|
||||
)
|
||||
Ok(logs::get_output_by_filename(profile_path, log_type, &filename).await?)
|
||||
}
|
||||
|
||||
/// Delete all logs for a profile by profile id
|
||||
#[tauri::command]
|
||||
pub async fn logs_delete_logs(profile_path: ProfilePathId) -> Result<()> {
|
||||
pub async fn logs_delete_logs(profile_path: &str) -> Result<()> {
|
||||
Ok(logs::delete_logs(profile_path).await?)
|
||||
}
|
||||
|
||||
/// Delete a log for a profile by profile id and filename string
|
||||
#[tauri::command]
|
||||
pub async fn logs_delete_logs_by_filename(
|
||||
profile_path: ProfilePathId,
|
||||
profile_path: &str,
|
||||
log_type: LogType,
|
||||
filename: String,
|
||||
) -> Result<()> {
|
||||
@@ -95,7 +78,7 @@ pub async fn logs_delete_logs_by_filename(
|
||||
/// Get live log from a cursor
|
||||
#[tauri::command]
|
||||
pub async fn logs_get_latest_log_cursor(
|
||||
profile_path: ProfilePathId,
|
||||
profile_path: &str,
|
||||
cursor: u64, // 0 to start at beginning of file
|
||||
) -> Result<LatestLogCursor> {
|
||||
Ok(logs::get_latest_log_cursor(profile_path, cursor).await?)
|
||||
|
||||
@@ -6,10 +6,7 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
tauri::plugin::Builder::new("metadata")
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
metadata_get_game_versions,
|
||||
metadata_get_fabric_versions,
|
||||
metadata_get_forge_versions,
|
||||
metadata_get_quilt_versions,
|
||||
metadata_get_neoforge_versions,
|
||||
metadata_get_loader_versions,
|
||||
])
|
||||
.build()
|
||||
}
|
||||
@@ -22,24 +19,6 @@ pub async fn metadata_get_game_versions() -> Result<VersionManifest> {
|
||||
|
||||
/// Gets the fabric versions from daedalus
|
||||
#[tauri::command]
|
||||
pub async fn metadata_get_fabric_versions() -> Result<Manifest> {
|
||||
Ok(theseus::metadata::get_fabric_versions().await?)
|
||||
}
|
||||
|
||||
/// Gets the forge versions from daedalus
|
||||
#[tauri::command]
|
||||
pub async fn metadata_get_forge_versions() -> Result<Manifest> {
|
||||
Ok(theseus::metadata::get_forge_versions().await?)
|
||||
}
|
||||
|
||||
/// Gets the quilt versions from daedalus
|
||||
#[tauri::command]
|
||||
pub async fn metadata_get_quilt_versions() -> Result<Manifest> {
|
||||
Ok(theseus::metadata::get_quilt_versions().await?)
|
||||
}
|
||||
|
||||
/// Gets the quilt versions from daedalus
|
||||
#[tauri::command]
|
||||
pub async fn metadata_get_neoforge_versions() -> Result<Manifest> {
|
||||
Ok(theseus::metadata::get_neoforge_versions().await?)
|
||||
pub async fn metadata_get_loader_versions(loader: &str) -> Result<Manifest> {
|
||||
Ok(theseus::metadata::get_loader_versions(loader).await?)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ pub mod settings;
|
||||
pub mod tags;
|
||||
pub mod utils;
|
||||
|
||||
pub mod cache;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, TheseusSerializableError>;
|
||||
|
||||
// // Main returnable Theseus GUI error
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
use crate::api::Result;
|
||||
use chrono::{Duration, Utc};
|
||||
use tauri::plugin::TauriPlugin;
|
||||
use tauri::{Manager, UserAttentionType};
|
||||
use theseus::prelude::*;
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> TauriPlugin<R> {
|
||||
tauri::plugin::Builder::new("mr_auth")
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
authenticate_begin_flow,
|
||||
authenticate_await_completion,
|
||||
cancel_flow,
|
||||
login_pass,
|
||||
login_2fa,
|
||||
create_account,
|
||||
refresh,
|
||||
logout,
|
||||
get,
|
||||
])
|
||||
@@ -19,19 +17,68 @@ pub fn init<R: tauri::Runtime>() -> TauriPlugin<R> {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn authenticate_begin_flow(provider: &str) -> Result<String> {
|
||||
Ok(theseus::mr_auth::authenticate_begin_flow(provider).await?)
|
||||
}
|
||||
pub async fn modrinth_auth_login(
|
||||
app: tauri::AppHandle,
|
||||
provider: &str,
|
||||
) -> Result<Option<ModrinthCredentialsResult>> {
|
||||
let redirect_uri = mr_auth::authenticate_begin_flow(provider);
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn authenticate_await_completion() -> Result<ModrinthCredentialsResult>
|
||||
{
|
||||
Ok(theseus::mr_auth::authenticate_await_complete_flow().await?)
|
||||
}
|
||||
let start = Utc::now();
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn cancel_flow() -> Result<()> {
|
||||
Ok(theseus::mr_auth::cancel_flow().await?)
|
||||
if let Some(window) = app.get_window("modrinth-signin") {
|
||||
window.close()?;
|
||||
}
|
||||
|
||||
let window = tauri::WindowBuilder::new(
|
||||
&app,
|
||||
"modrinth-signin",
|
||||
tauri::WindowUrl::External(redirect_uri.parse().map_err(|_| {
|
||||
theseus::ErrorKind::OtherError(
|
||||
"Error parsing auth redirect URL".to_string(),
|
||||
)
|
||||
.as_error()
|
||||
})?),
|
||||
)
|
||||
.title("Sign into Modrinth")
|
||||
.always_on_top(true)
|
||||
.center()
|
||||
.build()?;
|
||||
|
||||
window.request_user_attention(Some(UserAttentionType::Critical))?;
|
||||
|
||||
while (Utc::now() - start) < Duration::minutes(10) {
|
||||
if window.title().is_err() {
|
||||
// user closed window, cancelling flow
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if window
|
||||
.url()
|
||||
.as_str()
|
||||
.starts_with("https://launcher-files.modrinth.com/detect.txt")
|
||||
{
|
||||
let query = window
|
||||
.url()
|
||||
.query_pairs()
|
||||
.map(|(key, val)| {
|
||||
(
|
||||
key.to_string(),
|
||||
serde_json::Value::String(val.to_string()),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
window.close()?;
|
||||
|
||||
let val = mr_auth::authenticate_finish_flow(query).await?;
|
||||
|
||||
return Ok(Some(val));
|
||||
}
|
||||
|
||||
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
|
||||
}
|
||||
|
||||
window.close()?;
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -66,11 +113,6 @@ pub async fn create_account(
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn refresh() -> Result<()> {
|
||||
Ok(theseus::mr_auth::refresh().await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn logout() -> Result<()> {
|
||||
Ok(theseus::mr_auth::logout().await?)
|
||||
|
||||
@@ -20,8 +20,8 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
#[tauri::command]
|
||||
pub async fn pack_install(
|
||||
location: CreatePackLocation,
|
||||
profile: ProfilePathId,
|
||||
) -> Result<ProfilePathId> {
|
||||
profile: String,
|
||||
) -> Result<String> {
|
||||
Ok(install_zipped_mrpack(location, profile).await?)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,78 +1,33 @@
|
||||
use crate::api::Result;
|
||||
use theseus::prelude::*;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
tauri::plugin::Builder::new("process")
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
process_has_finished_by_uuid,
|
||||
process_get_exit_status_by_uuid,
|
||||
process_get_all_uuids,
|
||||
process_get_all_running_uuids,
|
||||
process_get_uuids_by_profile_path,
|
||||
process_get_all_running_profile_paths,
|
||||
process_get_all_running_profiles,
|
||||
process_kill_by_uuid,
|
||||
process_wait_for_by_uuid,
|
||||
process_get_all,
|
||||
process_get_by_profile_path,
|
||||
process_kill,
|
||||
process_wait_for,
|
||||
])
|
||||
.build()
|
||||
}
|
||||
|
||||
// Checks if a process has finished by process UUID
|
||||
#[tauri::command]
|
||||
pub async fn process_has_finished_by_uuid(uuid: Uuid) -> Result<bool> {
|
||||
Ok(process::has_finished_by_uuid(uuid).await?)
|
||||
pub async fn process_get_all() -> Result<Vec<Process>> {
|
||||
Ok(process::get_all().await?)
|
||||
}
|
||||
|
||||
// Gets process exit status by process UUID
|
||||
#[tauri::command]
|
||||
pub async fn process_get_exit_status_by_uuid(
|
||||
uuid: Uuid,
|
||||
) -> Result<Option<i32>> {
|
||||
Ok(process::get_exit_status_by_uuid(uuid).await?)
|
||||
pub async fn process_get_by_profile_path(path: &str) -> Result<Vec<Process>> {
|
||||
Ok(process::get_by_profile_path(path).await?)
|
||||
}
|
||||
|
||||
// Gets all process UUIDs
|
||||
#[tauri::command]
|
||||
pub async fn process_get_all_uuids() -> Result<Vec<Uuid>> {
|
||||
Ok(process::get_all_uuids().await?)
|
||||
pub async fn process_kill(pid: i32) -> Result<()> {
|
||||
Ok(process::kill(pid).await?)
|
||||
}
|
||||
|
||||
// Gets all running process UUIDs
|
||||
#[tauri::command]
|
||||
pub async fn process_get_all_running_uuids() -> Result<Vec<Uuid>> {
|
||||
Ok(process::get_all_running_uuids().await?)
|
||||
}
|
||||
|
||||
// Gets all process UUIDs by profile path
|
||||
#[tauri::command]
|
||||
pub async fn process_get_uuids_by_profile_path(
|
||||
profile_path: ProfilePathId,
|
||||
) -> Result<Vec<Uuid>> {
|
||||
Ok(process::get_uuids_by_profile_path(profile_path).await?)
|
||||
}
|
||||
|
||||
// Gets the Profile paths of each *running* stored process in the state
|
||||
#[tauri::command]
|
||||
pub async fn process_get_all_running_profile_paths(
|
||||
) -> Result<Vec<ProfilePathId>> {
|
||||
Ok(process::get_all_running_profile_paths().await?)
|
||||
}
|
||||
|
||||
// Gets the Profiles (cloned) of each *running* stored process in the state
|
||||
#[tauri::command]
|
||||
pub async fn process_get_all_running_profiles() -> Result<Vec<Profile>> {
|
||||
Ok(process::get_all_running_profiles().await?)
|
||||
}
|
||||
|
||||
// Kill a process by process UUID
|
||||
#[tauri::command]
|
||||
pub async fn process_kill_by_uuid(uuid: Uuid) -> Result<()> {
|
||||
Ok(process::kill_by_uuid(uuid).await?)
|
||||
}
|
||||
|
||||
// Wait for a process to finish by process UUID
|
||||
#[tauri::command]
|
||||
pub async fn process_wait_for_by_uuid(uuid: Uuid) -> Result<()> {
|
||||
Ok(process::wait_for_by_uuid(uuid).await?)
|
||||
pub async fn process_wait_for(pid: i32) -> Result<()> {
|
||||
Ok(process::wait_for(pid).await?)
|
||||
}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
use crate::api::Result;
|
||||
use daedalus::modded::LoaderVersion;
|
||||
use dashmap::DashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
use theseus::{prelude::*, InnerProjectPathUnix};
|
||||
use uuid::Uuid;
|
||||
use theseus::prelude::*;
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
tauri::plugin::Builder::new("profile")
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
profile_remove,
|
||||
profile_get,
|
||||
profile_get_many,
|
||||
profile_get_projects,
|
||||
profile_get_optimal_jre_key,
|
||||
profile_get_full_path,
|
||||
profile_get_mod_full_path,
|
||||
@@ -26,9 +27,8 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
profile_update_managed_modrinth_version,
|
||||
profile_repair_managed_modrinth,
|
||||
profile_run,
|
||||
profile_run_wait,
|
||||
profile_run_credentials,
|
||||
profile_run_wait_credentials,
|
||||
profile_kill,
|
||||
profile_edit,
|
||||
profile_edit_icon,
|
||||
profile_export_mrpack,
|
||||
@@ -40,27 +40,39 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
// Remove a profile
|
||||
// invoke('plugin:profile|profile_add_path',path)
|
||||
#[tauri::command]
|
||||
pub async fn profile_remove(path: ProfilePathId) -> Result<()> {
|
||||
profile::remove(&path).await?;
|
||||
pub async fn profile_remove(path: &str) -> Result<()> {
|
||||
profile::remove(path).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Get a profile by path
|
||||
// invoke('plugin:profile|profile_add_path',path)
|
||||
#[tauri::command]
|
||||
pub async fn profile_get(
|
||||
path: ProfilePathId,
|
||||
clear_projects: Option<bool>,
|
||||
) -> Result<Option<Profile>> {
|
||||
let res = profile::get(&path, clear_projects).await?;
|
||||
pub async fn profile_get(path: &str) -> Result<Option<Profile>> {
|
||||
let res = profile::get(path).await?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn profile_get_many(paths: Vec<String>) -> Result<Vec<Profile>> {
|
||||
let ids = paths.iter().map(|x| &**x).collect::<Vec<&str>>();
|
||||
let entries = profile::get_many(&ids).await?;
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn profile_get_projects(
|
||||
path: &str,
|
||||
) -> Result<DashMap<String, ProfileFile>> {
|
||||
let res = profile::get_projects(path).await?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
// Get a profile's full path
|
||||
// invoke('plugin:profile|profile_get_full_path',path)
|
||||
#[tauri::command]
|
||||
pub async fn profile_get_full_path(path: ProfilePathId) -> Result<PathBuf> {
|
||||
let res = profile::get_full_path(&path).await?;
|
||||
pub async fn profile_get_full_path(path: &str) -> Result<PathBuf> {
|
||||
let res = profile::get_full_path(path).await?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
@@ -68,43 +80,41 @@ pub async fn profile_get_full_path(path: ProfilePathId) -> Result<PathBuf> {
|
||||
// invoke('plugin:profile|profile_get_mod_full_path',path)
|
||||
#[tauri::command]
|
||||
pub async fn profile_get_mod_full_path(
|
||||
path: ProfilePathId,
|
||||
project_path: ProjectPathId,
|
||||
path: &str,
|
||||
project_path: &str,
|
||||
) -> Result<PathBuf> {
|
||||
let res = profile::get_mod_full_path(&path, &project_path).await?;
|
||||
let res = profile::get_mod_full_path(path, project_path).await?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
// Get optimal java version from profile
|
||||
#[tauri::command]
|
||||
pub async fn profile_get_optimal_jre_key(
|
||||
path: ProfilePathId,
|
||||
path: &str,
|
||||
) -> Result<Option<JavaVersion>> {
|
||||
let res = profile::get_optimal_jre_key(&path).await?;
|
||||
let res = profile::get_optimal_jre_key(path).await?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
// Get a copy of the profile set
|
||||
// invoke('plugin:profile|profile_list')
|
||||
#[tauri::command]
|
||||
pub async fn profile_list(
|
||||
clear_projects: Option<bool>,
|
||||
) -> Result<HashMap<ProfilePathId, Profile>> {
|
||||
let res = profile::list(clear_projects).await?;
|
||||
pub async fn profile_list() -> Result<Vec<Profile>> {
|
||||
let res = profile::list().await?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn profile_check_installed(
|
||||
path: ProfilePathId,
|
||||
project_id: String,
|
||||
path: &str,
|
||||
project_id: &str,
|
||||
) -> Result<bool> {
|
||||
let profile = profile_get(path, None).await?;
|
||||
if let Some(profile) = profile {
|
||||
Ok(profile.projects.into_iter().any(|(_, project)| {
|
||||
if let ProjectMetadata::Modrinth { project, .. } = &project.metadata
|
||||
{
|
||||
project.id == project_id
|
||||
let check_project_id = project_id;
|
||||
|
||||
if let Ok(projects) = profile::get_projects(path).await {
|
||||
Ok(projects.into_iter().any(|(_, project)| {
|
||||
if let Some(metadata) = &project.metadata {
|
||||
check_project_id == metadata.project_id
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@@ -117,49 +127,47 @@ pub async fn profile_check_installed(
|
||||
/// Installs/Repairs a profile
|
||||
/// invoke('plugin:profile|profile_install')
|
||||
#[tauri::command]
|
||||
pub async fn profile_install(path: ProfilePathId, force: bool) -> Result<()> {
|
||||
profile::install(&path, force).await?;
|
||||
pub async fn profile_install(path: &str, force: bool) -> Result<()> {
|
||||
profile::install(path, force).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Updates all of the profile's projects
|
||||
/// invoke('plugin:profile|profile_update_all')
|
||||
#[tauri::command]
|
||||
pub async fn profile_update_all(
|
||||
path: ProfilePathId,
|
||||
) -> Result<HashMap<ProjectPathId, ProjectPathId>> {
|
||||
Ok(profile::update_all_projects(&path).await?)
|
||||
pub async fn profile_update_all(path: &str) -> Result<HashMap<String, String>> {
|
||||
Ok(profile::update_all_projects(path).await?)
|
||||
}
|
||||
|
||||
/// Updates a specified project
|
||||
/// invoke('plugin:profile|profile_update_project')
|
||||
#[tauri::command]
|
||||
pub async fn profile_update_project(
|
||||
path: ProfilePathId,
|
||||
project_path: ProjectPathId,
|
||||
) -> Result<ProjectPathId> {
|
||||
Ok(profile::update_project(&path, &project_path, None).await?)
|
||||
path: &str,
|
||||
project_path: &str,
|
||||
) -> Result<String> {
|
||||
Ok(profile::update_project(path, project_path, None).await?)
|
||||
}
|
||||
|
||||
// Adds a project to a profile from a version ID
|
||||
// invoke('plugin:profile|profile_add_project_from_version')
|
||||
#[tauri::command]
|
||||
pub async fn profile_add_project_from_version(
|
||||
path: ProfilePathId,
|
||||
version_id: String,
|
||||
) -> Result<ProjectPathId> {
|
||||
Ok(profile::add_project_from_version(&path, version_id).await?)
|
||||
path: &str,
|
||||
version_id: &str,
|
||||
) -> Result<String> {
|
||||
Ok(profile::add_project_from_version(path, version_id).await?)
|
||||
}
|
||||
|
||||
// Adds a project to a profile from a path
|
||||
// invoke('plugin:profile|profile_add_project_from_path')
|
||||
#[tauri::command]
|
||||
pub async fn profile_add_project_from_path(
|
||||
path: ProfilePathId,
|
||||
path: &str,
|
||||
project_path: &Path,
|
||||
project_type: Option<String>,
|
||||
) -> Result<ProjectPathId> {
|
||||
let res = profile::add_project_from_path(&path, project_path, project_type)
|
||||
project_type: Option<ProjectType>,
|
||||
) -> Result<String> {
|
||||
let res = profile::add_project_from_path(path, project_path, project_type)
|
||||
.await?;
|
||||
Ok(res)
|
||||
}
|
||||
@@ -168,27 +176,27 @@ pub async fn profile_add_project_from_path(
|
||||
// invoke('plugin:profile|profile_toggle_disable_project')
|
||||
#[tauri::command]
|
||||
pub async fn profile_toggle_disable_project(
|
||||
path: ProfilePathId,
|
||||
project_path: ProjectPathId,
|
||||
) -> Result<ProjectPathId> {
|
||||
Ok(profile::toggle_disable_project(&path, &project_path).await?)
|
||||
path: &str,
|
||||
project_path: &str,
|
||||
) -> Result<String> {
|
||||
Ok(profile::toggle_disable_project(path, project_path).await?)
|
||||
}
|
||||
|
||||
// Removes a project from a profile
|
||||
// invoke('plugin:profile|profile_remove_project')
|
||||
#[tauri::command]
|
||||
pub async fn profile_remove_project(
|
||||
path: ProfilePathId,
|
||||
project_path: ProjectPathId,
|
||||
path: &str,
|
||||
project_path: &str,
|
||||
) -> Result<()> {
|
||||
profile::remove_project(&path, &project_path).await?;
|
||||
profile::remove_project(path, project_path).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Updates a managed Modrinth profile to a version of version_id
|
||||
#[tauri::command]
|
||||
pub async fn profile_update_managed_modrinth_version(
|
||||
path: ProfilePathId,
|
||||
path: String,
|
||||
version_id: String,
|
||||
) -> Result<()> {
|
||||
Ok(
|
||||
@@ -199,17 +207,15 @@ pub async fn profile_update_managed_modrinth_version(
|
||||
|
||||
// Repairs a managed Modrinth profile by updating it to the current version
|
||||
#[tauri::command]
|
||||
pub async fn profile_repair_managed_modrinth(
|
||||
path: ProfilePathId,
|
||||
) -> Result<()> {
|
||||
Ok(profile::update::repair_managed_modrinth(&path).await?)
|
||||
pub async fn profile_repair_managed_modrinth(path: &str) -> Result<()> {
|
||||
Ok(profile::update::repair_managed_modrinth(path).await?)
|
||||
}
|
||||
|
||||
// Exports a profile to a .mrpack file (export_location should end in .mrpack)
|
||||
// invoke('profile_export_mrpack')
|
||||
#[tauri::command]
|
||||
pub async fn profile_export_mrpack(
|
||||
path: ProfilePathId,
|
||||
path: &str,
|
||||
export_location: PathBuf,
|
||||
included_overrides: Vec<String>,
|
||||
version_id: Option<String>,
|
||||
@@ -217,7 +223,7 @@ pub async fn profile_export_mrpack(
|
||||
name: Option<String>, // only used to cache
|
||||
) -> Result<()> {
|
||||
profile::export_mrpack(
|
||||
&path,
|
||||
path,
|
||||
export_location,
|
||||
included_overrides,
|
||||
version_id,
|
||||
@@ -231,9 +237,9 @@ pub async fn profile_export_mrpack(
|
||||
/// See [`profile::get_pack_export_candidates`]
|
||||
#[tauri::command]
|
||||
pub async fn profile_get_pack_export_candidates(
|
||||
profile_path: ProfilePathId,
|
||||
) -> Result<Vec<InnerProjectPathUnix>> {
|
||||
let candidates = profile::get_pack_export_candidates(&profile_path).await?;
|
||||
profile_path: &str,
|
||||
) -> Result<Vec<String>> {
|
||||
let candidates = profile::get_pack_export_candidates(profile_path).await?;
|
||||
Ok(candidates)
|
||||
}
|
||||
|
||||
@@ -242,19 +248,10 @@ pub async fn profile_get_pack_export_candidates(
|
||||
// for the actual Child in the state.
|
||||
// invoke('plugin:profile|profile_run', path)
|
||||
#[tauri::command]
|
||||
pub async fn profile_run(path: ProfilePathId) -> Result<Uuid> {
|
||||
let minecraft_child = profile::run(&path).await?;
|
||||
let uuid = minecraft_child.read().await.uuid;
|
||||
Ok(uuid)
|
||||
}
|
||||
pub async fn profile_run(path: &str) -> Result<Process> {
|
||||
let process = profile::run(path).await?;
|
||||
|
||||
// Run Minecraft using a profile using the default credentials, and wait for the result
|
||||
// invoke('plugin:profile|profile_run_wait', path)
|
||||
#[tauri::command]
|
||||
pub async fn profile_run_wait(path: ProfilePathId) -> Result<()> {
|
||||
let proc_lock = profile::run(&path).await?;
|
||||
let mut proc = proc_lock.write().await;
|
||||
Ok(process::wait_for(&mut proc).await?)
|
||||
Ok(process)
|
||||
}
|
||||
|
||||
// Run Minecraft using a profile using chosen credentials
|
||||
@@ -263,85 +260,83 @@ pub async fn profile_run_wait(path: ProfilePathId) -> Result<()> {
|
||||
// invoke('plugin:profile|profile_run_credentials', {path, credentials})')
|
||||
#[tauri::command]
|
||||
pub async fn profile_run_credentials(
|
||||
path: ProfilePathId,
|
||||
path: &str,
|
||||
credentials: Credentials,
|
||||
) -> Result<Uuid> {
|
||||
let minecraft_child = profile::run_credentials(&path, &credentials).await?;
|
||||
let uuid = minecraft_child.read().await.uuid;
|
||||
) -> Result<Process> {
|
||||
let process = profile::run_credentials(path, &credentials).await?;
|
||||
|
||||
Ok(uuid)
|
||||
Ok(process)
|
||||
}
|
||||
|
||||
// Run Minecraft using a profile using the chosen credentials, and wait for the result
|
||||
// invoke('plugin:profile|profile_run_wait', {path, credentials)
|
||||
#[tauri::command]
|
||||
pub async fn profile_run_wait_credentials(
|
||||
path: ProfilePathId,
|
||||
credentials: Credentials,
|
||||
) -> Result<()> {
|
||||
let proc_lock = profile::run_credentials(&path, &credentials).await?;
|
||||
let mut proc = proc_lock.write().await;
|
||||
Ok(process::wait_for(&mut proc).await?)
|
||||
pub async fn profile_kill(path: &str) -> Result<()> {
|
||||
profile::kill(path).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct EditProfile {
|
||||
pub metadata: Option<EditProfileMetadata>,
|
||||
pub java: Option<JavaSettings>,
|
||||
pub memory: Option<MemorySettings>,
|
||||
pub resolution: Option<WindowSize>,
|
||||
pub hooks: Option<Hooks>,
|
||||
pub fullscreen: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct EditProfileMetadata {
|
||||
pub name: Option<String>,
|
||||
|
||||
pub game_version: Option<String>,
|
||||
pub loader: Option<ModLoader>,
|
||||
pub loader_version: Option<LoaderVersion>,
|
||||
pub linked_data: Option<LinkedData>,
|
||||
pub loader_version: Option<String>,
|
||||
|
||||
pub groups: Option<Vec<String>>,
|
||||
|
||||
pub linked_data: Option<LinkedData>,
|
||||
|
||||
pub java_path: Option<String>,
|
||||
pub extra_launch_args: Option<Vec<String>>,
|
||||
pub custom_env_vars: Option<Vec<(String, String)>>,
|
||||
|
||||
pub memory: Option<MemorySettings>,
|
||||
pub force_fullscreen: Option<bool>,
|
||||
pub game_resolution: Option<WindowSize>,
|
||||
pub hooks: Option<Hooks>,
|
||||
}
|
||||
|
||||
// Edits a profile
|
||||
// invoke('plugin:profile|profile_edit', {path, editProfile})
|
||||
#[tauri::command]
|
||||
pub async fn profile_edit(
|
||||
path: ProfilePathId,
|
||||
edit_profile: EditProfile,
|
||||
) -> Result<()> {
|
||||
profile::edit(&path, |prof| {
|
||||
if let Some(metadata) = edit_profile.metadata.clone() {
|
||||
if let Some(name) = metadata.name {
|
||||
prof.metadata.name = name;
|
||||
}
|
||||
if let Some(game_version) = metadata.game_version {
|
||||
prof.metadata.game_version = game_version;
|
||||
}
|
||||
if let Some(loader) = metadata.loader {
|
||||
prof.metadata.loader = loader;
|
||||
}
|
||||
prof.metadata.loader_version = metadata.loader_version;
|
||||
prof.metadata.linked_data = metadata.linked_data;
|
||||
pub async fn profile_edit(path: &str, edit_profile: EditProfile) -> Result<()> {
|
||||
profile::edit(path, |prof| {
|
||||
if let Some(name) = edit_profile.name.clone() {
|
||||
prof.name = name;
|
||||
}
|
||||
if let Some(game_version) = edit_profile.game_version.clone() {
|
||||
prof.game_version = game_version;
|
||||
}
|
||||
if let Some(loader) = edit_profile.loader {
|
||||
prof.loader = loader;
|
||||
}
|
||||
prof.loader_version.clone_from(&edit_profile.loader_version);
|
||||
prof.linked_data.clone_from(&edit_profile.linked_data);
|
||||
|
||||
if let Some(groups) = metadata.groups {
|
||||
prof.metadata.groups = groups;
|
||||
}
|
||||
if let Some(groups) = edit_profile.groups.clone() {
|
||||
prof.groups = groups;
|
||||
}
|
||||
|
||||
prof.java.clone_from(&edit_profile.java);
|
||||
prof.java_path.clone_from(&edit_profile.java_path);
|
||||
prof.memory = edit_profile.memory;
|
||||
prof.resolution = edit_profile.resolution;
|
||||
prof.fullscreen = edit_profile.fullscreen;
|
||||
prof.hooks.clone_from(&edit_profile.hooks);
|
||||
prof.game_resolution = edit_profile.game_resolution;
|
||||
prof.force_fullscreen = edit_profile.force_fullscreen;
|
||||
|
||||
prof.metadata.date_modified = chrono::Utc::now();
|
||||
if let Some(hooks) = edit_profile.hooks.clone() {
|
||||
prof.hooks = hooks;
|
||||
}
|
||||
|
||||
prof.modified = chrono::Utc::now();
|
||||
|
||||
prof.custom_env_vars
|
||||
.clone_from(&edit_profile.custom_env_vars);
|
||||
prof.extra_launch_args
|
||||
.clone_from(&edit_profile.extra_launch_args);
|
||||
|
||||
async { Ok(()) }
|
||||
})
|
||||
.await?;
|
||||
State::sync().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -350,9 +345,9 @@ pub async fn profile_edit(
|
||||
// invoke('plugin:profile|profile_edit_icon')
|
||||
#[tauri::command]
|
||||
pub async fn profile_edit_icon(
|
||||
path: ProfilePathId,
|
||||
path: &str,
|
||||
icon_path: Option<&Path>,
|
||||
) -> Result<()> {
|
||||
profile::edit_icon(&path, icon_path).await?;
|
||||
profile::edit_icon(path, icon_path).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::api::Result;
|
||||
use std::path::PathBuf;
|
||||
use theseus::prelude::*;
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
@@ -19,9 +18,9 @@ pub async fn profile_create(
|
||||
game_version: String, // the game version of the profile
|
||||
modloader: ModLoader, // the modloader to use
|
||||
loader_version: Option<String>, // the modloader version to use, set to "latest", "stable", or the ID of your chosen loader
|
||||
icon: Option<PathBuf>, // the icon for the profile
|
||||
no_watch: Option<bool>,
|
||||
) -> Result<ProfilePathId> {
|
||||
icon: Option<String>, // the icon for the profile
|
||||
skip_install: Option<bool>,
|
||||
) -> Result<String> {
|
||||
let res = profile::create::profile_create(
|
||||
name,
|
||||
game_version,
|
||||
@@ -29,9 +28,7 @@ pub async fn profile_create(
|
||||
loader_version,
|
||||
icon,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
no_watch,
|
||||
skip_install,
|
||||
)
|
||||
.await?;
|
||||
Ok(res)
|
||||
@@ -40,7 +37,7 @@ pub async fn profile_create(
|
||||
// Creates a profile from a duplicate
|
||||
// invoke('plugin:profile_create|profile_duplicate',profile)
|
||||
#[tauri::command]
|
||||
pub async fn profile_duplicate(path: ProfilePathId) -> Result<ProfilePathId> {
|
||||
pub async fn profile_duplicate(path: &str) -> Result<String> {
|
||||
let res = profile::create::profile_create_from_duplicate(path).await?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::api::Result;
|
||||
use theseus::prelude::*;
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
tauri::plugin::Builder::new("settings")
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
settings_get,
|
||||
settings_set,
|
||||
settings_change_config_dir,
|
||||
settings_is_dir_writeable
|
||||
])
|
||||
.invoke_handler(tauri::generate_handler![settings_get, settings_set])
|
||||
.build()
|
||||
}
|
||||
|
||||
@@ -29,20 +22,3 @@ pub async fn settings_set(settings: Settings) -> Result<()> {
|
||||
settings::set(settings).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Change config directory
|
||||
// Seizes the entire State to do it
|
||||
// invoke('plugin:settings|settings_change_config_dir', new_dir)
|
||||
#[tauri::command]
|
||||
pub async fn settings_change_config_dir(new_config_dir: PathBuf) -> Result<()> {
|
||||
settings::set_config_dir(new_config_dir).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn settings_is_dir_writeable(
|
||||
new_config_dir: PathBuf,
|
||||
) -> Result<bool> {
|
||||
let res = settings::is_dir_writeable(new_config_dir).await?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::api::Result;
|
||||
use theseus::tags::{Category, DonationPlatform, GameVersion, Loader, Tags};
|
||||
use theseus::tags::{Category, DonationPlatform, GameVersion, Loader};
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
tauri::plugin::Builder::new("tags")
|
||||
@@ -9,7 +9,6 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
tags_get_loaders,
|
||||
tags_get_game_versions,
|
||||
tags_get_donation_platforms,
|
||||
tags_get_tag_bundle,
|
||||
])
|
||||
.build()
|
||||
}
|
||||
@@ -43,9 +42,3 @@ pub async fn tags_get_game_versions() -> Result<Vec<GameVersion>> {
|
||||
pub async fn tags_get_donation_platforms() -> Result<Vec<DonationPlatform>> {
|
||||
Ok(theseus::tags::get_donation_platform_tags().await?)
|
||||
}
|
||||
|
||||
/// Gets cached tag bundle from the database
|
||||
#[tauri::command]
|
||||
pub async fn tags_get_tag_bundle() -> Result<Tags> {
|
||||
Ok(theseus::tags::get_tag_bundle().await?)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ use serde::{Deserialize, Serialize};
|
||||
use theseus::{
|
||||
handler,
|
||||
prelude::{CommandPayload, DirectoryInfo},
|
||||
State,
|
||||
};
|
||||
|
||||
use crate::api::Result;
|
||||
@@ -16,11 +15,7 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
show_in_folder,
|
||||
show_launcher_logs_folder,
|
||||
progress_bars_list,
|
||||
safety_check_safe_loading_bars,
|
||||
get_opening_command,
|
||||
await_sync,
|
||||
is_offline,
|
||||
refresh_offline
|
||||
get_opening_command
|
||||
])
|
||||
.build()
|
||||
}
|
||||
@@ -54,12 +49,6 @@ pub async fn progress_bars_list(
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
// Check if there are any safe loading bars running
|
||||
#[tauri::command]
|
||||
pub async fn safety_check_safe_loading_bars() -> Result<bool> {
|
||||
Ok(theseus::safety::check_safe_loading_bars().await?)
|
||||
}
|
||||
|
||||
// cfg only on mac os
|
||||
// disables mouseover and fixes a random crash error only fixed by recent versions of macos
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -82,7 +71,7 @@ pub async fn should_disable_mouseover() -> bool {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn show_in_folder(mut path: PathBuf) -> Result<()> {
|
||||
pub fn show_in_folder(path: PathBuf) -> Result<()> {
|
||||
{
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
@@ -101,6 +90,7 @@ pub fn show_in_folder(mut path: PathBuf) -> Result<()> {
|
||||
{
|
||||
use std::fs::metadata;
|
||||
|
||||
let mut path = path;
|
||||
let path_string = path.to_string_lossy().to_string();
|
||||
|
||||
if metadata(&path)?.is_dir() {
|
||||
@@ -171,28 +161,3 @@ pub async fn get_opening_command() -> Result<Option<CommandPayload>> {
|
||||
pub async fn handle_command(command: String) -> Result<()> {
|
||||
Ok(theseus::handler::parse_and_emit_command(&command).await?)
|
||||
}
|
||||
|
||||
// Waits for state to be synced
|
||||
#[tauri::command]
|
||||
pub async fn await_sync() -> Result<()> {
|
||||
State::sync().await?;
|
||||
tracing::debug!("State synced");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check if theseus is currently in offline mode, without a refresh attempt
|
||||
#[tauri::command]
|
||||
pub async fn is_offline() -> Result<bool> {
|
||||
let state = State::get().await?;
|
||||
let offline = *state.offline.read().await;
|
||||
Ok(offline)
|
||||
}
|
||||
|
||||
/// Refreshes whether or not theseus is in offline mode, and returns the new value
|
||||
#[tauri::command]
|
||||
pub async fn refresh_offline() -> Result<bool> {
|
||||
let state = State::get().await?;
|
||||
state.refresh_offline().await?;
|
||||
let offline = *state.offline.read().await;
|
||||
Ok(offline)
|
||||
}
|
||||
|
||||
@@ -17,10 +17,8 @@ mod macos;
|
||||
#[tauri::command]
|
||||
async fn initialize_state(app: tauri::AppHandle) -> api::Result<()> {
|
||||
theseus::EventState::init(app).await?;
|
||||
let s = State::get().await?;
|
||||
State::update();
|
||||
State::init().await?;
|
||||
|
||||
s.children.write().await.rescue_cache().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -50,7 +48,7 @@ struct Payload {
|
||||
// if Tauri app is called with arguments, then those arguments will be treated as commands
|
||||
// ie: deep links or filepaths for .mrpacks
|
||||
fn main() {
|
||||
tauri_plugin_deep_link::prepare("com.modrinth.theseus");
|
||||
tauri_plugin_deep_link::prepare("ModrinthApp");
|
||||
|
||||
/*
|
||||
tracing is set basd on the environment variable RUST_LOG=xxx, depending on the amount of logs to show
|
||||
@@ -128,6 +126,7 @@ fn main() {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let builder = builder
|
||||
.plugin(api::auth::init())
|
||||
.plugin(api::mr_auth::init())
|
||||
@@ -142,11 +141,13 @@ fn main() {
|
||||
.plugin(api::settings::init())
|
||||
.plugin(api::tags::init())
|
||||
.plugin(api::utils::init())
|
||||
.plugin(api::cache::init())
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
initialize_state,
|
||||
is_dev,
|
||||
toggle_decorations,
|
||||
api::auth::auth_login,
|
||||
api::mr_auth::modrinth_auth_login,
|
||||
]);
|
||||
|
||||
builder
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
},
|
||||
"package": {
|
||||
"productName": "Modrinth App",
|
||||
"version": "0.7.2"
|
||||
"version": "0.8.0-1"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
@@ -20,8 +20,8 @@
|
||||
"asset": true,
|
||||
"assetScope": [
|
||||
"$APPDATA/caches/icons/*",
|
||||
"$APPCONFIG/caches/icons/*",
|
||||
"$CONFIG/caches/icons/*"
|
||||
"$APPDATA/caches/icons/*",
|
||||
"$APPDATA/caches/icons/*"
|
||||
]
|
||||
},
|
||||
"shell": {
|
||||
@@ -56,7 +56,7 @@
|
||||
},
|
||||
"externalBin": [],
|
||||
"icon": ["icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"],
|
||||
"identifier": "com.modrinth.theseus",
|
||||
"identifier": "ModrinthApp",
|
||||
"longDescription": "",
|
||||
"macOS": {
|
||||
"entitlements": "App.entitlements",
|
||||
|
||||
158
packages/app-lib/.sqlx/query-03d1aeddf7788320530c447a82342aecdb4099ce183dd9106c4bcc47604cb080.json
generated
Normal file
158
packages/app-lib/.sqlx/query-03d1aeddf7788320530c447a82342aecdb4099ce183dd9106c4bcc47604cb080.json
generated
Normal file
@@ -0,0 +1,158 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT\n max_concurrent_writes, max_concurrent_downloads,\n theme, default_page, collapsed_navigation, advanced_rendering, native_decorations,\n discord_rpc, developer_mode, telemetry,\n onboarded,\n json(extra_launch_args) extra_launch_args, json(custom_env_vars) custom_env_vars,\n mc_memory_max, mc_force_fullscreen, mc_game_resolution_x, mc_game_resolution_y, hide_on_process_start,\n hook_pre_launch, hook_wrapper, hook_post_exit,\n custom_dir, prev_custom_dir, migrated\n FROM settings\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "max_concurrent_writes",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "max_concurrent_downloads",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "theme",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "default_page",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "collapsed_navigation",
|
||||
"ordinal": 4,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "advanced_rendering",
|
||||
"ordinal": 5,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "native_decorations",
|
||||
"ordinal": 6,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "discord_rpc",
|
||||
"ordinal": 7,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "developer_mode",
|
||||
"ordinal": 8,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "telemetry",
|
||||
"ordinal": 9,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "onboarded",
|
||||
"ordinal": 10,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "extra_launch_args",
|
||||
"ordinal": 11,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "custom_env_vars",
|
||||
"ordinal": 12,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "mc_memory_max",
|
||||
"ordinal": 13,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "mc_force_fullscreen",
|
||||
"ordinal": 14,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "mc_game_resolution_x",
|
||||
"ordinal": 15,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "mc_game_resolution_y",
|
||||
"ordinal": 16,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "hide_on_process_start",
|
||||
"ordinal": 17,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "hook_pre_launch",
|
||||
"ordinal": 18,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "hook_wrapper",
|
||||
"ordinal": 19,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "hook_post_exit",
|
||||
"ordinal": 20,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "custom_dir",
|
||||
"ordinal": 21,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "prev_custom_dir",
|
||||
"ordinal": 22,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "migrated",
|
||||
"ordinal": 23,
|
||||
"type_info": "Int64"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "03d1aeddf7788320530c447a82342aecdb4099ce183dd9106c4bcc47604cb080"
|
||||
}
|
||||
12
packages/app-lib/.sqlx/query-0cfb12e0553411b01b721d1c38ef27acd240bb2ff3e07dee962bf67e20f81f36.json
generated
Normal file
12
packages/app-lib/.sqlx/query-0cfb12e0553411b01b721d1c38ef27acd240bb2ff3e07dee962bf67e20f81f36.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n INSERT INTO minecraft_device_tokens (id, uuid, private_key, x, y, issue_instant, not_after, token, display_claims)\n VALUES (0, $1, $2, $3, $4, $5, $6, $7, $8)\n ON CONFLICT (id) DO UPDATE SET\n uuid = $1,\n private_key = $2,\n x = $3,\n y = $4,\n issue_instant = $5,\n not_after = $6,\n token = $7,\n display_claims = jsonb($8)\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 8
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "0cfb12e0553411b01b721d1c38ef27acd240bb2ff3e07dee962bf67e20f81f36"
|
||||
}
|
||||
12
packages/app-lib/.sqlx/query-12f8b2b9f0acca2ea29aa6a77266b2b27efc6a0433bab1d4bbe10c69fd417494.json
generated
Normal file
12
packages/app-lib/.sqlx/query-12f8b2b9f0acca2ea29aa6a77266b2b27efc6a0433bab1d4bbe10c69fd417494.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n UPDATE minecraft_users\n SET active = FALSE\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "12f8b2b9f0acca2ea29aa6a77266b2b27efc6a0433bab1d4bbe10c69fd417494"
|
||||
}
|
||||
32
packages/app-lib/.sqlx/query-1397c1825096fb402cdd3b5dae8cd3910b1719f433a0c34d40415dd7681ab272.json
generated
Normal file
32
packages/app-lib/.sqlx/query-1397c1825096fb402cdd3b5dae8cd3910b1719f433a0c34d40415dd7681ab272.json
generated
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT\n full_version, architecture, path\n FROM java_versions\n WHERE major_version = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "full_version",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "architecture",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "path",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "1397c1825096fb402cdd3b5dae8cd3910b1719f433a0c34d40415dd7681ab272"
|
||||
}
|
||||
12
packages/app-lib/.sqlx/query-169ce6afb8e9739dacff3f4bea024ed28df292a063d615514c67a38301d71806.json
generated
Normal file
12
packages/app-lib/.sqlx/query-169ce6afb8e9739dacff3f4bea024ed28df292a063d615514c67a38301d71806.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n DELETE FROM profiles\n WHERE path = $1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "169ce6afb8e9739dacff3f4bea024ed28df292a063d615514c67a38301d71806"
|
||||
}
|
||||
12
packages/app-lib/.sqlx/query-1769b7033985bfdd04ee8912d9f28e0d15a8b893db47aca3aec054c7134f1f3f.json
generated
Normal file
12
packages/app-lib/.sqlx/query-1769b7033985bfdd04ee8912d9f28e0d15a8b893db47aca3aec054c7134f1f3f.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n DELETE FROM processes WHERE pid = $1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "1769b7033985bfdd04ee8912d9f28e0d15a8b893db47aca3aec054c7134f1f3f"
|
||||
}
|
||||
38
packages/app-lib/.sqlx/query-18881c0c2ec1b0cc73fa13b4c242dfc577061b92479ce96ffb30a457939b5ffe.json
generated
Normal file
38
packages/app-lib/.sqlx/query-18881c0c2ec1b0cc73fa13b4c242dfc577061b92479ce96ffb30a457939b5ffe.json
generated
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT\n id, active, session_id, expires\n FROM modrinth_users\n WHERE active = TRUE\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "active",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "session_id",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "expires",
|
||||
"ordinal": 3,
|
||||
"type_info": "Int64"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "18881c0c2ec1b0cc73fa13b4c242dfc577061b92479ce96ffb30a457939b5ffe"
|
||||
}
|
||||
38
packages/app-lib/.sqlx/query-265f9c9ad992da0aeaf69c3f0077b54a186b98796ec549c9d891089ea33cf3fc.json
generated
Normal file
38
packages/app-lib/.sqlx/query-265f9c9ad992da0aeaf69c3f0077b54a186b98796ec549c9d891089ea33cf3fc.json
generated
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT\n major_version, full_version, architecture, path\n FROM java_versions\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "major_version",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "full_version",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "architecture",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "path",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "265f9c9ad992da0aeaf69c3f0077b54a186b98796ec549c9d891089ea33cf3fc"
|
||||
}
|
||||
44
packages/app-lib/.sqlx/query-28b3e3132d75e551c1fa14b8d3be36adca581f8ad1b90f85d3ec3d92ec61e65e.json
generated
Normal file
44
packages/app-lib/.sqlx/query-28b3e3132d75e551c1fa14b8d3be36adca581f8ad1b90f85d3ec3d92ec61e65e.json
generated
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT id, data_type, json(data) as \"data?: serde_json::Value\", alias, expires\n FROM cache\n WHERE data_type = $1 AND (\n id IN (SELECT value FROM json_each($2))\n OR\n alias IN (SELECT value FROM json_each($3))\n )\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "data_type",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "data?: serde_json::Value",
|
||||
"ordinal": 2,
|
||||
"type_info": "Null"
|
||||
},
|
||||
{
|
||||
"name": "alias",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "expires",
|
||||
"ordinal": 4,
|
||||
"type_info": "Int64"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "28b3e3132d75e551c1fa14b8d3be36adca581f8ad1b90f85d3ec3d92ec61e65e"
|
||||
}
|
||||
50
packages/app-lib/.sqlx/query-3cac786ad15ef1167bc50ca846d98facb3dee35c9e421209c1161ee7380b7a74.json
generated
Normal file
50
packages/app-lib/.sqlx/query-3cac786ad15ef1167bc50ca846d98facb3dee35c9e421209c1161ee7380b7a74.json
generated
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT\n pid, start_time, name, executable, profile_path, post_exit_command\n FROM processes\n WHERE 1=$1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "pid",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "start_time",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "executable",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "profile_path",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "post_exit_command",
|
||||
"ordinal": 5,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "3cac786ad15ef1167bc50ca846d98facb3dee35c9e421209c1161ee7380b7a74"
|
||||
}
|
||||
12
packages/app-lib/.sqlx/query-45c692b305b36540139b5956dcff5bd5aeacec7d0a8abd640a7365902e57a2fd.json
generated
Normal file
12
packages/app-lib/.sqlx/query-45c692b305b36540139b5956dcff5bd5aeacec7d0a8abd640a7365902e57a2fd.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n UPDATE modrinth_users\n SET active = FALSE\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "45c692b305b36540139b5956dcff5bd5aeacec7d0a8abd640a7365902e57a2fd"
|
||||
}
|
||||
170
packages/app-lib/.sqlx/query-4acd47f6bad3d2d4df5e5d43b3441fa2714cb8ad978adc108acc67f042380df1.json
generated
Normal file
170
packages/app-lib/.sqlx/query-4acd47f6bad3d2d4df5e5d43b3441fa2714cb8ad978adc108acc67f042380df1.json
generated
Normal file
@@ -0,0 +1,170 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT\n path, install_stage, name, icon_path,\n game_version, mod_loader, mod_loader_version,\n json(groups) as \"groups!: serde_json::Value\",\n linked_project_id, linked_version_id, locked,\n created, modified, last_played,\n submitted_time_played, recent_time_played,\n override_java_path,\n json(override_extra_launch_args) as \"override_extra_launch_args!: serde_json::Value\", json(override_custom_env_vars) as \"override_custom_env_vars!: serde_json::Value\",\n override_mc_memory_max, override_mc_force_fullscreen, override_mc_game_resolution_x, override_mc_game_resolution_y,\n override_hook_pre_launch, override_hook_wrapper, override_hook_post_exit\n FROM profiles\n WHERE 1=$1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "path",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "install_stage",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "icon_path",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "game_version",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "mod_loader",
|
||||
"ordinal": 5,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "mod_loader_version",
|
||||
"ordinal": 6,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "groups!: serde_json::Value",
|
||||
"ordinal": 7,
|
||||
"type_info": "Null"
|
||||
},
|
||||
{
|
||||
"name": "linked_project_id",
|
||||
"ordinal": 8,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "linked_version_id",
|
||||
"ordinal": 9,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "locked",
|
||||
"ordinal": 10,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "created",
|
||||
"ordinal": 11,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "modified",
|
||||
"ordinal": 12,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "last_played",
|
||||
"ordinal": 13,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "submitted_time_played",
|
||||
"ordinal": 14,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "recent_time_played",
|
||||
"ordinal": 15,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "override_java_path",
|
||||
"ordinal": 16,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "override_extra_launch_args!: serde_json::Value",
|
||||
"ordinal": 17,
|
||||
"type_info": "Null"
|
||||
},
|
||||
{
|
||||
"name": "override_custom_env_vars!: serde_json::Value",
|
||||
"ordinal": 18,
|
||||
"type_info": "Null"
|
||||
},
|
||||
{
|
||||
"name": "override_mc_memory_max",
|
||||
"ordinal": 19,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "override_mc_force_fullscreen",
|
||||
"ordinal": 20,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "override_mc_game_resolution_x",
|
||||
"ordinal": 21,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "override_mc_game_resolution_y",
|
||||
"ordinal": 22,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "override_hook_pre_launch",
|
||||
"ordinal": 23,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "override_hook_wrapper",
|
||||
"ordinal": 24,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "override_hook_post_exit",
|
||||
"ordinal": 25,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "4acd47f6bad3d2d4df5e5d43b3441fa2714cb8ad978adc108acc67f042380df1"
|
||||
}
|
||||
170
packages/app-lib/.sqlx/query-5265d5ad85da898855d628f6b45e39026908fc950aad3c7797be37b5d0b74094.json
generated
Normal file
170
packages/app-lib/.sqlx/query-5265d5ad85da898855d628f6b45e39026908fc950aad3c7797be37b5d0b74094.json
generated
Normal file
@@ -0,0 +1,170 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT\n path, install_stage, name, icon_path,\n game_version, mod_loader, mod_loader_version,\n json(groups) as \"groups!: serde_json::Value\",\n linked_project_id, linked_version_id, locked,\n created, modified, last_played,\n submitted_time_played, recent_time_played,\n override_java_path,\n json(override_extra_launch_args) as \"override_extra_launch_args!: serde_json::Value\", json(override_custom_env_vars) as \"override_custom_env_vars!: serde_json::Value\",\n override_mc_memory_max, override_mc_force_fullscreen, override_mc_game_resolution_x, override_mc_game_resolution_y,\n override_hook_pre_launch, override_hook_wrapper, override_hook_post_exit\n FROM profiles\n WHERE path IN (SELECT value FROM json_each($1))",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "path",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "install_stage",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "icon_path",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "game_version",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "mod_loader",
|
||||
"ordinal": 5,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "mod_loader_version",
|
||||
"ordinal": 6,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "groups!: serde_json::Value",
|
||||
"ordinal": 7,
|
||||
"type_info": "Null"
|
||||
},
|
||||
{
|
||||
"name": "linked_project_id",
|
||||
"ordinal": 8,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "linked_version_id",
|
||||
"ordinal": 9,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "locked",
|
||||
"ordinal": 10,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "created",
|
||||
"ordinal": 11,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "modified",
|
||||
"ordinal": 12,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "last_played",
|
||||
"ordinal": 13,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "submitted_time_played",
|
||||
"ordinal": 14,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "recent_time_played",
|
||||
"ordinal": 15,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "override_java_path",
|
||||
"ordinal": 16,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "override_extra_launch_args!: serde_json::Value",
|
||||
"ordinal": 17,
|
||||
"type_info": "Null"
|
||||
},
|
||||
{
|
||||
"name": "override_custom_env_vars!: serde_json::Value",
|
||||
"ordinal": 18,
|
||||
"type_info": "Null"
|
||||
},
|
||||
{
|
||||
"name": "override_mc_memory_max",
|
||||
"ordinal": 19,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "override_mc_force_fullscreen",
|
||||
"ordinal": 20,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "override_mc_game_resolution_x",
|
||||
"ordinal": 21,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "override_mc_game_resolution_y",
|
||||
"ordinal": 22,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "override_hook_pre_launch",
|
||||
"ordinal": 23,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "override_hook_wrapper",
|
||||
"ordinal": 24,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "override_hook_post_exit",
|
||||
"ordinal": 25,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "5265d5ad85da898855d628f6b45e39026908fc950aad3c7797be37b5d0b74094"
|
||||
}
|
||||
12
packages/app-lib/.sqlx/query-554805c9902e5a1cc4c0f03b4a633e6dc5b1d46f9c2454075eefe8df9a38f582.json
generated
Normal file
12
packages/app-lib/.sqlx/query-554805c9902e5a1cc4c0f03b4a633e6dc5b1d46f9c2454075eefe8df9a38f582.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n DELETE FROM modrinth_users WHERE id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "554805c9902e5a1cc4c0f03b4a633e6dc5b1d46f9c2454075eefe8df9a38f582"
|
||||
}
|
||||
12
packages/app-lib/.sqlx/query-55ad9c6b0b3172f0528e7ccd60f7c51c77946643b8f912fe265207da275a280f.json
generated
Normal file
12
packages/app-lib/.sqlx/query-55ad9c6b0b3172f0528e7ccd60f7c51c77946643b8f912fe265207da275a280f.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n INSERT INTO java_versions (major_version, full_version, architecture, path)\n VALUES ($1, $2, $3, $4)\n ON CONFLICT (major_version) DO UPDATE SET\n full_version = $2,\n architecture = $3,\n path = $4\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 4
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "55ad9c6b0b3172f0528e7ccd60f7c51c77946643b8f912fe265207da275a280f"
|
||||
}
|
||||
50
packages/app-lib/.sqlx/query-5f07a8b45063167074db8b3da51e220a7a0f5879fb8978d4033e259102ae3790.json
generated
Normal file
50
packages/app-lib/.sqlx/query-5f07a8b45063167074db8b3da51e220a7a0f5879fb8978d4033e259102ae3790.json
generated
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT\n pid, start_time, name, executable, profile_path, post_exit_command\n FROM processes\n WHERE profile_path = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "pid",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "start_time",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "executable",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "profile_path",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "post_exit_command",
|
||||
"ordinal": 5,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "5f07a8b45063167074db8b3da51e220a7a0f5879fb8978d4033e259102ae3790"
|
||||
}
|
||||
38
packages/app-lib/.sqlx/query-6d7ebc0f233dc730fa8c99c750421065f5e35f321954a9d5ae9cde907d5ce823.json
generated
Normal file
38
packages/app-lib/.sqlx/query-6d7ebc0f233dc730fa8c99c750421065f5e35f321954a9d5ae9cde907d5ce823.json
generated
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT\n id, active, session_id, expires\n FROM modrinth_users\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "active",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "session_id",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "expires",
|
||||
"ordinal": 3,
|
||||
"type_info": "Int64"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "6d7ebc0f233dc730fa8c99c750421065f5e35f321954a9d5ae9cde907d5ce823"
|
||||
}
|
||||
62
packages/app-lib/.sqlx/query-6e3fa492c085ebb8e7280dd4d55cdcf73da199ea6ac05ee3ee798ece80d877cf.json
generated
Normal file
62
packages/app-lib/.sqlx/query-6e3fa492c085ebb8e7280dd4d55cdcf73da199ea6ac05ee3ee798ece80d877cf.json
generated
Normal file
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT\n uuid, private_key, x, y, issue_instant, not_after, token, json(display_claims) as \"display_claims!: serde_json::Value\"\n FROM minecraft_device_tokens\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "uuid",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "private_key",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "x",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "issue_instant",
|
||||
"ordinal": 4,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "not_after",
|
||||
"ordinal": 5,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "token",
|
||||
"ordinal": 6,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "display_claims!: serde_json::Value",
|
||||
"ordinal": 7,
|
||||
"type_info": "Null"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "6e3fa492c085ebb8e7280dd4d55cdcf73da199ea6ac05ee3ee798ece80d877cf"
|
||||
}
|
||||
50
packages/app-lib/.sqlx/query-727e3e1bc8625bbcb833920059bb8cea926ac6c65d613904eff1d740df30acda.json
generated
Normal file
50
packages/app-lib/.sqlx/query-727e3e1bc8625bbcb833920059bb8cea926ac6c65d613904eff1d740df30acda.json
generated
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT\n uuid, active, username, access_token, refresh_token, expires\n FROM minecraft_users\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "uuid",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "active",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "username",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "access_token",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "refresh_token",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "expires",
|
||||
"ordinal": 5,
|
||||
"type_info": "Int64"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "727e3e1bc8625bbcb833920059bb8cea926ac6c65d613904eff1d740df30acda"
|
||||
}
|
||||
12
packages/app-lib/.sqlx/query-81a80df2f3fdbbb78d45e7420609c3ae945bc499b4229906c487533d1dcb280c.json
generated
Normal file
12
packages/app-lib/.sqlx/query-81a80df2f3fdbbb78d45e7420609c3ae945bc499b4229906c487533d1dcb280c.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n INSERT INTO modrinth_users (id, active, session_id, expires)\n VALUES ($1, $2, $3, $4)\n ON CONFLICT (id) DO UPDATE SET\n active = $2,\n session_id = $3,\n expires = $4\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 4
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "81a80df2f3fdbbb78d45e7420609c3ae945bc499b4229906c487533d1dcb280c"
|
||||
}
|
||||
50
packages/app-lib/.sqlx/query-bf7d47350092d87c478009adaab131168e87bb37aa65c2156ad2cb6198426d8c.json
generated
Normal file
50
packages/app-lib/.sqlx/query-bf7d47350092d87c478009adaab131168e87bb37aa65c2156ad2cb6198426d8c.json
generated
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT\n uuid, active, username, access_token, refresh_token, expires\n FROM minecraft_users\n WHERE active = TRUE\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "uuid",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "active",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "username",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "access_token",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "refresh_token",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "expires",
|
||||
"ordinal": 5,
|
||||
"type_info": "Int64"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "bf7d47350092d87c478009adaab131168e87bb37aa65c2156ad2cb6198426d8c"
|
||||
}
|
||||
12
packages/app-lib/.sqlx/query-d1b8f27c8150f9ae514a7c9ddc68f4a59f08b7df1c65758539220d7211ade682.json
generated
Normal file
12
packages/app-lib/.sqlx/query-d1b8f27c8150f9ae514a7c9ddc68f4a59f08b7df1c65758539220d7211ade682.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n INSERT INTO processes (pid, start_time, name, executable, profile_path, post_exit_command)\n VALUES ($1, $2, $3, $4, $5, $6)\n ON CONFLICT (pid) DO UPDATE SET\n start_time = $2,\n name = $3,\n executable = $4,\n profile_path = $5,\n post_exit_command = $6\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 6
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "d1b8f27c8150f9ae514a7c9ddc68f4a59f08b7df1c65758539220d7211ade682"
|
||||
}
|
||||
12
packages/app-lib/.sqlx/query-d21e8a5116c43a3b511321a2655d8217f8c46b816a2f4e60c11dfcd173120e7e.json
generated
Normal file
12
packages/app-lib/.sqlx/query-d21e8a5116c43a3b511321a2655d8217f8c46b816a2f4e60c11dfcd173120e7e.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n DELETE FROM minecraft_users WHERE uuid = $1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "d21e8a5116c43a3b511321a2655d8217f8c46b816a2f4e60c11dfcd173120e7e"
|
||||
}
|
||||
12
packages/app-lib/.sqlx/query-d63935a6e411b5ea145dfa1d4772899303d9b82b1ecd2e30dc71b411ee538f54.json
generated
Normal file
12
packages/app-lib/.sqlx/query-d63935a6e411b5ea145dfa1d4772899303d9b82b1ecd2e30dc71b411ee538f54.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n INSERT INTO cache (id, data_type, alias, data, expires)\n SELECT\n json_extract(value, '$.id') AS id,\n json_extract(value, '$.data_type') AS data_type,\n json_extract(value, '$.alias') AS alias,\n json_extract(value, '$.data') AS data,\n json_extract(value, '$.expires') AS expires\n FROM\n json_each($1)\n WHERE TRUE\n ON CONFLICT (id, data_type) DO UPDATE SET\n alias = excluded.alias,\n data = excluded.data,\n expires = excluded.expires\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "d63935a6e411b5ea145dfa1d4772899303d9b82b1ecd2e30dc71b411ee538f54"
|
||||
}
|
||||
12
packages/app-lib/.sqlx/query-d645daf951ff6fead3c86df685d99bacc81cb0a999c0f8d2ff7755b0089a79d8.json
generated
Normal file
12
packages/app-lib/.sqlx/query-d645daf951ff6fead3c86df685d99bacc81cb0a999c0f8d2ff7755b0089a79d8.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n UPDATE settings\n SET\n max_concurrent_writes = $1,\n max_concurrent_downloads = $2,\n\n theme = $3,\n default_page = $4,\n collapsed_navigation = $5,\n advanced_rendering = $6,\n native_decorations = $7,\n\n discord_rpc = $8,\n developer_mode = $9,\n telemetry = $10,\n\n onboarded = $11,\n\n extra_launch_args = jsonb($12),\n custom_env_vars = jsonb($13),\n mc_memory_max = $14,\n mc_force_fullscreen = $15,\n mc_game_resolution_x = $16,\n mc_game_resolution_y = $17,\n hide_on_process_start = $18,\n\n hook_pre_launch = $19,\n hook_wrapper = $20,\n hook_post_exit = $21,\n\n custom_dir = $22,\n prev_custom_dir = $23,\n migrated = $24\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 24
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "d645daf951ff6fead3c86df685d99bacc81cb0a999c0f8d2ff7755b0089a79d8"
|
||||
}
|
||||
12
packages/app-lib/.sqlx/query-d719cf2f6f87c5ea7ea6ace2d6a1828ee58a724f06a91633b8a40b4e04d0b9a0.json
generated
Normal file
12
packages/app-lib/.sqlx/query-d719cf2f6f87c5ea7ea6ace2d6a1828ee58a724f06a91633b8a40b4e04d0b9a0.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n INSERT INTO minecraft_users (uuid, active, username, access_token, refresh_token, expires)\n VALUES ($1, $2, $3, $4, $5, $6)\n ON CONFLICT (uuid) DO UPDATE SET\n active = $2,\n username = $3,\n access_token = $4,\n refresh_token = $5,\n expires = $6\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 6
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "d719cf2f6f87c5ea7ea6ace2d6a1828ee58a724f06a91633b8a40b4e04d0b9a0"
|
||||
}
|
||||
12
packages/app-lib/.sqlx/query-db1f94b9c17c790c029a7691620d6bbdcbdfcce4b069b8ed46dc3abd2f5f4e58.json
generated
Normal file
12
packages/app-lib/.sqlx/query-db1f94b9c17c790c029a7691620d6bbdcbdfcce4b069b8ed46dc3abd2f5f4e58.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n INSERT INTO profiles (\n path, install_stage, name, icon_path,\n game_version, mod_loader, mod_loader_version,\n groups,\n linked_project_id, linked_version_id, locked,\n created, modified, last_played,\n submitted_time_played, recent_time_played,\n override_java_path, override_extra_launch_args, override_custom_env_vars,\n override_mc_memory_max, override_mc_force_fullscreen, override_mc_game_resolution_x, override_mc_game_resolution_y,\n override_hook_pre_launch, override_hook_wrapper, override_hook_post_exit\n )\n VALUES (\n $1, $2, $3, $4,\n $5, $6, $7,\n jsonb($8),\n $9, $10, $11,\n $12, $13, $14,\n $15, $16,\n $17, jsonb($18), jsonb($19),\n $20, $21, $22, $23,\n $24, $25, $26\n )\n ON CONFLICT (path) DO UPDATE SET\n install_stage = $2,\n name = $3,\n icon_path = $4,\n\n game_version = $5,\n mod_loader = $6,\n mod_loader_version = $7,\n\n groups = jsonb($8),\n\n linked_project_id = $9,\n linked_version_id = $10,\n locked = $11,\n\n created = $12,\n modified = $13,\n last_played = $14,\n\n submitted_time_played = $15,\n recent_time_played = $16,\n\n override_java_path = $17,\n override_extra_launch_args = jsonb($18),\n override_custom_env_vars = jsonb($19),\n override_mc_memory_max = $20,\n override_mc_force_fullscreen = $21,\n override_mc_game_resolution_x = $22,\n override_mc_game_resolution_y = $23,\n\n override_hook_pre_launch = $24,\n override_hook_wrapper = $25,\n override_hook_post_exit = $26\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 26
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "db1f94b9c17c790c029a7691620d6bbdcbdfcce4b069b8ed46dc3abd2f5f4e58"
|
||||
}
|
||||
50
packages/app-lib/.sqlx/query-e18e960d33a140e522ca20b91d63560b921b922701b69d868dc231f6b0f4cf1c.json
generated
Normal file
50
packages/app-lib/.sqlx/query-e18e960d33a140e522ca20b91d63560b921b922701b69d868dc231f6b0f4cf1c.json
generated
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT\n pid, start_time, name, executable, profile_path, post_exit_command\n FROM processes\n WHERE pid = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "pid",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "start_time",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "executable",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "profile_path",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "post_exit_command",
|
||||
"ordinal": 5,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "e18e960d33a140e522ca20b91d63560b921b922701b69d868dc231f6b0f4cf1c"
|
||||
}
|
||||
@@ -1,14 +1,12 @@
|
||||
[package]
|
||||
name = "theseus"
|
||||
version = "0.7.2"
|
||||
version = "0.0.0"
|
||||
authors = ["Jai A <jaiagr+gpg@pm.me>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
theseus_macros = { path = "../app-macros" }
|
||||
|
||||
bytes = "1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
@@ -23,10 +21,10 @@ async_zip = { version = "0.0.17", features = ["full"] }
|
||||
flate2 = "1.0.28"
|
||||
tempfile = "3.5.0"
|
||||
urlencoding = "2.1.3"
|
||||
|
||||
dashmap = { version = "6.0.1", features = ["serde"] }
|
||||
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
daedalus = { version = "0.1.25" }
|
||||
daedalus = { version = "0.2.2" }
|
||||
dirs = "5.0.1"
|
||||
|
||||
regex = "1.5"
|
||||
@@ -59,13 +57,18 @@ dunce = "1.0.3"
|
||||
|
||||
whoami = "1.4.0"
|
||||
|
||||
discord-rich-presence = "0.2.3"
|
||||
discord-rich-presence = "0.2.4"
|
||||
|
||||
p256 = { version = "0.13.2", features = ["ecdsa"] }
|
||||
rand = "0.8"
|
||||
byteorder = "1.5.0"
|
||||
base64 = "0.22.0"
|
||||
|
||||
# TODO: Remove when new SQLX version is released
|
||||
# We force-upgrade SQLite so JSONB support is added (theseus)
|
||||
# https://github.com/launchbadge/sqlx/commit/352b02de6af70f1ff1bfbd15329120589a0f7337
|
||||
sqlx = { git = "https://github.com/launchbadge/sqlx.git", rev = "352b02de6af70f1ff1bfbd15329120589a0f7337", features = [ "runtime-tokio", "sqlite", "macros"] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winreg = "0.52.0"
|
||||
|
||||
|
||||
158
packages/app-lib/migrations/20240711194701_init.sql
Normal file
158
packages/app-lib/migrations/20240711194701_init.sql
Normal file
@@ -0,0 +1,158 @@
|
||||
CREATE TABLE settings (
|
||||
id INTEGER NOT NULL CHECK (id = 0),
|
||||
|
||||
max_concurrent_downloads INTEGER NOT NULL DEFAULT 10,
|
||||
max_concurrent_writes INTEGER NOT NULL DEFAULT 10,
|
||||
|
||||
theme TEXT NOT NULL DEFAULT 'dark',
|
||||
default_page TEXT NOT NULL DEFAULT 'home',
|
||||
collapsed_navigation INTEGER NOT NULL DEFAULT TRUE,
|
||||
advanced_rendering INTEGER NOT NULL DEFAULT TRUE,
|
||||
native_decorations INTEGER NOT NULL DEFAULT FALSE,
|
||||
|
||||
telemetry INTEGER NOT NULL DEFAULT TRUE,
|
||||
discord_rpc INTEGER NOT NULL DEFAULT TRUE,
|
||||
developer_mode INTEGER NOT NULL DEFAULT FALSE,
|
||||
|
||||
onboarded INTEGER NOT NULL DEFAULT FALSE,
|
||||
|
||||
-- array of strings
|
||||
extra_launch_args JSONB NOT NULL,
|
||||
-- array of (string, string)
|
||||
custom_env_vars JSONB NOT NULL,
|
||||
mc_memory_max INTEGER NOT NULL DEFAULT 2048,
|
||||
mc_force_fullscreen INTEGER NOT NULL DEFAULT FALSE,
|
||||
mc_game_resolution_x INTEGER NOT NULL DEFAULT 854,
|
||||
mc_game_resolution_y INTEGER NOT NULL DEFAULT 480,
|
||||
|
||||
hide_on_process_start INTEGER NOT NULL DEFAULT FALSE,
|
||||
|
||||
hook_pre_launch TEXT NULL,
|
||||
hook_wrapper TEXT NULL,
|
||||
hook_post_exit TEXT NULL,
|
||||
|
||||
custom_dir TEXT NULL,
|
||||
prev_custom_dir TEXT NULL,
|
||||
migrated INTEGER NOT NULL DEFAULT FALSE,
|
||||
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
INSERT INTO settings (id, extra_launch_args, custom_env_vars) VALUES (0, jsonb_array(), jsonb_array());
|
||||
|
||||
CREATE TABLE java_versions (
|
||||
major_version INTEGER NOT NULL,
|
||||
full_version TEXT NOT NULL,
|
||||
architecture TEXT NOT NULL,
|
||||
path TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (major_version)
|
||||
);
|
||||
|
||||
CREATE TABLE minecraft_users (
|
||||
uuid TEXT NOT NULL,
|
||||
active INTEGER NOT NULL DEFAULT FALSE,
|
||||
username TEXT NOT NULL,
|
||||
access_token TEXT NOT NULL,
|
||||
refresh_token TEXT NOT NULL,
|
||||
expires INTEGER NOT NULL,
|
||||
|
||||
PRIMARY KEY (uuid)
|
||||
);
|
||||
CREATE UNIQUE INDEX minecraft_users_active ON minecraft_users(active);
|
||||
|
||||
CREATE TABLE minecraft_device_tokens (
|
||||
id INTEGER NOT NULL CHECK (id = 0),
|
||||
|
||||
uuid TEXT NOT NULL,
|
||||
private_key TEXT NOT NULL,
|
||||
x TEXT NOT NULL,
|
||||
y TEXT NOT NULL,
|
||||
issue_instant INTEGER NOT NULL,
|
||||
not_after INTEGER NOT NULL,
|
||||
token TEXT NOT NULL,
|
||||
display_claims JSONB NOT NULL,
|
||||
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
CREATE TABLE modrinth_users (
|
||||
id TEXT NOT NULL,
|
||||
active INTEGER NOT NULL DEFAULT FALSE,
|
||||
session_id TEXT NOT NULL,
|
||||
expires INTEGER NOT NULL,
|
||||
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE UNIQUE INDEX modrinth_users_active ON modrinth_users(active);
|
||||
|
||||
CREATE TABLE cache (
|
||||
id TEXT NOT NULL,
|
||||
data_type TEXT NOT NULL,
|
||||
alias TEXT NULL,
|
||||
|
||||
data JSONB NULL,
|
||||
expires INTEGER NOT NULL,
|
||||
|
||||
UNIQUE (data_type, alias),
|
||||
PRIMARY KEY (id, data_type)
|
||||
);
|
||||
|
||||
CREATE TABLE profiles (
|
||||
path TEXT NOT NULL,
|
||||
install_stage TEXT NOT NULL,
|
||||
|
||||
name TEXT NOT NULL,
|
||||
icon_path TEXT NULL,
|
||||
|
||||
game_version TEXT NOT NULL,
|
||||
mod_loader TEXT NOT NULL,
|
||||
mod_loader_version TEXT NULL,
|
||||
|
||||
-- array of strings
|
||||
groups JSONB NOT NULL,
|
||||
|
||||
linked_project_id TEXT NULL,
|
||||
linked_version_id TEXT NULL,
|
||||
locked INTEGER NULL,
|
||||
|
||||
created INTEGER NOT NULL,
|
||||
modified INTEGER NOT NULL,
|
||||
last_played INTEGER NULL,
|
||||
|
||||
submitted_time_played INTEGER NOT NULL DEFAULT 0,
|
||||
recent_time_played INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
override_java_path TEXT NULL,
|
||||
|
||||
-- array of strings
|
||||
override_extra_launch_args JSONB NOT NULL,
|
||||
-- array of (string, string)
|
||||
override_custom_env_vars JSONB NOT NULL,
|
||||
|
||||
override_mc_memory_max INTEGER NULL,
|
||||
override_mc_force_fullscreen INTEGER NULL,
|
||||
override_mc_game_resolution_x INTEGER NULL,
|
||||
override_mc_game_resolution_y INTEGER NULL,
|
||||
|
||||
override_hook_pre_launch TEXT NULL,
|
||||
override_hook_wrapper TEXT NULL,
|
||||
override_hook_post_exit TEXT NULL,
|
||||
|
||||
|
||||
PRIMARY KEY (path)
|
||||
);
|
||||
|
||||
CREATE TABLE processes (
|
||||
pid INTEGER NOT NULL,
|
||||
start_time INTEGER NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
executable TEXT NOT NULL,
|
||||
profile_path TEXT NOT NULL,
|
||||
post_exit_command TEXT NULL,
|
||||
|
||||
UNIQUE (pid),
|
||||
PRIMARY KEY (pid),
|
||||
FOREIGN KEY (profile_path) REFERENCES profiles(path)
|
||||
);
|
||||
CREATE INDEX processes_profile_path ON processes(profile_path);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user