You've already forked AstralRinth
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
|
.turbo
|
||||||
target
|
target
|
||||||
generated
|
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'
|
resolver = '2'
|
||||||
members = [
|
members = [
|
||||||
'./packages/app-lib',
|
'./packages/app-lib',
|
||||||
'./packages/app-macros',
|
|
||||||
'./apps/app-playground',
|
'./apps/app-playground',
|
||||||
'./apps/app'
|
'./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
|
lto = true # Enables link to optimizations
|
||||||
opt-level = "s" # Optimize for binary size
|
opt-level = "s" # Optimize for binary size
|
||||||
strip = true # Remove debug symbols
|
strip = true # Remove debug symbols
|
||||||
|
|
||||||
|
[profile.dev.package.sqlx-macros]
|
||||||
|
opt-level = 3
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@modrinth/app-frontend",
|
"name": "@modrinth/app-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.7.2",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref, onMounted } from 'vue'
|
||||||
import { RouterView, RouterLink, useRouter, useRoute } from 'vue-router'
|
import { RouterView, RouterLink, useRouter, useRoute } from 'vue-router'
|
||||||
import {
|
import {
|
||||||
HomeIcon,
|
HomeIcon,
|
||||||
@@ -21,11 +21,11 @@ import SplashScreen from '@/components/ui/SplashScreen.vue'
|
|||||||
import ErrorModal from '@/components/ui/ErrorModal.vue'
|
import ErrorModal from '@/components/ui/ErrorModal.vue'
|
||||||
import ModrinthLoadingIndicator from '@/components/modrinth-loading-indicator'
|
import ModrinthLoadingIndicator from '@/components/modrinth-loading-indicator'
|
||||||
import { handleError, useNotifications } from '@/store/notifications.js'
|
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 { MinimizeIcon, MaximizeIcon, ChatIcon } from '@/assets/icons'
|
||||||
import { type } from '@tauri-apps/api/os'
|
import { type } from '@tauri-apps/api/os'
|
||||||
import { appWindow } from '@tauri-apps/api/window'
|
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 {
|
import {
|
||||||
mixpanel_track,
|
mixpanel_track,
|
||||||
mixpanel_init,
|
mixpanel_init,
|
||||||
@@ -36,18 +36,27 @@ import { saveWindowState, StateFlags } from 'tauri-plugin-window-state-api'
|
|||||||
import { getVersion } from '@tauri-apps/api/app'
|
import { getVersion } from '@tauri-apps/api/app'
|
||||||
import { window as TauriWindow } from '@tauri-apps/api'
|
import { window as TauriWindow } from '@tauri-apps/api'
|
||||||
import { TauriEvent } from '@tauri-apps/api/event'
|
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 URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
|
||||||
import OnboardingScreen from '@/components/ui/tutorial/OnboardingScreen.vue'
|
import OnboardingScreen from '@/components/ui/tutorial/OnboardingScreen.vue'
|
||||||
import { install_from_file } from './helpers/pack'
|
import { install_from_file } from './helpers/pack'
|
||||||
import { useError } from '@/store/error.js'
|
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 themeStore = useTheming()
|
||||||
const urlModal = ref(null)
|
const urlModal = ref(null)
|
||||||
const isLoading = ref(true)
|
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 showOnboarding = ref(false)
|
||||||
const nativeDecorations = ref(false)
|
const nativeDecorations = ref(false)
|
||||||
|
|
||||||
@@ -62,16 +71,16 @@ defineExpose({
|
|||||||
const {
|
const {
|
||||||
native_decorations,
|
native_decorations,
|
||||||
theme,
|
theme,
|
||||||
opt_out_analytics,
|
telemetry,
|
||||||
collapsed_navigation,
|
collapsed_navigation,
|
||||||
advanced_rendering,
|
advanced_rendering,
|
||||||
fully_onboarded,
|
onboarded,
|
||||||
} = await get()
|
} = await get()
|
||||||
// video should play if the user is not on linux, and has not onboarded
|
// video should play if the user is not on linux, and has not onboarded
|
||||||
os.value = await getOS()
|
os.value = await getOS()
|
||||||
const dev = await isDev()
|
const dev = await isDev()
|
||||||
const version = await getVersion()
|
const version = await getVersion()
|
||||||
showOnboarding.value = !fully_onboarded
|
showOnboarding.value = !onboarded
|
||||||
|
|
||||||
nativeDecorations.value = native_decorations
|
nativeDecorations.value = native_decorations
|
||||||
if (os.value !== 'MacOS') appWindow.setDecorations(native_decorations)
|
if (os.value !== 'MacOS') appWindow.setDecorations(native_decorations)
|
||||||
@@ -81,10 +90,10 @@ defineExpose({
|
|||||||
themeStore.advancedRendering = advanced_rendering
|
themeStore.advancedRendering = advanced_rendering
|
||||||
|
|
||||||
mixpanel_init('014c7d6a336d0efaefe3aca91063748d', { debug: dev, persistence: 'localStorage' })
|
mixpanel_init('014c7d6a336d0efaefe3aca91063748d', { debug: dev, persistence: 'localStorage' })
|
||||||
if (opt_out_analytics) {
|
if (telemetry) {
|
||||||
mixpanel_opt_out_tracking()
|
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())
|
if (!dev) document.addEventListener('contextmenu', (event) => event.preventDefault())
|
||||||
|
|
||||||
@@ -94,11 +103,6 @@ defineExpose({
|
|||||||
document.getElementsByTagName('html')[0].classList.add('windows')
|
document.getElementsByTagName('html')[0].classList.add('windows')
|
||||||
}
|
}
|
||||||
|
|
||||||
offline.value = await isOffline()
|
|
||||||
await offline_listener((b) => {
|
|
||||||
offline.value = b
|
|
||||||
})
|
|
||||||
|
|
||||||
await warning_listener((e) =>
|
await warning_listener((e) =>
|
||||||
notificationsWrapper.value.addNotification({
|
notificationsWrapper.value.addNotification({
|
||||||
title: 'Warning',
|
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 () => {
|
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()
|
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 () => {
|
TauriWindow.getCurrent().listen(TauriEvent.WINDOW_CLOSE_REQUESTED, async () => {
|
||||||
await handleClose()
|
await handleClose()
|
||||||
})
|
})
|
||||||
@@ -179,15 +144,22 @@ const loading = useLoading()
|
|||||||
const notifications = useNotifications()
|
const notifications = useNotifications()
|
||||||
const notificationsWrapper = ref()
|
const notificationsWrapper = ref()
|
||||||
|
|
||||||
watch(notificationsWrapper, () => {
|
|
||||||
notifications.setNotifs(notificationsWrapper.value)
|
|
||||||
})
|
|
||||||
|
|
||||||
const error = useError()
|
const error = useError()
|
||||||
const errorModal = ref()
|
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)
|
error.setErrorModal(errorModal.value)
|
||||||
|
|
||||||
|
install.setIncompatibilityWarningModal(incompatibilityWarningModal)
|
||||||
|
install.setInstallConfirmModal(installConfirmModal)
|
||||||
|
install.setModInstallModal(modInstallModal)
|
||||||
})
|
})
|
||||||
|
|
||||||
document.querySelector('body').addEventListener('click', function (e) {
|
document.querySelector('body').addEventListener('click', function (e) {
|
||||||
@@ -284,7 +256,7 @@ command_listener(async (e) => {
|
|||||||
<div class="button-row push-right">
|
<div class="button-row push-right">
|
||||||
<Button @click="showLauncherLogsFolder"><FileIcon />Open launcher logs</Button>
|
<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>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
@@ -385,6 +357,9 @@ command_listener(async (e) => {
|
|||||||
<URLConfirmModal ref="urlModal" />
|
<URLConfirmModal ref="urlModal" />
|
||||||
<Notifications ref="notificationsWrapper" />
|
<Notifications ref="notificationsWrapper" />
|
||||||
<ErrorModal ref="errorModal" />
|
<ErrorModal ref="errorModal" />
|
||||||
|
<ModInstallModal ref="modInstallModal" />
|
||||||
|
<IncompatibilityWarningModal ref="incompatibilityWarningModal" />
|
||||||
|
<InstallConfirmModal ref="installConfirmModal" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<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 {
|
.nav-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -643,14 +569,6 @@ command_listener(async (e) => {
|
|||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video {
|
|
||||||
margin-top: 2.25rem;
|
|
||||||
width: 100vw;
|
|
||||||
height: calc(100vh - 2.25rem);
|
|
||||||
object-fit: cover;
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-row {
|
.button-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|||||||
@@ -127,46 +127,46 @@ const sortBy = ref('Name')
|
|||||||
|
|
||||||
const filteredResults = computed(() => {
|
const filteredResults = computed(() => {
|
||||||
let instances = props.instances.filter((instance) => {
|
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') {
|
if (sortBy.value === 'Name') {
|
||||||
instances.sort((a, b) => {
|
instances.sort((a, b) => {
|
||||||
return a.metadata.name.localeCompare(b.metadata.name)
|
return a.name.localeCompare(b.name)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sortBy.value === 'Game version') {
|
if (sortBy.value === 'Game version') {
|
||||||
instances.sort((a, b) => {
|
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') {
|
if (sortBy.value === 'Last played') {
|
||||||
instances.sort((a, b) => {
|
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') {
|
if (sortBy.value === 'Date created') {
|
||||||
instances.sort((a, b) => {
|
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') {
|
if (sortBy.value === 'Date modified') {
|
||||||
instances.sort((a, b) => {
|
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') {
|
if (filters.value === 'Custom instances') {
|
||||||
instances = instances.filter((instance) => {
|
instances = instances.filter((instance) => {
|
||||||
return !instance.metadata?.linked_data
|
return !instance.linked_data
|
||||||
})
|
})
|
||||||
} else if (filters.value === 'Downloaded modpacks') {
|
} else if (filters.value === 'Downloaded modpacks') {
|
||||||
instances = instances.filter((instance) => {
|
instances = instances.filter((instance) => {
|
||||||
return instance.metadata?.linked_data
|
return instance.linked_data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,7 +174,7 @@ const filteredResults = computed(() => {
|
|||||||
|
|
||||||
if (group.value === 'Loader') {
|
if (group.value === 'Loader') {
|
||||||
instances.forEach((instance) => {
|
instances.forEach((instance) => {
|
||||||
const loader = formatCategoryHeader(instance.metadata.loader)
|
const loader = formatCategoryHeader(instance.loader)
|
||||||
if (!instanceMap.has(loader)) {
|
if (!instanceMap.has(loader)) {
|
||||||
instanceMap.set(loader, [])
|
instanceMap.set(loader, [])
|
||||||
}
|
}
|
||||||
@@ -183,19 +183,19 @@ const filteredResults = computed(() => {
|
|||||||
})
|
})
|
||||||
} else if (group.value === 'Game version') {
|
} else if (group.value === 'Game version') {
|
||||||
instances.forEach((instance) => {
|
instances.forEach((instance) => {
|
||||||
if (!instanceMap.has(instance.metadata.game_version)) {
|
if (!instanceMap.has(instance.game_version)) {
|
||||||
instanceMap.set(instance.metadata.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') {
|
} else if (group.value === 'Category') {
|
||||||
instances.forEach((instance) => {
|
instances.forEach((instance) => {
|
||||||
if (instance.metadata.groups.length === 0) {
|
if (instance.groups.length === 0) {
|
||||||
instance.metadata.groups.push('None')
|
instance.groups.push('None')
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const category of instance.metadata.groups) {
|
for (const category of instance.groups) {
|
||||||
if (!instanceMap.has(category)) {
|
if (!instanceMap.has(category)) {
|
||||||
instanceMap.set(category, [])
|
instanceMap.set(category, [])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,22 +17,15 @@ import Instance from '@/components/ui/Instance.vue'
|
|||||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||||
import ProjectCard from '@/components/ui/ProjectCard.vue'
|
import ProjectCard from '@/components/ui/ProjectCard.vue'
|
||||||
import InstallConfirmModal from '@/components/ui/InstallConfirmModal.vue'
|
import { get_by_profile_path } from '@/helpers/process.js'
|
||||||
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 { handleError } from '@/store/notifications.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 { useRouter } from 'vue-router'
|
||||||
import { showProfileInFolder } from '@/helpers/utils.js'
|
import { showProfileInFolder } from '@/helpers/utils.js'
|
||||||
import { useFetch } from '@/helpers/fetch.js'
|
|
||||||
import { install as pack_install } from '@/helpers/pack.js'
|
|
||||||
import { useTheming } from '@/store/state.js'
|
import { useTheming } from '@/store/state.js'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { handleSevereError } from '@/store/error.js'
|
import { handleSevereError } from '@/store/error.js'
|
||||||
|
import { install as installVersion } from '@/store/install.js'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
@@ -58,9 +51,7 @@ const modsRow = ref(null)
|
|||||||
const instanceOptions = ref(null)
|
const instanceOptions = ref(null)
|
||||||
const instanceComponents = ref(null)
|
const instanceComponents = ref(null)
|
||||||
const rows = ref(null)
|
const rows = ref(null)
|
||||||
const confirmModal = ref(null)
|
|
||||||
const deleteConfirmModal = ref(null)
|
const deleteConfirmModal = ref(null)
|
||||||
const modInstallModal = ref(null)
|
|
||||||
|
|
||||||
const themeStore = useTheming()
|
const themeStore = useTheming()
|
||||||
const currentDeleteInstance = ref(null)
|
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)
|
const options =
|
||||||
? [
|
runningProcesses.length > 0
|
||||||
{
|
? [
|
||||||
name: 'stop',
|
{
|
||||||
color: 'danger',
|
name: 'stop',
|
||||||
},
|
color: 'danger',
|
||||||
...baseOptions,
|
},
|
||||||
]
|
...baseOptions,
|
||||||
: [
|
]
|
||||||
{
|
: [
|
||||||
name: 'play',
|
{
|
||||||
color: 'primary',
|
name: 'play',
|
||||||
},
|
color: 'primary',
|
||||||
...baseOptions,
|
},
|
||||||
]
|
...baseOptions,
|
||||||
|
]
|
||||||
|
|
||||||
instanceOptions.value.showMenu(event, passedInstance, options)
|
instanceOptions.value.showMenu(event, passedInstance, options)
|
||||||
}
|
}
|
||||||
@@ -132,22 +124,20 @@ const handleOptionsClick = async (args) => {
|
|||||||
case 'play':
|
case 'play':
|
||||||
await run(args.item.path).catch(handleSevereError)
|
await run(args.item.path).catch(handleSevereError)
|
||||||
mixpanel_track('InstanceStart', {
|
mixpanel_track('InstanceStart', {
|
||||||
loader: args.item.metadata.loader,
|
loader: args.item.loader,
|
||||||
game_version: args.item.metadata.game_version,
|
game_version: args.item.game_version,
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
case 'stop':
|
case 'stop':
|
||||||
for (const u of await get_uuids_by_profile_path(args.item.path).catch(handleError)) {
|
await kill(args.item.path).catch(handleError)
|
||||||
await kill_by_uuid(u).catch(handleError)
|
|
||||||
}
|
|
||||||
mixpanel_track('InstanceStop', {
|
mixpanel_track('InstanceStop', {
|
||||||
loader: args.item.metadata.loader,
|
loader: args.item.loader,
|
||||||
game_version: args.item.metadata.game_version,
|
game_version: args.item.game_version,
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
case 'add_content':
|
case 'add_content':
|
||||||
await router.push({
|
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 },
|
query: { i: args.item.path },
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
@@ -170,21 +160,8 @@ const handleOptionsClick = async (args) => {
|
|||||||
await navigator.clipboard.writeText(args.item.path)
|
await navigator.clipboard.writeText(args.item.path)
|
||||||
break
|
break
|
||||||
case 'install': {
|
case 'install': {
|
||||||
const versions = await useFetch(
|
await installVersion(args.item.project_id, null, null, 'ProjectCardContextMenu')
|
||||||
`https://api.modrinth.com/v2/project/${args.item.project_id}/version`,
|
|
||||||
'project versions',
|
|
||||||
)
|
|
||||||
|
|
||||||
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
|
break
|
||||||
}
|
}
|
||||||
case 'open_link':
|
case 'open_link':
|
||||||
@@ -243,7 +220,7 @@ onUnmounted(() => {
|
|||||||
<router-link :to="row.route">{{ row.label }}</router-link>
|
<router-link :to="row.route">{{ row.label }}</router-link>
|
||||||
<ChevronRightIcon />
|
<ChevronRightIcon />
|
||||||
</div>
|
</div>
|
||||||
<section v-if="row.instances[0].metadata" ref="modsRow" class="instances">
|
<section v-if="row.instance" ref="modsRow" class="instances">
|
||||||
<Instance
|
<Instance
|
||||||
v-for="instance in row.instances.slice(0, maxInstancesPerRow)"
|
v-for="instance in row.instances.slice(0, maxInstancesPerRow)"
|
||||||
:key="(instance?.project_id || instance?.id) + instance.install_stage"
|
:key="(instance?.project_id || instance?.id) + instance.install_stage"
|
||||||
@@ -258,8 +235,6 @@ onUnmounted(() => {
|
|||||||
ref="instanceComponents"
|
ref="instanceComponents"
|
||||||
class="item"
|
class="item"
|
||||||
:project="project"
|
:project="project"
|
||||||
:confirm-modal="confirmModal"
|
|
||||||
:mod-install-modal="modInstallModal"
|
|
||||||
@contextmenu.prevent.stop="(event) => handleProjectClick(event, project)"
|
@contextmenu.prevent.stop="(event) => handleProjectClick(event, project)"
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
@@ -278,8 +253,6 @@ onUnmounted(() => {
|
|||||||
<template #open_link> <GlobeIcon /> Open in Modrinth <ExternalIcon /> </template>
|
<template #open_link> <GlobeIcon /> Open in Modrinth <ExternalIcon /> </template>
|
||||||
<template #copy_link> <ClipboardCopyIcon /> Copy link </template>
|
<template #copy_link> <ClipboardCopyIcon /> Copy link </template>
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
<InstallConfirmModal ref="confirmModal" />
|
|
||||||
<ModInstallModal ref="modInstallModal" />
|
|
||||||
</template>
|
</template>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.content {
|
.content {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { DropdownIcon, FolderOpenIcon, SearchIcon } from '@modrinth/assets'
|
import { DropdownIcon, FolderOpenIcon, SearchIcon } from '@modrinth/assets'
|
||||||
import { Button, OverflowMenu } from '@modrinth/ui'
|
import { Button, OverflowMenu } from '@modrinth/ui'
|
||||||
import { open } from '@tauri-apps/api/dialog'
|
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 { handleError } from '@/store/notifications.js'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
@@ -20,14 +20,13 @@ const handleAddContentFromFile = async () => {
|
|||||||
if (!newProject) return
|
if (!newProject) return
|
||||||
|
|
||||||
for (const project of newProject) {
|
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 () => {
|
const handleSearchContent = async () => {
|
||||||
await router.push({
|
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 },
|
query: { i: props.instance.path },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,9 +60,9 @@ defineExpose({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const isLinkedData = (item) => {
|
const isLinkedData = (item) => {
|
||||||
if (item.instance != undefined && item.instance.metadata.linked_data) {
|
if (item.instance != undefined && item.instance.linked_data) {
|
||||||
return true
|
return true
|
||||||
} else if (item.metadata != undefined && item.metadata.linked_data) {
|
} else if (item != undefined && item.linked_data) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ defineExpose({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const exportModal = ref(null)
|
const exportModal = ref(null)
|
||||||
const nameInput = ref(props.instance.metadata.name)
|
const nameInput = ref(props.instance.name)
|
||||||
const exportDescription = ref('')
|
const exportDescription = ref('')
|
||||||
const versionInput = ref('1.0.0')
|
const versionInput = ref('1.0.0')
|
||||||
const files = ref([])
|
const files = ref([])
|
||||||
|
|||||||
@@ -1,22 +1,14 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onUnmounted, ref, watch } from 'vue'
|
import { onUnmounted, ref, computed } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
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 { Card, Avatar, AnimatedLogo } from '@modrinth/ui'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||||
import InstallConfirmModal from '@/components/ui/InstallConfirmModal.vue'
|
import { kill, run } from '@/helpers/profile'
|
||||||
import { install as pack_install } from '@/helpers/pack'
|
import { get_by_profile_path } from '@/helpers/process'
|
||||||
import { list, run } from '@/helpers/profile'
|
|
||||||
import {
|
|
||||||
get_all_running_profile_paths,
|
|
||||||
get_uuids_by_profile_path,
|
|
||||||
kill_by_uuid,
|
|
||||||
} from '@/helpers/process'
|
|
||||||
import { process_listener } from '@/helpers/events'
|
import { process_listener } from '@/helpers/events'
|
||||||
import { useFetch } from '@/helpers/fetch.js'
|
|
||||||
import { handleError } from '@/store/state.js'
|
import { handleError } from '@/store/state.js'
|
||||||
import { showProfileInFolder } from '@/helpers/utils.js'
|
import { showProfileInFolder } from '@/helpers/utils.js'
|
||||||
import ModInstallModal from '@/components/ui/ModInstallModal.vue'
|
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { handleSevereError } from '@/store/error.js'
|
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 playing = ref(false)
|
||||||
|
|
||||||
const uuid = ref(null)
|
const modLoading = computed(() => props.instance.install_stage !== 'installed')
|
||||||
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 router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const seeInstance = async () => {
|
const seeInstance = async () => {
|
||||||
const instancePath = props.instance.metadata
|
await router.push(`/instance/${encodeURIComponent(props.instance.path)}/`)
|
||||||
? `/instance/${encodeURIComponent(props.instance.path)}/`
|
|
||||||
: `/project/${encodeURIComponent(props.instance.project_id)}/`
|
|
||||||
|
|
||||||
await router.push(instancePath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkProcess = async () => {
|
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 = runningProcesses.length > 0
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const play = async (e, context) => {
|
const play = async (e, context) => {
|
||||||
e?.stopPropagation()
|
e?.stopPropagation()
|
||||||
modLoading.value = true
|
modLoading.value = true
|
||||||
uuid.value = await run(props.instance.path).catch(handleSevereError)
|
await run(props.instance.path).catch(handleSevereError)
|
||||||
modLoading.value = false
|
modLoading.value = false
|
||||||
playing.value = true
|
|
||||||
|
|
||||||
mixpanel_track('InstancePlay', {
|
mixpanel_track('InstancePlay', {
|
||||||
loader: props.instance.metadata.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.metadata.game_version,
|
game_version: props.instance.game_version,
|
||||||
source: context,
|
source: context,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -138,22 +54,13 @@ const stop = async (e, context) => {
|
|||||||
e?.stopPropagation()
|
e?.stopPropagation()
|
||||||
playing.value = false
|
playing.value = false
|
||||||
|
|
||||||
// If we lost the uuid for some reason, such as a user navigating
|
await kill(props.instance.path).catch(handleError)
|
||||||
// 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
|
|
||||||
|
|
||||||
mixpanel_track('InstanceStop', {
|
mixpanel_track('InstanceStop', {
|
||||||
loader: props.instance.metadata.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.metadata.game_version,
|
game_version: props.instance.game_version,
|
||||||
source: context,
|
source: context,
|
||||||
})
|
})
|
||||||
|
|
||||||
uuid.value = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const openFolder = async () => {
|
const openFolder = async () => {
|
||||||
@@ -162,14 +69,12 @@ const openFolder = async () => {
|
|||||||
|
|
||||||
const addContent = async () => {
|
const addContent = async () => {
|
||||||
await router.push({
|
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 },
|
query: { i: props.instance.path },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
install,
|
|
||||||
playing,
|
|
||||||
play,
|
play,
|
||||||
stop,
|
stop,
|
||||||
seeInstance,
|
seeInstance,
|
||||||
@@ -179,7 +84,7 @@ defineExpose({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const unlisten = await process_listener((e) => {
|
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())
|
onUnmounted(() => unlisten())
|
||||||
@@ -190,46 +95,32 @@ onUnmounted(() => unlisten())
|
|||||||
<Card class="instance-card-item button-base" @click="seeInstance" @mouseenter="checkProcess">
|
<Card class="instance-card-item button-base" @click="seeInstance" @mouseenter="checkProcess">
|
||||||
<Avatar
|
<Avatar
|
||||||
size="lg"
|
size="lg"
|
||||||
:src="
|
:src="props.instance.icon_path ? convertFileSrc(props.instance.icon_path) : null"
|
||||||
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
|
|
||||||
"
|
|
||||||
alt="Mod card"
|
alt="Mod card"
|
||||||
class="mod-image"
|
class="mod-image"
|
||||||
/>
|
/>
|
||||||
<div class="project-info">
|
<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">
|
<p class="description">
|
||||||
{{ props.instance.metadata?.loader }}
|
{{ props.instance.loader }}
|
||||||
{{ props.instance.metadata?.game_version || props.instance.latest_version }}
|
{{ props.instance.game_version }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<div
|
<div
|
||||||
v-if="props.instance.metadata && playing === false && modLoading === false"
|
v-if="playing === true"
|
||||||
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"
|
|
||||||
class="stop cta button-base"
|
class="stop cta button-base"
|
||||||
@click="(e) => stop(e, 'InstanceCard')"
|
@click="(e) => stop(e, 'InstanceCard')"
|
||||||
@mousehover="checkProcess"
|
@mousehover="checkProcess"
|
||||||
>
|
>
|
||||||
<StopCircleIcon />
|
<StopCircleIcon />
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="install cta button-base" @click="install"><DownloadIcon /></div>
|
<div v-else-if="modLoading === true && playing === false" class="cta loading-cta">
|
||||||
<InstallConfirmModal ref="confirmModal" />
|
<AnimatedLogo class="loading-indicator" />
|
||||||
<ModInstallModal ref="modInstallModal" />
|
</div>
|
||||||
|
<div v-else class="install cta button-base" @click="(e) => play(e, 'InstanceCard')">
|
||||||
|
<PlayIcon />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -213,13 +213,7 @@ import { get_loaders } from '@/helpers/tags'
|
|||||||
import { create } from '@/helpers/profile'
|
import { create } from '@/helpers/profile'
|
||||||
import { open } from '@tauri-apps/api/dialog'
|
import { open } from '@tauri-apps/api/dialog'
|
||||||
import { tauri } from '@tauri-apps/api'
|
import { tauri } from '@tauri-apps/api'
|
||||||
import {
|
import { get_game_versions, get_loader_versions } from '@/helpers/metadata'
|
||||||
get_game_versions,
|
|
||||||
get_fabric_versions,
|
|
||||||
get_forge_versions,
|
|
||||||
get_quilt_versions,
|
|
||||||
get_neoforge_versions,
|
|
||||||
} from '@/helpers/metadata'
|
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import Multiselect from 'vue-multiselect'
|
import Multiselect from 'vue-multiselect'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
@@ -304,10 +298,10 @@ const [
|
|||||||
all_game_versions,
|
all_game_versions,
|
||||||
loaders,
|
loaders,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
get_fabric_versions().then(shallowRef).catch(handleError),
|
get_loader_versions('fabric').then(shallowRef).catch(handleError),
|
||||||
get_forge_versions().then(shallowRef).catch(handleError),
|
get_loader_versions('forge').then(shallowRef).catch(handleError),
|
||||||
get_quilt_versions().then(shallowRef).catch(handleError),
|
get_loader_versions('quilt').then(shallowRef).catch(handleError),
|
||||||
get_neoforge_versions().then(shallowRef).catch(handleError),
|
get_loader_versions('neo').then(shallowRef).catch(handleError),
|
||||||
get_game_versions().then(shallowRef).catch(handleError),
|
get_game_versions().then(shallowRef).catch(handleError),
|
||||||
get_loaders()
|
get_loaders()
|
||||||
.then((value) =>
|
.then((value) =>
|
||||||
|
|||||||
@@ -53,9 +53,6 @@ defineExpose({
|
|||||||
show: async (version, currentSelectedJava) => {
|
show: async (version, currentSelectedJava) => {
|
||||||
chosenInstallOptions.value = await find_filtered_jres(version).catch(handleError)
|
chosenInstallOptions.value = await find_filtered_jres(version).catch(handleError)
|
||||||
|
|
||||||
console.log(chosenInstallOptions.value)
|
|
||||||
console.log(version)
|
|
||||||
|
|
||||||
currentSelected.value = currentSelectedJava
|
currentSelected.value = currentSelectedJava
|
||||||
if (!currentSelected.value) {
|
if (!currentSelected.value) {
|
||||||
currentSelected.value = { path: '', version: '' }
|
currentSelected.value = { path: '', version: '' }
|
||||||
|
|||||||
@@ -162,7 +162,6 @@ async function reinstallJava() {
|
|||||||
const path = await auto_install_java(props.version).catch(handleError)
|
const path = await auto_install_java(props.version).catch(handleError)
|
||||||
let result = await get_jre(path)
|
let result = await get_jre(path)
|
||||||
|
|
||||||
console.log('java result ' + result)
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
result = {
|
result = {
|
||||||
path: path,
|
path: path,
|
||||||
@@ -205,6 +204,10 @@ async function reinstallJava() {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
width: max-content;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.test-success {
|
.test-success {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const filteredVersions = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const modpackVersionModal = ref(null)
|
const modpackVersionModal = ref(null)
|
||||||
const installedVersion = computed(() => props.instance?.metadata?.linked_data?.version_id)
|
const installedVersion = computed(() => props.instance?.linked_data?.version_id)
|
||||||
const installing = computed(() => props.instance.install_stage !== 'installed')
|
const installing = computed(() => props.instance.install_stage !== 'installed')
|
||||||
const inProgress = ref(false)
|
const inProgress = ref(false)
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ const switchVersion = async (versionId) => {
|
|||||||
:noblur="!themeStore.advancedRendering"
|
:noblur="!themeStore.advancedRendering"
|
||||||
>
|
>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<Card v-if="instance.metadata.linked_data" class="mod-card">
|
<Card v-if="instance.linked_data" class="mod-card">
|
||||||
<div class="table">
|
<div class="table">
|
||||||
<div class="table-row with-columns table-head">
|
<div class="table-row with-columns table-head">
|
||||||
<div class="table-cell table-text download-cell" />
|
<div class="table-cell table-text download-cell" />
|
||||||
|
|||||||
@@ -6,10 +6,8 @@ import { computed, ref } from 'vue'
|
|||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useFetch } from '@/helpers/fetch.js'
|
import { install as installVersion } from '@/store/install.js'
|
||||||
import { list } from '@/helpers/profile.js'
|
|
||||||
import { handleError } from '@/store/notifications.js'
|
|
||||||
import { install as pack_install } from '@/helpers/pack.js'
|
|
||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -22,18 +20,6 @@ const props = defineProps({
|
|||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
confirmModal: {
|
|
||||||
type: Object,
|
|
||||||
default() {
|
|
||||||
return {}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
modInstallModal: {
|
|
||||||
type: Object,
|
|
||||||
default() {
|
|
||||||
return {}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const toColor = computed(() => {
|
const toColor = computed(() => {
|
||||||
@@ -65,40 +51,15 @@ const toTransparent = computed(() => {
|
|||||||
const install = async (e) => {
|
const install = async (e) => {
|
||||||
e?.stopPropagation()
|
e?.stopPropagation()
|
||||||
installing.value = true
|
installing.value = true
|
||||||
const versions = await useFetch(
|
await installVersion(
|
||||||
`https://api.modrinth.com/v2/project/${props.project.project_id}/version`,
|
props.project.project_id,
|
||||||
'project versions',
|
null,
|
||||||
)
|
props.instance ? props.instance.path : null,
|
||||||
|
'ProjectCard',
|
||||||
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)
|
|
||||||
installing.value = false
|
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>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -15,15 +15,15 @@
|
|||||||
</Button>
|
</Button>
|
||||||
<div v-if="offline" class="status">
|
<div v-if="offline" class="status">
|
||||||
<span class="circle stopped" />
|
<span class="circle stopped" />
|
||||||
<div class="running-text clickable" @click="refreshInternet()">
|
<div class="running-text">
|
||||||
<span> Offline </span>
|
<span> Offline </span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="selectedProfile" class="status">
|
<div v-if="selectedProcess" class="status">
|
||||||
<span class="circle running" />
|
<span class="circle running" />
|
||||||
<div ref="profileButton" class="running-text">
|
<div ref="profileButton" class="running-text">
|
||||||
<router-link :to="`/instance/${encodeURIComponent(selectedProfile.path)}`">
|
<router-link :to="`/instance/${encodeURIComponent(selectedProcess.profile.path)}`">
|
||||||
{{ selectedProfile.metadata.name }}
|
{{ selectedProcess.profile.name }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<div
|
<div
|
||||||
v-if="currentProcesses.length > 1"
|
v-if="currentProcesses.length > 1"
|
||||||
@@ -34,7 +34,12 @@
|
|||||||
<DropdownIcon />
|
<DropdownIcon />
|
||||||
</div>
|
</div>
|
||||||
</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 />
|
<StopCircleIcon />
|
||||||
</Button>
|
</Button>
|
||||||
<Button v-tooltip="'View logs'" icon-only class="icon-button" @click="goToTerminal()">
|
<Button v-tooltip="'View logs'" icon-only class="icon-button" @click="goToTerminal()">
|
||||||
@@ -75,17 +80,17 @@
|
|||||||
class="profile-card"
|
class="profile-card"
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
v-for="profile in currentProcesses"
|
v-for="process in currentProcesses"
|
||||||
:key="profile.id"
|
:key="process.pid"
|
||||||
class="profile-button"
|
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
|
<Button
|
||||||
v-tooltip="'Stop instance'"
|
v-tooltip="'Stop instance'"
|
||||||
icon-only
|
icon-only
|
||||||
class="icon-button stop"
|
class="icon-button stop"
|
||||||
@click.stop="stop(profile.path)"
|
@click.stop="stop(process)"
|
||||||
>
|
>
|
||||||
<StopCircleIcon />
|
<StopCircleIcon />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -93,7 +98,7 @@
|
|||||||
v-tooltip="'View logs'"
|
v-tooltip="'View logs'"
|
||||||
icon-only
|
icon-only
|
||||||
class="icon-button"
|
class="icon-button"
|
||||||
@click.stop="goToTerminal(profile.path)"
|
@click.stop="goToTerminal(process.profile.path)"
|
||||||
>
|
>
|
||||||
<TerminalSquareIcon />
|
<TerminalSquareIcon />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -106,19 +111,15 @@
|
|||||||
import { DownloadIcon, StopCircleIcon, TerminalSquareIcon, DropdownIcon } from '@modrinth/assets'
|
import { DownloadIcon, StopCircleIcon, TerminalSquareIcon, DropdownIcon } from '@modrinth/assets'
|
||||||
import { Button, Card } from '@modrinth/ui'
|
import { Button, Card } from '@modrinth/ui'
|
||||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||||
import {
|
import { get_all as getRunningProcesses, kill as killProcess } from '@/helpers/process'
|
||||||
get_all_running_profiles as getRunningProfiles,
|
import { loading_listener, process_listener } from '@/helpers/events'
|
||||||
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 { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { progress_bars_list } from '@/helpers/state.js'
|
import { progress_bars_list } from '@/helpers/state.js'
|
||||||
import { refreshOffline, isOffline } from '@/helpers/utils.js'
|
|
||||||
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { ChatIcon } from '@/assets/icons'
|
import { ChatIcon } from '@/assets/icons'
|
||||||
|
import { get_many } from '@/helpers/profile.js'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const card = ref(null)
|
const card = ref(null)
|
||||||
@@ -129,38 +130,44 @@ const showCard = ref(false)
|
|||||||
|
|
||||||
const showProfiles = ref(false)
|
const showProfiles = ref(false)
|
||||||
|
|
||||||
const currentProcesses = ref(await getRunningProfiles().catch(handleError))
|
const currentProcesses = ref([])
|
||||||
const selectedProfile = ref(currentProcesses.value[0])
|
const selectedProcess = ref()
|
||||||
|
|
||||||
const offline = ref(await isOffline().catch(handleError))
|
const refresh = async () => {
|
||||||
const refreshInternet = async () => {
|
const processes = await getRunningProcesses().catch(handleError)
|
||||||
offline.value = await refreshOffline().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 () => {
|
const unlistenProcess = await process_listener(async () => {
|
||||||
await refresh()
|
await refresh()
|
||||||
})
|
})
|
||||||
|
|
||||||
const unlistenRefresh = await offline_listener(async (b) => {
|
const stop = async (process) => {
|
||||||
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) => {
|
|
||||||
try {
|
try {
|
||||||
const processes = await getProfileProcesses(path ?? selectedProfile.value.path)
|
console.log(process.pid)
|
||||||
await killProfile(processes[0])
|
await killProcess(process.pid).catch(handleError)
|
||||||
|
|
||||||
mixpanel_track('InstanceStop', {
|
mixpanel_track('InstanceStop', {
|
||||||
loader: currentProcesses.value[0].metadata.loader,
|
loader: process.profile.loader,
|
||||||
game_version: currentProcesses.value[0].metadata.game_version,
|
game_version: process.profile.game_version,
|
||||||
source: 'AppBar',
|
source: 'AppBar',
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -170,7 +177,7 @@ const stop = async (path) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const goToTerminal = (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([])
|
const currentLoadingBars = ref([])
|
||||||
@@ -182,8 +189,8 @@ const refreshInfo = async () => {
|
|||||||
if (x.bar_type.type === 'java_download') {
|
if (x.bar_type.type === 'java_download') {
|
||||||
x.title = 'Downloading Java ' + x.bar_type.version
|
x.title = 'Downloading Java ' + x.bar_type.version
|
||||||
}
|
}
|
||||||
if (x.bar_type.profile_name) {
|
if (x.bar_type.profile_path) {
|
||||||
x.title = x.bar_type.profile_name
|
x.title = x.bar_type.profile_path
|
||||||
}
|
}
|
||||||
if (x.bar_type.pack_name) {
|
if (x.bar_type.pack_name) {
|
||||||
x.title = x.bar_type.pack_name
|
x.title = x.bar_type.pack_name
|
||||||
@@ -215,8 +222,8 @@ const unlistenLoading = await loading_listener(async () => {
|
|||||||
await refreshInfo()
|
await refreshInfo()
|
||||||
})
|
})
|
||||||
|
|
||||||
const selectProfile = (profile) => {
|
const selectProcess = (process) => {
|
||||||
selectedProfile.value = profile
|
selectedProcess.value = process
|
||||||
showProfiles.value = false
|
showProfiles.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,7 +274,6 @@ onBeforeUnmount(() => {
|
|||||||
window.removeEventListener('click', handleClickOutsideProfile)
|
window.removeEventListener('click', handleClickOutsideProfile)
|
||||||
unlistenProcess()
|
unlistenProcess()
|
||||||
unlistenLoading()
|
unlistenLoading()
|
||||||
unlistenRefresh()
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -69,12 +69,7 @@ import { formatNumber, formatCategory } from '@modrinth/utils'
|
|||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { add_project_from_version as installMod, list } from '@/helpers/profile.js'
|
import { install as installVersion } from '@/store/install.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'
|
|
||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -94,18 +89,6 @@ const props = defineProps({
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
confirmModal: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
modInstallModal: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
incompatibilityWarningModal: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
featured: {
|
featured: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
@@ -123,93 +106,19 @@ const installed = ref(props.installed)
|
|||||||
|
|
||||||
async function install() {
|
async function install() {
|
||||||
installing.value = true
|
installing.value = true
|
||||||
const versions = await useFetch(
|
await installVersion(
|
||||||
`https://api.modrinth.com/v2/project/${props.project.project_id}/version`,
|
props.project.project_id,
|
||||||
'project versions',
|
null,
|
||||||
)
|
props.instance ? props.instance.path : null,
|
||||||
let queuedVersionData
|
'SearchCard',
|
||||||
|
(version) => {
|
||||||
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,
|
|
||||||
)
|
|
||||||
installing.value = false
|
installing.value = false
|
||||||
return
|
|
||||||
}
|
|
||||||
if (props.instance) installed.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
installing.value = false
|
if (props.instance && version) {
|
||||||
|
installed.value = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,77 +1,37 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { Modal, Button } from '@modrinth/ui'
|
import { Modal, Button } from '@modrinth/ui'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useFetch } from '@/helpers/fetch.js'
|
|
||||||
import SearchCard from '@/components/ui/SearchCard.vue'
|
import SearchCard from '@/components/ui/SearchCard.vue'
|
||||||
import { get_categories } from '@/helpers/tags.js'
|
import { get_categories } from '@/helpers/tags.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { install as packInstall } from '@/helpers/pack.js'
|
import { get_version, get_project } from '@/helpers/cache.js'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import { install as installVersion } from '@/store/install.js'
|
||||||
import ModInstallModal from '@/components/ui/ModInstallModal.vue'
|
|
||||||
|
|
||||||
const confirmModal = ref(null)
|
const confirmModal = ref(null)
|
||||||
const project = ref(null)
|
const project = ref(null)
|
||||||
const version = ref(null)
|
const version = ref(null)
|
||||||
const categories = ref(null)
|
const categories = ref(null)
|
||||||
const installing = ref(false)
|
const installing = ref(false)
|
||||||
const modInstallModal = ref(null)
|
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
async show(event) {
|
async show(event) {
|
||||||
if (event.event === 'InstallVersion') {
|
if (event.event === 'InstallVersion') {
|
||||||
version.value = await useFetch(
|
version.value = await get_version(event.id).catch(handleError)
|
||||||
`https://api.modrinth.com/v2/version/${encodeURIComponent(event.id)}`,
|
project.value = await get_project(version.value.project_id).catch(handleError)
|
||||||
'version',
|
|
||||||
)
|
|
||||||
project.value = await useFetch(
|
|
||||||
`https://api.modrinth.com/v2/project/${encodeURIComponent(version.value.project_id)}`,
|
|
||||||
'project',
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
project.value = await useFetch(
|
project.value = await get_project(event.id).catch(handleError)
|
||||||
`https://api.modrinth.com/v2/project/${encodeURIComponent(event.id)}`,
|
version.value = await get_version(project.value.versions[0]).catch(handleError)
|
||||||
'project',
|
|
||||||
)
|
|
||||||
version.value = await useFetch(
|
|
||||||
`https://api.modrinth.com/v2/version/${encodeURIComponent(project.value.versions[0])}`,
|
|
||||||
'version',
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
categories.value = (await get_categories().catch(handleError)).filter(
|
categories.value = (await get_categories().catch(handleError)).filter(
|
||||||
(cat) => project.value.categories.includes(cat.name) && cat.project_type === 'mod',
|
(cat) => project.value.categories.includes(cat.name) && cat.project_type === 'mod',
|
||||||
)
|
)
|
||||||
confirmModal.value.show()
|
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() {
|
async function install() {
|
||||||
confirmModal.value.hide()
|
confirmModal.value.hide()
|
||||||
if (project.value.project_type === 'modpack') {
|
await installVersion(project.value.id, version.value.id, null, 'URLConfirmModal')
|
||||||
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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -96,7 +56,6 @@ async function install() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
<ModInstallModal ref="modInstallModal" />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
ref="incompatibleModal"
|
ref="incompatibleModal"
|
||||||
header="Incompatibility warning"
|
header="Incompatibility warning"
|
||||||
:noblur="!themeStore.advancedRendering"
|
:noblur="!themeStore.advancedRendering"
|
||||||
|
:on-hide="onInstall"
|
||||||
>
|
>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>
|
<p>
|
||||||
@@ -12,13 +13,11 @@
|
|||||||
</p>
|
</p>
|
||||||
<table>
|
<table>
|
||||||
<tr class="header">
|
<tr class="header">
|
||||||
<th>{{ instance?.metadata.name }}</th>
|
<th>{{ instance?.name }}</th>
|
||||||
<th>{{ projectTitle }}</th>
|
<th>{{ project.title }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="content">
|
<tr class="content">
|
||||||
<td class="data">
|
<td class="data">{{ instance?.loader }} {{ instance?.game_version }}</td>
|
||||||
{{ instance?.metadata.loader }} {{ instance?.metadata.game_version }}
|
|
||||||
</td>
|
|
||||||
<td>
|
<td>
|
||||||
<DropdownSelect
|
<DropdownSelect
|
||||||
v-if="versions?.length > 1"
|
v-if="versions?.length > 1"
|
||||||
@@ -68,34 +67,25 @@ const themeStore = useTheming()
|
|||||||
|
|
||||||
const instance = ref(null)
|
const instance = ref(null)
|
||||||
const project = ref(null)
|
const project = ref(null)
|
||||||
const projectType = ref(null)
|
|
||||||
const projectTitle = ref(null)
|
|
||||||
const versions = ref(null)
|
const versions = ref(null)
|
||||||
const selectedVersion = ref(null)
|
const selectedVersion = ref(null)
|
||||||
const incompatibleModal = ref(null)
|
const incompatibleModal = ref(null)
|
||||||
const installing = ref(false)
|
const installing = ref(false)
|
||||||
|
|
||||||
let markInstalled = () => {}
|
let onInstall = ref(() => {})
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
show: (
|
show: (instanceVal, projectVal, projectVersions, callback) => {
|
||||||
instanceVal,
|
|
||||||
projectTitleVal,
|
|
||||||
selectedVersions,
|
|
||||||
extMarkInstalled,
|
|
||||||
projectIdVal,
|
|
||||||
projectTypeVal,
|
|
||||||
) => {
|
|
||||||
instance.value = instanceVal
|
instance.value = instanceVal
|
||||||
projectTitle.value = projectTitleVal
|
versions.value = projectVersions
|
||||||
versions.value = selectedVersions
|
selectedVersion.value = projectVersions[0]
|
||||||
selectedVersion.value = selectedVersions[0]
|
|
||||||
|
|
||||||
project.value = projectIdVal
|
project.value = projectVal
|
||||||
projectType.value = projectTypeVal
|
|
||||||
|
onInstall.value = callback
|
||||||
|
installing.value = false
|
||||||
|
|
||||||
incompatibleModal.value.show()
|
incompatibleModal.value.show()
|
||||||
markInstalled = extMarkInstalled
|
|
||||||
|
|
||||||
mixpanel_track('ProjectInstallStart', { source: 'ProjectIncompatibilityWarningModal' })
|
mixpanel_track('ProjectInstallStart', { source: 'ProjectIncompatibilityWarningModal' })
|
||||||
},
|
},
|
||||||
@@ -105,16 +95,16 @@ const install = async () => {
|
|||||||
installing.value = true
|
installing.value = true
|
||||||
await installMod(instance.value.path, selectedVersion.value.id).catch(handleError)
|
await installMod(instance.value.path, selectedVersion.value.id).catch(handleError)
|
||||||
installing.value = false
|
installing.value = false
|
||||||
markInstalled()
|
onInstall.value(selectedVersion.value.id)
|
||||||
incompatibleModal.value.hide()
|
incompatibleModal.value.hide()
|
||||||
|
|
||||||
mixpanel_track('ProjectInstall', {
|
mixpanel_track('ProjectInstall', {
|
||||||
loader: instance.value.metadata.loader,
|
loader: instance.value.loader,
|
||||||
game_version: instance.value.metadata.game_version,
|
game_version: instance.value.game_version,
|
||||||
id: project.value,
|
id: project.value,
|
||||||
version_id: selectedVersion.value.id,
|
version_id: selectedVersion.value.id,
|
||||||
project_type: projectType.value,
|
project_type: project.value.project_type,
|
||||||
title: projectTitle.value,
|
title: project.value.title,
|
||||||
source: 'ProjectIncompatibilityWarningModal',
|
source: 'ProjectIncompatibilityWarningModal',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -9,47 +9,55 @@ import { handleError } from '@/store/state.js'
|
|||||||
|
|
||||||
const themeStore = useTheming()
|
const themeStore = useTheming()
|
||||||
|
|
||||||
const version = ref('')
|
const versionId = ref()
|
||||||
const title = ref('')
|
const project = ref()
|
||||||
const projectId = ref('')
|
|
||||||
const icon = ref('')
|
|
||||||
const confirmModal = ref(null)
|
const confirmModal = ref(null)
|
||||||
const installing = ref(false)
|
const installing = ref(false)
|
||||||
|
|
||||||
|
let onInstall = ref(() => {})
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
show: (projectIdVal, versionId, projectTitle, projectIcon) => {
|
show: (projectVal, versionIdVal, callback) => {
|
||||||
projectId.value = projectIdVal
|
project.value = projectVal
|
||||||
version.value = versionId
|
versionId.value = versionIdVal
|
||||||
title.value = projectTitle
|
|
||||||
icon.value = projectIcon
|
|
||||||
installing.value = false
|
installing.value = false
|
||||||
confirmModal.value.show()
|
confirmModal.value.show()
|
||||||
|
|
||||||
|
onInstall.value = callback
|
||||||
|
|
||||||
mixpanel_track('PackInstallStart')
|
mixpanel_track('PackInstallStart')
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
async function install() {
|
async function install() {
|
||||||
installing.value = true
|
installing.value = true
|
||||||
console.log(`Installing ${projectId.value} ${version.value} ${title.value} ${icon.value}`)
|
|
||||||
confirmModal.value.hide()
|
confirmModal.value.hide()
|
||||||
|
|
||||||
await pack_install(
|
await pack_install(
|
||||||
projectId.value,
|
project.value.id,
|
||||||
version.value,
|
versionId.value,
|
||||||
title.value,
|
project.value.title,
|
||||||
icon.value ? icon.value : null,
|
project.value.icon_url,
|
||||||
).catch(handleError)
|
).catch(handleError)
|
||||||
mixpanel_track('PackInstall', {
|
mixpanel_track('PackInstall', {
|
||||||
id: projectId.value,
|
id: project.value.id,
|
||||||
version_id: version.value,
|
version_id: versionId.value,
|
||||||
title: title.value,
|
title: project.value.title,
|
||||||
source: 'ConfirmModal',
|
source: 'ConfirmModal',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onInstall.value(versionId.value)
|
||||||
|
installing.value = false
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<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">
|
<div class="modal-body">
|
||||||
<p>You already have this modpack installed. Are you sure you want to install it again?</p>
|
<p>You already have this modpack installed. Are you sure you want to install it again?</p>
|
||||||
<div class="input-group push-right">
|
<div class="input-group push-right">
|
||||||
@@ -14,10 +14,10 @@ import {
|
|||||||
check_installed,
|
check_installed,
|
||||||
get,
|
get,
|
||||||
list,
|
list,
|
||||||
|
create,
|
||||||
} from '@/helpers/profile'
|
} from '@/helpers/profile'
|
||||||
import { open } from '@tauri-apps/api/dialog'
|
import { open } from '@tauri-apps/api/dialog'
|
||||||
import { create } from '@/helpers/profile'
|
import { installVersionDependencies } from '@/store/install.js'
|
||||||
import { installVersionDependencies } from '@/helpers/utils'
|
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { useTheming } from '@/store/theme.js'
|
import { useTheming } from '@/store/theme.js'
|
||||||
@@ -27,13 +27,12 @@ import { tauri } from '@tauri-apps/api'
|
|||||||
const themeStore = useTheming()
|
const themeStore = useTheming()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const versions = ref([])
|
const versions = ref()
|
||||||
const project = ref('')
|
const project = ref()
|
||||||
const projectTitle = ref('')
|
|
||||||
const projectType = ref('')
|
|
||||||
|
|
||||||
const installModal = ref(null)
|
const installModal = ref()
|
||||||
const searchFilter = ref('')
|
const searchFilter = ref('')
|
||||||
|
|
||||||
const showCreation = ref(false)
|
const showCreation = ref(false)
|
||||||
const icon = ref(null)
|
const icon = ref(null)
|
||||||
const name = ref(null)
|
const name = ref(null)
|
||||||
@@ -42,33 +41,65 @@ const loader = ref(null)
|
|||||||
const gameVersion = ref(null)
|
const gameVersion = ref(null)
|
||||||
const creatingInstance = ref(false)
|
const creatingInstance = ref(false)
|
||||||
|
|
||||||
defineExpose({
|
const profiles = ref([])
|
||||||
show: async (projectId, selectedVersions, title, type) => {
|
|
||||||
project.value = projectId
|
|
||||||
versions.value = selectedVersions
|
|
||||||
projectTitle.value = title
|
|
||||||
projectType.value = type
|
|
||||||
|
|
||||||
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 = ''
|
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' })
|
mixpanel_track('ProjectInstallStart', { source: 'ProjectInstallModal' })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const profiles = ref([])
|
|
||||||
|
|
||||||
async function install(instance) {
|
async function install(instance) {
|
||||||
instance.installing = true
|
instance.installing = true
|
||||||
const version = versions.value.find((v) => {
|
const version = versions.value.find((v) => {
|
||||||
return (
|
return (
|
||||||
v.game_versions.includes(instance.metadata.game_version) &&
|
v.game_versions.includes(instance.game_version) &&
|
||||||
(v.loaders.includes(instance.metadata.loader) ||
|
(project.value.project_type === 'mod'
|
||||||
v.loaders.includes('minecraft') ||
|
? v.loaders.includes(instance.loader) || v.loaders.includes('minecraft')
|
||||||
v.loaders.includes('iris') ||
|
: true)
|
||||||
v.loaders.includes('optifine'))
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -85,45 +116,18 @@ async function install(instance) {
|
|||||||
instance.installing = false
|
instance.installing = false
|
||||||
|
|
||||||
mixpanel_track('ProjectInstall', {
|
mixpanel_track('ProjectInstall', {
|
||||||
loader: instance.metadata.loader,
|
loader: instance.loader,
|
||||||
game_version: instance.metadata.game_version,
|
game_version: instance.game_version,
|
||||||
id: project.value,
|
id: project.value.id,
|
||||||
version_id: version.id,
|
version_id: version.id,
|
||||||
project_type: projectType.value,
|
project_type: project.value.project_type,
|
||||||
title: projectTitle.value,
|
title: project.value.title,
|
||||||
source: 'ProjectInstallModal',
|
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 = () => {
|
const toggleCreation = () => {
|
||||||
showCreation.value = !showCreation.value
|
showCreation.value = !showCreation.value
|
||||||
name.value = null
|
name.value = null
|
||||||
@@ -132,8 +136,7 @@ const toggleCreation = () => {
|
|||||||
gameVersion.value = null
|
gameVersion.value = null
|
||||||
loader.value = null
|
loader.value = null
|
||||||
|
|
||||||
if (!alreadySentCreation.value) {
|
if (showCreation.value) {
|
||||||
alreadySentCreation.value = false
|
|
||||||
mixpanel_track('InstanceCreateStart', { source: 'ProjectInstallModal' })
|
mixpanel_track('InstanceCreateStart', { source: 'ProjectInstallModal' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,18 +200,16 @@ const createInstance = async () => {
|
|||||||
game_version: versions.value[0].game_versions[0],
|
game_version: versions.value[0].game_versions[0],
|
||||||
id: project.value,
|
id: project.value,
|
||||||
version_id: versions.value[0].id,
|
version_id: versions.value[0].id,
|
||||||
project_type: projectType.value,
|
project_type: project.value.project_type,
|
||||||
title: projectTitle.value,
|
title: project.value.title,
|
||||||
source: 'ProjectInstallModal',
|
source: 'ProjectInstallModal',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onInstall.value(versions.value[0].id)
|
||||||
|
|
||||||
if (installModal.value) installModal.value.hide()
|
if (installModal.value) installModal.value.hide()
|
||||||
creatingInstance.value = false
|
creatingInstance.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const check_valid = computed(() => {
|
|
||||||
return name.value
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -216,6 +217,7 @@ const check_valid = computed(() => {
|
|||||||
ref="installModal"
|
ref="installModal"
|
||||||
header="Install project to instance"
|
header="Install project to instance"
|
||||||
:noblur="!themeStore.advancedRendering"
|
:noblur="!themeStore.advancedRendering"
|
||||||
|
:on-hide="onInstall"
|
||||||
>
|
>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<input
|
<input
|
||||||
@@ -226,34 +228,27 @@ const check_valid = computed(() => {
|
|||||||
placeholder="Search for an instance"
|
placeholder="Search for an instance"
|
||||||
/>
|
/>
|
||||||
<div class="profiles" :class="{ 'hide-creation': !showCreation }">
|
<div class="profiles" :class="{ 'hide-creation': !showCreation }">
|
||||||
<div v-for="profile in profiles" :key="profile.metadata.name" class="option">
|
<div v-for="profile in shownProfiles" :key="profile.name" class="option">
|
||||||
<Button
|
<router-link
|
||||||
transparent
|
class="btn btn-transparent profile-button"
|
||||||
class="profile-button"
|
:to="`/instance/${encodeURIComponent(profile.path)}`"
|
||||||
@click="$router.push(`/instance/${encodeURIComponent(profile.path)}`)"
|
@click="installModal.hide()"
|
||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
:src="
|
:src="profile.icon_path ? tauri.convertFileSrc(profile.icon_path) : null"
|
||||||
!profile.metadata.icon ||
|
|
||||||
(profile.metadata.icon && profile.metadata.icon.startsWith('http'))
|
|
||||||
? profile.metadata.icon
|
|
||||||
: tauri.convertFileSrc(profile.metadata?.icon)
|
|
||||||
"
|
|
||||||
class="profile-image"
|
class="profile-image"
|
||||||
/>
|
/>
|
||||||
{{ profile.metadata.name }}
|
{{ profile.name }}
|
||||||
</Button>
|
</router-link>
|
||||||
<div
|
<div
|
||||||
v-tooltip="
|
v-tooltip="
|
||||||
profile.metadata.linked_data?.locked && !profile.installedMod
|
profile.linked_data?.locked && !profile.installedMod
|
||||||
? 'Unpair or unlock an instance to add mods.'
|
? 'Unpair or unlock an instance to add mods.'
|
||||||
: ''
|
: ''
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
:disabled="
|
:disabled="profile.installedMod || profile.installing || profile.linked_data?.locked"
|
||||||
profile.installedMod || profile.installing || profile.metadata.linked_data?.locked
|
|
||||||
"
|
|
||||||
@click="install(profile)"
|
@click="install(profile)"
|
||||||
>
|
>
|
||||||
<DownloadIcon v-if="!profile.installedMod && !profile.installing" />
|
<DownloadIcon v-if="!profile.installedMod && !profile.installing" />
|
||||||
@@ -263,7 +258,7 @@ const check_valid = computed(() => {
|
|||||||
? 'Installing...'
|
? 'Installing...'
|
||||||
: profile.installedMod
|
: profile.installedMod
|
||||||
? 'Installed'
|
? 'Installed'
|
||||||
: profile.metadata.linked_data && profile.metadata.linked_data.locked
|
: profile.linked_data && profile.linked_data.locked
|
||||||
? 'Paired'
|
? 'Paired'
|
||||||
: 'Install'
|
: 'Install'
|
||||||
}}
|
}}
|
||||||
@@ -294,7 +289,7 @@ const check_valid = computed(() => {
|
|||||||
placeholder="Name"
|
placeholder="Name"
|
||||||
class="creation-input"
|
class="creation-input"
|
||||||
/>
|
/>
|
||||||
<Button :disabled="creatingInstance === true || !check_valid" @click="createInstance()">
|
<Button :disabled="creatingInstance === true || !name" @click="createInstance()">
|
||||||
<RightArrowIcon />
|
<RightArrowIcon />
|
||||||
{{ creatingInstance ? 'Creating...' : 'Create' }}
|
{{ creatingInstance ? 'Creating...' : 'Create' }}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { UserIcon, LockIcon, MailIcon } from '@modrinth/assets'
|
import { UserIcon, LockIcon, MailIcon } from '@modrinth/assets'
|
||||||
import { Button, Card, Checkbox } from '@modrinth/ui'
|
import { Button, Card, Checkbox, Modal } from '@modrinth/ui'
|
||||||
import {
|
import {
|
||||||
DiscordIcon,
|
DiscordIcon,
|
||||||
GithubIcon,
|
GithubIcon,
|
||||||
@@ -9,31 +9,57 @@ import {
|
|||||||
SteamIcon,
|
SteamIcon,
|
||||||
GitLabIcon,
|
GitLabIcon,
|
||||||
} from '@/assets/external'
|
} from '@/assets/external'
|
||||||
import {
|
import { login, login_2fa, create_account, login_pass } from '@/helpers/mr_auth.js'
|
||||||
authenticate_begin_flow,
|
|
||||||
authenticate_await_completion,
|
|
||||||
login_2fa,
|
|
||||||
create_account,
|
|
||||||
login_pass,
|
|
||||||
} from '@/helpers/mr_auth.js'
|
|
||||||
import { handleError, useNotifications } from '@/store/state.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({
|
const props = defineProps({
|
||||||
nextPage: {
|
callback: {
|
||||||
type: Function,
|
type: Function,
|
||||||
required: true,
|
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 loggingIn = ref(true)
|
||||||
const twoFactorFlow = ref(null)
|
const twoFactorFlow = ref(null)
|
||||||
const twoFactorCode = ref('')
|
const twoFactorCode = ref('')
|
||||||
@@ -45,22 +71,13 @@ const confirmPassword = ref('')
|
|||||||
const subscribe = ref(true)
|
const subscribe = ref(true)
|
||||||
|
|
||||||
async function signInOauth(provider) {
|
async function signInOauth(provider) {
|
||||||
const url = await authenticate_begin_flow(provider).catch(handleError)
|
const creds = await login(provider).catch(handleSevereError)
|
||||||
|
|
||||||
await window.__TAURI_INVOKE__('tauri', {
|
|
||||||
__tauriModule: 'Shell',
|
|
||||||
message: {
|
|
||||||
cmd: 'open',
|
|
||||||
path: url,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const creds = await authenticate_await_completion().catch(handleError)
|
|
||||||
|
|
||||||
if (creds && creds.type === 'two_factor_required') {
|
if (creds && creds.type === 'two_factor_required') {
|
||||||
twoFactorFlow.value = creds.flow
|
twoFactorFlow.value = creds.flow
|
||||||
} else if (creds && creds.session) {
|
} 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)
|
const creds = await login_2fa(twoFactorCode.value, twoFactorFlow.value).catch(handleError)
|
||||||
|
|
||||||
if (creds && creds.session) {
|
if (creds && creds.session) {
|
||||||
props.nextPage()
|
props.callback()
|
||||||
|
modal.value.hide()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function signIn() {
|
async function signIn() {
|
||||||
const creds = await login_pass(
|
const creds = await login_pass(username.value, password.value, turnstileToken.value).catch(
|
||||||
username.value,
|
handleError,
|
||||||
password.value,
|
)
|
||||||
window.turnstile.getResponse(),
|
window.turnstile.reset(widgetId.value)
|
||||||
).catch(handleError)
|
|
||||||
window.turnstile.reset()
|
|
||||||
|
|
||||||
if (creds && creds.type === 'two_factor_required') {
|
if (creds && creds.type === 'two_factor_required') {
|
||||||
twoFactorFlow.value = creds.flow
|
twoFactorFlow.value = creds.flow
|
||||||
} else if (creds && creds.session) {
|
} else if (creds && creds.session) {
|
||||||
props.nextPage()
|
props.callback()
|
||||||
|
modal.value.hide()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,117 +119,128 @@ async function createAccount() {
|
|||||||
username.value,
|
username.value,
|
||||||
email.value,
|
email.value,
|
||||||
password.value,
|
password.value,
|
||||||
window.turnstile.getResponse(),
|
turnstileToken.value,
|
||||||
subscribe.value,
|
subscribe.value,
|
||||||
).catch(handleError)
|
).catch(handleError)
|
||||||
window.turnstile.reset()
|
window.turnstile.reset(widgetId.value)
|
||||||
|
|
||||||
if (creds && creds.session) {
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Card>
|
<Modal ref="modal" :on-hide="removeWidget">
|
||||||
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAHWfmKCm7cUG869"></div>
|
<Card>
|
||||||
<template v-if="twoFactorFlow">
|
<template v-if="twoFactorFlow">
|
||||||
<h1>Enter two-factor code</h1>
|
<h1>Enter two-factor code</h1>
|
||||||
<p>Please enter a two-factor code to proceed.</p>
|
<p>Please enter a two-factor code to proceed.</p>
|
||||||
<input v-model="twoFactorCode" maxlength="11" type="text" placeholder="Enter code..." />
|
<input v-model="twoFactorCode" maxlength="11" type="text" placeholder="Enter code..." />
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<h1 v-if="loggingIn">Login to Modrinth</h1>
|
<h1 v-if="loggingIn">Login to Modrinth</h1>
|
||||||
<h1 v-else>Create an account</h1>
|
<h1 v-else>Create an account</h1>
|
||||||
<div class="button-grid">
|
<div class="button-grid">
|
||||||
<Button class="discord" large @click="signInOauth('discord')">
|
<Button class="discord" large @click="signInOauth('discord')">
|
||||||
<DiscordIcon />
|
<DiscordIcon />
|
||||||
Discord
|
Discord
|
||||||
</Button>
|
</Button>
|
||||||
<Button class="github" large @click="signInOauth('github')">
|
<Button class="github" large @click="signInOauth('github')">
|
||||||
<GithubIcon />
|
<GithubIcon />
|
||||||
Github
|
Github
|
||||||
</Button>
|
</Button>
|
||||||
<Button class="white" large @click="signInOauth('microsoft')">
|
<Button class="white" large @click="signInOauth('microsoft')">
|
||||||
<MicrosoftIcon />
|
<MicrosoftIcon />
|
||||||
Microsoft
|
Microsoft
|
||||||
</Button>
|
</Button>
|
||||||
<Button class="google" large @click="signInOauth('google')">
|
<Button class="google" large @click="signInOauth('google')">
|
||||||
<GoogleIcon />
|
<GoogleIcon />
|
||||||
Google
|
Google
|
||||||
</Button>
|
</Button>
|
||||||
<Button class="white" large @click="signInOauth('steam')">
|
<Button class="white" large @click="signInOauth('steam')">
|
||||||
<SteamIcon />
|
<SteamIcon />
|
||||||
Steam
|
Steam
|
||||||
</Button>
|
</Button>
|
||||||
<Button class="gitlab" large @click="signInOauth('gitlab')">
|
<Button class="gitlab" large @click="signInOauth('gitlab')">
|
||||||
<GitLabIcon />
|
<GitLabIcon />
|
||||||
GitLab
|
GitLab
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div class="divider">
|
<div class="divider">
|
||||||
<hr />
|
<hr />
|
||||||
<p>Or</p>
|
<p>Or</p>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!loggingIn" class="iconified-input username">
|
<div v-if="!loggingIn" class="iconified-input username">
|
||||||
<MailIcon />
|
<MailIcon />
|
||||||
<input v-model="email" type="text" placeholder="Email" />
|
<input v-model="email" type="text" placeholder="Email" />
|
||||||
</div>
|
</div>
|
||||||
<div class="iconified-input username">
|
<div class="iconified-input username">
|
||||||
<UserIcon />
|
<UserIcon />
|
||||||
<input
|
<input
|
||||||
v-model="username"
|
v-model="username"
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="loggingIn ? 'Email or username' : 'Username'"
|
: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>
|
||||||
<div class="iconified-input" :class="{ username: !loggingIn }">
|
</Card>
|
||||||
<LockIcon />
|
</Modal>
|
||||||
<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>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
:deep(.modal-container) {
|
||||||
|
.modal-body {
|
||||||
|
width: auto;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
width: 25rem;
|
width: 25rem;
|
||||||
}
|
}
|
||||||
@@ -321,4 +349,19 @@ onMounted(() => {
|
|||||||
:deep(.checkbox) {
|
:deep(.checkbox) {
|
||||||
border: none;
|
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>
|
</style>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const prevPage = () => {
|
|||||||
const finishOnboarding = async () => {
|
const finishOnboarding = async () => {
|
||||||
mixpanel.track('OnboardingFinish')
|
mixpanel.track('OnboardingFinish')
|
||||||
const settings = await get()
|
const settings = await get()
|
||||||
settings.fully_onboarded = true
|
settings.onboarded = true
|
||||||
await set(settings)
|
await set(settings)
|
||||||
props.finish()
|
props.finish()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,11 +45,3 @@ export async function remove_user(user) {
|
|||||||
export async function users() {
|
export async function users() {
|
||||||
return await invoke('plugin:auth|auth_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 })
|
||||||
|
}
|
||||||
@@ -93,15 +93,3 @@ export async function command_listener(callback) {
|
|||||||
export async function warning_listener(callback) {
|
export async function warning_listener(callback) {
|
||||||
return await listen('warning', (event) => callback(event.payload))
|
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
|
// Finds all the installation of Java 7, if it exists
|
||||||
// Returns [JavaVersion]
|
// Returns [JavaVersion]
|
||||||
export async function find_filtered_jres(version) {
|
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')
|
return await invoke('plugin:metadata|metadata_get_game_versions')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the fabric versions from daedalus
|
// Gets the given loader versions from daedalus
|
||||||
// Returns Manifest
|
// Returns Manifest
|
||||||
export async function get_fabric_versions() {
|
export async function get_loader_versions(loader) {
|
||||||
const c = await invoke('plugin:metadata|metadata_get_fabric_versions')
|
return await invoke('plugin:metadata|metadata_get_loader_versions', { loader })
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,17 +5,10 @@
|
|||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
export async function authenticate_begin_flow(provider) {
|
export async function login(provider) {
|
||||||
return await invoke('plugin:mr_auth|authenticate_begin_flow', { 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) {
|
export async function login_pass(username, password, challenge) {
|
||||||
return await invoke('plugin:mr_auth|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() {
|
export async function logout() {
|
||||||
return await invoke('plugin:mr_auth|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.gameVersion,
|
||||||
profile_creator.modloader,
|
profile_creator.modloader,
|
||||||
profile_creator.loaderVersion,
|
profile_creator.loaderVersion,
|
||||||
profile_creator.icon,
|
null,
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
|
|
||||||
return await invoke('plugin:pack|pack_install', { location, profile })
|
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.gameVersion,
|
||||||
profile_creator.modloader,
|
profile_creator.modloader,
|
||||||
profile_creator.loaderVersion,
|
profile_creator.loaderVersion,
|
||||||
profile_creator.icon,
|
null,
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
return await invoke('plugin:pack|pack_install', { location, profile })
|
return await invoke('plugin:pack|pack_install', { location, profile })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,49 +5,19 @@
|
|||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
/// Gets if a process has finished by UUID
|
/// Gets all running process IDs with a given profile path
|
||||||
/// 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
|
|
||||||
/// Returns [u32]
|
/// Returns [u32]
|
||||||
export async function get_all_uuids() {
|
export async function get_by_profile_path(path) {
|
||||||
return await invoke('plugin:process|process_get_all_uuids')
|
return await invoke('plugin:process|process_get_by_profile_path', { path })
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets all running process IDs
|
|
||||||
/// Returns [u32]
|
|
||||||
export async function get_all_running_uuids() {
|
|
||||||
return await invoke('plugin:process|process_get_all_running_uuids')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets all running process IDs with a given profile path
|
/// Gets all running process IDs with a given profile path
|
||||||
/// Returns [u32]
|
/// Returns [u32]
|
||||||
export async function get_uuids_by_profile_path(profilePath) {
|
export async function get_all() {
|
||||||
return await invoke('plugin:process|process_get_uuids_by_profile_path', { profilePath })
|
return await invoke('plugin:process|process_get_all')
|
||||||
}
|
|
||||||
|
|
||||||
/// 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')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Kills a process by UUID
|
/// Kills a process by UUID
|
||||||
export async function kill_by_uuid(uuid) {
|
export async function kill(pid) {
|
||||||
return await invoke('plugin:process|process_kill_by_uuid', { uuid })
|
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
|
- 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"
|
//Trim string name to avoid "Unable to find directory"
|
||||||
name = name.trim()
|
name = name.trim()
|
||||||
return await invoke('plugin:profile_create|profile_create', {
|
return await invoke('plugin:profile_create|profile_create', {
|
||||||
@@ -24,8 +24,8 @@ export async function create(name, gameVersion, modloader, loaderVersion, icon,
|
|||||||
gameVersion,
|
gameVersion,
|
||||||
modloader,
|
modloader,
|
||||||
loaderVersion,
|
loaderVersion,
|
||||||
icon,
|
iconPath,
|
||||||
noWatch,
|
skipInstall,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,8 +41,18 @@ export async function remove(path) {
|
|||||||
|
|
||||||
// Get a profile by path
|
// Get a profile by path
|
||||||
// Returns a Profile
|
// Returns a Profile
|
||||||
export async function get(path, clearProjects) {
|
export async function get(path) {
|
||||||
return await invoke('plugin:profile|profile_get', { path, clearProjects })
|
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
|
// 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
|
// Get a copy of the profile set
|
||||||
// Returns hashmap of path -> Profile
|
// Returns hashmap of path -> Profile
|
||||||
export async function list(clearProjects) {
|
export async function list() {
|
||||||
return await invoke('plugin:profile|profile_list', { clearProjects })
|
return await invoke('plugin:profile|profile_list')
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function check_installed(path, projectId) {
|
export async function check_installed(path, projectId) {
|
||||||
@@ -163,10 +173,8 @@ export async function run(path) {
|
|||||||
return await invoke('plugin:profile|profile_run', { path })
|
return await invoke('plugin:profile|profile_run', { path })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run Minecraft using a pathed profile
|
export async function kill(path) {
|
||||||
// Waits for end
|
return await invoke('plugin:profile|profile_kill', { path })
|
||||||
export async function run_wait(path) {
|
|
||||||
return await invoke('plugin:profile|profile_run_wait', { path })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edits a profile
|
// Edits a profile
|
||||||
|
|||||||
@@ -16,11 +16,6 @@ export async function progress_bars_list() {
|
|||||||
return await invoke('plugin:utils|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
|
// Get opening command
|
||||||
// For example, if a user clicks on an .mrpack to open the app.
|
// 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
|
// 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() {
|
export async function get_opening_command() {
|
||||||
return await invoke('plugin:utils|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'
|
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
|
// Gets cached category tags
|
||||||
export async function get_categories() {
|
export async function get_categories() {
|
||||||
return await invoke('plugin:tags|tags_get_categories')
|
return await invoke('plugin:tags|tags_get_categories')
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
import {
|
import { get_full_path, get_mod_full_path } from '@/helpers/profile'
|
||||||
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 { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
export async function isDev() {
|
export async function isDev() {
|
||||||
@@ -49,55 +42,16 @@ export const releaseColor = (releaseType) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const installVersionDependencies = async (profile, version) => {
|
export function debounce(fn, wait) {
|
||||||
for (const dep of version.dependencies) {
|
let timer
|
||||||
if (dep.dependency_type !== 'required') continue
|
return function (...args) {
|
||||||
// disallow fabric api install on quilt
|
if (timer) {
|
||||||
if (dep.project_id === 'P7dR8mSH' && profile.metadata.loader === 'quilt') continue
|
clearTimeout(timer) // clear any pre-existing timer
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// 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,
|
NavRow,
|
||||||
Card,
|
Card,
|
||||||
SearchFilter,
|
SearchFilter,
|
||||||
|
Avatar,
|
||||||
} from '@modrinth/ui'
|
} from '@modrinth/ui'
|
||||||
import { formatCategoryHeader, formatCategory } from '@modrinth/utils'
|
import { formatCategoryHeader, formatCategory } from '@modrinth/utils'
|
||||||
import Multiselect from 'vue-multiselect'
|
import Multiselect from 'vue-multiselect'
|
||||||
@@ -17,29 +18,22 @@ import { handleError } from '@/store/state'
|
|||||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||||
import { get_categories, get_loaders, get_game_versions } from '@/helpers/tags'
|
import { get_categories, get_loaders, get_game_versions } from '@/helpers/tags'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { Avatar } from '@modrinth/ui'
|
|
||||||
import SearchCard from '@/components/ui/SearchCard.vue'
|
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 SplashScreen from '@/components/ui/SplashScreen.vue'
|
||||||
import IncompatibilityWarningModal from '@/components/ui/IncompatibilityWarningModal.vue'
|
import { get as getInstance, get_projects as getInstanceProjects } from '@/helpers/profile.js'
|
||||||
import { useFetch } from '@/helpers/fetch.js'
|
|
||||||
import { check_installed, get, get as getInstance } from '@/helpers/profile.js'
|
|
||||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||||
import { isOffline } from '@/helpers/utils'
|
import { get_search_results } from '@/helpers/cache.js'
|
||||||
import { offline_listener } from '@/helpers/events'
|
import { debounce } from '@/helpers/utils.js'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
const offline = ref(await isOffline())
|
const offline = ref(!navigator.onLine)
|
||||||
const unlistenOffline = await offline_listener((b) => {
|
window.addEventListener('offline', () => {
|
||||||
offline.value = b
|
offline.value = true
|
||||||
|
})
|
||||||
|
window.addEventListener('online', () => {
|
||||||
|
offline.value = false
|
||||||
})
|
})
|
||||||
|
|
||||||
const confirmModal = ref(null)
|
|
||||||
const modInstallModal = ref(null)
|
|
||||||
const incompatibilityWarningModal = ref(null)
|
|
||||||
|
|
||||||
const breadcrumbs = useBreadcrumbs()
|
const breadcrumbs = useBreadcrumbs()
|
||||||
breadcrumbs.setContext({ name: 'Browse', link: route.path, query: route.query })
|
breadcrumbs.setContext({ name: 'Browse', link: route.path, query: route.query })
|
||||||
@@ -65,6 +59,7 @@ const maxResults = ref(20)
|
|||||||
const currentPage = ref(1)
|
const currentPage = ref(1)
|
||||||
const projectType = ref(route.params.projectType)
|
const projectType = ref(route.params.projectType)
|
||||||
const instanceContext = ref(null)
|
const instanceContext = ref(null)
|
||||||
|
const instanceProjects = ref(null)
|
||||||
const ignoreInstanceLoaders = ref(false)
|
const ignoreInstanceLoaders = ref(false)
|
||||||
const ignoreInstanceGameVersions = ref(false)
|
const ignoreInstanceGameVersions = ref(false)
|
||||||
|
|
||||||
@@ -88,7 +83,10 @@ if (route.query.il) {
|
|||||||
ignoreInstanceLoaders.value = route.query.il === 'true'
|
ignoreInstanceLoaders.value = route.query.il === 'true'
|
||||||
}
|
}
|
||||||
if (route.query.i) {
|
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) {
|
if (route.query.q) {
|
||||||
query.value = route.query.q
|
query.value = route.query.q
|
||||||
@@ -144,18 +142,16 @@ if (route.query.ai) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function refreshSearch() {
|
async function refreshSearch() {
|
||||||
const base = 'https://api.modrinth.com/v2/'
|
|
||||||
|
|
||||||
const params = [`limit=${maxResults.value}`, `index=${sortType.value.name}`]
|
const params = [`limit=${maxResults.value}`, `index=${sortType.value.name}`]
|
||||||
if (query.value.length > 0) {
|
if (query.value.length > 0) {
|
||||||
params.push(`query=${query.value.replace(/ /g, '+')}`)
|
params.push(`query=${query.value.replace(/ /g, '+')}`)
|
||||||
}
|
}
|
||||||
if (instanceContext.value) {
|
if (instanceContext.value) {
|
||||||
if (!ignoreInstanceLoaders.value && projectType.value === 'mod') {
|
if (!ignoreInstanceLoaders.value && projectType.value === 'mod') {
|
||||||
orFacets.value = [`categories:${encodeURIComponent(instanceContext.value.metadata.loader)}`]
|
orFacets.value = [`categories:${encodeURIComponent(instanceContext.value.loader)}`]
|
||||||
}
|
}
|
||||||
if (!ignoreInstanceGameVersions.value) {
|
if (!ignoreInstanceGameVersions.value) {
|
||||||
selectedVersions.value = [instanceContext.value.metadata.game_version]
|
selectedVersions.value = [instanceContext.value.game_version]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
@@ -224,13 +220,11 @@ async function refreshSearch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hideAlreadyInstalled.value) {
|
if (hideAlreadyInstalled.value) {
|
||||||
const installedMods = await get(instanceContext.value.path, false).then((x) =>
|
const installedMods = Object.values(instanceProjects.value)
|
||||||
Object.values(x.projects)
|
.filter((x) => x.metadata)
|
||||||
.filter((x) => x.metadata.project)
|
.map((x) => x.metadata.project_id)
|
||||||
.map((x) => x.metadata.project.id),
|
|
||||||
)
|
|
||||||
installedMods.map((x) => [`project_id != ${x}`]).forEach((x) => formattedFacets.push(x))
|
installedMods.map((x) => [`project_id != ${x}`]).forEach((x) => formattedFacets.push(x))
|
||||||
console.log(`facets=${JSON.stringify(formattedFacets)}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
params.push(`facets=${JSON.stringify(formattedFacets)}`)
|
params.push(`facets=${JSON.stringify(formattedFacets)}`)
|
||||||
@@ -246,24 +240,24 @@ async function refreshSearch() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let val = `${base}${url}`
|
let rawResults = await get_search_results(`?${url}`)
|
||||||
|
|
||||||
let rawResults = await useFetch(val, 'search results', offline.value)
|
|
||||||
if (!rawResults) {
|
if (!rawResults) {
|
||||||
rawResults = {
|
rawResults = {
|
||||||
hits: [],
|
result: {
|
||||||
total_hits: 0,
|
hits: [],
|
||||||
limit: 1,
|
total_hits: 0,
|
||||||
|
limit: 1,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (instanceContext.value) {
|
if (instanceContext.value) {
|
||||||
for (val of rawResults.hits) {
|
for (const val of rawResults.result.hits) {
|
||||||
val.installed = await check_installed(instanceContext.value.path, val.project_id).then(
|
val.installed = Object.values(instanceProjects.value).some(
|
||||||
(x) => (val.installed = x),
|
(x) => x.metadata && x.metadata.project_id === val.project_id,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
results.value = rawResults
|
results.value = rawResults.result
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onSearchChange(newPageNumber) {
|
async function onSearchChange(newPageNumber) {
|
||||||
@@ -282,6 +276,8 @@ async function onSearchChange(newPageNumber) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const debouncedSearchChange = debounce(() => onSearchChange(1), 200)
|
||||||
|
|
||||||
const searchWrapper = ref(null)
|
const searchWrapper = ref(null)
|
||||||
async function onSearchChangeToTop(newPageNumber) {
|
async function onSearchChangeToTop(newPageNumber) {
|
||||||
await onSearchChange(newPageNumber)
|
await onSearchChange(newPageNumber)
|
||||||
@@ -505,13 +501,13 @@ const selectableProjectTypes = computed(() => {
|
|||||||
if (instanceContext.value) {
|
if (instanceContext.value) {
|
||||||
if (
|
if (
|
||||||
availableGameVersions.value.findIndex(
|
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')
|
) <= availableGameVersions.value.findIndex((x) => x.version === '1.13')
|
||||||
) {
|
) {
|
||||||
values.unshift({ label: 'Data Packs', href: `/browse/datapack` })
|
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' })
|
values.unshift({ label: 'Mods', href: '/browse/mod' })
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -528,8 +524,6 @@ const showVersions = computed(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const isModProject = computed(() => ['modpack', 'mod'].includes(projectType.value))
|
const isModProject = computed(() => ['modpack', 'mod'].includes(projectType.value))
|
||||||
|
|
||||||
onUnmounted(() => unlistenOffline())
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -538,27 +532,19 @@ onUnmounted(() => unlistenOffline())
|
|||||||
<Card v-if="instanceContext" class="small-instance">
|
<Card v-if="instanceContext" class="small-instance">
|
||||||
<router-link :to="`/instance/${encodeURIComponent(instanceContext.path)}`" class="instance">
|
<router-link :to="`/instance/${encodeURIComponent(instanceContext.path)}`" class="instance">
|
||||||
<Avatar
|
<Avatar
|
||||||
:src="
|
:src="instanceContext.icon_path ? convertFileSrc(instanceContext.icon_path) : null"
|
||||||
!instanceContext.metadata.icon ||
|
:alt="instanceContext.name"
|
||||||
(instanceContext.metadata.icon && instanceContext.metadata.icon.startsWith('http'))
|
|
||||||
? instanceContext.metadata.icon
|
|
||||||
: convertFileSrc(instanceContext.metadata.icon)
|
|
||||||
"
|
|
||||||
:alt="instanceContext.metadata.name"
|
|
||||||
size="sm"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
<div class="small-instance_info">
|
<div class="small-instance_info">
|
||||||
<span class="title">{{
|
<span class="title">{{
|
||||||
instanceContext.metadata.name.length > 20
|
instanceContext.name.length > 20
|
||||||
? instanceContext.metadata.name.substring(0, 20) + '...'
|
? instanceContext.name.substring(0, 20) + '...'
|
||||||
: instanceContext.metadata.name
|
: instanceContext.name
|
||||||
}}</span>
|
}}</span>
|
||||||
<span>
|
<span>
|
||||||
{{
|
{{ instanceContext.loader.charAt(0).toUpperCase() + instanceContext.loader.slice(1) }}
|
||||||
instanceContext.metadata.loader.charAt(0).toUpperCase() +
|
{{ instanceContext.game_version }}
|
||||||
instanceContext.metadata.loader.slice(1)
|
|
||||||
}}
|
|
||||||
{{ instanceContext.metadata.game_version }}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
@@ -598,7 +584,10 @@ onUnmounted(() => unlistenOffline())
|
|||||||
>
|
>
|
||||||
<ClearIcon /> Clear filters
|
<ClearIcon /> Clear filters
|
||||||
</Button>
|
</Button>
|
||||||
<div v-if="isModProject || projectType === 'shader'" class="loaders">
|
<div
|
||||||
|
v-if="(isModProject && ignoreInstanceLoaders) || projectType === 'shader'"
|
||||||
|
class="loaders"
|
||||||
|
>
|
||||||
<h2>Loaders</h2>
|
<h2>Loaders</h2>
|
||||||
<div v-for="loader in filteredLoaders" :key="loader">
|
<div v-for="loader in filteredLoaders" :key="loader">
|
||||||
<SearchFilter
|
<SearchFilter
|
||||||
@@ -693,9 +682,10 @@ onUnmounted(() => unlistenOffline())
|
|||||||
<input
|
<input
|
||||||
v-model="query"
|
v-model="query"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
|
spellcheck="false"
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="`Search ${projectType}s...`"
|
:placeholder="`Search ${projectType}s...`"
|
||||||
@input="onSearchChange(1)"
|
@input="debouncedSearchChange()"
|
||||||
/>
|
/>
|
||||||
<Button class="r-btn" @click="() => clearSearch()">
|
<Button class="r-btn" @click="() => clearSearch()">
|
||||||
<XIcon />
|
<XIcon />
|
||||||
@@ -752,9 +742,6 @@ onUnmounted(() => unlistenOffline())
|
|||||||
loader.supported_project_types?.includes(projectType),
|
loader.supported_project_types?.includes(projectType),
|
||||||
),
|
),
|
||||||
]"
|
]"
|
||||||
:confirm-modal="confirmModal"
|
|
||||||
:mod-install-modal="modInstallModal"
|
|
||||||
:incompatibility-warning-modal="incompatibilityWarningModal"
|
|
||||||
:installed="result.installed"
|
:installed="result.installed"
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
@@ -768,9 +755,6 @@ onUnmounted(() => unlistenOffline())
|
|||||||
<br />
|
<br />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<InstallConfirmModal ref="confirmModal" />
|
|
||||||
<ModInstallModal ref="modInstallModal" />
|
|
||||||
<IncompatibilityWarningModal ref="incompatibilityWarningModal" />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style src="vue-multiselect/dist/vue-multiselect.css"></style>
|
<style src="vue-multiselect/dist/vue-multiselect.css"></style>
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onUnmounted, shallowRef, computed } from 'vue'
|
import { ref, onUnmounted, computed } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import RowDisplay from '@/components/RowDisplay.vue'
|
import RowDisplay from '@/components/RowDisplay.vue'
|
||||||
import { list } from '@/helpers/profile.js'
|
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 { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||||
import { useFetch } from '@/helpers/fetch.js'
|
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { isOffline } from '@/helpers/utils'
|
import { get_search_results } from '@/helpers/cache.js'
|
||||||
|
|
||||||
const featuredModpacks = ref({})
|
const featuredModpacks = ref({})
|
||||||
const featuredMods = ref({})
|
const featuredMods = ref({})
|
||||||
@@ -19,45 +18,55 @@ const breadcrumbs = useBreadcrumbs()
|
|||||||
|
|
||||||
breadcrumbs.setRootContext({ name: 'Home', link: route.path })
|
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 getInstances = async () => {
|
||||||
const profiles = await list(true).catch(handleError)
|
const profiles = await list().catch(handleError)
|
||||||
recentInstances.value = Object.values(profiles).sort((a, b) => {
|
|
||||||
return dayjs(b.metadata.last_played ?? 0).diff(dayjs(a.metadata.last_played ?? 0))
|
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 = []
|
let filters = []
|
||||||
for (const instance of recentInstances.value) {
|
for (const instance of recentInstances.value) {
|
||||||
if (instance.metadata.linked_data && instance.metadata.linked_data.project_id) {
|
if (instance.linked_data && instance.linked_data.project_id) {
|
||||||
filters.push(`NOT"project_id"="${instance.metadata.linked_data.project_id}"`)
|
filters.push(`NOT"project_id"="${instance.linked_data.project_id}"`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filter.value = filters.join(' AND ')
|
filter.value = filters.join(' AND ')
|
||||||
}
|
}
|
||||||
|
|
||||||
const getFeaturedModpacks = async () => {
|
const getFeaturedModpacks = async () => {
|
||||||
const response = await useFetch(
|
const response = await get_search_results(
|
||||||
`https://api.modrinth.com/v2/search?facets=[["project_type:modpack"]]&limit=10&index=follows&filters=${filter.value}`,
|
`?facets=[["project_type:modpack"]]&limit=10&index=follows&filters=${filter.value}`,
|
||||||
'featured modpacks',
|
|
||||||
offline.value,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (response) {
|
if (response) {
|
||||||
featuredModpacks.value = response.hits
|
featuredModpacks.value = response.result.hits
|
||||||
} else {
|
} else {
|
||||||
featuredModpacks.value = []
|
featuredModpacks.value = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const getFeaturedMods = async () => {
|
const getFeaturedMods = async () => {
|
||||||
const response = await useFetch(
|
const response = await get_search_results('?facets=[["project_type:mod"]]&limit=10&index=follows')
|
||||||
'https://api.modrinth.com/v2/search?facets=[["project_type:mod"]]&limit=10&index=follows',
|
|
||||||
'featured mods',
|
|
||||||
offline.value,
|
|
||||||
)
|
|
||||||
if (response) {
|
if (response) {
|
||||||
featuredMods.value = response.hits
|
featuredMods.value = response.result.hits
|
||||||
} else {
|
} else {
|
||||||
featuredModpacks.value = []
|
featuredModpacks.value = []
|
||||||
}
|
}
|
||||||
@@ -69,14 +78,8 @@ await Promise.all([getFeaturedModpacks(), getFeaturedMods()])
|
|||||||
|
|
||||||
const unlistenProfile = await profile_listener(async (e) => {
|
const unlistenProfile = await profile_listener(async (e) => {
|
||||||
await getInstances()
|
await getInstances()
|
||||||
if (e.event === 'created' || e.event === 'removed') {
|
|
||||||
await Promise.all([getFeaturedModpacks(), getFeaturedMods()])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const unlistenOffline = await offline_listener(async (b) => {
|
if (e.event === 'added' || e.event === 'created' || e.event === 'removed') {
|
||||||
offline.value = b
|
|
||||||
if (!b) {
|
|
||||||
await Promise.all([getFeaturedModpacks(), getFeaturedMods()])
|
await Promise.all([getFeaturedModpacks(), getFeaturedMods()])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -92,7 +95,6 @@ const total = computed(() => {
|
|||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
unlistenProfile()
|
unlistenProfile()
|
||||||
unlistenOffline()
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -105,6 +107,7 @@ onUnmounted(() => {
|
|||||||
label: 'Jump back in',
|
label: 'Jump back in',
|
||||||
route: '/library',
|
route: '/library',
|
||||||
instances: recentInstances,
|
instances: recentInstances,
|
||||||
|
instance: true,
|
||||||
downloaded: true,
|
downloaded: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,34 +4,33 @@ import GridDisplay from '@/components/GridDisplay.vue'
|
|||||||
import { list } from '@/helpers/profile.js'
|
import { list } from '@/helpers/profile.js'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
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 { handleError } from '@/store/notifications.js'
|
||||||
import { Button } from '@modrinth/ui'
|
import { Button } from '@modrinth/ui'
|
||||||
import { PlusIcon } from '@modrinth/assets'
|
import { PlusIcon } from '@modrinth/assets'
|
||||||
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
|
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
|
||||||
import { NewInstanceImage } from '@/assets/icons'
|
import { NewInstanceImage } from '@/assets/icons'
|
||||||
import { isOffline } from '@/helpers/utils'
|
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const breadcrumbs = useBreadcrumbs()
|
const breadcrumbs = useBreadcrumbs()
|
||||||
|
|
||||||
breadcrumbs.setRootContext({ name: 'Library', link: route.path })
|
breadcrumbs.setRootContext({ name: 'Library', link: route.path })
|
||||||
|
|
||||||
const profiles = await list(true).catch(handleError)
|
const instances = shallowRef(await list().catch(handleError))
|
||||||
const instances = shallowRef(Object.values(profiles))
|
|
||||||
|
|
||||||
const offline = ref(await isOffline())
|
const offline = ref(!navigator.onLine)
|
||||||
const unlistenOffline = await offline_listener((b) => {
|
window.addEventListener('offline', () => {
|
||||||
offline.value = b
|
offline.value = true
|
||||||
|
})
|
||||||
|
window.addEventListener('online', () => {
|
||||||
|
offline.value = false
|
||||||
})
|
})
|
||||||
|
|
||||||
const unlistenProfile = await profile_listener(async () => {
|
const unlistenProfile = await profile_listener(async () => {
|
||||||
const profiles = await list(true).catch(handleError)
|
instances.value = await list().catch(handleError)
|
||||||
instances.value = Object.values(profiles)
|
|
||||||
})
|
})
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
unlistenProfile()
|
unlistenProfile()
|
||||||
unlistenOffline()
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { LogOutIcon, LogInIcon, BoxIcon, FolderSearchIcon, UpdatedIcon } from '@
|
|||||||
import { Card, Slider, DropdownSelect, Toggle, Modal, Button } from '@modrinth/ui'
|
import { Card, Slider, DropdownSelect, Toggle, Modal, Button } from '@modrinth/ui'
|
||||||
import { handleError, useTheming } from '@/store/state'
|
import { handleError, useTheming } from '@/store/state'
|
||||||
import { is_dir_writeable, change_config_dir, get, set } from '@/helpers/settings'
|
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 { get as getCreds, logout } from '@/helpers/mr_auth.js'
|
||||||
import JavaSelector from '@/components/ui/JavaSelector.vue'
|
import JavaSelector from '@/components/ui/JavaSelector.vue'
|
||||||
import ModrinthLoginScreen from '@/components/ui/tutorial/ModrinthLoginScreen.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 { open } from '@tauri-apps/api/dialog'
|
||||||
import { getOS } from '@/helpers/utils.js'
|
import { getOS } from '@/helpers/utils.js'
|
||||||
import { getVersion } from '@tauri-apps/api/app'
|
import { getVersion } from '@tauri-apps/api/app'
|
||||||
|
import { get_user } from '@/helpers/cache.js'
|
||||||
|
|
||||||
const pageOptions = ['Home', 'Library']
|
const pageOptions = ['Home', 'Library']
|
||||||
|
|
||||||
@@ -22,8 +23,8 @@ const version = await getVersion()
|
|||||||
const accessSettings = async () => {
|
const accessSettings = async () => {
|
||||||
const settings = await get()
|
const settings = await get()
|
||||||
|
|
||||||
settings.javaArgs = settings.custom_java_args.join(' ')
|
settings.launchArgs = settings.extra_launch_args.join(' ')
|
||||||
settings.envArgs = settings.custom_env_args.map((x) => x.join('=')).join(' ')
|
settings.envVars = settings.custom_env_vars.map((x) => x.join('=')).join(' ')
|
||||||
|
|
||||||
return settings
|
return settings
|
||||||
}
|
}
|
||||||
@@ -31,7 +32,8 @@ const accessSettings = async () => {
|
|||||||
const fetchSettings = await accessSettings().catch(handleError)
|
const fetchSettings = await accessSettings().catch(handleError)
|
||||||
|
|
||||||
const settings = ref(fetchSettings)
|
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))
|
const maxMemory = ref(Math.floor((await get_max_memory().catch(handleError)) / 1024))
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@@ -43,26 +45,14 @@ watch(
|
|||||||
|
|
||||||
const setSettings = JSON.parse(JSON.stringify(newSettings))
|
const setSettings = JSON.parse(JSON.stringify(newSettings))
|
||||||
|
|
||||||
if (setSettings.opt_out_analytics) {
|
if (setSettings.telemetry) {
|
||||||
mixpanel_opt_out_tracking()
|
mixpanel_opt_out_tracking()
|
||||||
} else {
|
} else {
|
||||||
mixpanel_opt_in_tracking()
|
mixpanel_opt_in_tracking()
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(setSettings.java_globals)) {
|
setSettings.extra_launch_args = setSettings.launchArgs.trim().split(/\s+/).filter(Boolean)
|
||||||
if (value?.path === '') {
|
setSettings.custom_env_vars = setSettings.envVars
|
||||||
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
|
|
||||||
.trim()
|
.trim()
|
||||||
.split(/\s+/)
|
.split(/\s+/)
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
@@ -78,22 +68,49 @@ watch(
|
|||||||
setSettings.hooks.post_exit = null
|
setSettings.hooks.post_exit = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!setSettings.custom_dir) {
|
||||||
|
setSettings.custom_dir = null
|
||||||
|
}
|
||||||
|
|
||||||
await set(setSettings)
|
await set(setSettings)
|
||||||
},
|
},
|
||||||
{ deep: true },
|
{ 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()
|
const loginScreenModal = ref()
|
||||||
|
|
||||||
async function logOut() {
|
async function logOut() {
|
||||||
await logout().catch(handleError)
|
await logout().catch(handleError)
|
||||||
credentials.value = await getCreds().catch(handleError)
|
await fetchCredentials()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function signInAfter() {
|
async function signInAfter() {
|
||||||
loginScreenModal.value.hide()
|
await fetchCredentials()
|
||||||
credentials.value = await getCreds().catch(handleError)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findLauncherDir() {
|
async function findLauncherDir() {
|
||||||
@@ -103,24 +120,10 @@ async function findLauncherDir() {
|
|||||||
title: 'Select a new app directory',
|
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) {
|
if (newDir) {
|
||||||
settingsDir.value = newDir
|
settings.value.custom_dir = newDir
|
||||||
await refreshDir()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -131,13 +134,7 @@ async function refreshDir() {
|
|||||||
<span class="label__title size-card-header">General settings</span>
|
<span class="label__title size-card-header">General settings</span>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<Modal
|
<ModrinthLoginScreen ref="loginScreenModal" :callback="signInAfter" />
|
||||||
ref="loginScreenModal"
|
|
||||||
class="login-screen-modal"
|
|
||||||
:noblur="!themeStore.advancedRendering"
|
|
||||||
>
|
|
||||||
<ModrinthLoginScreen :modal="true" :prev-page="signInAfter" :next-page="signInAfter" />
|
|
||||||
</Modal>
|
|
||||||
<div class="adjacent-input">
|
<div class="adjacent-input">
|
||||||
<label for="theme">
|
<label for="theme">
|
||||||
<span class="label__title">Manage account</span>
|
<span class="label__title">Manage account</span>
|
||||||
@@ -164,15 +161,11 @@ async function refreshDir() {
|
|||||||
<div class="app-directory">
|
<div class="app-directory">
|
||||||
<div class="iconified-input">
|
<div class="iconified-input">
|
||||||
<BoxIcon />
|
<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">
|
<Button class="r-btn" @click="findLauncherDir">
|
||||||
<FolderSearchIcon />
|
<FolderSearchIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Button large @click="refreshDir">
|
|
||||||
<UpdatedIcon />
|
|
||||||
Refresh
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<Card>
|
<Card>
|
||||||
@@ -230,11 +223,11 @@ async function refreshDir() {
|
|||||||
</label>
|
</label>
|
||||||
<Toggle
|
<Toggle
|
||||||
id="minimize-launcher"
|
id="minimize-launcher"
|
||||||
:model-value="settings.hide_on_process"
|
:model-value="settings.hide_on_process_start"
|
||||||
:checked="settings.hide_on_process"
|
:checked="settings.hide_on_process_start"
|
||||||
@update:model-value="
|
@update:model-value="
|
||||||
(e) => {
|
(e) => {
|
||||||
settings.hide_on_process = e
|
settings.hide_on_process_start = e
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
@@ -285,10 +278,11 @@ async function refreshDir() {
|
|||||||
<div class="adjacent-input">
|
<div class="adjacent-input">
|
||||||
<label for="max-downloads">
|
<label for="max-downloads">
|
||||||
<span class="label__title">Maximum concurrent downloads</span>
|
<span class="label__title">Maximum concurrent downloads</span>
|
||||||
<span class="label__description"
|
<span class="label__description">
|
||||||
>The maximum amount of files the launcher can download at the same time. Set this to a
|
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
|
lower value if you have a poor internet connection. (app restart required to take
|
||||||
>
|
effect)
|
||||||
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<Slider
|
<Slider
|
||||||
id="max-downloads"
|
id="max-downloads"
|
||||||
@@ -302,10 +296,11 @@ async function refreshDir() {
|
|||||||
<div class="adjacent-input">
|
<div class="adjacent-input">
|
||||||
<label for="max-writes">
|
<label for="max-writes">
|
||||||
<span class="label__title">Maximum concurrent writes</span>
|
<span class="label__title">Maximum concurrent writes</span>
|
||||||
<span class="label__description"
|
<span class="label__description">
|
||||||
>The maximum amount of files the launcher can write to the disk at once. Set this to a
|
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
|
lower value if you are frequently getting I/O errors. (app restart required to take
|
||||||
>
|
effect)
|
||||||
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<Slider
|
<Slider
|
||||||
id="max-writes"
|
id="max-writes"
|
||||||
@@ -324,37 +319,38 @@ async function refreshDir() {
|
|||||||
</div>
|
</div>
|
||||||
<div class="adjacent-input">
|
<div class="adjacent-input">
|
||||||
<label for="opt-out-analytics">
|
<label for="opt-out-analytics">
|
||||||
<span class="label__title">Disable analytics</span>
|
<span class="label__title">Telemetry</span>
|
||||||
<span class="label__description">
|
<span class="label__description">
|
||||||
Modrinth collects anonymized analytics and usage data to improve our user experience and
|
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.
|
longer be collected.
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<Toggle
|
<Toggle
|
||||||
id="opt-out-analytics"
|
id="opt-out-analytics"
|
||||||
:model-value="settings.opt_out_analytics"
|
:model-value="settings.telemetry"
|
||||||
:checked="settings.opt_out_analytics"
|
:checked="settings.telemetry"
|
||||||
@update:model-value="
|
@update:model-value="
|
||||||
(e) => {
|
(e) => {
|
||||||
settings.opt_out_analytics = e
|
settings.telemetry = e
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="adjacent-input">
|
<div class="adjacent-input">
|
||||||
<label for="disable-discord-rpc">
|
<label for="disable-discord-rpc">
|
||||||
<span class="label__title">Disable Discord RPC</span>
|
<span class="label__title">Discord RPC</span>
|
||||||
<span class="label__description">
|
<span class="label__description">
|
||||||
Disables the Discord Rich Presence integration. 'Modrinth' will no longer show up as a
|
Manages the Discord Rich Presence integration. Disabling this will cause 'Modrinth' to
|
||||||
game or app you are using on your Discord profile. This does not disable any
|
no longer show up as a game or app you are using on your Discord profile. This does not
|
||||||
instance-specific Discord Rich Presence integrations, such as those added by mods.
|
disable any instance-specific Discord Rich Presence integrations, such as those added by
|
||||||
|
mods. (app restart required to take effect)
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<Toggle
|
<Toggle
|
||||||
id="disable-discord-rpc"
|
id="disable-discord-rpc"
|
||||||
v-model="settings.disable_discord_rpc"
|
v-model="settings.discord_rpc"
|
||||||
:checked="settings.disable_discord_rpc"
|
:checked="settings.discord_rpc"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -364,25 +360,24 @@ async function refreshDir() {
|
|||||||
<span class="label__title size-card-header">Java settings</span>
|
<span class="label__title size-card-header">Java settings</span>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<label for="java-21">
|
<template v-for="version in [21, 17, 8]">
|
||||||
<span class="label__title">Java 21 location</span>
|
<label :for="'java-' + version">
|
||||||
</label>
|
<span class="label__title">Java {{ version }} location</span>
|
||||||
<JavaSelector id="java-17" v-model="settings.java_globals.JAVA_21" :version="21" />
|
</label>
|
||||||
<label for="java-17">
|
<JavaSelector
|
||||||
<span class="label__title">Java 17 location</span>
|
:id="'java-selector-' + version"
|
||||||
</label>
|
v-model="javaVersions[version]"
|
||||||
<JavaSelector id="java-17" v-model="settings.java_globals.JAVA_17" :version="17" />
|
:version="version"
|
||||||
<label for="java-8">
|
@update:model-value="updateJavaVersion"
|
||||||
<span class="label__title">Java 8 location</span>
|
/>
|
||||||
</label>
|
</template>
|
||||||
<JavaSelector id="java-8" v-model="settings.java_globals.JAVA_8" :version="8" />
|
|
||||||
<hr class="card-divider" />
|
<hr class="card-divider" />
|
||||||
<label for="java-args">
|
<label for="java-args">
|
||||||
<span class="label__title">Java arguments</span>
|
<span class="label__title">Java arguments</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="java-args"
|
id="java-args"
|
||||||
v-model="settings.javaArgs"
|
v-model="settings.launchArgs"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
type="text"
|
type="text"
|
||||||
class="installation-input"
|
class="installation-input"
|
||||||
@@ -393,7 +388,7 @@ async function refreshDir() {
|
|||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="env-vars"
|
id="env-vars"
|
||||||
v-model="settings.envArgs"
|
v-model="settings.envVars"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
type="text"
|
type="text"
|
||||||
class="installation-input"
|
class="installation-input"
|
||||||
@@ -526,7 +521,7 @@ async function refreshDir() {
|
|||||||
<div>
|
<div>
|
||||||
<label>
|
<label>
|
||||||
<span class="label__title">App version</span>
|
<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>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -551,16 +546,6 @@ async function refreshDir() {
|
|||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.login-screen-modal) {
|
|
||||||
.modal-container .modal-body {
|
|
||||||
width: auto;
|
|
||||||
|
|
||||||
.content {
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-directory {
|
.app-directory {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|||||||
@@ -2,20 +2,10 @@
|
|||||||
<div class="instance-container">
|
<div class="instance-container">
|
||||||
<div class="side-cards">
|
<div class="side-cards">
|
||||||
<Card class="instance-card" @contextmenu.prevent.stop="handleRightClick">
|
<Card class="instance-card" @contextmenu.prevent.stop="handleRightClick">
|
||||||
<Avatar
|
<Avatar size="lg" :src="instance.icon_path ? convertFileSrc(instance.icon_path) : null" />
|
||||||
size="lg"
|
|
||||||
:src="
|
|
||||||
!instance.metadata.icon ||
|
|
||||||
(instance.metadata.icon && instance.metadata.icon.startsWith('http'))
|
|
||||||
? instance.metadata.icon
|
|
||||||
: convertFileSrc(instance.metadata?.icon)
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<div class="instance-info">
|
<div class="instance-info">
|
||||||
<h2 class="name">{{ instance.metadata.name }}</h2>
|
<h2 class="name">{{ instance.name }}</h2>
|
||||||
<span class="metadata">
|
<span class="metadata"> {{ instance.loader }} {{ instance.game_version }} </span>
|
||||||
{{ instance.metadata.loader }} {{ instance.metadata.game_version }}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<span class="button-group">
|
<span class="button-group">
|
||||||
<Button v-if="instance.install_stage !== 'installed'" disabled class="instance-button">
|
<Button v-if="instance.install_stage !== 'installed'" disabled class="instance-button">
|
||||||
@@ -26,7 +16,6 @@
|
|||||||
color="danger"
|
color="danger"
|
||||||
class="instance-button"
|
class="instance-button"
|
||||||
@click="stopInstance('InstancePage')"
|
@click="stopInstance('InstancePage')"
|
||||||
@mouseover="checkProcess"
|
|
||||||
>
|
>
|
||||||
<StopCircleIcon />
|
<StopCircleIcon />
|
||||||
Stop
|
Stop
|
||||||
@@ -36,7 +25,6 @@
|
|||||||
color="primary"
|
color="primary"
|
||||||
class="instance-button"
|
class="instance-button"
|
||||||
@click="startInstance('InstancePage')"
|
@click="startInstance('InstancePage')"
|
||||||
@mouseover="checkProcess"
|
|
||||||
>
|
>
|
||||||
<PlayIcon />
|
<PlayIcon />
|
||||||
Play
|
Play
|
||||||
@@ -135,22 +123,20 @@ import {
|
|||||||
CheckCircleIcon,
|
CheckCircleIcon,
|
||||||
UpdatedIcon,
|
UpdatedIcon,
|
||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import { get, run } from '@/helpers/profile'
|
import { get, kill, run } from '@/helpers/profile'
|
||||||
import {
|
import { get_by_profile_path } from '@/helpers/process'
|
||||||
get_all_running_profile_paths,
|
import { process_listener, profile_listener } from '@/helpers/events'
|
||||||
get_uuids_by_profile_path,
|
|
||||||
kill_by_uuid,
|
|
||||||
} from '@/helpers/process'
|
|
||||||
import { offline_listener, process_listener, profile_listener } from '@/helpers/events'
|
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { ref, onUnmounted } from 'vue'
|
import { ref, onUnmounted } from 'vue'
|
||||||
import { handleError, useBreadcrumbs, useLoading } from '@/store/state'
|
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 ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||||
import { useFetch } from '@/helpers/fetch'
|
import { useFetch } from '@/helpers/fetch'
|
||||||
import { handleSevereError } from '@/store/error.js'
|
import { handleSevereError } from '@/store/error.js'
|
||||||
|
import { get_project, get_version_many } from '@/helpers/cache.js'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
@@ -161,72 +147,71 @@ const instance = ref(await get(route.params.id).catch(handleError))
|
|||||||
|
|
||||||
breadcrumbs.setName(
|
breadcrumbs.setName(
|
||||||
'Instance',
|
'Instance',
|
||||||
instance.value.metadata.name.length > 40
|
instance.value.name.length > 40
|
||||||
? instance.value.metadata.name.substring(0, 40) + '...'
|
? instance.value.name.substring(0, 40) + '...'
|
||||||
: instance.value.metadata.name,
|
: instance.value.name,
|
||||||
)
|
)
|
||||||
|
|
||||||
breadcrumbs.setContext({
|
breadcrumbs.setContext({
|
||||||
name: instance.value.metadata.name,
|
name: instance.value.name,
|
||||||
link: route.path,
|
link: route.path,
|
||||||
query: route.query,
|
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 loadingBar = useLoading()
|
||||||
|
|
||||||
const uuid = ref(null)
|
|
||||||
const playing = ref(false)
|
const playing = ref(false)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const options = ref(null)
|
const options = ref(null)
|
||||||
|
|
||||||
const startInstance = async (context) => {
|
const startInstance = async (context) => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
uuid.value = await run(route.params.id).catch(handleSevereError)
|
run(route.params.id).catch(handleSevereError)
|
||||||
loading.value = false
|
loading.value = false
|
||||||
playing.value = true
|
playing.value = true
|
||||||
|
|
||||||
mixpanel_track('InstanceStart', {
|
mixpanel_track('InstanceStart', {
|
||||||
loader: instance.value.metadata.loader,
|
loader: instance.value.loader,
|
||||||
game_version: instance.value.metadata.game_version,
|
game_version: instance.value.game_version,
|
||||||
source: context,
|
source: context,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkProcess = async () => {
|
const checkProcess = async () => {
|
||||||
const runningPaths = await get_all_running_profile_paths().catch(handleError)
|
const runningProcesses = await get_by_profile_path(route.params.id).catch(handleError)
|
||||||
if (runningPaths.includes(instance.value.path)) {
|
|
||||||
playing.value = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
playing.value = false
|
playing.value = runningProcesses.length > 0
|
||||||
uuid.value = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get information on associated modrinth versions, if any
|
// Get information on associated modrinth versions, if any
|
||||||
const modrinthVersions = ref([])
|
const modrinthVersions = ref([])
|
||||||
if (!(await isOffline()) && instance.value.metadata.linked_data?.project_id) {
|
if (!offline.value && instance.value.linked_data && instance.value.linked_data.project_id) {
|
||||||
modrinthVersions.value = await useFetch(
|
const project = await get_project(instance.value.linked_data.project_id).catch(handleError)
|
||||||
`https://api.modrinth.com/v2/project/${instance.value.metadata.linked_data.project_id}/version`,
|
|
||||||
'project',
|
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()
|
await checkProcess()
|
||||||
|
|
||||||
const stopInstance = async (context) => {
|
const stopInstance = async (context) => {
|
||||||
playing.value = false
|
playing.value = false
|
||||||
if (!uuid.value) {
|
await kill(route.params.id).catch(handleError)
|
||||||
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)
|
|
||||||
|
|
||||||
mixpanel_track('InstanceStop', {
|
mixpanel_track('InstanceStop', {
|
||||||
loader: instance.value.metadata.loader,
|
loader: instance.value.loader,
|
||||||
game_version: instance.value.metadata.game_version,
|
game_version: instance.value.game_version,
|
||||||
source: context,
|
source: context,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -271,7 +256,7 @@ const handleOptionsClick = async (args) => {
|
|||||||
break
|
break
|
||||||
case 'add_content':
|
case 'add_content':
|
||||||
await router.push({
|
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 },
|
query: { i: route.params.id },
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
@@ -302,17 +287,12 @@ const unlistenProfiles = await profile_listener(async (event) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const unlistenProcesses = await process_listener((e) => {
|
const unlistenProcesses = await process_listener((e) => {
|
||||||
if (e.event === 'finished' && uuid.value === e.uuid) playing.value = false
|
if (e.event === 'finished' && e.profile_path_id === route.params.id) playing.value = false
|
||||||
})
|
|
||||||
|
|
||||||
const unlistenOffline = await offline_listener((b) => {
|
|
||||||
offline.value = b
|
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
unlistenProcesses()
|
unlistenProcesses()
|
||||||
unlistenProfiles()
|
unlistenProfiles()
|
||||||
unlistenOffline()
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ import { computed, nextTick, onBeforeUnmount, onMounted, onUnmounted, ref, watch
|
|||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import isToday from 'dayjs/plugin/isToday'
|
import isToday from 'dayjs/plugin/isToday'
|
||||||
import isYesterday from 'dayjs/plugin/isYesterday'
|
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 { useRoute } from 'vue-router'
|
||||||
import { process_listener } from '@/helpers/events.js'
|
import { process_listener } from '@/helpers/events.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
@@ -209,9 +209,9 @@ const processedLogs = computed(() => {
|
|||||||
|
|
||||||
async function getLiveStdLog() {
|
async function getLiveStdLog() {
|
||||||
if (route.params.id) {
|
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
|
let returnValue
|
||||||
if (uuids.length === 0) {
|
if (processes.length === 0) {
|
||||||
returnValue = emptyText.join('\n')
|
returnValue = emptyText.join('\n')
|
||||||
} else {
|
} else {
|
||||||
const logCursor = await get_latest_log_cursor(
|
const logCursor = await get_latest_log_cursor(
|
||||||
|
|||||||
@@ -281,7 +281,7 @@
|
|||||||
<div class="markdown-body">
|
<div class="markdown-body">
|
||||||
<p>
|
<p>
|
||||||
Are you sure you want to remove
|
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 />
|
<br />
|
||||||
This action <strong>cannot</strong> be undone.
|
This action <strong>cannot</strong> be undone.
|
||||||
</p>
|
</p>
|
||||||
@@ -304,7 +304,7 @@
|
|||||||
>{{ Array.from(projects.values()).filter((x) => x.disabled).length }} disabled
|
>{{ Array.from(projects.values()).filter((x) => x.disabled).length }} disabled
|
||||||
project(s)</strong
|
project(s)</strong
|
||||||
>
|
>
|
||||||
from {{ instance.metadata.name }}?
|
from {{ instance.name }}?
|
||||||
<br />
|
<br />
|
||||||
This action <strong>cannot</strong> be undone.
|
This action <strong>cannot</strong> be undone.
|
||||||
</p>
|
</p>
|
||||||
@@ -326,7 +326,7 @@
|
|||||||
/>
|
/>
|
||||||
<ExportModal v-if="projects.length > 0" ref="exportModal" :instance="instance" />
|
<ExportModal v-if="projects.length > 0" ref="exportModal" :instance="instance" />
|
||||||
<ModpackVersionModal
|
<ModpackVersionModal
|
||||||
v-if="instance.metadata.linked_data"
|
v-if="instance.linked_data"
|
||||||
ref="modpackVersionModal"
|
ref="modpackVersionModal"
|
||||||
:instance="instance"
|
:instance="instance"
|
||||||
:versions="props.versions"
|
:versions="props.versions"
|
||||||
@@ -364,6 +364,7 @@ import { computed, onUnmounted, ref, watch } from 'vue'
|
|||||||
import {
|
import {
|
||||||
add_project_from_path,
|
add_project_from_path,
|
||||||
get,
|
get,
|
||||||
|
get_projects,
|
||||||
remove_project,
|
remove_project,
|
||||||
toggle_disable_project,
|
toggle_disable_project,
|
||||||
update_all,
|
update_all,
|
||||||
@@ -372,12 +373,18 @@ import {
|
|||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { listen } from '@tauri-apps/api/event'
|
import { listen } from '@tauri-apps/api/event'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
|
||||||
import { highlightModInProfile } from '@/helpers/utils.js'
|
import { highlightModInProfile } from '@/helpers/utils.js'
|
||||||
import { MenuIcon, ToggleIcon, TextInputIcon, AddProjectImage, PackageIcon } from '@/assets/icons'
|
import { MenuIcon, ToggleIcon, TextInputIcon, AddProjectImage, PackageIcon } from '@/assets/icons'
|
||||||
import ExportModal from '@/components/ui/ExportModal.vue'
|
import ExportModal from '@/components/ui/ExportModal.vue'
|
||||||
import ModpackVersionModal from '@/components/ui/ModpackVersionModal.vue'
|
import ModpackVersionModal from '@/components/ui/ModpackVersionModal.vue'
|
||||||
import AddContentButton from '@/components/ui/AddContentButton.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({
|
const props = defineProps({
|
||||||
instance: {
|
instance: {
|
||||||
@@ -404,65 +411,102 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const projects = ref([])
|
const unlistenProfiles = await profile_listener(async (event) => {
|
||||||
const selectionMap = ref(new Map())
|
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 showingOptions = ref(false)
|
||||||
const isPackLocked = computed(() => {
|
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(() => {
|
const canUpdatePack = computed(() => {
|
||||||
if (!props.instance.metadata.linked_data) return false
|
if (!props.instance.linked_data || !props.versions || !props.versions[0]) return false
|
||||||
return props.instance.metadata.linked_data.version_id !== props.instance.modrinth_update_version
|
return props.instance.linked_data.version_id !== props.versions[0].id
|
||||||
})
|
})
|
||||||
const exportModal = ref(null)
|
const exportModal = ref(null)
|
||||||
|
|
||||||
const initProjects = (initInstance) => {
|
const projects = ref([])
|
||||||
projects.value = []
|
const selectionMap = ref(new Map())
|
||||||
if (!initInstance || !initInstance.projects) return
|
|
||||||
for (const [path, project] of Object.entries(initInstance.projects)) {
|
const initProjects = async () => {
|
||||||
if (project.metadata.type === 'modrinth' && !props.offline) {
|
const newProjects = []
|
||||||
let owner = project.metadata.members.find((x) => x.role === 'Owner')
|
|
||||||
projects.value.push({
|
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,
|
path,
|
||||||
name: project.metadata.project.title,
|
name: project.title,
|
||||||
slug: project.metadata.project.slug,
|
slug: project.slug,
|
||||||
author: owner ? owner.user.username : null,
|
author: owner,
|
||||||
version: project.metadata.version.version_number,
|
version: version.version_number,
|
||||||
file_name: project.file_name,
|
file_name: file.file_name,
|
||||||
icon: project.metadata.project.icon_url,
|
icon: project.icon_url,
|
||||||
disabled: project.disabled,
|
disabled: file.file_name.endsWith('.disabled'),
|
||||||
updateVersion: project.metadata.update_version,
|
updateVersion: file.update_version_id,
|
||||||
outdated: !!project.metadata.update_version,
|
outdated: !!file.update_version_id,
|
||||||
project_type: project.metadata.project.project_type,
|
project_type: project.project_type,
|
||||||
id: project.metadata.project.id,
|
id: 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,
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
projects.value.push({
|
newProjects.push({
|
||||||
path,
|
path,
|
||||||
name: project.file_name,
|
name: file.file_name.replace('.disabled', ''),
|
||||||
author: '',
|
author: '',
|
||||||
version: null,
|
version: null,
|
||||||
file_name: project.file_name,
|
file_name: file.file_name,
|
||||||
icon: null,
|
icon: null,
|
||||||
disabled: project.disabled,
|
disabled: file.file_name.endsWith('.disabled'),
|
||||||
outdated: false,
|
outdated: false,
|
||||||
project_type: null,
|
project_type: file.project_type,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
projects.value = newProjects
|
||||||
|
|
||||||
const newSelectionMap = new Map()
|
const newSelectionMap = new Map()
|
||||||
for (const project of projects.value) {
|
for (const project of projects.value) {
|
||||||
newSelectionMap.set(
|
newSelectionMap.set(
|
||||||
@@ -475,22 +519,7 @@ const initProjects = (initInstance) => {
|
|||||||
}
|
}
|
||||||
selectionMap.value = newSelectionMap
|
selectionMap.value = newSelectionMap
|
||||||
}
|
}
|
||||||
|
await initProjects()
|
||||||
initProjects(props.instance)
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.instance.projects,
|
|
||||||
() => {
|
|
||||||
initProjects(props.instance)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.offline,
|
|
||||||
() => {
|
|
||||||
if (props.instance) initProjects(props.instance)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
const modpackVersionModal = ref(null)
|
const modpackVersionModal = ref(null)
|
||||||
const installing = computed(() => props.instance.install_stage !== 'installed')
|
const installing = computed(() => props.instance.install_stage !== 'installed')
|
||||||
@@ -633,8 +662,8 @@ const updateAll = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mixpanel_track('InstanceUpdateAll', {
|
mixpanel_track('InstanceUpdateAll', {
|
||||||
loader: props.instance.metadata.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.metadata.game_version,
|
game_version: props.instance.game_version,
|
||||||
count: setProjects.length,
|
count: setProjects.length,
|
||||||
selected: selected.value.length > 1,
|
selected: selected.value.length > 1,
|
||||||
})
|
})
|
||||||
@@ -659,8 +688,8 @@ const updateProject = async (mod) => {
|
|||||||
mod.updateVersion = null
|
mod.updateVersion = null
|
||||||
|
|
||||||
mixpanel_track('InstanceProjectUpdate', {
|
mixpanel_track('InstanceProjectUpdate', {
|
||||||
loader: props.instance.metadata.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.metadata.game_version,
|
game_version: props.instance.game_version,
|
||||||
id: mod.id,
|
id: mod.id,
|
||||||
name: mod.name,
|
name: mod.name,
|
||||||
project_type: mod.project_type,
|
project_type: mod.project_type,
|
||||||
@@ -686,8 +715,8 @@ const toggleDisableMod = async (mod) => {
|
|||||||
mod.path = newPath
|
mod.path = newPath
|
||||||
mod.disabled = !mod.disabled
|
mod.disabled = !mod.disabled
|
||||||
mixpanel_track('InstanceProjectDisable', {
|
mixpanel_track('InstanceProjectDisable', {
|
||||||
loader: props.instance.metadata.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.metadata.game_version,
|
game_version: props.instance.game_version,
|
||||||
id: mod.id,
|
id: mod.id,
|
||||||
name: mod.name,
|
name: mod.name,
|
||||||
project_type: mod.project_type,
|
project_type: mod.project_type,
|
||||||
@@ -707,8 +736,8 @@ const removeMod = async (mod) => {
|
|||||||
projects.value = projects.value.filter((x) => mod.path !== x.path)
|
projects.value = projects.value.filter((x) => mod.path !== x.path)
|
||||||
|
|
||||||
mixpanel_track('InstanceProjectRemove', {
|
mixpanel_track('InstanceProjectRemove', {
|
||||||
loader: props.instance.metadata.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.metadata.game_version,
|
game_version: props.instance.game_version,
|
||||||
id: mod.id,
|
id: mod.id,
|
||||||
name: mod.name,
|
name: mod.name,
|
||||||
project_type: mod.project_type,
|
project_type: mod.project_type,
|
||||||
@@ -820,7 +849,7 @@ watch(selectAll, () => {
|
|||||||
const unlisten = await listen('tauri://file-drop', async (event) => {
|
const unlisten = await listen('tauri://file-drop', async (event) => {
|
||||||
for (const file of event.payload) {
|
for (const file of event.payload) {
|
||||||
if (file.endsWith('.mrpack')) continue
|
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))
|
initProjects(await get(props.instance.path).catch(handleError))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<XIcon />
|
<XIcon />
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-danger" :disabled="action_disabled" @click="unlockProfile">
|
<button class="btn btn-danger" @click="unlockProfile">
|
||||||
<LockIcon />
|
<LockIcon />
|
||||||
Unlock
|
Unlock
|
||||||
</button>
|
</button>
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
<XIcon />
|
<XIcon />
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-danger" :disabled="action_disabled" @click="unpairProfile">
|
<button class="btn btn-danger" @click="unpairProfile">
|
||||||
<XIcon />
|
<XIcon />
|
||||||
Unpair
|
Unpair
|
||||||
</button>
|
</button>
|
||||||
@@ -117,21 +117,13 @@
|
|||||||
<span class="label__title">Icon</span>
|
<span class="label__title">Icon</span>
|
||||||
</label>
|
</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<Avatar
|
<Avatar :src="icon ? convertFileSrc(icon) : icon" size="md" class="project__icon" />
|
||||||
:src="!icon || (icon && icon.startsWith('http')) ? icon : convertFileSrc(icon)"
|
|
||||||
size="md"
|
|
||||||
class="project__icon"
|
|
||||||
/>
|
|
||||||
<div class="input-stack">
|
<div class="input-stack">
|
||||||
<button id="instance-icon" class="btn" @click="setIcon">
|
<button id="instance-icon" class="btn" @click="setIcon">
|
||||||
<UploadIcon />
|
<UploadIcon />
|
||||||
Select icon
|
Select icon
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button :disabled="!icon" class="btn" @click="resetIcon">
|
||||||
:disabled="!(!icon || (icon && icon.startsWith('http')) ? icon : convertFileSrc(icon))"
|
|
||||||
class="btn"
|
|
||||||
@click="resetIcon"
|
|
||||||
>
|
|
||||||
<TrashIcon />
|
<TrashIcon />
|
||||||
Remove icon
|
Remove icon
|
||||||
</button>
|
</button>
|
||||||
@@ -147,7 +139,7 @@
|
|||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
maxlength="80"
|
maxlength="80"
|
||||||
type="text"
|
type="text"
|
||||||
:disabled="instance.metadata.linked_data"
|
:disabled="instance.linked_data"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="adjacent-input">
|
<div class="adjacent-input">
|
||||||
@@ -358,7 +350,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<Card v-if="instance.metadata.linked_data">
|
<Card v-if="instance.linked_data">
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<h3>
|
<h3>
|
||||||
<span class="label__title size-card-header">Modpack</span>
|
<span class="label__title size-card-header">Modpack</span>
|
||||||
@@ -366,9 +358,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="adjacent-input">
|
<div class="adjacent-input">
|
||||||
<label for="general-modpack-info">
|
<label for="general-modpack-info">
|
||||||
<span class="label__description">
|
<span class="label__description"> <strong>Modpack: </strong> {{ instance.name }} </span>
|
||||||
<strong>Modpack: </strong> {{ instance.metadata.name }}
|
|
||||||
</span>
|
|
||||||
<span class="label__description">
|
<span class="label__description">
|
||||||
<strong>Version: </strong>
|
<strong>Version: </strong>
|
||||||
{{
|
{{
|
||||||
@@ -414,7 +404,7 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</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">
|
<label for="change-modpack-version">
|
||||||
<span class="label__title">Change modpack version</span>
|
<span class="label__title">Change modpack version</span>
|
||||||
<span class="label__description">
|
<span class="label__description">
|
||||||
@@ -502,7 +492,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<ModpackVersionModal
|
<ModpackVersionModal
|
||||||
v-if="instance.metadata.linked_data"
|
v-if="instance.linked_data"
|
||||||
ref="modpackVersionModal"
|
ref="modpackVersionModal"
|
||||||
:instance="instance"
|
:instance="instance"
|
||||||
:versions="props.versions"
|
:versions="props.versions"
|
||||||
@@ -553,12 +543,7 @@ import { get } from '@/helpers/settings.js'
|
|||||||
import JavaSelector from '@/components/ui/JavaSelector.vue'
|
import JavaSelector from '@/components/ui/JavaSelector.vue'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||||
import { open } from '@tauri-apps/api/dialog'
|
import { open } from '@tauri-apps/api/dialog'
|
||||||
import {
|
import { get_loader_versions } from '@/helpers/metadata.js'
|
||||||
get_fabric_versions,
|
|
||||||
get_forge_versions,
|
|
||||||
get_neoforge_versions,
|
|
||||||
get_quilt_versions,
|
|
||||||
} from '@/helpers/metadata.js'
|
|
||||||
import { get_game_versions, get_loaders } from '@/helpers/tags.js'
|
import { get_game_versions, get_loaders } from '@/helpers/tags.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
@@ -587,17 +572,17 @@ const props = defineProps({
|
|||||||
|
|
||||||
const themeStore = useTheming()
|
const themeStore = useTheming()
|
||||||
|
|
||||||
const title = ref(props.instance.metadata.name)
|
const title = ref(props.instance.name)
|
||||||
const icon = ref(props.instance.metadata.icon)
|
const icon = ref(props.instance.icon_path)
|
||||||
const groups = ref(props.instance.metadata.groups)
|
const groups = ref(props.instance.groups)
|
||||||
|
|
||||||
const modpackVersionModal = ref(null)
|
const modpackVersionModal = ref(null)
|
||||||
|
|
||||||
const instancesList = Object.values(await list(true))
|
const instancesList = await list()
|
||||||
const availableGroups = ref([
|
const availableGroups = ref([
|
||||||
...new Set(
|
...new Set(
|
||||||
instancesList.reduce((acc, obj) => {
|
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 modalConfirmUnlock = ref(null)
|
||||||
const modalConfirmUnpair = ref(null)
|
const modalConfirmUnpair = ref(null)
|
||||||
|
|
||||||
const javaSettings = props.instance.java ?? {}
|
const overrideJavaInstall = ref(!!props.instance.java_path)
|
||||||
|
|
||||||
const overrideJavaInstall = ref(!!javaSettings.override_version)
|
|
||||||
const optimalJava = readonly(await get_optimal_jre_key(props.instance.path).catch(handleError))
|
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 overrideJavaArgs = ref(!!props.instance.extra_launch_args)
|
||||||
const javaArgs = ref((javaSettings.extra_arguments ?? globalSettings.custom_java_args).join(' '))
|
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(
|
const envVars = ref(
|
||||||
(javaSettings.custom_env_args ?? globalSettings.custom_env_args)
|
(props.instance.custom_env_vars ?? globalSettings.custom_env_vars)
|
||||||
.map((x) => x.join('='))
|
.map((x) => x.join('='))
|
||||||
.join(' '),
|
.join(' '),
|
||||||
)
|
)
|
||||||
@@ -652,18 +637,22 @@ const overrideMemorySettings = ref(!!props.instance.memory)
|
|||||||
const memory = ref(props.instance.memory ?? globalSettings.memory)
|
const memory = ref(props.instance.memory ?? globalSettings.memory)
|
||||||
const maxMemory = Math.floor((await get_max_memory().catch(handleError)) / 1024)
|
const maxMemory = Math.floor((await get_max_memory().catch(handleError)) / 1024)
|
||||||
|
|
||||||
const overrideWindowSettings = ref(!!props.instance.resolution || !!props.instance.fullscreen)
|
const overrideWindowSettings = ref(
|
||||||
const resolution = ref(props.instance.resolution ?? globalSettings.game_resolution)
|
!!props.instance.game_resolution || !!props.instance.force_fullscreen,
|
||||||
const overrideHooks = ref(!!props.instance.hooks)
|
)
|
||||||
|
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 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 unlinkModpack = ref(false)
|
||||||
|
|
||||||
const inProgress = ref(false)
|
const inProgress = ref(false)
|
||||||
const installing = computed(() => props.instance.install_stage !== 'installed')
|
const installing = computed(() => props.instance.install_stage !== 'installed')
|
||||||
const installedVersion = computed(() => props.instance?.metadata?.linked_data?.version_id)
|
const installedVersion = computed(() => props.instance?.linked_data?.version_id)
|
||||||
const installedVersionData = computed(() => {
|
const installedVersionData = computed(() => {
|
||||||
if (!installedVersion.value) return null
|
if (!installedVersion.value) return null
|
||||||
return props.versions.find((version) => version.id === installedVersion.value)
|
return props.versions.find((version) => version.id === installedVersion.value)
|
||||||
@@ -706,34 +695,29 @@ const getLocalVersion = (path) => {
|
|||||||
|
|
||||||
const editProfileObject = computed(() => {
|
const editProfileObject = computed(() => {
|
||||||
const editProfile = {
|
const editProfile = {
|
||||||
metadata: {
|
name: title.value.trim().substring(0, 32) ?? 'Instance',
|
||||||
name: title.value.trim().substring(0, 32) ?? 'Instance',
|
groups: groups.value.map((x) => x.trim().substring(0, 32)).filter((x) => x.length > 0),
|
||||||
groups: groups.value.map((x) => x.trim().substring(0, 32)).filter((x) => x.length > 0),
|
loader_version: props.instance.loader_version,
|
||||||
loader_version: props.instance.metadata.loader_version,
|
linked_data: props.instance.linked_data,
|
||||||
linked_data: props.instance.metadata.linked_data,
|
|
||||||
},
|
|
||||||
java: {},
|
java: {},
|
||||||
|
hooks: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overrideJavaInstall.value) {
|
if (overrideJavaInstall.value) {
|
||||||
if (javaInstall.value.path !== '') {
|
if (javaInstall.value.path !== '') {
|
||||||
editProfile.java.override_version = javaInstall.value
|
editProfile.java_path = javaInstall.value.path.replace('java.exe', 'javaw.exe')
|
||||||
editProfile.java.override_version.path = editProfile.java.override_version.path.replace(
|
|
||||||
'java.exe',
|
|
||||||
'javaw.exe',
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overrideJavaArgs.value) {
|
if (overrideJavaArgs.value) {
|
||||||
if (javaArgs.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 (overrideEnvVars.value) {
|
||||||
if (envVars.value !== '') {
|
if (envVars.value !== '') {
|
||||||
editProfile.java.custom_env_args = envVars.value
|
editProfile.custom_env_vars = envVars.value
|
||||||
.trim()
|
.trim()
|
||||||
.split(/\s+/)
|
.split(/\s+/)
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
@@ -746,10 +730,10 @@ const editProfileObject = computed(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (overrideWindowSettings.value) {
|
if (overrideWindowSettings.value) {
|
||||||
editProfile.fullscreen = fullscreenSetting.value
|
editProfile.force_fullscreen = fullscreenSetting.value
|
||||||
|
|
||||||
if (!fullscreenSetting.value) {
|
if (!fullscreenSetting.value) {
|
||||||
editProfile.resolution = resolution.value
|
editProfile.game_resolution = resolution.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -758,10 +742,10 @@ const editProfileObject = computed(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (unlinkModpack.value) {
|
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
|
return editProfile
|
||||||
})
|
})
|
||||||
@@ -771,8 +755,8 @@ const repairing = ref(false)
|
|||||||
async function duplicateProfile() {
|
async function duplicateProfile() {
|
||||||
await duplicate(props.instance.path).catch(handleError)
|
await duplicate(props.instance.path).catch(handleError)
|
||||||
mixpanel_track('InstanceDuplicate', {
|
mixpanel_track('InstanceDuplicate', {
|
||||||
loader: props.instance.metadata.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.metadata.game_version,
|
game_version: props.instance.game_version,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -782,14 +766,14 @@ async function repairProfile(force) {
|
|||||||
repairing.value = false
|
repairing.value = false
|
||||||
|
|
||||||
mixpanel_track('InstanceRepair', {
|
mixpanel_track('InstanceRepair', {
|
||||||
loader: props.instance.metadata.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.metadata.game_version,
|
game_version: props.instance.game_version,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function unpairProfile() {
|
async function unpairProfile() {
|
||||||
const editProfile = props.instance
|
const editProfile = props.instance
|
||||||
editProfile.metadata.linked_data = null
|
editProfile.linked_data = null
|
||||||
await edit(props.instance.path, editProfile)
|
await edit(props.instance.path, editProfile)
|
||||||
installedVersion.value = null
|
installedVersion.value = null
|
||||||
installedVersionData.value = null
|
installedVersionData.value = null
|
||||||
@@ -798,13 +782,13 @@ async function unpairProfile() {
|
|||||||
|
|
||||||
async function unlockProfile() {
|
async function unlockProfile() {
|
||||||
const editProfile = props.instance
|
const editProfile = props.instance
|
||||||
editProfile.metadata.linked_data.locked = false
|
editProfile.linked_data.locked = false
|
||||||
await edit(props.instance.path, editProfile)
|
await edit(props.instance.path, editProfile)
|
||||||
modalConfirmUnlock.value.hide()
|
modalConfirmUnlock.value.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
const isPackLocked = computed(() => {
|
const isPackLocked = computed(() => {
|
||||||
return props.instance.metadata.linked_data && props.instance.metadata.linked_data.locked
|
return props.instance.linked_data && props.instance.linked_data.locked
|
||||||
})
|
})
|
||||||
|
|
||||||
async function repairModpack() {
|
async function repairModpack() {
|
||||||
@@ -813,8 +797,8 @@ async function repairModpack() {
|
|||||||
inProgress.value = false
|
inProgress.value = false
|
||||||
|
|
||||||
mixpanel_track('InstanceRepair', {
|
mixpanel_track('InstanceRepair', {
|
||||||
loader: props.instance.metadata.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.metadata.game_version,
|
game_version: props.instance.game_version,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -825,8 +809,8 @@ async function removeProfile() {
|
|||||||
removing.value = false
|
removing.value = false
|
||||||
|
|
||||||
mixpanel_track('InstanceRemove', {
|
mixpanel_track('InstanceRemove', {
|
||||||
loader: props.instance.metadata.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.metadata.game_version,
|
game_version: props.instance.game_version,
|
||||||
})
|
})
|
||||||
|
|
||||||
await router.push({ path: '/' })
|
await router.push({ path: '/' })
|
||||||
@@ -843,10 +827,10 @@ const [
|
|||||||
all_game_versions,
|
all_game_versions,
|
||||||
loaders,
|
loaders,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
get_fabric_versions().then(shallowRef).catch(handleError),
|
get_loader_versions('fabric').then(shallowRef).catch(handleError),
|
||||||
get_forge_versions().then(shallowRef).catch(handleError),
|
get_loader_versions('forge').then(shallowRef).catch(handleError),
|
||||||
get_quilt_versions().then(shallowRef).catch(handleError),
|
get_loader_versions('quilt').then(shallowRef).catch(handleError),
|
||||||
get_neoforge_versions().then(shallowRef).catch(handleError),
|
get_loader_versions('neo').then(shallowRef).catch(handleError),
|
||||||
get_game_versions().then(shallowRef).catch(handleError),
|
get_game_versions().then(shallowRef).catch(handleError),
|
||||||
get_loaders()
|
get_loaders()
|
||||||
.then((value) =>
|
.then((value) =>
|
||||||
@@ -859,8 +843,8 @@ const [
|
|||||||
])
|
])
|
||||||
loaders.value.unshift('vanilla')
|
loaders.value.unshift('vanilla')
|
||||||
|
|
||||||
const loader = ref(props.instance.metadata.loader)
|
const loader = ref(props.instance.loader)
|
||||||
const gameVersion = ref(props.instance.metadata.game_version)
|
const gameVersion = ref(props.instance.game_version)
|
||||||
const selectableGameVersions = computed(() => {
|
const selectableGameVersions = computed(() => {
|
||||||
return all_game_versions.value
|
return all_game_versions.value
|
||||||
.filter((item) => {
|
.filter((item) => {
|
||||||
@@ -896,9 +880,7 @@ const selectableLoaderVersions = computed(() => {
|
|||||||
return []
|
return []
|
||||||
})
|
})
|
||||||
const loaderVersionIndex = ref(
|
const loaderVersionIndex = ref(
|
||||||
selectableLoaderVersions.value.findIndex(
|
selectableLoaderVersions.value.findIndex((x) => x.id === props.instance.loader_version),
|
||||||
(x) => x.id === props.instance.metadata.loader_version?.id,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const isValid = computed(() => {
|
const isValid = computed(() => {
|
||||||
@@ -910,10 +892,9 @@ const isValid = computed(() => {
|
|||||||
|
|
||||||
const isChanged = computed(() => {
|
const isChanged = computed(() => {
|
||||||
return (
|
return (
|
||||||
loader.value != props.instance.metadata.loader ||
|
loader.value !== props.instance.loader ||
|
||||||
gameVersion.value != props.instance.metadata.game_version ||
|
gameVersion.value !== props.instance.game_version ||
|
||||||
JSON.stringify(selectableLoaderVersions.value[loaderVersionIndex.value]) !==
|
selectableLoaderVersions.value[loaderVersionIndex.value].id !== props.instance.loader_version
|
||||||
JSON.stringify(props.instance.metadata.loader_version)
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -924,11 +905,11 @@ async function saveGvLoaderEdits() {
|
|||||||
editing.value = true
|
editing.value = true
|
||||||
|
|
||||||
let editProfile = editProfileObject.value
|
let editProfile = editProfileObject.value
|
||||||
editProfile.metadata.loader = loader.value
|
editProfile.loader = loader.value
|
||||||
editProfile.metadata.game_version = gameVersion.value
|
editProfile.game_version = gameVersion.value
|
||||||
|
|
||||||
if (loader.value !== 'vanilla') {
|
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 edit(props.instance.path, editProfile).catch(handleError)
|
||||||
await repairProfile(false)
|
await repairProfile(false)
|
||||||
|
|||||||
@@ -4,26 +4,17 @@
|
|||||||
<Card v-if="instance" class="small-instance">
|
<Card v-if="instance" class="small-instance">
|
||||||
<router-link class="instance" :to="`/instance/${encodeURIComponent(instance.path)}`">
|
<router-link class="instance" :to="`/instance/${encodeURIComponent(instance.path)}`">
|
||||||
<Avatar
|
<Avatar
|
||||||
:src="
|
:src="instance.icon_path ? convertFileSrc(instance.icon_path) : null"
|
||||||
!instance.metadata.icon ||
|
:alt="instance.name"
|
||||||
(instance.metadata.icon && instance.metadata.icon.startsWith('http'))
|
|
||||||
? instance.metadata.icon
|
|
||||||
: convertFileSrc(instance.metadata?.icon)
|
|
||||||
"
|
|
||||||
:alt="instance.metadata.name"
|
|
||||||
size="sm"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
<div class="small-instance_info">
|
<div class="small-instance_info">
|
||||||
<span class="title">{{
|
<span class="title">{{
|
||||||
instance.metadata.name.length > 20
|
instance.name.length > 20 ? instance.name.substring(0, 20) + '...' : instance.name
|
||||||
? instance.metadata.name.substring(0, 20) + '...'
|
|
||||||
: instance.metadata.name
|
|
||||||
}}</span>
|
}}</span>
|
||||||
<span>
|
<span>
|
||||||
{{
|
{{ instance.loader.charAt(0).toUpperCase() + instance.loader.slice(1) }}
|
||||||
instance.metadata.loader.charAt(0).toUpperCase() + instance.metadata.loader.slice(1)
|
{{ instance.game_version }}
|
||||||
}}
|
|
||||||
{{ instance.metadata.game_version }}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
@@ -209,7 +200,6 @@
|
|||||||
:project="data"
|
:project="data"
|
||||||
:versions="versions"
|
:versions="versions"
|
||||||
:members="members"
|
:members="members"
|
||||||
:dependencies="dependencies"
|
|
||||||
:instance="instance"
|
:instance="instance"
|
||||||
:install="install"
|
:install="install"
|
||||||
:installed="installed"
|
:installed="installed"
|
||||||
@@ -218,9 +208,6 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<InstallConfirmModal ref="confirmModal" />
|
|
||||||
<ModInstallModal ref="modInstallModal" />
|
|
||||||
<IncompatibilityWarningModal ref="incompatibilityWarning" />
|
|
||||||
<ContextMenu ref="options" @option-clicked="handleOptionsClick">
|
<ContextMenu ref="options" @option-clicked="handleOptionsClick">
|
||||||
<template #install> <DownloadIcon /> Install </template>
|
<template #install> <DownloadIcon /> Install </template>
|
||||||
<template #open_link> <GlobeIcon /> Open in Modrinth <ExternalIcon /> </template>
|
<template #open_link> <GlobeIcon /> Open in Modrinth <ExternalIcon /> </template>
|
||||||
@@ -263,79 +250,63 @@ import {
|
|||||||
OpenCollectiveIcon,
|
OpenCollectiveIcon,
|
||||||
} from '@/assets/external'
|
} from '@/assets/external'
|
||||||
import { get_categories } from '@/helpers/tags'
|
import { get_categories } from '@/helpers/tags'
|
||||||
import { install as packInstall } from '@/helpers/pack'
|
import { get as getInstance, get_projects as getInstanceProjects } from '@/helpers/profile'
|
||||||
import {
|
|
||||||
list,
|
|
||||||
add_project_from_version as installMod,
|
|
||||||
check_installed,
|
|
||||||
get as getInstance,
|
|
||||||
remove_project,
|
|
||||||
} from '@/helpers/profile'
|
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { ref, shallowRef, watch } from 'vue'
|
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 { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||||
import IncompatibilityWarningModal from '@/components/ui/IncompatibilityWarningModal.vue'
|
|
||||||
import { useFetch } from '@/helpers/fetch.js'
|
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
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 route = useRoute()
|
||||||
const breadcrumbs = useBreadcrumbs()
|
const breadcrumbs = useBreadcrumbs()
|
||||||
|
|
||||||
const confirmModal = ref(null)
|
|
||||||
const modInstallModal = ref(null)
|
|
||||||
const incompatibilityWarning = ref(null)
|
|
||||||
|
|
||||||
const options = ref(null)
|
const options = ref(null)
|
||||||
const installing = ref(false)
|
const installing = ref(false)
|
||||||
const data = shallowRef(null)
|
const data = shallowRef(null)
|
||||||
const versions = shallowRef([])
|
const versions = shallowRef([])
|
||||||
const members = shallowRef([])
|
const members = shallowRef([])
|
||||||
const dependencies = shallowRef([])
|
|
||||||
const categories = shallowRef([])
|
const categories = shallowRef([])
|
||||||
const instance = ref(null)
|
const instance = ref(null)
|
||||||
|
const instanceProjects = ref(null)
|
||||||
|
|
||||||
const installed = ref(false)
|
const installed = ref(false)
|
||||||
const installedVersion = ref(null)
|
const installedVersion = ref(null)
|
||||||
|
|
||||||
const offline = ref(await isOffline())
|
|
||||||
|
|
||||||
async function fetchProjectData() {
|
async function fetchProjectData() {
|
||||||
;[
|
const project = await get_project(route.params.id).catch(handleError)
|
||||||
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(),
|
|
||||||
])
|
|
||||||
|
|
||||||
installed.value =
|
data.value = project
|
||||||
instance.value?.path &&
|
;[versions.value, members.value, categories.value, instance.value, instanceProjects.value] =
|
||||||
(await check_installed(instance.value.path, data.value.id).catch(handleError))
|
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)
|
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(
|
watch(
|
||||||
() => route.params.id,
|
() => route.params.id,
|
||||||
@@ -346,162 +317,22 @@ watch(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
dayjs.extend(relativeTime)
|
|
||||||
|
|
||||||
const markInstalled = () => {
|
|
||||||
installed.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
async function install(version) {
|
async function install(version) {
|
||||||
installing.value = true
|
installing.value = true
|
||||||
let queuedVersionData
|
await installVersion(
|
||||||
if (instance.value) {
|
data.value.id,
|
||||||
instance.value = await getInstance(instance.value.path, false).catch(handleError)
|
version,
|
||||||
}
|
instance.value ? instance.value.path : null,
|
||||||
|
'ProjectPage',
|
||||||
if (installed.value) {
|
(version) => {
|
||||||
const old_project = Object.entries(instance.value.projects)
|
|
||||||
.map(([key, value]) => ({
|
|
||||||
key,
|
|
||||||
value,
|
|
||||||
}))
|
|
||||||
.find((p) => p.value.metadata?.version?.project_id === data.value.id)
|
|
||||||
if (!old_project) {
|
|
||||||
// Switching too fast, old project is not recognized as a Modrinth project yet
|
|
||||||
installing.value = false
|
installing.value = false
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await remove_project(instance.value.path, old_project.key)
|
if (instance.value && version) {
|
||||||
}
|
installed.value = true
|
||||||
|
installedVersion.value = version
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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) => {
|
const handleRightClick = (e) => {
|
||||||
|
|||||||
@@ -190,6 +190,7 @@ import { ref, watch, computed } from 'vue'
|
|||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||||
import { SwapIcon } from '@/assets/icons'
|
import { SwapIcon } from '@/assets/icons'
|
||||||
|
import { get_project_many, get_version_many } from '@/helpers/cache.js'
|
||||||
|
|
||||||
const breadcrumbs = useBreadcrumbs()
|
const breadcrumbs = useBreadcrumbs()
|
||||||
|
|
||||||
@@ -204,10 +205,6 @@ const props = defineProps({
|
|||||||
type: Array,
|
type: Array,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
dependencies: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
members: {
|
members: {
|
||||||
type: Array,
|
type: Array,
|
||||||
required: true,
|
required: true,
|
||||||
@@ -238,20 +235,45 @@ watch(
|
|||||||
async () => {
|
async () => {
|
||||||
if (route.params.version) {
|
if (route.params.version) {
|
||||||
version.value = props.versions.find((version) => version.id === route.params.version)
|
version.value = props.versions.find((version) => version.id === route.params.version)
|
||||||
|
await refreshDisplayDependencies()
|
||||||
breadcrumbs.setName('Version', version.value.name)
|
breadcrumbs.setName('Version', version.value.name)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
const author = computed(() =>
|
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(() =>
|
const displayDependencies = ref({})
|
||||||
version.value.dependencies.map((dependency) => {
|
|
||||||
const version = props.dependencies.versions.find((obj) => obj.id === dependency.version_id)
|
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) {
|
if (version) {
|
||||||
const project = props.dependencies.projects.find(
|
const project = dependencies.projects.find(
|
||||||
(obj) => obj.id === version.project_id || obj.id === dependency.project_id,
|
(obj) => obj.id === version.project_id || obj.id === dependency.project_id,
|
||||||
)
|
)
|
||||||
return {
|
return {
|
||||||
@@ -261,7 +283,7 @@ const displayDependencies = computed(() =>
|
|||||||
link: `/project/${project.slug}/version/${version.id}`,
|
link: `/project/${project.slug}/version/${version.id}`,
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
if (project) {
|
||||||
return {
|
return {
|
||||||
@@ -279,8 +301,9 @@ const displayDependencies = computed(() =>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
)
|
}
|
||||||
|
await refreshDisplayDependencies()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -187,8 +187,8 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const filterVersions = ref([])
|
const filterVersions = ref([])
|
||||||
const filterLoader = ref(props.instance ? [props.instance?.metadata?.loader] : [])
|
const filterLoader = ref(props.instance ? [props.instance?.loader] : [])
|
||||||
const filterGameVersions = ref(props.instance ? [props.instance?.metadata?.game_version] : [])
|
const filterGameVersions = ref(props.instance ? [props.instance?.game_version] : [])
|
||||||
|
|
||||||
const currentPage = ref(1)
|
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 { useBreadcrumbs } from './breadcrumbs'
|
||||||
import { useLoading } from './loading'
|
import { useLoading } from './loading'
|
||||||
import { useNotifications, handleError } from './notifications'
|
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"] }
|
tokio-stream = { version = "0.1", features = ["fs"] }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
daedalus = {version = "0.1.15", features = ["bincode"] }
|
|
||||||
uuid = { version = "1.1", features = ["serde", "v4"] }
|
uuid = { version = "1.1", features = ["serde", "v4"] }
|
||||||
|
|
||||||
tracing = "0.1.37"
|
tracing = "0.1.37"
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
windows_subsystem = "windows"
|
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::prelude::*;
|
||||||
|
|
||||||
use theseus::profile::create::profile_create;
|
use theseus::profile::create::profile_create;
|
||||||
@@ -39,21 +41,12 @@ async fn main() -> theseus::Result<()> {
|
|||||||
let _log_guard = theseus::start_logger();
|
let _log_guard = theseus::start_logger();
|
||||||
|
|
||||||
// Initialize state
|
// Initialize state
|
||||||
let st = State::get().await?;
|
State::init().await?;
|
||||||
//State::update();
|
|
||||||
|
|
||||||
if minecraft_auth::users().await?.is_empty() {
|
if minecraft_auth::users().await?.is_empty() {
|
||||||
println!("No users found, authenticating.");
|
println!("No users found, authenticating.");
|
||||||
authenticate_run().await?; // could take credentials from here direct, but also deposited in state users
|
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
|
// st.settings
|
||||||
// .write()
|
// .write()
|
||||||
@@ -63,56 +56,59 @@ async fn main() -> theseus::Result<()> {
|
|||||||
// Clear profiles
|
// Clear profiles
|
||||||
println!("Clearing profiles.");
|
println!("Clearing profiles.");
|
||||||
{
|
{
|
||||||
let h = profile::list(None).await?;
|
let h = profile::list().await?;
|
||||||
for (path, _) in h.into_iter() {
|
for profile in h.into_iter() {
|
||||||
profile::remove(&path).await?;
|
profile::remove(&profile.path).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Creating/adding profile.");
|
println!("Creating/adding profile.");
|
||||||
|
|
||||||
let name = "Example".to_string();
|
// let name = "Example".to_string();
|
||||||
let game_version = "1.19.2".to_string();
|
// let game_version = "1.21".to_string();
|
||||||
let modloader = ModLoader::Vanilla;
|
// let modloader = ModLoader::Fabric;
|
||||||
let loader_version = "stable".to_string();
|
// 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(
|
let profile_path = profile_create(
|
||||||
name.clone(),
|
profile.name,
|
||||||
game_version,
|
profile.game_version,
|
||||||
modloader,
|
profile.modloader,
|
||||||
Some(loader_version),
|
profile.loader_version,
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.await?;
|
.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");
|
println!("running");
|
||||||
// Run a profile, running minecraft and store the RwLock to the process
|
// Run a profile, running minecraft and store the RwLock to the process
|
||||||
let proc_lock = profile::run(&profile_path).await?;
|
let process = profile::run(&profile_path).await?;
|
||||||
let uuid = proc_lock.read().await.uuid;
|
|
||||||
let pid = proc_lock.read().await.current_child.read().await.id();
|
|
||||||
|
|
||||||
println!("Minecraft UUID: {}", uuid);
|
println!("Minecraft PID: {}", process.pid);
|
||||||
println!("Minecraft PID: {:?}", pid);
|
|
||||||
|
|
||||||
println!(
|
println!("All running process UUID {:?}", process::get_all().await?);
|
||||||
"All running process UUID {:?}",
|
|
||||||
process::get_all_running_uuids().await?
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"All running process paths {:?}",
|
|
||||||
process::get_all_running_profile_paths().await?
|
|
||||||
);
|
|
||||||
|
|
||||||
// hold the lock to the process until it ends
|
// hold the lock to the process until it ends
|
||||||
println!("Waiting for process to end...");
|
println!("Waiting for process to end...");
|
||||||
let mut proc = proc_lock.write().await;
|
process.wait_for().await?;
|
||||||
process::wait_for(&mut proc).await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "theseus_gui"
|
name = "theseus_gui"
|
||||||
version = "0.7.2"
|
version = "0.0.0"
|
||||||
description = "A Tauri App"
|
description = "A Tauri App"
|
||||||
authors = ["you"]
|
authors = ["you"]
|
||||||
license = ""
|
license = ""
|
||||||
@@ -28,7 +28,7 @@ tokio = { version = "1", features = ["full"] }
|
|||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
tokio-stream = { version = "0.1", features = ["fs"] }
|
tokio-stream = { version = "0.1", features = ["fs"] }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
daedalus = {version = "0.1.15", features = ["bincode"] }
|
daedalus = "0.2.2"
|
||||||
chrono = "0.4.26"
|
chrono = "0.4.26"
|
||||||
|
|
||||||
dirs = "5.0.1"
|
dirs = "5.0.1"
|
||||||
@@ -46,6 +46,9 @@ sentry-rust-minidump = "0.7.0"
|
|||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
once_cell = "1"
|
once_cell = "1"
|
||||||
|
|
||||||
|
dashmap = "6.0.1"
|
||||||
|
paste = "1.0.15"
|
||||||
|
|
||||||
[target.'cfg(not(target_os = "linux"))'.dependencies]
|
[target.'cfg(not(target_os = "linux"))'.dependencies]
|
||||||
window-shadows = "0.2.1"
|
window-shadows = "0.2.1"
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleURLName</key>
|
<key>CFBundleURLName</key>
|
||||||
<!-- Obviously needs to be replaced with your app's bundle identifier -->
|
<!-- Obviously needs to be replaced with your app's bundle identifier -->
|
||||||
<string>com.modrinth.theseus</string>
|
<string>ModrinthApp</string>
|
||||||
<key>CFBundleURLSchemes</key>
|
<key>CFBundleURLSchemes</key>
|
||||||
<array>
|
<array>
|
||||||
<!-- register the myapp:// and myscheme:// schemes -->
|
<!-- register the myapp:// and myscheme:// schemes -->
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
<string>Owner</string>
|
<string>Owner</string>
|
||||||
<key>LSItemContentTypes</key>
|
<key>LSItemContentTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>com.modrinth.theseus-type</string>
|
<string>ModrinthApp-type</string>
|
||||||
</array>
|
</array>
|
||||||
<key>NSDocumentClass</key>
|
<key>NSDocumentClass</key>
|
||||||
<string>NSDocument</string>
|
<string>NSDocument</string>
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
<key>UTTypeIcons</key>
|
<key>UTTypeIcons</key>
|
||||||
<dict/>
|
<dict/>
|
||||||
<key>UTTypeIdentifier</key>
|
<key>UTTypeIdentifier</key>
|
||||||
<string>com.modrinth.theseus-type</string>
|
<string>ModrinthApp-type</string>
|
||||||
<key>UTTypeTagSpecification</key>
|
<key>UTTypeTagSpecification</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>public.filename-extension</key>
|
<key>public.filename-extension</key>
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ pub fn init<R: tauri::Runtime>() -> TauriPlugin<R> {
|
|||||||
auth_set_default_user,
|
auth_set_default_user,
|
||||||
auth_remove_user,
|
auth_remove_user,
|
||||||
auth_users,
|
auth_users,
|
||||||
auth_get_user,
|
|
||||||
])
|
])
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
@@ -96,11 +95,3 @@ pub async fn auth_set_default_user(user: uuid::Uuid) -> Result<()> {
|
|||||||
pub async fn auth_users() -> Result<Vec<Credentials>> {
|
pub async fn auth_users() -> Result<Vec<Credentials>> {
|
||||||
Ok(minecraft_auth::users().await?)
|
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::ImportLauncherType;
|
||||||
|
|
||||||
use theseus::pack::import;
|
use theseus::pack::import;
|
||||||
use theseus::prelude::ProfilePathId;
|
|
||||||
|
|
||||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||||
tauri::plugin::Builder::new("import")
|
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")
|
/// eg: import_instance(ImportLauncherType::MultiMC, PathBuf::from("C:/MultiMC"), "Instance 1")
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn import_import_instance(
|
pub async fn import_import_instance(
|
||||||
profile_path: ProfilePathId,
|
profile_path: &str,
|
||||||
launcher_type: ImportLauncherType,
|
launcher_type: ImportLauncherType,
|
||||||
base_path: PathBuf,
|
base_path: PathBuf,
|
||||||
instance_folder: String,
|
instance_folder: String,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use crate::api::Result;
|
use crate::api::Result;
|
||||||
|
use dashmap::DashMap;
|
||||||
|
use std::path::PathBuf;
|
||||||
use tauri::plugin::TauriPlugin;
|
use tauri::plugin::TauriPlugin;
|
||||||
use theseus::prelude::JavaVersion;
|
use theseus::prelude::JavaVersion;
|
||||||
use theseus::prelude::*;
|
use theseus::prelude::*;
|
||||||
@@ -8,6 +8,8 @@ use theseus::prelude::*;
|
|||||||
pub fn init<R: tauri::Runtime>() -> TauriPlugin<R> {
|
pub fn init<R: tauri::Runtime>() -> TauriPlugin<R> {
|
||||||
tauri::plugin::Builder::new("jre")
|
tauri::plugin::Builder::new("jre")
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
|
get_java_versions,
|
||||||
|
set_java_version,
|
||||||
jre_find_filtered_jres,
|
jre_find_filtered_jres,
|
||||||
jre_get_jre,
|
jre_get_jre,
|
||||||
jre_test_jre,
|
jre_test_jre,
|
||||||
@@ -17,6 +19,17 @@ pub fn init<R: tauri::Runtime>() -> TauriPlugin<R> {
|
|||||||
.build()
|
.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
|
// Finds the installation of Java 8, if it exists
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn jre_find_filtered_jres(
|
pub async fn jre_find_filtered_jres(
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
use crate::api::Result;
|
use crate::api::Result;
|
||||||
use theseus::logs::LogType;
|
use theseus::logs::LogType;
|
||||||
use theseus::{
|
use theseus::logs::{self, CensoredString, LatestLogCursor, Logs};
|
||||||
logs::{self, CensoredString, LatestLogCursor, Logs},
|
|
||||||
prelude::ProfilePathId,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A log is a struct containing the filename string, stdout, and stderr, as follows:
|
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
|
/// Get all Logs for a profile, sorted by filename
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn logs_get_logs(
|
pub async fn logs_get_logs(
|
||||||
profile_path: ProfilePathId,
|
profile_path: &str,
|
||||||
clear_contents: Option<bool>,
|
clear_contents: Option<bool>,
|
||||||
) -> Result<Vec<Logs>> {
|
) -> Result<Vec<Logs>> {
|
||||||
let val = logs::get_logs(profile_path, clear_contents).await?;
|
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
|
/// Get a Log struct for a profile by profile id and filename string
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn logs_get_logs_by_filename(
|
pub async fn logs_get_logs_by_filename(
|
||||||
profile_path: ProfilePathId,
|
profile_path: &str,
|
||||||
log_type: LogType,
|
log_type: LogType,
|
||||||
filename: String,
|
filename: String,
|
||||||
) -> Result<Logs> {
|
) -> 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
|
/// Get the stdout for a profile by profile id and filename string
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn logs_get_output_by_filename(
|
pub async fn logs_get_output_by_filename(
|
||||||
profile_path: ProfilePathId,
|
profile_path: &str,
|
||||||
log_type: LogType,
|
log_type: LogType,
|
||||||
filename: String,
|
filename: String,
|
||||||
) -> Result<CensoredString> {
|
) -> Result<CensoredString> {
|
||||||
let profile_path = if let Some(p) =
|
Ok(logs::get_output_by_filename(profile_path, log_type, &filename).await?)
|
||||||
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?,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete all logs for a profile by profile id
|
/// Delete all logs for a profile by profile id
|
||||||
#[tauri::command]
|
#[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?)
|
Ok(logs::delete_logs(profile_path).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete a log for a profile by profile id and filename string
|
/// Delete a log for a profile by profile id and filename string
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn logs_delete_logs_by_filename(
|
pub async fn logs_delete_logs_by_filename(
|
||||||
profile_path: ProfilePathId,
|
profile_path: &str,
|
||||||
log_type: LogType,
|
log_type: LogType,
|
||||||
filename: String,
|
filename: String,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@@ -95,7 +78,7 @@ pub async fn logs_delete_logs_by_filename(
|
|||||||
/// Get live log from a cursor
|
/// Get live log from a cursor
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn logs_get_latest_log_cursor(
|
pub async fn logs_get_latest_log_cursor(
|
||||||
profile_path: ProfilePathId,
|
profile_path: &str,
|
||||||
cursor: u64, // 0 to start at beginning of file
|
cursor: u64, // 0 to start at beginning of file
|
||||||
) -> Result<LatestLogCursor> {
|
) -> Result<LatestLogCursor> {
|
||||||
Ok(logs::get_latest_log_cursor(profile_path, cursor).await?)
|
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")
|
tauri::plugin::Builder::new("metadata")
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
metadata_get_game_versions,
|
metadata_get_game_versions,
|
||||||
metadata_get_fabric_versions,
|
metadata_get_loader_versions,
|
||||||
metadata_get_forge_versions,
|
|
||||||
metadata_get_quilt_versions,
|
|
||||||
metadata_get_neoforge_versions,
|
|
||||||
])
|
])
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
@@ -22,24 +19,6 @@ pub async fn metadata_get_game_versions() -> Result<VersionManifest> {
|
|||||||
|
|
||||||
/// Gets the fabric versions from daedalus
|
/// Gets the fabric versions from daedalus
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn metadata_get_fabric_versions() -> Result<Manifest> {
|
pub async fn metadata_get_loader_versions(loader: &str) -> Result<Manifest> {
|
||||||
Ok(theseus::metadata::get_fabric_versions().await?)
|
Ok(theseus::metadata::get_loader_versions(loader).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?)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ pub mod settings;
|
|||||||
pub mod tags;
|
pub mod tags;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
|
pub mod cache;
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, TheseusSerializableError>;
|
pub type Result<T> = std::result::Result<T, TheseusSerializableError>;
|
||||||
|
|
||||||
// // Main returnable Theseus GUI error
|
// // Main returnable Theseus GUI error
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
use crate::api::Result;
|
use crate::api::Result;
|
||||||
|
use chrono::{Duration, Utc};
|
||||||
use tauri::plugin::TauriPlugin;
|
use tauri::plugin::TauriPlugin;
|
||||||
|
use tauri::{Manager, UserAttentionType};
|
||||||
use theseus::prelude::*;
|
use theseus::prelude::*;
|
||||||
|
|
||||||
pub fn init<R: tauri::Runtime>() -> TauriPlugin<R> {
|
pub fn init<R: tauri::Runtime>() -> TauriPlugin<R> {
|
||||||
tauri::plugin::Builder::new("mr_auth")
|
tauri::plugin::Builder::new("mr_auth")
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
authenticate_begin_flow,
|
|
||||||
authenticate_await_completion,
|
|
||||||
cancel_flow,
|
|
||||||
login_pass,
|
login_pass,
|
||||||
login_2fa,
|
login_2fa,
|
||||||
create_account,
|
create_account,
|
||||||
refresh,
|
|
||||||
logout,
|
logout,
|
||||||
get,
|
get,
|
||||||
])
|
])
|
||||||
@@ -19,19 +17,68 @@ pub fn init<R: tauri::Runtime>() -> TauriPlugin<R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn authenticate_begin_flow(provider: &str) -> Result<String> {
|
pub async fn modrinth_auth_login(
|
||||||
Ok(theseus::mr_auth::authenticate_begin_flow(provider).await?)
|
app: tauri::AppHandle,
|
||||||
}
|
provider: &str,
|
||||||
|
) -> Result<Option<ModrinthCredentialsResult>> {
|
||||||
|
let redirect_uri = mr_auth::authenticate_begin_flow(provider);
|
||||||
|
|
||||||
#[tauri::command]
|
let start = Utc::now();
|
||||||
pub async fn authenticate_await_completion() -> Result<ModrinthCredentialsResult>
|
|
||||||
{
|
|
||||||
Ok(theseus::mr_auth::authenticate_await_complete_flow().await?)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
if let Some(window) = app.get_window("modrinth-signin") {
|
||||||
pub async fn cancel_flow() -> Result<()> {
|
window.close()?;
|
||||||
Ok(theseus::mr_auth::cancel_flow().await?)
|
}
|
||||||
|
|
||||||
|
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]
|
#[tauri::command]
|
||||||
@@ -66,11 +113,6 @@ pub async fn create_account(
|
|||||||
.await?)
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn refresh() -> Result<()> {
|
|
||||||
Ok(theseus::mr_auth::refresh().await?)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn logout() -> Result<()> {
|
pub async fn logout() -> Result<()> {
|
||||||
Ok(theseus::mr_auth::logout().await?)
|
Ok(theseus::mr_auth::logout().await?)
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
|||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn pack_install(
|
pub async fn pack_install(
|
||||||
location: CreatePackLocation,
|
location: CreatePackLocation,
|
||||||
profile: ProfilePathId,
|
profile: String,
|
||||||
) -> Result<ProfilePathId> {
|
) -> Result<String> {
|
||||||
Ok(install_zipped_mrpack(location, profile).await?)
|
Ok(install_zipped_mrpack(location, profile).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,78 +1,33 @@
|
|||||||
use crate::api::Result;
|
use crate::api::Result;
|
||||||
use theseus::prelude::*;
|
use theseus::prelude::*;
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||||
tauri::plugin::Builder::new("process")
|
tauri::plugin::Builder::new("process")
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
process_has_finished_by_uuid,
|
process_get_all,
|
||||||
process_get_exit_status_by_uuid,
|
process_get_by_profile_path,
|
||||||
process_get_all_uuids,
|
process_kill,
|
||||||
process_get_all_running_uuids,
|
process_wait_for,
|
||||||
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,
|
|
||||||
])
|
])
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if a process has finished by process UUID
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn process_has_finished_by_uuid(uuid: Uuid) -> Result<bool> {
|
pub async fn process_get_all() -> Result<Vec<Process>> {
|
||||||
Ok(process::has_finished_by_uuid(uuid).await?)
|
Ok(process::get_all().await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets process exit status by process UUID
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn process_get_exit_status_by_uuid(
|
pub async fn process_get_by_profile_path(path: &str) -> Result<Vec<Process>> {
|
||||||
uuid: Uuid,
|
Ok(process::get_by_profile_path(path).await?)
|
||||||
) -> Result<Option<i32>> {
|
|
||||||
Ok(process::get_exit_status_by_uuid(uuid).await?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets all process UUIDs
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn process_get_all_uuids() -> Result<Vec<Uuid>> {
|
pub async fn process_kill(pid: i32) -> Result<()> {
|
||||||
Ok(process::get_all_uuids().await?)
|
Ok(process::kill(pid).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets all running process UUIDs
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn process_get_all_running_uuids() -> Result<Vec<Uuid>> {
|
pub async fn process_wait_for(pid: i32) -> Result<()> {
|
||||||
Ok(process::get_all_running_uuids().await?)
|
Ok(process::wait_for(pid).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?)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
use crate::api::Result;
|
use crate::api::Result;
|
||||||
use daedalus::modded::LoaderVersion;
|
use dashmap::DashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use theseus::{prelude::*, InnerProjectPathUnix};
|
use theseus::prelude::*;
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||||
tauri::plugin::Builder::new("profile")
|
tauri::plugin::Builder::new("profile")
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
profile_remove,
|
profile_remove,
|
||||||
profile_get,
|
profile_get,
|
||||||
|
profile_get_many,
|
||||||
|
profile_get_projects,
|
||||||
profile_get_optimal_jre_key,
|
profile_get_optimal_jre_key,
|
||||||
profile_get_full_path,
|
profile_get_full_path,
|
||||||
profile_get_mod_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_update_managed_modrinth_version,
|
||||||
profile_repair_managed_modrinth,
|
profile_repair_managed_modrinth,
|
||||||
profile_run,
|
profile_run,
|
||||||
profile_run_wait,
|
|
||||||
profile_run_credentials,
|
profile_run_credentials,
|
||||||
profile_run_wait_credentials,
|
profile_kill,
|
||||||
profile_edit,
|
profile_edit,
|
||||||
profile_edit_icon,
|
profile_edit_icon,
|
||||||
profile_export_mrpack,
|
profile_export_mrpack,
|
||||||
@@ -40,27 +40,39 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
|||||||
// Remove a profile
|
// Remove a profile
|
||||||
// invoke('plugin:profile|profile_add_path',path)
|
// invoke('plugin:profile|profile_add_path',path)
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_remove(path: ProfilePathId) -> Result<()> {
|
pub async fn profile_remove(path: &str) -> Result<()> {
|
||||||
profile::remove(&path).await?;
|
profile::remove(path).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a profile by path
|
// Get a profile by path
|
||||||
// invoke('plugin:profile|profile_add_path',path)
|
// invoke('plugin:profile|profile_add_path',path)
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_get(
|
pub async fn profile_get(path: &str) -> Result<Option<Profile>> {
|
||||||
path: ProfilePathId,
|
let res = profile::get(path).await?;
|
||||||
clear_projects: Option<bool>,
|
Ok(res)
|
||||||
) -> Result<Option<Profile>> {
|
}
|
||||||
let res = profile::get(&path, clear_projects).await?;
|
|
||||||
|
#[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)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a profile's full path
|
// Get a profile's full path
|
||||||
// invoke('plugin:profile|profile_get_full_path',path)
|
// invoke('plugin:profile|profile_get_full_path',path)
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_get_full_path(path: ProfilePathId) -> Result<PathBuf> {
|
pub async fn profile_get_full_path(path: &str) -> Result<PathBuf> {
|
||||||
let res = profile::get_full_path(&path).await?;
|
let res = profile::get_full_path(path).await?;
|
||||||
Ok(res)
|
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)
|
// invoke('plugin:profile|profile_get_mod_full_path',path)
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_get_mod_full_path(
|
pub async fn profile_get_mod_full_path(
|
||||||
path: ProfilePathId,
|
path: &str,
|
||||||
project_path: ProjectPathId,
|
project_path: &str,
|
||||||
) -> Result<PathBuf> {
|
) -> 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)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get optimal java version from profile
|
// Get optimal java version from profile
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_get_optimal_jre_key(
|
pub async fn profile_get_optimal_jre_key(
|
||||||
path: ProfilePathId,
|
path: &str,
|
||||||
) -> Result<Option<JavaVersion>> {
|
) -> Result<Option<JavaVersion>> {
|
||||||
let res = profile::get_optimal_jre_key(&path).await?;
|
let res = profile::get_optimal_jre_key(path).await?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a copy of the profile set
|
// Get a copy of the profile set
|
||||||
// invoke('plugin:profile|profile_list')
|
// invoke('plugin:profile|profile_list')
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_list(
|
pub async fn profile_list() -> Result<Vec<Profile>> {
|
||||||
clear_projects: Option<bool>,
|
let res = profile::list().await?;
|
||||||
) -> Result<HashMap<ProfilePathId, Profile>> {
|
|
||||||
let res = profile::list(clear_projects).await?;
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_check_installed(
|
pub async fn profile_check_installed(
|
||||||
path: ProfilePathId,
|
path: &str,
|
||||||
project_id: String,
|
project_id: &str,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
let profile = profile_get(path, None).await?;
|
let check_project_id = project_id;
|
||||||
if let Some(profile) = profile {
|
|
||||||
Ok(profile.projects.into_iter().any(|(_, project)| {
|
if let Ok(projects) = profile::get_projects(path).await {
|
||||||
if let ProjectMetadata::Modrinth { project, .. } = &project.metadata
|
Ok(projects.into_iter().any(|(_, project)| {
|
||||||
{
|
if let Some(metadata) = &project.metadata {
|
||||||
project.id == project_id
|
check_project_id == metadata.project_id
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -117,49 +127,47 @@ pub async fn profile_check_installed(
|
|||||||
/// Installs/Repairs a profile
|
/// Installs/Repairs a profile
|
||||||
/// invoke('plugin:profile|profile_install')
|
/// invoke('plugin:profile|profile_install')
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_install(path: ProfilePathId, force: bool) -> Result<()> {
|
pub async fn profile_install(path: &str, force: bool) -> Result<()> {
|
||||||
profile::install(&path, force).await?;
|
profile::install(path, force).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates all of the profile's projects
|
/// Updates all of the profile's projects
|
||||||
/// invoke('plugin:profile|profile_update_all')
|
/// invoke('plugin:profile|profile_update_all')
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_update_all(
|
pub async fn profile_update_all(path: &str) -> Result<HashMap<String, String>> {
|
||||||
path: ProfilePathId,
|
Ok(profile::update_all_projects(path).await?)
|
||||||
) -> Result<HashMap<ProjectPathId, ProjectPathId>> {
|
|
||||||
Ok(profile::update_all_projects(&path).await?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates a specified project
|
/// Updates a specified project
|
||||||
/// invoke('plugin:profile|profile_update_project')
|
/// invoke('plugin:profile|profile_update_project')
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_update_project(
|
pub async fn profile_update_project(
|
||||||
path: ProfilePathId,
|
path: &str,
|
||||||
project_path: ProjectPathId,
|
project_path: &str,
|
||||||
) -> Result<ProjectPathId> {
|
) -> Result<String> {
|
||||||
Ok(profile::update_project(&path, &project_path, None).await?)
|
Ok(profile::update_project(path, project_path, None).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a project to a profile from a version ID
|
// Adds a project to a profile from a version ID
|
||||||
// invoke('plugin:profile|profile_add_project_from_version')
|
// invoke('plugin:profile|profile_add_project_from_version')
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_add_project_from_version(
|
pub async fn profile_add_project_from_version(
|
||||||
path: ProfilePathId,
|
path: &str,
|
||||||
version_id: String,
|
version_id: &str,
|
||||||
) -> Result<ProjectPathId> {
|
) -> Result<String> {
|
||||||
Ok(profile::add_project_from_version(&path, version_id).await?)
|
Ok(profile::add_project_from_version(path, version_id).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a project to a profile from a path
|
// Adds a project to a profile from a path
|
||||||
// invoke('plugin:profile|profile_add_project_from_path')
|
// invoke('plugin:profile|profile_add_project_from_path')
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_add_project_from_path(
|
pub async fn profile_add_project_from_path(
|
||||||
path: ProfilePathId,
|
path: &str,
|
||||||
project_path: &Path,
|
project_path: &Path,
|
||||||
project_type: Option<String>,
|
project_type: Option<ProjectType>,
|
||||||
) -> Result<ProjectPathId> {
|
) -> Result<String> {
|
||||||
let res = profile::add_project_from_path(&path, project_path, project_type)
|
let res = profile::add_project_from_path(path, project_path, project_type)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
@@ -168,27 +176,27 @@ pub async fn profile_add_project_from_path(
|
|||||||
// invoke('plugin:profile|profile_toggle_disable_project')
|
// invoke('plugin:profile|profile_toggle_disable_project')
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_toggle_disable_project(
|
pub async fn profile_toggle_disable_project(
|
||||||
path: ProfilePathId,
|
path: &str,
|
||||||
project_path: ProjectPathId,
|
project_path: &str,
|
||||||
) -> Result<ProjectPathId> {
|
) -> Result<String> {
|
||||||
Ok(profile::toggle_disable_project(&path, &project_path).await?)
|
Ok(profile::toggle_disable_project(path, project_path).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes a project from a profile
|
// Removes a project from a profile
|
||||||
// invoke('plugin:profile|profile_remove_project')
|
// invoke('plugin:profile|profile_remove_project')
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_remove_project(
|
pub async fn profile_remove_project(
|
||||||
path: ProfilePathId,
|
path: &str,
|
||||||
project_path: ProjectPathId,
|
project_path: &str,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
profile::remove_project(&path, &project_path).await?;
|
profile::remove_project(path, project_path).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updates a managed Modrinth profile to a version of version_id
|
// Updates a managed Modrinth profile to a version of version_id
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_update_managed_modrinth_version(
|
pub async fn profile_update_managed_modrinth_version(
|
||||||
path: ProfilePathId,
|
path: String,
|
||||||
version_id: String,
|
version_id: String,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
Ok(
|
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
|
// Repairs a managed Modrinth profile by updating it to the current version
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_repair_managed_modrinth(
|
pub async fn profile_repair_managed_modrinth(path: &str) -> Result<()> {
|
||||||
path: ProfilePathId,
|
Ok(profile::update::repair_managed_modrinth(path).await?)
|
||||||
) -> Result<()> {
|
|
||||||
Ok(profile::update::repair_managed_modrinth(&path).await?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exports a profile to a .mrpack file (export_location should end in .mrpack)
|
// Exports a profile to a .mrpack file (export_location should end in .mrpack)
|
||||||
// invoke('profile_export_mrpack')
|
// invoke('profile_export_mrpack')
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_export_mrpack(
|
pub async fn profile_export_mrpack(
|
||||||
path: ProfilePathId,
|
path: &str,
|
||||||
export_location: PathBuf,
|
export_location: PathBuf,
|
||||||
included_overrides: Vec<String>,
|
included_overrides: Vec<String>,
|
||||||
version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
@@ -217,7 +223,7 @@ pub async fn profile_export_mrpack(
|
|||||||
name: Option<String>, // only used to cache
|
name: Option<String>, // only used to cache
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
profile::export_mrpack(
|
profile::export_mrpack(
|
||||||
&path,
|
path,
|
||||||
export_location,
|
export_location,
|
||||||
included_overrides,
|
included_overrides,
|
||||||
version_id,
|
version_id,
|
||||||
@@ -231,9 +237,9 @@ pub async fn profile_export_mrpack(
|
|||||||
/// See [`profile::get_pack_export_candidates`]
|
/// See [`profile::get_pack_export_candidates`]
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_get_pack_export_candidates(
|
pub async fn profile_get_pack_export_candidates(
|
||||||
profile_path: ProfilePathId,
|
profile_path: &str,
|
||||||
) -> Result<Vec<InnerProjectPathUnix>> {
|
) -> Result<Vec<String>> {
|
||||||
let candidates = profile::get_pack_export_candidates(&profile_path).await?;
|
let candidates = profile::get_pack_export_candidates(profile_path).await?;
|
||||||
Ok(candidates)
|
Ok(candidates)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,19 +248,10 @@ pub async fn profile_get_pack_export_candidates(
|
|||||||
// for the actual Child in the state.
|
// for the actual Child in the state.
|
||||||
// invoke('plugin:profile|profile_run', path)
|
// invoke('plugin:profile|profile_run', path)
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_run(path: ProfilePathId) -> Result<Uuid> {
|
pub async fn profile_run(path: &str) -> Result<Process> {
|
||||||
let minecraft_child = profile::run(&path).await?;
|
let process = profile::run(path).await?;
|
||||||
let uuid = minecraft_child.read().await.uuid;
|
|
||||||
Ok(uuid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run Minecraft using a profile using the default credentials, and wait for the result
|
Ok(process)
|
||||||
// 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?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run Minecraft using a profile using chosen credentials
|
// 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})')
|
// invoke('plugin:profile|profile_run_credentials', {path, credentials})')
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_run_credentials(
|
pub async fn profile_run_credentials(
|
||||||
path: ProfilePathId,
|
path: &str,
|
||||||
credentials: Credentials,
|
credentials: Credentials,
|
||||||
) -> Result<Uuid> {
|
) -> Result<Process> {
|
||||||
let minecraft_child = profile::run_credentials(&path, &credentials).await?;
|
let process = profile::run_credentials(path, &credentials).await?;
|
||||||
let uuid = minecraft_child.read().await.uuid;
|
|
||||||
|
|
||||||
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]
|
#[tauri::command]
|
||||||
pub async fn profile_run_wait_credentials(
|
pub async fn profile_kill(path: &str) -> Result<()> {
|
||||||
path: ProfilePathId,
|
profile::kill(path).await?;
|
||||||
credentials: Credentials,
|
|
||||||
) -> Result<()> {
|
Ok(())
|
||||||
let proc_lock = profile::run_credentials(&path, &credentials).await?;
|
|
||||||
let mut proc = proc_lock.write().await;
|
|
||||||
Ok(process::wait_for(&mut proc).await?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct EditProfile {
|
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 name: Option<String>,
|
||||||
|
|
||||||
pub game_version: Option<String>,
|
pub game_version: Option<String>,
|
||||||
pub loader: Option<ModLoader>,
|
pub loader: Option<ModLoader>,
|
||||||
pub loader_version: Option<LoaderVersion>,
|
pub loader_version: Option<String>,
|
||||||
pub linked_data: Option<LinkedData>,
|
|
||||||
pub groups: Option<Vec<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
|
// Edits a profile
|
||||||
// invoke('plugin:profile|profile_edit', {path, editProfile})
|
// invoke('plugin:profile|profile_edit', {path, editProfile})
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_edit(
|
pub async fn profile_edit(path: &str, edit_profile: EditProfile) -> Result<()> {
|
||||||
path: ProfilePathId,
|
profile::edit(path, |prof| {
|
||||||
edit_profile: EditProfile,
|
if let Some(name) = edit_profile.name.clone() {
|
||||||
) -> Result<()> {
|
prof.name = name;
|
||||||
profile::edit(&path, |prof| {
|
}
|
||||||
if let Some(metadata) = edit_profile.metadata.clone() {
|
if let Some(game_version) = edit_profile.game_version.clone() {
|
||||||
if let Some(name) = metadata.name {
|
prof.game_version = game_version;
|
||||||
prof.metadata.name = name;
|
}
|
||||||
}
|
if let Some(loader) = edit_profile.loader {
|
||||||
if let Some(game_version) = metadata.game_version {
|
prof.loader = loader;
|
||||||
prof.metadata.game_version = game_version;
|
}
|
||||||
}
|
prof.loader_version.clone_from(&edit_profile.loader_version);
|
||||||
if let Some(loader) = metadata.loader {
|
prof.linked_data.clone_from(&edit_profile.linked_data);
|
||||||
prof.metadata.loader = loader;
|
|
||||||
}
|
|
||||||
prof.metadata.loader_version = metadata.loader_version;
|
|
||||||
prof.metadata.linked_data = metadata.linked_data;
|
|
||||||
|
|
||||||
if let Some(groups) = metadata.groups {
|
if let Some(groups) = edit_profile.groups.clone() {
|
||||||
prof.metadata.groups = groups;
|
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.memory = edit_profile.memory;
|
||||||
prof.resolution = edit_profile.resolution;
|
prof.game_resolution = edit_profile.game_resolution;
|
||||||
prof.fullscreen = edit_profile.fullscreen;
|
prof.force_fullscreen = edit_profile.force_fullscreen;
|
||||||
prof.hooks.clone_from(&edit_profile.hooks);
|
|
||||||
|
|
||||||
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(()) }
|
async { Ok(()) }
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
State::sync().await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -350,9 +345,9 @@ pub async fn profile_edit(
|
|||||||
// invoke('plugin:profile|profile_edit_icon')
|
// invoke('plugin:profile|profile_edit_icon')
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn profile_edit_icon(
|
pub async fn profile_edit_icon(
|
||||||
path: ProfilePathId,
|
path: &str,
|
||||||
icon_path: Option<&Path>,
|
icon_path: Option<&Path>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
profile::edit_icon(&path, icon_path).await?;
|
profile::edit_icon(path, icon_path).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use crate::api::Result;
|
use crate::api::Result;
|
||||||
use std::path::PathBuf;
|
|
||||||
use theseus::prelude::*;
|
use theseus::prelude::*;
|
||||||
|
|
||||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
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
|
game_version: String, // the game version of the profile
|
||||||
modloader: ModLoader, // the modloader to use
|
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
|
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
|
icon: Option<String>, // the icon for the profile
|
||||||
no_watch: Option<bool>,
|
skip_install: Option<bool>,
|
||||||
) -> Result<ProfilePathId> {
|
) -> Result<String> {
|
||||||
let res = profile::create::profile_create(
|
let res = profile::create::profile_create(
|
||||||
name,
|
name,
|
||||||
game_version,
|
game_version,
|
||||||
@@ -29,9 +28,7 @@ pub async fn profile_create(
|
|||||||
loader_version,
|
loader_version,
|
||||||
icon,
|
icon,
|
||||||
None,
|
None,
|
||||||
None,
|
skip_install,
|
||||||
None,
|
|
||||||
no_watch,
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
@@ -40,7 +37,7 @@ pub async fn profile_create(
|
|||||||
// Creates a profile from a duplicate
|
// Creates a profile from a duplicate
|
||||||
// invoke('plugin:profile_create|profile_duplicate',profile)
|
// invoke('plugin:profile_create|profile_duplicate',profile)
|
||||||
#[tauri::command]
|
#[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?;
|
let res = profile::create::profile_create_from_duplicate(path).await?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,9 @@
|
|||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use crate::api::Result;
|
use crate::api::Result;
|
||||||
use theseus::prelude::*;
|
use theseus::prelude::*;
|
||||||
|
|
||||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||||
tauri::plugin::Builder::new("settings")
|
tauri::plugin::Builder::new("settings")
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![settings_get, settings_set])
|
||||||
settings_get,
|
|
||||||
settings_set,
|
|
||||||
settings_change_config_dir,
|
|
||||||
settings_is_dir_writeable
|
|
||||||
])
|
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,20 +22,3 @@ pub async fn settings_set(settings: Settings) -> Result<()> {
|
|||||||
settings::set(settings).await?;
|
settings::set(settings).await?;
|
||||||
Ok(())
|
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 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> {
|
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||||
tauri::plugin::Builder::new("tags")
|
tauri::plugin::Builder::new("tags")
|
||||||
@@ -9,7 +9,6 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
|||||||
tags_get_loaders,
|
tags_get_loaders,
|
||||||
tags_get_game_versions,
|
tags_get_game_versions,
|
||||||
tags_get_donation_platforms,
|
tags_get_donation_platforms,
|
||||||
tags_get_tag_bundle,
|
|
||||||
])
|
])
|
||||||
.build()
|
.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>> {
|
pub async fn tags_get_donation_platforms() -> Result<Vec<DonationPlatform>> {
|
||||||
Ok(theseus::tags::get_donation_platform_tags().await?)
|
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::{
|
use theseus::{
|
||||||
handler,
|
handler,
|
||||||
prelude::{CommandPayload, DirectoryInfo},
|
prelude::{CommandPayload, DirectoryInfo},
|
||||||
State,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::api::Result;
|
use crate::api::Result;
|
||||||
@@ -16,11 +15,7 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
|||||||
show_in_folder,
|
show_in_folder,
|
||||||
show_launcher_logs_folder,
|
show_launcher_logs_folder,
|
||||||
progress_bars_list,
|
progress_bars_list,
|
||||||
safety_check_safe_loading_bars,
|
get_opening_command
|
||||||
get_opening_command,
|
|
||||||
await_sync,
|
|
||||||
is_offline,
|
|
||||||
refresh_offline
|
|
||||||
])
|
])
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
@@ -54,12 +49,6 @@ pub async fn progress_bars_list(
|
|||||||
Ok(res)
|
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
|
// cfg only on mac os
|
||||||
// disables mouseover and fixes a random crash error only fixed by recent versions of macos
|
// disables mouseover and fixes a random crash error only fixed by recent versions of macos
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
@@ -82,7 +71,7 @@ pub async fn should_disable_mouseover() -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn show_in_folder(mut path: PathBuf) -> Result<()> {
|
pub fn show_in_folder(path: PathBuf) -> Result<()> {
|
||||||
{
|
{
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
{
|
{
|
||||||
@@ -101,6 +90,7 @@ pub fn show_in_folder(mut path: PathBuf) -> Result<()> {
|
|||||||
{
|
{
|
||||||
use std::fs::metadata;
|
use std::fs::metadata;
|
||||||
|
|
||||||
|
let mut path = path;
|
||||||
let path_string = path.to_string_lossy().to_string();
|
let path_string = path.to_string_lossy().to_string();
|
||||||
|
|
||||||
if metadata(&path)?.is_dir() {
|
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<()> {
|
pub async fn handle_command(command: String) -> Result<()> {
|
||||||
Ok(theseus::handler::parse_and_emit_command(&command).await?)
|
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]
|
#[tauri::command]
|
||||||
async fn initialize_state(app: tauri::AppHandle) -> api::Result<()> {
|
async fn initialize_state(app: tauri::AppHandle) -> api::Result<()> {
|
||||||
theseus::EventState::init(app).await?;
|
theseus::EventState::init(app).await?;
|
||||||
let s = State::get().await?;
|
State::init().await?;
|
||||||
State::update();
|
|
||||||
|
|
||||||
s.children.write().await.rescue_cache().await?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +48,7 @@ struct Payload {
|
|||||||
// if Tauri app is called with arguments, then those arguments will be treated as commands
|
// if Tauri app is called with arguments, then those arguments will be treated as commands
|
||||||
// ie: deep links or filepaths for .mrpacks
|
// ie: deep links or filepaths for .mrpacks
|
||||||
fn main() {
|
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
|
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
|
let builder = builder
|
||||||
.plugin(api::auth::init())
|
.plugin(api::auth::init())
|
||||||
.plugin(api::mr_auth::init())
|
.plugin(api::mr_auth::init())
|
||||||
@@ -142,11 +141,13 @@ fn main() {
|
|||||||
.plugin(api::settings::init())
|
.plugin(api::settings::init())
|
||||||
.plugin(api::tags::init())
|
.plugin(api::tags::init())
|
||||||
.plugin(api::utils::init())
|
.plugin(api::utils::init())
|
||||||
|
.plugin(api::cache::init())
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
initialize_state,
|
initialize_state,
|
||||||
is_dev,
|
is_dev,
|
||||||
toggle_decorations,
|
toggle_decorations,
|
||||||
api::auth::auth_login,
|
api::auth::auth_login,
|
||||||
|
api::mr_auth::modrinth_auth_login,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
builder
|
builder
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "Modrinth App",
|
"productName": "Modrinth App",
|
||||||
"version": "0.7.2"
|
"version": "0.8.0-1"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"allowlist": {
|
"allowlist": {
|
||||||
@@ -20,8 +20,8 @@
|
|||||||
"asset": true,
|
"asset": true,
|
||||||
"assetScope": [
|
"assetScope": [
|
||||||
"$APPDATA/caches/icons/*",
|
"$APPDATA/caches/icons/*",
|
||||||
"$APPCONFIG/caches/icons/*",
|
"$APPDATA/caches/icons/*",
|
||||||
"$CONFIG/caches/icons/*"
|
"$APPDATA/caches/icons/*"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"shell": {
|
"shell": {
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
},
|
},
|
||||||
"externalBin": [],
|
"externalBin": [],
|
||||||
"icon": ["icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"],
|
"icon": ["icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"],
|
||||||
"identifier": "com.modrinth.theseus",
|
"identifier": "ModrinthApp",
|
||||||
"longDescription": "",
|
"longDescription": "",
|
||||||
"macOS": {
|
"macOS": {
|
||||||
"entitlements": "App.entitlements",
|
"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]
|
[package]
|
||||||
name = "theseus"
|
name = "theseus"
|
||||||
version = "0.7.2"
|
version = "0.0.0"
|
||||||
authors = ["Jai A <jaiagr+gpg@pm.me>"]
|
authors = ["Jai A <jaiagr+gpg@pm.me>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
theseus_macros = { path = "../app-macros" }
|
|
||||||
|
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
@@ -23,10 +21,10 @@ async_zip = { version = "0.0.17", features = ["full"] }
|
|||||||
flate2 = "1.0.28"
|
flate2 = "1.0.28"
|
||||||
tempfile = "3.5.0"
|
tempfile = "3.5.0"
|
||||||
urlencoding = "2.1.3"
|
urlencoding = "2.1.3"
|
||||||
|
dashmap = { version = "6.0.1", features = ["serde"] }
|
||||||
|
|
||||||
chrono = { version = "0.4.19", features = ["serde"] }
|
chrono = { version = "0.4.19", features = ["serde"] }
|
||||||
daedalus = { version = "0.1.25" }
|
daedalus = { version = "0.2.2" }
|
||||||
dirs = "5.0.1"
|
dirs = "5.0.1"
|
||||||
|
|
||||||
regex = "1.5"
|
regex = "1.5"
|
||||||
@@ -59,13 +57,18 @@ dunce = "1.0.3"
|
|||||||
|
|
||||||
whoami = "1.4.0"
|
whoami = "1.4.0"
|
||||||
|
|
||||||
discord-rich-presence = "0.2.3"
|
discord-rich-presence = "0.2.4"
|
||||||
|
|
||||||
p256 = { version = "0.13.2", features = ["ecdsa"] }
|
p256 = { version = "0.13.2", features = ["ecdsa"] }
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
base64 = "0.22.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]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winreg = "0.52.0"
|
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