feat: paper channel badges (#5850)
This commit is contained in:
@@ -40,12 +40,16 @@ export function useInstallationForm(
|
||||
)
|
||||
|
||||
const loaderVersionOptions = computed(() =>
|
||||
loaderVersionEntries.value.map((v, index) => ({ value: index, label: v.id })),
|
||||
loaderVersionEntries.value.map((v, index) => ({
|
||||
value: index,
|
||||
label: v.label ?? v.id,
|
||||
})),
|
||||
)
|
||||
|
||||
const loaderVersionDisplayValue = computed(() => {
|
||||
const idx = selectedLoaderVersion.value
|
||||
return idx >= 0 && loaderVersionEntries.value[idx] ? loaderVersionEntries.value[idx].id : ''
|
||||
const e = loaderVersionEntries.value[idx]
|
||||
return idx >= 0 && e ? (e.label ?? e.id) : ''
|
||||
})
|
||||
|
||||
const hasSnapshots = computed(() => ctx.resolveHasSnapshots(selectedPlatform.value))
|
||||
|
||||
@@ -21,6 +21,7 @@ import Avatar from '#ui/components/base/Avatar.vue'
|
||||
import ButtonStyled from '#ui/components/base/ButtonStyled.vue'
|
||||
import Chips from '#ui/components/base/Chips.vue'
|
||||
import Combobox from '#ui/components/base/Combobox.vue'
|
||||
import PaperChannelBadge from '#ui/components/base/PaperChannelBadge.vue'
|
||||
import ConfirmLeaveModal from '#ui/components/modal/ConfirmLeaveModal.vue'
|
||||
import { defineMessages, useVIntl } from '#ui/composables/i18n'
|
||||
import { commonMessages } from '#ui/utils/common-messages'
|
||||
@@ -35,6 +36,7 @@ import ContentDiffModal from './components/ContentDiffModal.vue'
|
||||
import IncompatibleContentModal from './components/IncompatibleContentModal.vue'
|
||||
import { useInstallationForm } from './composables'
|
||||
import { injectInstallationSettings } from './providers/installation-settings'
|
||||
import type { LoaderVersionEntry } from './types'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const ctx = injectInstallationSettings()
|
||||
@@ -58,6 +60,16 @@ const form = useInstallationForm(
|
||||
incompatibleContentModal,
|
||||
)
|
||||
|
||||
function paperLoaderChannelTag(index: number): LoaderVersionEntry['channelTag'] | null {
|
||||
if (form.selectedPlatform.value !== 'paper') return null
|
||||
const entries = ctx.resolveLoaderVersions(
|
||||
form.selectedPlatform.value,
|
||||
form.selectedGameVersion.value,
|
||||
)
|
||||
const tag = entries[index]?.channelTag
|
||||
return tag === 'ALPHA' || tag === 'BETA' ? tag : null
|
||||
}
|
||||
|
||||
function handleBeforeUnload(e: BeforeUnloadEvent) {
|
||||
if (form.isSaving.value) {
|
||||
e.preventDefault()
|
||||
@@ -525,6 +537,7 @@ const messages = defineMessages({
|
||||
formatMessage(commonMessages.selectVersionPlaceholder)
|
||||
"
|
||||
:aria-label="formatMessage(messages.selectGameVersionAriaLabel)"
|
||||
@option-hover="ctx.onGameVersionHover?.($event)"
|
||||
>
|
||||
<template v-if="form.hasSnapshots.value" #dropdown-footer>
|
||||
<button
|
||||
@@ -574,7 +587,33 @@ const messages = defineMessages({
|
||||
loader: form.formattedLoaderName.value,
|
||||
})
|
||||
"
|
||||
/>
|
||||
>
|
||||
<template
|
||||
v-if="form.selectedPlatform.value === 'paper'"
|
||||
#option="{ item, isSelected }"
|
||||
>
|
||||
<div class="flex w-full items-center justify-between gap-2">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span
|
||||
class="font-semibold leading-tight"
|
||||
:class="isSelected ? 'text-contrast' : 'text-primary'"
|
||||
>
|
||||
{{ item.label }}
|
||||
</span>
|
||||
<PaperChannelBadge :channel="paperLoaderChannelTag(item.value)" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template
|
||||
v-if="form.selectedPlatform.value === 'paper'"
|
||||
#search-selection-affix="{ option }"
|
||||
>
|
||||
<PaperChannelBadge
|
||||
affix
|
||||
:channel="option ? paperLoaderChannelTag(option.value) : null"
|
||||
/>
|
||||
</template>
|
||||
</Combobox>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
|
||||
@@ -29,6 +29,9 @@ export interface InstallationSettingsContext {
|
||||
resolveLoaderVersions: (loader: string, gameVersion: string) => LoaderVersionEntry[]
|
||||
resolveHasSnapshots: (loader: string) => boolean
|
||||
|
||||
/** Prefetch loader build lists when the user hovers a game version (e.g. Paper/Purpur). */
|
||||
onGameVersionHover?: (option: GameVersionOption) => void
|
||||
|
||||
save: (platform: string, gameVersion: string, loaderVersionId: string | null) => Promise<void>
|
||||
repair: () => Promise<void>
|
||||
reinstallModpack: () => Promise<void>
|
||||
|
||||
@@ -29,6 +29,10 @@ export interface GameVersionOption {
|
||||
export interface LoaderVersionEntry {
|
||||
id: string
|
||||
stable?: boolean
|
||||
/** Shown in the loader-version combobox when set; defaults to `id` */
|
||||
label?: string
|
||||
/** Paper build channel for optional UI (e.g. combobox pill); not used by Combobox itself */
|
||||
channelTag?: 'ALPHA' | 'BETA'
|
||||
}
|
||||
|
||||
export interface ContentDiffItem {
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Archon, LauncherMeta } from '@modrinth/api-client'
|
||||
import type { Archon } from '@modrinth/api-client'
|
||||
import { RotateCounterClockwiseIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
@@ -75,12 +75,14 @@ import {
|
||||
ConfirmModal,
|
||||
defineMessages,
|
||||
formatLoaderLabel,
|
||||
type GameVersionOption,
|
||||
injectModrinthClient,
|
||||
injectModrinthServerContext,
|
||||
injectNotificationManager,
|
||||
injectServerSettings,
|
||||
injectTags,
|
||||
InstallationSettingsLayout,
|
||||
type LoaderVersionEntry,
|
||||
provideInstallationSettings,
|
||||
ServerSetupModal,
|
||||
UploadProgressModal,
|
||||
@@ -295,7 +297,21 @@ const purpurSupportedVersionsQuery = useQuery({
|
||||
staleTime: 5 * 60 * 1000,
|
||||
})
|
||||
|
||||
type LoaderVersionEntry = LauncherMeta.Manifest.v0.LoaderVersion
|
||||
function handleGameVersionHover(option: GameVersionOption) {
|
||||
if (editingPlatform.value === 'paper') {
|
||||
void queryClient.prefetchQuery({
|
||||
queryKey: ['paper-builds', option.value] as const,
|
||||
queryFn: () => client.paper.versions_v3.getBuilds(option.value),
|
||||
staleTime: 5 * 60 * 1000,
|
||||
})
|
||||
} else if (editingPlatform.value === 'purpur') {
|
||||
void queryClient.prefetchQuery({
|
||||
queryKey: ['purpur-builds', option.value] as const,
|
||||
queryFn: () => client.purpur.versions_v2.getBuilds(option.value),
|
||||
staleTime: 5 * 60 * 1000,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function getLoaderVersionsForGameVersion(
|
||||
loader: string,
|
||||
@@ -303,8 +319,18 @@ function getLoaderVersionsForGameVersion(
|
||||
): LoaderVersionEntry[] {
|
||||
if (loader === 'paper') {
|
||||
return (paperBuildsQuery.data.value?.builds ?? [])
|
||||
.toSorted((a, b) => b - a)
|
||||
.map((b) => ({ id: String(b), stable: true }))
|
||||
.toSorted((a, b) => b.id - a.id)
|
||||
.map((b): LoaderVersionEntry => {
|
||||
const u = String(b.channel).toUpperCase()
|
||||
let channelTag: LoaderVersionEntry['channelTag'] | undefined
|
||||
if (u === 'ALPHA' || u === 'BETA') channelTag = u
|
||||
return {
|
||||
id: String(b.id),
|
||||
stable: b.channel === 'STABLE',
|
||||
label: `Build ${b.id}`,
|
||||
channelTag,
|
||||
}
|
||||
})
|
||||
}
|
||||
if (loader === 'purpur') {
|
||||
return (purpurBuildsQuery.data.value?.builds.all ?? [])
|
||||
@@ -328,6 +354,7 @@ function toApiLoader(loader: string): Archon.Content.v1.Modloader {
|
||||
}
|
||||
|
||||
provideInstallationSettings({
|
||||
onGameVersionHover: handleGameVersionHover,
|
||||
loading: computed(() => !server.value || addonsQuery.isLoading.value),
|
||||
installationInfo: computed(() => {
|
||||
const addons = addonsQuery.data.value
|
||||
|
||||
Reference in New Issue
Block a user