You've already forked AstralRinth
forked from didirus/AstralRinth
Bug fixes round 3 (#298)
* fixed bugs * title case * bugs; ioerror * reset breadcrumbs * more fixes * more fixes * scrolling bug * more fixes * more fixes * clippy * canonicalize fix * fixed requested changes * removed debouncer update
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import { RouterView, RouterLink, useRouter } from 'vue-router'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { RouterView, RouterLink, useRouter, useRoute } from 'vue-router'
|
||||
import {
|
||||
HomeIcon,
|
||||
SearchIcon,
|
||||
@@ -106,6 +106,8 @@ router.afterEach((to, from, failure) => {
|
||||
mixpanel.track('PageView', { path: to.path, fromPath: from.path, failed: failure })
|
||||
}
|
||||
})
|
||||
const route = useRoute()
|
||||
const isOnBrowse = computed(() => route.path.startsWith('/browse'))
|
||||
|
||||
const loading = useLoading()
|
||||
|
||||
@@ -179,6 +181,7 @@ const accounts = ref(null)
|
||||
'icon-only': themeStore.collapsedNavigation,
|
||||
'collapsed-button': themeStore.collapsedNavigation,
|
||||
'expanded-button': !themeStore.collapsedNavigation,
|
||||
'router-link-active': isOnBrowse,
|
||||
}"
|
||||
>
|
||||
<SearchIcon />
|
||||
@@ -340,7 +343,7 @@ const accounts = ref(null)
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: var(--color-raised-bg);
|
||||
box-shadow: var(--shadow-inset-sm), var(--shadow-floating);
|
||||
box-shadow: inset 0px -3px 0px black;
|
||||
text-align: center;
|
||||
padding: var(--gap-md);
|
||||
height: 3.25rem;
|
||||
|
||||
@@ -122,9 +122,9 @@
|
||||
</div>
|
||||
<Chips
|
||||
v-model="javaSelectionType"
|
||||
:items="['automatically install', 'use existing installation']"
|
||||
:items="['Automatically install', 'Use existing installation']"
|
||||
/>
|
||||
<div v-if="javaSelectionType === 'use existing installation'" class="settings-group">
|
||||
<div v-if="javaSelectionType === 'Use existing installation'" class="settings-group">
|
||||
<h3>Java location</h3>
|
||||
<JavaSelector v-model="settings.java_globals.JAVA_17" compact />
|
||||
</div>
|
||||
@@ -230,7 +230,7 @@ async function pageTurn() {
|
||||
}
|
||||
}
|
||||
|
||||
const javaSelectionType = ref('automatically install')
|
||||
const javaSelectionType = ref('Automatically install')
|
||||
|
||||
async function autoInstallJava() {
|
||||
const path = await auto_install_java(17).catch(handleError)
|
||||
@@ -239,6 +239,7 @@ async function autoInstallJava() {
|
||||
// weird vue bug, ignore
|
||||
settings.value.java_globals.JAVA_17 = version
|
||||
settings.value.java_globals.JAVA_17 = version
|
||||
set(settings.value)
|
||||
mixpanel.track('OnboardingAutoInstallJava')
|
||||
}
|
||||
|
||||
@@ -258,6 +259,10 @@ onBeforeUnmount(() => {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
:deep(.chips .btn) {
|
||||
text-transform: none !important;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div class="breadcrumbs">
|
||||
{{ breadcrumbData.resetToNames(breadcrumbs) }}
|
||||
<div v-for="breadcrumb in breadcrumbs" :key="breadcrumb.name" class="breadcrumbs__item">
|
||||
<router-link
|
||||
v-if="breadcrumb.link"
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
</div>
|
||||
<div v-else class="status">
|
||||
<span class="circle stopped" />
|
||||
<span class="running-text"> No running instances </span>
|
||||
<span class="running-text"> No instances running </span>
|
||||
</div>
|
||||
</div>
|
||||
<transition name="download">
|
||||
|
||||
@@ -14,6 +14,7 @@ defineProps({
|
||||
|
||||
<style scoped lang="scss">
|
||||
.page-loading {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
@@ -272,6 +272,11 @@ async function onSearchChangeToTop(newPageNumber) {
|
||||
searchWrapper.value.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
|
||||
async function clearSearch() {
|
||||
query.value = ''
|
||||
await onSearchChange(1)
|
||||
}
|
||||
|
||||
function getSearchUrl(offset, useObj) {
|
||||
const queryItems = []
|
||||
const obj = {}
|
||||
@@ -355,6 +360,33 @@ const sortedCategories = computed(() => {
|
||||
return values
|
||||
})
|
||||
|
||||
// Sorts alphabetically, but correctly identifies 8x, 128x, 256x, etc
|
||||
// identifier[0], then if it ties, identifier[1], etc
|
||||
async function sortByNameOrNumber(sortable, identifiers) {
|
||||
console.log(sortable)
|
||||
sortable.sort((a, b) => {
|
||||
for (let identifier of identifiers) {
|
||||
let aNum = parseFloat(a[identifier])
|
||||
let bNum = parseFloat(b[identifier])
|
||||
if (isNaN(aNum) && isNaN(bNum)) {
|
||||
// Both are strings, sort alphabetically
|
||||
let stringComp = a[identifier].localeCompare(b[identifier])
|
||||
if (stringComp != 0) return stringComp
|
||||
} else if (!isNaN(aNum) && !isNaN(bNum)) {
|
||||
// Both are numbers, sort numerically
|
||||
let numComp = aNum - bNum
|
||||
if (numComp != 0) return numComp
|
||||
} else {
|
||||
// One is a number and one is a string, numbers go first
|
||||
let numStringComp = isNaN(aNum) ? 1 : -1
|
||||
if (numStringComp != 0) return numStringComp
|
||||
}
|
||||
}
|
||||
return 0
|
||||
})
|
||||
return sortable
|
||||
}
|
||||
|
||||
async function clearFilters() {
|
||||
for (const facet of [...facets.value]) {
|
||||
await toggleFacet(facet, true)
|
||||
@@ -426,7 +458,10 @@ watch(
|
||||
)
|
||||
|
||||
const [categories, loaders, availableGameVersions] = await Promise.all([
|
||||
get_categories().catch(handleError).then(ref),
|
||||
get_categories()
|
||||
.catch(handleError)
|
||||
.then((s) => sortByNameOrNumber(s, ['header', 'name']))
|
||||
.then(ref),
|
||||
get_loaders().catch(handleError).then(ref),
|
||||
get_game_versions().catch(handleError).then(ref),
|
||||
refreshSearch(),
|
||||
@@ -473,7 +508,7 @@ const showLoaders = computed(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="search-container">
|
||||
<div ref="searchWrapper" class="search-container">
|
||||
<aside class="filter-panel">
|
||||
<Card v-if="instanceContext" class="small-instance">
|
||||
<router-link :to="`/instance/${encodeURIComponent(instanceContext.path)}`" class="instance">
|
||||
@@ -525,7 +560,7 @@ const showLoaders = computed(
|
||||
"
|
||||
@click="clearFilters"
|
||||
>
|
||||
<ClearIcon /> Clear Filters
|
||||
<ClearIcon /> Clear filters
|
||||
</Button>
|
||||
<div v-if="showLoaders" class="loaders">
|
||||
<h2>Loaders</h2>
|
||||
@@ -618,7 +653,7 @@ const showLoaders = computed(
|
||||
</div>
|
||||
</Card>
|
||||
</aside>
|
||||
<div ref="searchWrapper" class="search">
|
||||
<div class="search">
|
||||
<Promotion class="promotion" />
|
||||
<Card class="project-type-container">
|
||||
<NavRow :links="selectableProjectTypes" />
|
||||
@@ -633,7 +668,7 @@ const showLoaders = computed(
|
||||
:placeholder="`Search ${projectType}s...`"
|
||||
@input="onSearchChange(1)"
|
||||
/>
|
||||
<Button @click="() => (searchStore.searchInput = '')">
|
||||
<Button @click="() => clearSearch()">
|
||||
<XIcon />
|
||||
</Button>
|
||||
</div>
|
||||
@@ -698,6 +733,7 @@ const showLoaders = computed(
|
||||
class="pagination-after"
|
||||
@switch-page="onSearchChangeToTop"
|
||||
/>
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
<InstallConfirmModal ref="confirmModal" />
|
||||
@@ -818,6 +854,9 @@ const showLoaders = computed(
|
||||
|
||||
.search-container {
|
||||
display: flex;
|
||||
height: 100%; /* takes up only the necessary height */
|
||||
overflow-y: auto;
|
||||
scroll-behavior: smooth;
|
||||
|
||||
.filter-panel {
|
||||
position: fixed;
|
||||
@@ -846,7 +885,6 @@ const showLoaders = computed(
|
||||
}
|
||||
|
||||
.search {
|
||||
scroll-behavior: smooth;
|
||||
margin: 0 1rem 0.5rem 20.5rem;
|
||||
width: calc(100% - 20.5rem);
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
</template>
|
||||
<template #filter_update>
|
||||
<UpdatedIcon />
|
||||
Select Updatable
|
||||
Select updatable
|
||||
</template>
|
||||
</DropdownButton>
|
||||
<Button v-if="selected.length > 0" class="no-wrap" @click="deleteWarning.show()">
|
||||
@@ -155,21 +155,16 @@
|
||||
<TrashIcon />
|
||||
</Button>
|
||||
<AnimatedLogo v-if="mod.updating" class="btn icon-only updating-indicator"></AnimatedLogo>
|
||||
<Button
|
||||
v-else
|
||||
v-tooltip="'Update project'"
|
||||
:disabled="!mod.outdated"
|
||||
icon-only
|
||||
@click="updateProject(mod)"
|
||||
>
|
||||
<UpdatedIcon v-if="mod.outdated" />
|
||||
<CheckIcon v-else />
|
||||
<Button v-else :disabled="!mod.outdated" icon-only @click="updateProject(mod)">
|
||||
<UpdatedIcon v-if="mod.outdated" v-tooltip="'Update project'" />
|
||||
<CheckIcon v-else v-tooltip="'Updated'" />
|
||||
</Button>
|
||||
<input
|
||||
id="switch-1"
|
||||
autocomplete="off"
|
||||
type="checkbox"
|
||||
class="switch stylized-toggle"
|
||||
:disabled="mod.toggleInProgress"
|
||||
:checked="!mod.disabled"
|
||||
@change="toggleDisableMod(mod)"
|
||||
/>
|
||||
@@ -454,7 +449,6 @@ async function updateProject(mod) {
|
||||
async function toggleDisableMod(mod) {
|
||||
mod.path = await toggle_disable_project(props.instance.path, mod.path).catch(handleError)
|
||||
mod.disabled = !mod.disabled
|
||||
|
||||
mixpanel.track('InstanceProjectDisable', {
|
||||
loader: props.instance.metadata.loader,
|
||||
game_version: props.instance.metadata.game_version,
|
||||
|
||||
@@ -610,6 +610,10 @@ async function saveGvLoaderEdits() {
|
||||
padding: 1rem;
|
||||
gap: 1rem;
|
||||
|
||||
:deep(.animated-dropdown .options) {
|
||||
max-height: 13.375rem;
|
||||
}
|
||||
|
||||
.input-label {
|
||||
font-size: 1rem;
|
||||
font-weight: bolder;
|
||||
|
||||
@@ -207,6 +207,7 @@
|
||||
:dependencies="dependencies"
|
||||
:install="install"
|
||||
:installed="installed"
|
||||
:installing="installing"
|
||||
:installed-version="installedVersion"
|
||||
/>
|
||||
</div>
|
||||
@@ -310,7 +311,7 @@ async function fetchProjectData() {
|
||||
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, true).catch(handleError) : Promise.resolve(),
|
||||
route.query.i ? getInstance(route.query.i, false).catch(handleError) : Promise.resolve(),
|
||||
])
|
||||
|
||||
installed.value =
|
||||
@@ -344,6 +345,7 @@ const markInstalled = () => {
|
||||
async function install(version) {
|
||||
installing.value = true
|
||||
let queuedVersionData
|
||||
instance.value = await getInstance(instance.value.path, false).catch(handleError)
|
||||
|
||||
if (installed.value) {
|
||||
await remove_project(
|
||||
|
||||
@@ -14,10 +14,21 @@
|
||||
<h2>{{ version.name }}</h2>
|
||||
</div>
|
||||
<div class="button-group">
|
||||
<Button color="primary" :action="() => install(version.id)" :disabled="installed">
|
||||
<Button
|
||||
color="primary"
|
||||
:action="() => install(version.id)"
|
||||
:disabled="installing || (installed && installedVersion === version.id)"
|
||||
>
|
||||
<DownloadIcon v-if="!installed" />
|
||||
<SwapIcon v-else-if="installedVersion !== version.id" />
|
||||
<CheckIcon v-else />
|
||||
{{ installed ? 'Installed' : 'Install' }}
|
||||
{{
|
||||
installing
|
||||
? 'Installing...'
|
||||
: installed && installedVersion === version.id
|
||||
? 'Installed'
|
||||
: 'Install'
|
||||
}}
|
||||
</Button>
|
||||
<Button>
|
||||
<ReportIcon />
|
||||
@@ -29,7 +40,7 @@
|
||||
class="btn"
|
||||
>
|
||||
<ExternalIcon />
|
||||
Modrinth Website
|
||||
Modrinth website
|
||||
</a>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -195,6 +206,7 @@ import { releaseColor } from '@/helpers/utils'
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||
import { SwapIcon } from '@/assets/icons'
|
||||
|
||||
const breadcrumbs = useBreadcrumbs()
|
||||
|
||||
@@ -225,6 +237,14 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
installing: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
installedVersion: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const version = ref(props.versions.find((version) => version.id === route.params.version))
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
<Button
|
||||
:color="installed && version.id === installedVersion ? '' : 'primary'"
|
||||
icon-only
|
||||
:disabled="installed && version.id === installedVersion"
|
||||
:disabled="installing || (installed && version.id === installedVersion)"
|
||||
@click.stop="() => install(version.id)"
|
||||
>
|
||||
<DownloadIcon v-if="!installed" />
|
||||
@@ -191,6 +191,10 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: null,
|
||||
},
|
||||
installing: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
instance: {
|
||||
type: Object,
|
||||
default: null,
|
||||
|
||||
@@ -8,11 +8,24 @@ export const useBreadcrumbs = defineStore('breadcrumbsStore', {
|
||||
}),
|
||||
actions: {
|
||||
getName(route) {
|
||||
return this.names.get(route) ?? route
|
||||
return this.names.get(route) ?? ''
|
||||
},
|
||||
setName(route, title) {
|
||||
this.names.set(route, title)
|
||||
},
|
||||
// resets breadcrumbs to only included ones as to not have stale breadcrumbs
|
||||
resetToNames(breadcrumbs) {
|
||||
// names is an array of every breadcrumb.name that starts with a ?
|
||||
const names = breadcrumbs
|
||||
.filter((breadcrumb) => breadcrumb.name.charAt(0) === '?')
|
||||
.map((breadcrumb) => breadcrumb.name.slice(1))
|
||||
// remove all names that are not in the names array
|
||||
for (const [route] of this.names) {
|
||||
if (!names.includes(route)) {
|
||||
this.names.delete(route)
|
||||
}
|
||||
}
|
||||
},
|
||||
setContext(context) {
|
||||
this.context = context
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user