You've already forked AstralRinth
forked from didirus/AstralRinth
More app fixes 0.9.0 (#3054)
* initial set of fixes (toggle sidebar, profile pagination) * more fixes, bump version * fix lint: * fix quick switcher ordering
This commit is contained in:
@@ -218,14 +218,14 @@ const filteredResults = computed(() => {
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div class="iconified-input">
|
||||
<SearchIcon />
|
||||
<input v-model="search" type="text" class="h-12" placeholder="Search" />
|
||||
<Button class="r-btn" @click="() => (search = '')">
|
||||
<XIcon />
|
||||
</Button>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<div class="iconified-input flex-1">
|
||||
<SearchIcon />
|
||||
<input v-model="search" type="text" placeholder="Search" />
|
||||
<Button class="r-btn" @click="() => (search = '')">
|
||||
<XIcon />
|
||||
</Button>
|
||||
</div>
|
||||
<DropdownSelect
|
||||
v-slot="{ selected }"
|
||||
v-model="sortBy"
|
||||
@@ -363,7 +363,7 @@ const filteredResults = computed(() => {
|
||||
|
||||
.instances {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
|
||||
width: 100%;
|
||||
gap: 0.75rem;
|
||||
margin-right: auto;
|
||||
|
||||
@@ -207,13 +207,18 @@ const calculateCardsPerRow = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const rowContainer = ref(null)
|
||||
const resizeObserver = ref(null)
|
||||
onMounted(() => {
|
||||
calculateCardsPerRow()
|
||||
resizeObserver.value = new ResizeObserver(calculateCardsPerRow)
|
||||
resizeObserver.value.observe(rowContainer.value)
|
||||
window.addEventListener('resize', calculateCardsPerRow)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', calculateCardsPerRow)
|
||||
resizeObserver.value.unobserve(rowContainer.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -226,7 +231,7 @@ onUnmounted(() => {
|
||||
proceed-label="Delete"
|
||||
@proceed="deleteProfile"
|
||||
/>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div ref="rowContainer" class="flex flex-col gap-4">
|
||||
<div v-for="(row, rowIndex) in actualInstances" ref="rows" :key="row.label" class="row">
|
||||
<router-link
|
||||
class="flex mb-3 leading-none items-center gap-1 text-primary text-lg font-bold hover:underline group"
|
||||
|
||||
@@ -12,11 +12,9 @@ import { showProfileInFolder } from '@/helpers/utils.js'
|
||||
import { handleSevereError } from '@/store/error.js'
|
||||
import { trackEvent } from '@/helpers/analytics'
|
||||
import dayjs from 'dayjs'
|
||||
import duration from 'dayjs/plugin/duration'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
import { formatCategory } from '@modrinth/utils'
|
||||
|
||||
dayjs.extend(duration)
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
const props = defineProps({
|
||||
@@ -169,7 +167,7 @@ onUnmounted(() => unlisten())
|
||||
>
|
||||
<div class="relative flex items-center justify-center">
|
||||
<Avatar
|
||||
size="96px"
|
||||
size="48px"
|
||||
:src="instance.icon_path ? convertFileSrc(instance.icon_path) : null"
|
||||
:tint-by="instance.path"
|
||||
alt="Mod card"
|
||||
@@ -205,7 +203,7 @@ onUnmounted(() => unlisten())
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<p class="m-0 text-lg font-bold text-contrast leading-tight line-clamp-2">
|
||||
<p class="m-0 text-md font-bold text-contrast leading-tight line-clamp-1">
|
||||
{{ instance.name }}
|
||||
</p>
|
||||
<div class="flex items-center col-span-3 gap-1 text-secondary font-semibold mt-auto">
|
||||
@@ -214,17 +212,6 @@ onUnmounted(() => unlisten())
|
||||
{{ formatCategory(instance.loader) }} {{ instance.game_version }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center col-span-3 gap-1 text-secondary font-semibold">
|
||||
<TimerIcon class="shrink-0" />
|
||||
<span class="text-sm line-clamp-1">
|
||||
Played for
|
||||
{{
|
||||
dayjs
|
||||
.duration(instance.recent_time_played + instance.submitted_time_played, 'seconds')
|
||||
.humanize()
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,29 +1,24 @@
|
||||
<template>
|
||||
<div class="tooltip-parent flex items-center justify-center">
|
||||
<RouterLink
|
||||
v-if="typeof to === 'string'"
|
||||
:to="to"
|
||||
v-bind="$attrs"
|
||||
:class="{
|
||||
'router-link-active': isPrimary && isPrimary(route),
|
||||
'subpage-active': isSubpage && isSubpage(route),
|
||||
}"
|
||||
class="w-12 h-12 rounded-full flex items-center justify-center text-2xl transition-all bg-transparent hover:bg-button-bg hover:text-contrast"
|
||||
>
|
||||
<slot />
|
||||
</RouterLink>
|
||||
<button
|
||||
v-else
|
||||
v-bind="$attrs"
|
||||
class="button-animation border-none text-primary cursor-pointer w-12 h-12 rounded-full flex items-center justify-center text-2xl transition-all bg-transparent hover:bg-button-bg hover:text-contrast"
|
||||
@click="to"
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
<div class="tooltip-label">
|
||||
<slot name="label" />
|
||||
</div>
|
||||
</div>
|
||||
<RouterLink
|
||||
v-if="typeof to === 'string'"
|
||||
:to="to"
|
||||
v-bind="$attrs"
|
||||
:class="{
|
||||
'router-link-active': isPrimary && isPrimary(route),
|
||||
'subpage-active': isSubpage && isSubpage(route),
|
||||
}"
|
||||
class="w-12 h-12 rounded-full flex items-center justify-center text-2xl transition-all bg-transparent hover:bg-button-bg hover:text-contrast"
|
||||
>
|
||||
<slot />
|
||||
</RouterLink>
|
||||
<button
|
||||
v-else
|
||||
v-bind="$attrs"
|
||||
class="button-animation border-none text-primary cursor-pointer w-12 h-12 rounded-full flex items-center justify-center text-2xl transition-all bg-transparent hover:bg-button-bg hover:text-contrast"
|
||||
@click="to"
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -61,50 +56,4 @@ defineOptions({
|
||||
.subpage-active {
|
||||
@apply text-contrast bg-button-bg;
|
||||
}
|
||||
|
||||
.tooltip-parent {
|
||||
position: relative;
|
||||
border-radius: var(--radius-max);
|
||||
}
|
||||
|
||||
.tooltip-parent:hover .tooltip-label {
|
||||
opacity: 1;
|
||||
translate: 0 0;
|
||||
scale: 1;
|
||||
}
|
||||
|
||||
.tooltip-label:not(:empty) {
|
||||
--_tooltip-bg: black;
|
||||
--_tooltip-color: var(--dark-color-contrast);
|
||||
|
||||
position: absolute;
|
||||
background-color: var(--_tooltip-bg);
|
||||
color: var(--_tooltip-color);
|
||||
text-wrap: nowrap;
|
||||
padding: 0.5rem 0.5rem;
|
||||
border-radius: var(--radius-sm);
|
||||
left: calc(100% + 0.5rem);
|
||||
font-size: 1rem;
|
||||
line-height: 1;
|
||||
font-weight: bold;
|
||||
filter: drop-shadow(5px 5px 0.8rem rgba(0, 0, 0, 0.35));
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
|
||||
opacity: 0;
|
||||
translate: -0.5rem 0;
|
||||
scale: 0.9;
|
||||
transition: all ease-in-out 0.1s;
|
||||
}
|
||||
|
||||
.tooltip-label:not(:empty)::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 100%; /* To the left of the tooltip */
|
||||
margin-top: -5px;
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border-color: transparent var(--_tooltip-bg) transparent transparent;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -25,7 +25,7 @@ onMounted(() => {
|
||||
|
||||
function updateAdPosition() {
|
||||
if (adsWrapper.value) {
|
||||
init_ads_window(true)
|
||||
init_ads_window()
|
||||
initDevicePixelRatioWatcher()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,14 @@ const getInstances = async () => {
|
||||
|
||||
recentInstances.value = profiles
|
||||
.sort((a, b) => {
|
||||
const dateA = dayjs(a.created > a.last_played ? a.last_played : a.created)
|
||||
const dateB = dayjs(b.created > b.last_played ? b.last_played : b.created)
|
||||
const dateACreated = dayjs(a.created)
|
||||
const dateAPlayed = dayjs(a.last_played)
|
||||
|
||||
const dateBCreated = dayjs(b.created)
|
||||
const dateBPlayed = dayjs(b.last_played)
|
||||
|
||||
const dateA = dateACreated.isAfter(dateAPlayed) ? dateACreated : dateAPlayed
|
||||
const dateB = dateBCreated.isAfter(dateBPlayed) ? dateBCreated : dateBPlayed
|
||||
|
||||
if (dateA.isSame(dateB)) {
|
||||
return a.name.localeCompare(b.name)
|
||||
@@ -42,6 +48,7 @@ onUnmounted(() => {
|
||||
<NavButton
|
||||
v-for="instance in recentInstances"
|
||||
:key="instance.id"
|
||||
v-tooltip.right="instance.name"
|
||||
:to="`/instance/${encodeURIComponent(instance.path)}`"
|
||||
>
|
||||
<Avatar
|
||||
@@ -56,8 +63,8 @@ onUnmounted(() => {
|
||||
>
|
||||
<SpinnerIcon class="animate-spin w-4 h-4" />
|
||||
</div>
|
||||
<template #label>{{ instance.name }}</template>
|
||||
</NavButton>
|
||||
<div v-if="recentInstances.length > 0" class="h-px w-6 mx-auto my-2 bg-button-bg"></div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
<template>
|
||||
<div class="action-groups">
|
||||
<a href="https://support.modrinth.com" class="link">
|
||||
<ChatIcon />
|
||||
<span> Get support </span>
|
||||
</a>
|
||||
<Button
|
||||
v-if="currentLoadingBars.length > 0"
|
||||
ref="infoButton"
|
||||
@@ -123,7 +119,6 @@ import { useRouter } from 'vue-router'
|
||||
import { progress_bars_list } from '@/helpers/state.js'
|
||||
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { ChatIcon } from '@/assets/icons'
|
||||
import { get_many } from '@/helpers/profile.js'
|
||||
import { trackEvent } from '@/helpers/analytics'
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
<div class="m-0 line-clamp-2">
|
||||
{{ project.description }}
|
||||
</div>
|
||||
<div class="mt-auto flex items-center gap-1 no-wrap">
|
||||
<div v-if="categories.length > 0" class="mt-auto flex items-center gap-1 no-wrap">
|
||||
<TagsIcon class="h-4 w-4 shrink-0" />
|
||||
<div
|
||||
v-for="tag in categories"
|
||||
|
||||
@@ -190,6 +190,7 @@ const messages = defineMessages({
|
||||
description="If you proceed, all data for your instance will be permanently erased, including your worlds. You will not be able to recover it."
|
||||
:has-to-type="false"
|
||||
proceed-label="Delete"
|
||||
:show-ad-on-close="false"
|
||||
@proceed="removeProfile"
|
||||
/>
|
||||
<div class="block">
|
||||
|
||||
@@ -457,6 +457,7 @@ const messages = defineMessages({
|
||||
:proceed-icon="HammerIcon"
|
||||
:proceed-label="formatMessage(messages.repairButton)"
|
||||
:danger="false"
|
||||
:show-ad-on-close="false"
|
||||
@proceed="() => repairProfile(true)"
|
||||
/>
|
||||
<ModpackVersionModal
|
||||
@@ -480,6 +481,7 @@ const messages = defineMessages({
|
||||
:description="formatMessage(messages.unlinkInstanceConfirmDescription)"
|
||||
:proceed-icon="UnlinkIcon"
|
||||
:proceed-label="formatMessage(messages.unlinkInstanceButton)"
|
||||
:show-ad-on-close="false"
|
||||
@proceed="() => unpairProfile()"
|
||||
/>
|
||||
<ConfirmModalWrapper
|
||||
@@ -488,6 +490,7 @@ const messages = defineMessages({
|
||||
:description="formatMessage(messages.reinstallModpackConfirmDescription)"
|
||||
:proceed-icon="DownloadIcon"
|
||||
:proceed-label="formatMessage(messages.reinstallModpackButton)"
|
||||
:show-ad-on-close="false"
|
||||
@proceed="() => repairModpack()"
|
||||
/>
|
||||
<div>
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
CoffeeIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { TabbedModal } from '@modrinth/ui'
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useVIntl, defineMessage } from '@vintl/vintl'
|
||||
import AppearanceSettings from '@/components/ui/settings/AppearanceSettings.vue'
|
||||
import JavaSettings from '@/components/ui/settings/JavaSettings.vue'
|
||||
@@ -22,6 +22,7 @@ import { version as getOsVersion, platform as getOsPlatform } from '@tauri-apps/
|
||||
import { useTheming } from '@/store/state'
|
||||
import FeatureFlagSettings from '@/components/ui/settings/FeatureFlagSettings.vue'
|
||||
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
||||
import { get, set } from '@/helpers/settings'
|
||||
|
||||
const themeStore = useTheming()
|
||||
|
||||
@@ -99,6 +100,28 @@ defineExpose({ show, isOpen })
|
||||
const version = await getVersion()
|
||||
const osPlatform = getOsPlatform()
|
||||
const osVersion = getOsVersion()
|
||||
const settings = ref(await get())
|
||||
|
||||
watch(
|
||||
settings,
|
||||
async () => {
|
||||
await set(settings.value)
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
function devModeCount() {
|
||||
devModeCounter.value++
|
||||
if (devModeCounter.value > 5) {
|
||||
themeStore.devMode = !themeStore.devMode
|
||||
settings.value.developer_mode = !!themeStore.devMode
|
||||
devModeCounter.value = 0
|
||||
|
||||
if (!themeStore.devMode && tabs[modal.value.selectedTab].developerOnly) {
|
||||
modal.value.setTab(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<ModalWrapper ref="modal">
|
||||
@@ -118,19 +141,7 @@ const osVersion = getOsVersion()
|
||||
<button
|
||||
class="p-0 m-0 bg-transparent border-none cursor-pointer button-animation"
|
||||
:class="{ 'text-brand': themeStore.devMode, 'text-secondary': !themeStore.devMode }"
|
||||
@click="
|
||||
() => {
|
||||
devModeCounter++
|
||||
if (devModeCounter > 5) {
|
||||
themeStore.devMode = !themeStore.devMode
|
||||
devModeCounter = 0
|
||||
|
||||
if (!themeStore.devMode && tabs[modal.selectedTab].developerOnly) {
|
||||
modal.setTab(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
"
|
||||
@click="devModeCount"
|
||||
>
|
||||
<ModrinthIcon class="w-6 h-6" />
|
||||
</button>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useTheming } from '@/store/theme.js'
|
||||
|
||||
const themeStore = useTheming()
|
||||
|
||||
defineProps({
|
||||
const props = defineProps({
|
||||
confirmationText: {
|
||||
type: String,
|
||||
default: '',
|
||||
@@ -37,6 +37,10 @@ defineProps({
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showAdOnClose: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['proceed'])
|
||||
@@ -54,7 +58,9 @@ defineExpose({
|
||||
})
|
||||
|
||||
function onModalHide() {
|
||||
show_ads_window()
|
||||
if (props.showAdOnClose) {
|
||||
show_ads_window()
|
||||
}
|
||||
}
|
||||
|
||||
function proceed() {
|
||||
|
||||
@@ -23,8 +23,13 @@ watch(
|
||||
<p class="m-0 mt-1">Select your preferred color theme for Modrinth App.</p>
|
||||
|
||||
<ThemeSelector
|
||||
:update-color-theme="themeStore.setThemeState"
|
||||
:current-theme="themeStore.selectedTheme"
|
||||
:update-color-theme="
|
||||
(theme) => {
|
||||
themeStore.setThemeState(theme)
|
||||
settings.theme = theme
|
||||
}
|
||||
"
|
||||
:current-theme="settings.theme"
|
||||
:theme-options="themeStore.themeOptions"
|
||||
system-theme-color="system"
|
||||
/>
|
||||
@@ -97,4 +102,22 @@ watch(
|
||||
:options="['Home', 'Library']"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 flex items-center justify-between">
|
||||
<div>
|
||||
<h2 class="m-0 text-lg font-extrabold text-contrast">Toggle sidebar</h2>
|
||||
<p class="m-0 mt-1">Enables the ability to toggle the sidebar.</p>
|
||||
</div>
|
||||
<Toggle
|
||||
id="toggle-sidebar"
|
||||
:model-value="settings.toggle_sidebar"
|
||||
:checked="settings.toggle_sidebar"
|
||||
@update:model-value="
|
||||
(e) => {
|
||||
settings.toggle_sidebar = e
|
||||
themeStore.toggleSidebar = settings.toggle_sidebar
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,34 +1,36 @@
|
||||
<script setup lang="ts">
|
||||
import { Toggle } from '@modrinth/ui'
|
||||
import { useTheming } from '@/store/state'
|
||||
import { computed } from 'vue'
|
||||
import type { Ref } from 'vue'
|
||||
import { ref, watch } from 'vue'
|
||||
import { get, set } from '@/helpers/settings'
|
||||
|
||||
const themeStore = useTheming()
|
||||
|
||||
type ThemeStoreKeys = keyof typeof themeStore
|
||||
const settings = ref(await get())
|
||||
const options = ref(['project_background', 'page_path'])
|
||||
|
||||
const options: Ref<ThemeStoreKeys[]> = computed(() => {
|
||||
return Object.keys(themeStore).filter((key) => key.startsWith('featureFlag_')) as ThemeStoreKeys[]
|
||||
})
|
||||
|
||||
function getStoreValue<K extends ThemeStoreKeys>(key: K): (typeof themeStore)[K] {
|
||||
return themeStore[key]
|
||||
function getStoreValue(key: string) {
|
||||
return themeStore.featureFlags[key] ?? false
|
||||
}
|
||||
|
||||
function setStoreValue<K extends ThemeStoreKeys>(key: K, value: (typeof themeStore)[K]) {
|
||||
themeStore[key] = value
|
||||
function setStoreValue(key: string, value: boolean) {
|
||||
themeStore.featureFlags[key] = value
|
||||
settings.value.feature_flags[key] = value
|
||||
}
|
||||
|
||||
function formatFlagName(name: string) {
|
||||
return name.replace('featureFlag_', '')
|
||||
}
|
||||
watch(
|
||||
settings,
|
||||
async () => {
|
||||
await set(settings.value)
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
</script>
|
||||
<template>
|
||||
<div v-for="option in options" :key="option" class="mt-4 flex items-center justify-between">
|
||||
<div>
|
||||
<h2 class="m-0 text-lg font-extrabold text-contrast capitalize">
|
||||
{{ formatFlagName(option) }}
|
||||
{{ option }}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
@@ -36,7 +38,7 @@ function formatFlagName(name: string) {
|
||||
id="advanced-rendering"
|
||||
:model-value="getStoreValue(option)"
|
||||
:checked="getStoreValue(option)"
|
||||
@update:model-value="() => setStoreValue(option, !themeStore[option])"
|
||||
@update:model-value="() => setStoreValue(option, !themeStore.featureFlags[option])"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -80,6 +80,7 @@ async function findLauncherDir() {
|
||||
description="If you proceed, your entire cache will be purged. This may slow down the app temporarily."
|
||||
:has-to-type="false"
|
||||
proceed-label="Purge cache"
|
||||
:show-ad-on-close="false"
|
||||
@proceed="purgeCache"
|
||||
/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user