Initial bug fixes (#127)

* Initial bug fixes

* fix compile error on non-mac

* Fix even more bugs

* Fix more

* fix more

* fix build

* fix build

* address review comments
This commit is contained in:
Geometrically
2023-06-02 07:09:46 -07:00
committed by GitHub
parent 9ea548cfe3
commit ee61951698
57 changed files with 3823 additions and 2813 deletions

View File

@@ -1,6 +1,5 @@
<script setup>
import Instance from '@/components/ui/Instance.vue'
import { ref } from 'vue'
const props = defineProps({
instances: {
@@ -9,19 +8,11 @@ const props = defineProps({
return []
},
},
news: {
type: Array,
default() {
return []
},
},
label: {
type: String,
default: '',
},
canPaginate: Boolean,
})
const modsRow = ref(null)
</script>
<template>
<div class="row">
@@ -29,7 +20,7 @@ const modsRow = ref(null)
<p>{{ props.label }}</p>
<hr />
</div>
<section ref="modsRow" class="instances">
<section class="instances">
<Instance
v-for="instance in props.instances"
:key="instance.id"
@@ -56,6 +47,7 @@ const modsRow = ref(null)
gap: 1rem;
p {
margin: 0;
font-size: 1rem;
white-space: nowrap;
color: var(--color-contrast);
@@ -101,7 +93,6 @@ const modsRow = ref(null)
width: 100%;
gap: 1rem;
margin-right: auto;
margin-top: 0.8rem;
scroll-behavior: smooth;
overflow-y: auto;
}

View File

@@ -42,16 +42,10 @@ onMounted(() => {
handlePaginationDisplay()
})
onUnmounted(() => {
if (props.canPaginate) window.removeEventListener('resize', handlePaginationDisplay)
})
const handleLeftPage = () => {
modsRow.value.scrollLeft -= 170
}
const handleRightPage = () => {
modsRow.value.scrollLeft += 170
}
</script>
<template>
<div v-if="props.instances.length > 0" class="row">
@@ -59,8 +53,8 @@ const handleRightPage = () => {
<p>{{ props.label }}</p>
<hr aria-hidden="true" />
<div v-if="allowPagination" class="pagination">
<ChevronLeftIcon role="button" @click="handleLeftPage" />
<ChevronRightIcon role="button" @click="handleRightPage" />
<ChevronLeftIcon role="button" @click="modsRow.value.scrollLeft -= 170" />
<ChevronRightIcon role="button" @click="modsRow.value.scrollLeft += 170" />
</div>
</div>
<section ref="modsRow" class="instances">
@@ -95,6 +89,7 @@ const handleRightPage = () => {
gap: 1rem;
p {
margin: 0;
font-size: 1rem;
white-space: nowrap;
color: var(--color-contrast);
@@ -140,11 +135,14 @@ const handleRightPage = () => {
width: 100%;
gap: 1rem;
margin-right: auto;
margin-top: 0.8rem;
scroll-behavior: smooth;
overflow-x: scroll;
overflow-y: hidden;
:deep(.instance-card-item) {
margin-bottom: 0.1rem;
}
&::-webkit-scrollbar {
width: 0px;
background: transparent;

View File

@@ -10,7 +10,7 @@
<div class="text no-select">
{{ selectedAccount ? selectedAccount.username : 'Offline' }}
</div>
<p class="no-select">
<p class="accounts-text no-select">
<UsersIcon />
Accounts
</p>
@@ -24,13 +24,13 @@
<h4>{{ selectedAccount.username }}</h4>
<p>Selected</p>
</div>
<Button icon-only color="raised" @click="logout(selectedAccount.id)">
<Button v-tooltip="'Log out'" icon-only color="raised" @click="logout(selectedAccount.id)">
<XIcon />
</Button>
</div>
<div v-else class="logged-out account">
<h4>Not signed in</h4>
<Button icon-only color="primary" @click="login()">
<Button v-tooltip="'Log in'" icon-only color="primary" @click="login()">
<LogInIcon />
</Button>
</div>
@@ -40,7 +40,7 @@
<Avatar :src="account.profile_picture" class="icon" />
<p>{{ account.username }}</p>
</Button>
<Button icon-only @click="logout(account.id)">
<Button v-tooltip="'Log out'" icon-only @click="logout(account.id)">
<XIcon />
</Button>
</div>
@@ -79,7 +79,7 @@ const appendProfiles = (accounts) => {
return accounts.map((account) => {
return {
...account,
profile_picture: `https://crafthead.net/helm/${account.id.replace(/-/g, '')}/128`,
profile_picture: `https://mc-heads.net/avatar/${account.id}/128`,
}
})
}
@@ -187,6 +187,11 @@ onBeforeUnmount(() => {
align-items: center;
text-align: left;
padding: 0.5rem 1rem;
h4,
p {
margin: 0;
}
}
.account-card {
@@ -287,4 +292,11 @@ onBeforeUnmount(() => {
overflow: hidden;
text-overflow: ellipsis;
}
.accounts-text {
display: flex;
align-items: center;
gap: 0.25rem;
margin: 0;
}
</style>

View File

@@ -5,13 +5,15 @@ import { ref } from 'vue'
const version = ref('')
const title = ref('')
const projectId = ref('')
const icon = ref('')
const confirmModal = ref(null)
const installing = ref(false)
defineExpose({
show: (id, projectTitle, projectIcon) => {
show: (id, projectId, projectTitle, projectIcon) => {
version.value = id
projectId.value = projectId
title.value = projectTitle
icon.value = projectIcon
confirmModal.value.show()
@@ -20,7 +22,7 @@ defineExpose({
async function install() {
installing.value = true
await pack_install(version.value, title.value, icon.value ? icon.value : null)
await pack_install(projectId.value, version.value, title.value, icon.value ? icon.value : null)
confirmModal.value.hide()
}
</script>
@@ -28,10 +30,8 @@ async function install() {
<template>
<Modal ref="confirmModal" header="Are you sure?">
<div class="modal-body">
<p>
This project is already installed on your system. Are you sure you want to install it again?
</p>
<div class="button-group">
<p>You already have this modpack installed. Are you sure you want to install it again?</p>
<div class="input-group push-right">
<Button @click="() => $refs.confirmModal.hide()"><XIcon />Cancel</Button>
<Button color="primary" :disabled="installing" @click="install()"
><DownloadIcon /> {{ installing ? 'Installing' : 'Install' }}</Button
@@ -48,11 +48,4 @@ async function install() {
gap: 1rem;
padding: 1rem;
}
.button-group {
display: flex;
flex-direction: row;
gap: 0.5rem;
justify-content: flex-end;
}
</style>

View File

@@ -1,9 +1,8 @@
<script setup>
import { onUnmounted, ref, useSlots } from 'vue'
import { onUnmounted, ref, useSlots, watch } from 'vue'
import { useRouter } from 'vue-router'
import { Card, DownloadIcon, XIcon, Avatar, AnimatedLogo, PlayIcon } from 'omorphia'
import { convertFileSrc } from '@tauri-apps/api/tauri'
import InstallConfirmModal from '@/components/ui/InstallConfirmModal.vue'
import { install as pack_install } from '@/helpers/pack'
import { run, list } from '@/helpers/profile'
import {
@@ -14,6 +13,8 @@ import {
import { process_listener } from '@/helpers/events'
import { useFetch } from '@/helpers/fetch.js'
import { handleError } from '@/store/state.js'
import InstallConfirmModal from '@/components/ui/InstallConfirmModal.vue'
import InstanceInstallModal from '@/components/ui/InstanceInstallModal.vue'
const props = defineProps({
instance: {
@@ -29,10 +30,20 @@ const props = defineProps({
})
const confirmModal = ref(null)
const modInstallModal = ref(null)
const playing = ref(false)
const uuid = ref(null)
const modLoading = ref(false)
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 slots = useSlots()
const router = useRouter()
@@ -74,21 +85,26 @@ const install = async (e) => {
.map((value) => value.metadata)
.find((pack) => pack.linked_data?.project_id === props.instance.project_id)
) {
try {
modLoading.value = true
await pack_install(versions[0].id, props.instance.title, props.instance.icon_url).catch(
handleError
)
modLoading.value = false
} catch (err) {
console.error(err)
modLoading.value = false
}
} else confirmModal.value.show(versions[0].id, props.instance.title, props.instance.icon_url)
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
} 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)
}
modLoading.value = false
// TODO: Add condition for installing a mod
}
const play = async (e) => {
@@ -103,21 +119,14 @@ const stop = async (e) => {
e.stopPropagation()
playing.value = false
try {
// If we lost the uuid for some reason, such as a user navigating
// from-then-back to this page, we will get all uuids by the instance path.
// For-each uuid, kill the process.
if (!uuid.value) {
const uuids = await get_uuids_by_profile_path(props.instance.path).catch(handleError)
uuid.value = uuids[0]
uuids.forEach(async (u) => await kill_by_uuid(u).catch(handleError))
} else await kill_by_uuid(uuid.value).catch(handleError) // If we still have the uuid, just kill it
} catch (err) {
// Theseus currently throws:
// "Error launching Minecraft: Minecraft exited with non-zero code 1" error
// For now, we will catch and just warn
console.warn(err)
}
// If we lost the uuid for some reason, such as a user navigating
// from-then-back to this page, we will get all uuids by the instance path.
// For-each uuid, kill the process.
if (!uuid.value) {
const uuids = await get_uuids_by_profile_path(props.instance.path).catch(handleError)
uuid.value = uuids[0]
uuids.forEach(async (u) => await kill_by_uuid(u).catch(handleError))
} else await kill_by_uuid(uuid.value).catch(handleError) // If we still have the uuid, just kill it
uuid.value = null
}
@@ -206,9 +215,10 @@ onUnmounted(() => unlisten())
>
<XIcon />
</div>
<div v-else class="install cta buttonbase" @click="install"><DownloadIcon /></div>
<div v-else class="install cta button-base" @click="install"><DownloadIcon /></div>
</template>
<InstallConfirmModal ref="confirmModal" />
<InstanceInstallModal ref="modInstallModal" />
</div>
</template>
@@ -276,7 +286,7 @@ onUnmounted(() => unlisten())
&:hover {
.cta {
opacity: 1;
bottom: 4.5rem;
bottom: 5.5rem;
}
.instance-card-item {
@@ -312,11 +322,12 @@ onUnmounted(() => unlisten())
z-index: 1;
width: 3rem;
height: 3rem;
right: 1rem;
bottom: 3.5rem;
right: 1.25rem;
bottom: 5rem;
opacity: 0;
transition: 0.3s ease-in-out bottom, 0.1s ease-in-out opacity !important;
transition: 0.2s ease-in-out bottom, 0.1s ease-in-out opacity, 0.1s ease-in-out filter !important;
cursor: pointer;
box-shadow: var(--shadow-floating);
svg {
color: var(--color-accent-contrast);
@@ -324,11 +335,6 @@ onUnmounted(() => unlisten())
height: 1.5rem !important;
}
&:hover {
filter: none !important; /* overrides button-base class */
box-shadow: var(--shadow-floating);
}
&.install {
background: var(--color-brand);
display: flex;
@@ -400,6 +406,8 @@ onUnmounted(() => unlisten())
line-height: 125%;
margin: 0.25rem 0 0;
text-transform: capitalize;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}

View File

@@ -1,6 +1,6 @@
<template>
<Modal ref="modal" header="Create instance">
<div v-if="showContent" class="modal-body">
<div class="modal-body">
<div class="image-upload">
<Avatar :src="display_icon" size="md" :rounded="true" />
<div class="image-input">
@@ -16,7 +16,7 @@
</div>
<div class="input-row">
<p class="input-label">Name</p>
<input v-model="profile_name" class="text-input" type="text" />
<input v-model="profile_name" autocomplete="off" class="text-input" type="text" />
</div>
<div class="input-row">
<p class="input-label">Loader</p>
@@ -25,7 +25,16 @@
<div class="input-row">
<p class="input-label">Game version</p>
<div class="versions">
<DropdownSelect v-model="game_version" :options="game_versions" render-up />
<multiselect
v-model="game_version"
class="selector"
:options="game_versions"
:multiple="false"
:searchable="true"
placeholder="Select game version"
open-direction="top"
:show-labels="false"
/>
<Checkbox
v-if="showAdvanced"
v-model="showSnapshots"
@@ -41,17 +50,21 @@
<div v-if="showAdvanced && loader_version === 'other' && loader !== 'vanilla'">
<div v-if="game_version" class="input-row">
<p class="input-label">Select version</p>
<DropdownSelect
<multiselect
v-model="specified_loader_version"
class="selector"
:options="selectable_versions"
render-up
:searchable="true"
placeholder="Select loader version"
open-direction="top"
:show-labels="false"
/>
</div>
<div v-else class="input-row">
<p class="warning">Select a game version before you select a loader version</p>
</div>
</div>
<div class="button-group">
<div class="input-group push-right">
<Button @click="toggle_advanced">
<CodeIcon />
{{ showAdvanced ? 'Hide advanced' : 'Show advanced' }}
@@ -74,7 +87,6 @@ import {
Avatar,
Button,
Chips,
DropdownSelect,
Modal,
PlusIcon,
UploadIcon,
@@ -83,19 +95,24 @@ import {
Checkbox,
} from 'omorphia'
import { computed, ref, shallowRef } from 'vue'
import { get_game_versions, get_loaders } from '@/helpers/tags'
import { get_loaders } from '@/helpers/tags'
import { create } from '@/helpers/profile'
import { open } from '@tauri-apps/api/dialog'
import { tauri } from '@tauri-apps/api'
import { get_fabric_versions, get_forge_versions, get_quilt_versions } from '@/helpers/metadata'
import {
get_game_versions,
get_fabric_versions,
get_forge_versions,
get_quilt_versions,
} from '@/helpers/metadata'
import { handleError } from '@/store/notifications.js'
import Multiselect from 'vue-multiselect'
const profile_name = ref('')
const game_version = ref('')
const loader = ref('vanilla')
const loader_version = ref('stable')
const specified_loader_version = ref('')
const showContent = ref(false)
const icon = ref(null)
const display_icon = ref(null)
const showAdvanced = ref(false)
@@ -104,22 +121,17 @@ const showSnapshots = ref(false)
defineExpose({
show: () => {
showContent.value = false
modal.value.show()
game_version.value = ''
specified_loader_version.value = ''
profile_name.value = ''
creating.value = false
showAdvanced.value = false
showSnapshots.value = false
loader.value = ''
loader.value = 'vanilla'
loader_version.value = 'stable'
icon.value = null
display_icon.value = null
setTimeout(() => {
showContent.value = true
}, 100)
modal.value.show()
},
})
@@ -138,53 +150,50 @@ const [fabric_versions, forge_versions, quilt_versions, all_game_versions, loade
.then(ref)
.catch(handleError),
])
loaders.value.push('vanilla')
loaders.value.unshift('vanilla')
const game_versions = computed(() => {
return all_game_versions.value
return all_game_versions.value.versions
.filter((item) => {
let defaultVal = item.version_type === 'release' || showSnapshots.value
let defaultVal = item.type === 'release' || showSnapshots.value
if (loader.value === 'fabric') {
defaultVal &= fabric_versions.value.gameVersions.some((x) => item.version === x.id)
defaultVal &= fabric_versions.value.gameVersions.some((x) => item.id === x.id)
} else if (loader.value === 'forge') {
defaultVal &= forge_versions.value.gameVersions.some((x) => item.version === x.id)
defaultVal &= forge_versions.value.gameVersions.some((x) => item.id === x.id)
} else if (loader.value === 'quilt') {
defaultVal &= quilt_versions.value.gameVersions.some((x) => item.version === x.id)
defaultVal &= quilt_versions.value.gameVersions.some((x) => item.id === x.id)
}
return defaultVal
})
.map((item) => item.version)
.map((item) => item.id)
})
const modal = ref(null)
const check_valid = computed(() => {
return (
profile_name.value && game_version.value && game_versions.value.includes(game_version.value)
profile_name.value.trim() &&
game_version.value &&
game_versions.value.includes(game_version.value)
)
})
const create_instance = async () => {
try {
creating.value = true
const loader_version_value =
loader_version.value === 'other' ? specified_loader_version.value : loader_version.value
creating.value = true
const loader_version_value =
loader_version.value === 'other' ? specified_loader_version.value : loader_version.value
await create(
profile_name.value,
game_version.value,
loader.value,
loader.value === 'vanilla' ? null : loader_version_value ?? 'stable',
icon.value
).catch(handleError)
create(
profile_name.value,
game_version.value,
loader.value,
loader.value === 'vanilla' ? null : loader_version_value ?? 'stable',
icon.value
).catch(handleError)
modal.value.hide()
creating.value = false
} catch (e) {
console.error(e)
creating.value = false
}
modal.value.hide()
creating.value = false
}
const upload_icon = async () => {
@@ -246,12 +255,6 @@ const toggle_advanced = () => {
width: 20rem;
}
.button-group {
display: flex;
gap: 0.5rem;
justify-content: flex-end;
}
.image-upload {
display: flex;
gap: 1rem;
@@ -277,4 +280,8 @@ const toggle_advanced = () => {
:deep(button.checkbox) {
border: none;
}
.selector {
max-width: 20rem;
}
</style>

View File

@@ -143,9 +143,15 @@ const check_valid = computed(() => {
</script>
<template>
<Modal ref="installModal" header="Install mod to instance">
<Modal ref="installModal" header="Install project to instance">
<div class="modal-body">
<input v-model="searchFilter" type="text" class="search" placeholder="Search for a profile" />
<input
v-model="searchFilter"
autocomplete="off"
type="text"
class="search"
placeholder="Search for an instance"
/>
<div class="profiles" :class="{ 'hide-creation': !showCreation }">
<div v-for="profile in profiles" :key="profile.metadata.name" class="option">
<Button
@@ -181,7 +187,13 @@ const check_valid = computed(() => {
</div>
</div>
<div class="creation-settings">
<input v-model="name" type="text" placeholder="Name" class="creation-input" />
<input
v-model="name"
autocomplete="off"
type="text"
placeholder="Name"
class="creation-input"
/>
<Button :disabled="creatingInstance === true || !check_valid" @click="createInstance()">
<RightArrowIcon />
{{ creatingInstance ? 'Creating...' : 'Create' }}
@@ -189,7 +201,7 @@ const check_valid = computed(() => {
</div>
</div>
</Card>
<div class="footer">
<div class="input-group push-right">
<Button :color="showCreation ? '' : 'primary'" @click="toggleCreation()">
<PlusIcon />
{{ showCreation ? 'Hide New Instance' : 'Create new instance' }}
@@ -302,12 +314,4 @@ const check_valid = computed(() => {
.profile-image {
--size: 2rem !important;
}
.footer {
display: flex;
flex-direction: row;
justify-content: flex-end;
gap: 0.5rem;
margin-left: auto;
}
</style>

View File

@@ -11,7 +11,7 @@
<div class="table-cell table-text">
<span>{{ javaInstall.version }}</span>
</div>
<div class="table-cell table-text">
<div v-tooltip="javaInstall.path" class="table-cell table-text">
<span>{{ javaInstall.path }}</span>
</div>
<div class="table-cell table-text manage">
@@ -22,10 +22,10 @@
</div>
</div>
<div v-if="chosenInstallOptions.length === 0" class="table-row entire-row">
<div class="table-cell table-text">No JARS Found!</div>
<div class="table-cell table-text">No java installations found!</div>
</div>
</div>
<div class="button-group">
<div class="input-group push-right">
<Button @click="$refs.detectJavaModal.hide()">
<XIcon />
Cancel
@@ -96,13 +96,6 @@ function setJavaInstall(javaInstall) {
}
}
.button-group {
margin-top: 1rem;
display: flex;
gap: 0.5rem;
justify-content: flex-end;
}
.manage {
display: flex;
gap: 0.5rem;

View File

@@ -2,6 +2,7 @@
<JavaDetectionModal ref="detectJavaModal" @submit="(val) => emit('update:modelValue', val)" />
<div class="toggle-setting">
<input
autocomplete="off"
:disabled="props.disabled"
:value="props.modelValue ? props.modelValue.path : ''"
type="text"
@@ -28,30 +29,25 @@
<FolderSearchIcon />
Browse
</Button>
<Button :disabled="props.disabled" @click="testJava">
<Button v-if="testingJava" disabled> Testing... </Button>
<Button v-else-if="testingJavaSuccess === true">
<CheckIcon class="test-success" />
Success
</Button>
<Button v-else-if="testingJavaSuccess === false">
<XIcon class="test-fail" />
Failed
</Button>
<Button v-else :disabled="props.disabled" @click="testJava">
<PlayIcon />
Test
</Button>
<AnimatedLogo v-if="testingJava === true" class="testing-loader" />
<CheckIcon
v-else-if="testingJavaSuccess === true && testingJava === false"
class="test-success"
/>
<XIcon v-else-if="testingJavaSuccess === false && testingJava === false" class="test-fail" />
</span>
</div>
</template>
<script setup>
import {
Button,
SearchIcon,
PlayIcon,
CheckIcon,
XIcon,
AnimatedLogo,
FolderSearchIcon,
} from 'omorphia'
import { Button, SearchIcon, PlayIcon, CheckIcon, XIcon, FolderSearchIcon } from 'omorphia'
import { get_jre } from '@/helpers/jre.js'
import { ref } from 'vue'
import { open } from '@tauri-apps/api/dialog'
@@ -143,14 +139,3 @@ async function handleJavaFileInput() {
color: var(--color-red);
}
</style>
<style lang="scss">
.testing-loader {
height: 1rem !important;
width: 1rem !important;
svg {
height: inherit !important;
width: inherit !important;
}
}
</style>

View File

@@ -4,10 +4,10 @@
<span class="running-text">
{{ currentProcesses[0].metadata.name }}
</span>
<Button icon-only class="icon-button stop" @click="stop()">
<Button v-tooltip="'Stop instance'" icon-only class="icon-button stop" @click="stop()">
<StopCircleIcon />
</Button>
<Button icon-only class="icon-button" @click="goToTerminal()">
<Button v-tooltip="'View logs'" icon-only class="icon-button" @click="goToTerminal()">
<TerminalSquareIcon />
</Button>
<Button
@@ -22,7 +22,7 @@
</div>
<div v-else class="status">
<span class="circle stopped" />
<span class="running-text"> No running profiles </span>
<span class="running-text"> No running instances </span>
<Button
v-if="currentLoadingBars.length > 0"
ref="infoButton"

View File

@@ -140,7 +140,7 @@ async function install() {
queuedVersionData = versions.find(
(v) =>
v.game_versions.includes(props.instance.metadata.game_version) &&
v.loaders.includes(props.instance.metadata.loader)
(props.project.project_type !== 'mod' || v.loaders.includes(props.instance.metadata.loader))
)
}
@@ -152,11 +152,19 @@ async function install() {
.map((value) => value.metadata)
.find((pack) => pack.linked_data?.project_id === props.project.project_id)
) {
await packInstall(queuedVersionData.id, props.project.title, props.project.icon_url).catch(
handleError
)
await packInstall(
props.project.project_id,
queuedVersionData.id,
props.project.title,
props.project.icon_url
).catch(handleError)
} else {
props.confirmModal.show(queuedVersionData.id)
props.confirmModal.show(
props.project.project_id,
queuedVersionData.id,
props.project.title,
props.project.icon_url
)
}
} else {
if (props.instance) {

View File

@@ -35,6 +35,8 @@ defineProps({
svg {
width: 12rem;
height: 12rem;
fill: var(--color-brand);
color: var(--color-brand);
}
}
</style>