mirror of
https://github.com/modrinth/code.git
synced 2026-09-04 05:48:57 +00:00
feat: implement incompatible options due to base project
This commit is contained in:
@@ -17,20 +17,12 @@
|
|||||||
>
|
>
|
||||||
<template #option="{ item, isSelected }">
|
<template #option="{ item, isSelected }">
|
||||||
<div
|
<div
|
||||||
v-tooltip="
|
v-tooltip="gameVersionOptionTooltip(item.value)"
|
||||||
!possibleGameVersions.includes(item.value)
|
|
||||||
? formatMessage(messages.gameVersionUnsupportedTooltip, {
|
|
||||||
title: project.title,
|
|
||||||
gameVersion: item.value,
|
|
||||||
platform: currentPlatformText,
|
|
||||||
})
|
|
||||||
: null
|
|
||||||
"
|
|
||||||
class="flex w-full items-center justify-between gap-2"
|
class="flex w-full items-center justify-between gap-2"
|
||||||
:class="{
|
:class="{
|
||||||
'text-brand-red opacity-40': !possibleGameVersions.includes(item.value),
|
'text-brand-red opacity-40': isGameVersionUnavailable(item.value),
|
||||||
'text-green': isSelected,
|
'text-green': isSelected,
|
||||||
'text-primary': possibleGameVersions.includes(item.value) && !isSelected,
|
'text-primary': !isGameVersionUnavailable(item.value) && !isSelected,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<span class="min-w-0 truncate font-semibold leading-tight">{{ item.label }}</span>
|
<span class="min-w-0 truncate font-semibold leading-tight">{{ item.label }}</span>
|
||||||
@@ -61,20 +53,12 @@
|
|||||||
>
|
>
|
||||||
<template #option="{ item, isSelected }">
|
<template #option="{ item, isSelected }">
|
||||||
<div
|
<div
|
||||||
v-tooltip="
|
v-tooltip="platformOptionTooltip(item.value, item.label)"
|
||||||
!possiblePlatforms.includes(item.value)
|
|
||||||
? formatMessage(messages.platformUnsupportedTooltip, {
|
|
||||||
title: project.title,
|
|
||||||
platform: item.label,
|
|
||||||
gameVersion: currentGameVersion,
|
|
||||||
})
|
|
||||||
: null
|
|
||||||
"
|
|
||||||
class="flex w-full items-center justify-between gap-2"
|
class="flex w-full items-center justify-between gap-2"
|
||||||
:class="{
|
:class="{
|
||||||
'text-brand-red opacity-40': !possiblePlatforms.includes(item.value),
|
'text-brand-red opacity-40': isPlatformUnavailable(item.value),
|
||||||
'text-green': isSelected,
|
'text-green': isSelected,
|
||||||
'text-primary': possiblePlatforms.includes(item.value) && !isSelected,
|
'text-primary': !isPlatformUnavailable(item.value) && !isSelected,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<span class="min-w-0 truncate font-semibold leading-tight">{{ item.label }}</span>
|
<span class="min-w-0 truncate font-semibold leading-tight">{{ item.label }}</span>
|
||||||
@@ -166,6 +150,8 @@ const props = withDefaults(
|
|||||||
downloadReason?: CdnDownloadReason
|
downloadReason?: CdnDownloadReason
|
||||||
initialGameVersion?: string | null
|
initialGameVersion?: string | null
|
||||||
initialPlatform?: string | null
|
initialPlatform?: string | null
|
||||||
|
incompatibleGameVersions?: string[]
|
||||||
|
incompatibleLoaders?: string[]
|
||||||
resetKey?: number
|
resetKey?: number
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
@@ -173,6 +159,8 @@ const props = withDefaults(
|
|||||||
downloadReason: 'standalone',
|
downloadReason: 'standalone',
|
||||||
initialGameVersion: null,
|
initialGameVersion: null,
|
||||||
initialPlatform: null,
|
initialPlatform: null,
|
||||||
|
incompatibleGameVersions: () => [],
|
||||||
|
incompatibleLoaders: () => [],
|
||||||
resetKey: 0,
|
resetKey: 0,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -193,6 +181,9 @@ const userSelectedPlatform = ref<string | null>(props.initialPlatform)
|
|||||||
const showAllVersions = ref(defaultShowAllVersions())
|
const showAllVersions = ref(defaultShowAllVersions())
|
||||||
const versionFilter = ref('')
|
const versionFilter = ref('')
|
||||||
|
|
||||||
|
const incompatibleGameVersionsSet = computed(() => new Set(props.incompatibleGameVersions))
|
||||||
|
const incompatibleLoadersSet = computed(() => new Set(props.incompatibleLoaders))
|
||||||
|
|
||||||
const showAllVersionsModel = computed({
|
const showAllVersionsModel = computed({
|
||||||
get() {
|
get() {
|
||||||
return showAllVersions.value
|
return showAllVersions.value
|
||||||
@@ -419,7 +410,58 @@ function defaultShowAllVersions() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isGameVersionUnavailable(gameVersion: string) {
|
||||||
|
return (
|
||||||
|
incompatibleGameVersionsSet.value.has(gameVersion) ||
|
||||||
|
!possibleGameVersions.value.includes(gameVersion)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPlatformUnavailable(platform: string) {
|
||||||
|
return incompatibleLoadersSet.value.has(platform) || !possiblePlatforms.value.includes(platform)
|
||||||
|
}
|
||||||
|
|
||||||
|
function gameVersionOptionTooltip(gameVersion: string) {
|
||||||
|
if (incompatibleGameVersionsSet.value.has(gameVersion)) {
|
||||||
|
return formatMessage(messages.baseGameVersionIncompatibleTooltip)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!possibleGameVersions.value.includes(gameVersion)) {
|
||||||
|
return formatMessage(messages.gameVersionUnsupportedTooltip, {
|
||||||
|
title: props.project.title,
|
||||||
|
gameVersion,
|
||||||
|
platform: currentPlatformText.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function platformOptionTooltip(platform: string, platformLabel: string) {
|
||||||
|
if (incompatibleLoadersSet.value.has(platform)) {
|
||||||
|
return formatMessage(messages.baseLoaderIncompatibleTooltip)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!possiblePlatforms.value.includes(platform)) {
|
||||||
|
return formatMessage(messages.platformUnsupportedTooltip, {
|
||||||
|
title: props.project.title,
|
||||||
|
platform: platformLabel,
|
||||||
|
gameVersion: currentGameVersion.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
baseGameVersionIncompatibleTooltip: {
|
||||||
|
id: 'project.download.base-game-version-incompatible-tooltip',
|
||||||
|
defaultMessage: 'This game version is incompatible with the base project.',
|
||||||
|
},
|
||||||
|
baseLoaderIncompatibleTooltip: {
|
||||||
|
id: 'project.download.base-loader-incompatible-tooltip',
|
||||||
|
defaultMessage: 'This loader is incompatible with the base project.',
|
||||||
|
},
|
||||||
gameVersionUnsupportedTooltip: {
|
gameVersionUnsupportedTooltip: {
|
||||||
id: 'project.download.game-version-unsupported-tooltip',
|
id: 'project.download.game-version-unsupported-tooltip',
|
||||||
defaultMessage: '{title} does not support {gameVersion} for {platform}',
|
defaultMessage: '{title} does not support {gameVersion} for {platform}',
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
:download-reason="downloadReason"
|
:download-reason="downloadReason"
|
||||||
:initial-game-version="initialGameVersion"
|
:initial-game-version="initialGameVersion"
|
||||||
:initial-platform="initialPlatform"
|
:initial-platform="initialPlatform"
|
||||||
|
:incompatible-game-versions="showOptions.incompatibleGameVersions"
|
||||||
|
:incompatible-loaders="showOptions.incompatibleLoaders"
|
||||||
:reset-key="downloadProjectResetKey"
|
:reset-key="downloadProjectResetKey"
|
||||||
@select-game-version="selectGameVersion"
|
@select-game-version="selectGameVersion"
|
||||||
@select-platform="selectPlatform"
|
@select-platform="selectPlatform"
|
||||||
@@ -89,7 +91,7 @@ import {
|
|||||||
} from '@modrinth/ui'
|
} from '@modrinth/ui'
|
||||||
import type { DisplayProjectType } from '@modrinth/utils'
|
import type { DisplayProjectType } from '@modrinth/utils'
|
||||||
import { useQuery } from '@tanstack/vue-query'
|
import { useQuery } from '@tanstack/vue-query'
|
||||||
import { computed, nextTick, ref, watch } from 'vue'
|
import { computed, nextTick, onUnmounted, ref, watch } from 'vue'
|
||||||
|
|
||||||
import { navigateTo } from '#app'
|
import { navigateTo } from '#app'
|
||||||
import { saveFeatureFlags } from '~/composables/featureFlags.ts'
|
import { saveFeatureFlags } from '~/composables/featureFlags.ts'
|
||||||
@@ -116,6 +118,18 @@ type NewModalRef = {
|
|||||||
hide: () => void
|
hide: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ProjectDownloadModalShowOptions = {
|
||||||
|
projectId?: string
|
||||||
|
incompatibleGameVersions?: string[]
|
||||||
|
incompatibleLoaders?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResolvedProjectDownloadModalShowOptions = {
|
||||||
|
projectId?: string
|
||||||
|
incompatibleGameVersions: string[]
|
||||||
|
incompatibleLoaders: string[]
|
||||||
|
}
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
projectId?: string
|
projectId?: string
|
||||||
@@ -143,8 +157,11 @@ const debug = useDebugLogger('DownloadModal')
|
|||||||
const modal = ref<NewModalRef | null>(null)
|
const modal = ref<NewModalRef | null>(null)
|
||||||
const modalOpen = ref(false)
|
const modalOpen = ref(false)
|
||||||
const showProjectId = ref<string | null>(null)
|
const showProjectId = ref<string | null>(null)
|
||||||
|
const showOptions = ref<ResolvedProjectDownloadModalShowOptions>(getDefaultShowOptions())
|
||||||
const downloadProjectResetKey = ref(0)
|
const downloadProjectResetKey = ref(0)
|
||||||
const projectDownloadSelection = ref<ProjectDownloadSelection>(getDefaultProjectDownloadSelection())
|
const projectDownloadSelection = ref<ProjectDownloadSelection>(getDefaultProjectDownloadSelection())
|
||||||
|
const MODAL_CLOSE_STATE_RESET_MS = 350
|
||||||
|
let closeStateResetTimeout: ReturnType<typeof setTimeout> | null = null
|
||||||
|
|
||||||
const routeProjectId = computed(() => showProjectId.value ?? props.projectId ?? null)
|
const routeProjectId = computed(() => showProjectId.value ?? props.projectId ?? null)
|
||||||
|
|
||||||
@@ -320,6 +337,7 @@ function selectPlatform(platform: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onShow() {
|
function onShow() {
|
||||||
|
clearCloseStateResetTimeout()
|
||||||
modalOpen.value = true
|
modalOpen.value = true
|
||||||
debug('on-show fired')
|
debug('on-show fired')
|
||||||
versionsEnabled.value = true
|
versionsEnabled.value = true
|
||||||
@@ -331,17 +349,30 @@ function onShow() {
|
|||||||
function onHide() {
|
function onHide() {
|
||||||
const hadShowProjectId = !!showProjectId.value
|
const hadShowProjectId = !!showProjectId.value
|
||||||
modalOpen.value = false
|
modalOpen.value = false
|
||||||
showProjectId.value = null
|
clearCloseStateResetTimeout()
|
||||||
|
closeStateResetTimeout = setTimeout(() => {
|
||||||
|
showProjectId.value = null
|
||||||
|
showOptions.value = getDefaultShowOptions()
|
||||||
|
closeStateResetTimeout = null
|
||||||
|
}, MODAL_CLOSE_STATE_RESET_MS)
|
||||||
if (props.useRouteHash && !hadShowProjectId) {
|
if (props.useRouteHash && !hadShowProjectId) {
|
||||||
navigateTo({ query: route.query, hash: '' }, { replace: true })
|
navigateTo({ query: route.query, hash: '' }, { replace: true })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function show(event?: MouseEvent, projectId?: string): Promise<void> {
|
async function show(
|
||||||
|
event?: MouseEvent,
|
||||||
|
options: ProjectDownloadModalShowOptions = {},
|
||||||
|
): Promise<void> {
|
||||||
if (!modal.value || modalOpen.value) return
|
if (!modal.value || modalOpen.value) return
|
||||||
showProjectId.value = projectId ?? null
|
clearCloseStateResetTimeout()
|
||||||
|
showOptions.value = {
|
||||||
|
...getDefaultShowOptions(),
|
||||||
|
...options,
|
||||||
|
}
|
||||||
|
showProjectId.value = showOptions.value.projectId ?? null
|
||||||
await nextTick()
|
await nextTick()
|
||||||
if (!(await loadProjectForModal(!!projectId))) return
|
if (!(await loadProjectForModal(!!showOptions.value.projectId))) return
|
||||||
modalOpen.value = true
|
modalOpen.value = true
|
||||||
modal.value.show(event)
|
modal.value.show(event)
|
||||||
}
|
}
|
||||||
@@ -366,6 +397,20 @@ function getDefaultProjectDownloadSelection(): ProjectDownloadSelection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDefaultShowOptions(): ResolvedProjectDownloadModalShowOptions {
|
||||||
|
return {
|
||||||
|
projectId: undefined,
|
||||||
|
incompatibleGameVersions: [],
|
||||||
|
incompatibleLoaders: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearCloseStateResetTimeout() {
|
||||||
|
if (!closeStateResetTimeout) return
|
||||||
|
clearTimeout(closeStateResetTimeout)
|
||||||
|
closeStateResetTimeout = null
|
||||||
|
}
|
||||||
|
|
||||||
async function loadProjectForModal(forceRefetch: boolean) {
|
async function loadProjectForModal(forceRefetch: boolean) {
|
||||||
if (!routeProjectId.value) return false
|
if (!routeProjectId.value) return false
|
||||||
if (!forceRefetch && projectRaw.value) return true
|
if (!forceRefetch && projectRaw.value) return true
|
||||||
@@ -411,5 +456,7 @@ watch(routeProjectId, () => {
|
|||||||
downloadProjectResetKey.value += 1
|
downloadProjectResetKey.value += 1
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onUnmounted(clearCloseStateResetTimeout)
|
||||||
|
|
||||||
defineExpose({ show, hide })
|
defineExpose({ show, hide })
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -374,7 +374,7 @@
|
|||||||
v-else-if="dependency.project"
|
v-else-if="dependency.project"
|
||||||
v-tooltip="formatMessage(messages.downloadProject)"
|
v-tooltip="formatMessage(messages.downloadProject)"
|
||||||
:aria-label="formatMessage(messages.downloadProject)"
|
:aria-label="formatMessage(messages.downloadProject)"
|
||||||
@click="openDependencyDownloadModal(dependency.project.id, $event)"
|
@click="openDependencyDownloadModal(dependency.project, $event)"
|
||||||
>
|
>
|
||||||
<DownloadIcon />
|
<DownloadIcon />
|
||||||
</button>
|
</button>
|
||||||
@@ -804,8 +804,20 @@ function handleOpenEditVersionModal(versionId: string, projectId: string, stageI
|
|||||||
editModal.value?.openEditVersionModal(versionId, projectId, stageId)
|
editModal.value?.openEditVersionModal(versionId, projectId, stageId)
|
||||||
}
|
}
|
||||||
|
|
||||||
function openDependencyDownloadModal(projectId: string, event: MouseEvent) {
|
function openDependencyDownloadModal(
|
||||||
dependencyDownloadModal.value?.show(event, projectId)
|
dependencyProject: Labrinth.Projects.v2.Project,
|
||||||
|
event: MouseEvent,
|
||||||
|
) {
|
||||||
|
const baseGameVersions = new Set(version.value?.game_versions ?? [])
|
||||||
|
const baseLoaders = new Set(dependencyResolutionLoaders.value)
|
||||||
|
|
||||||
|
dependencyDownloadModal.value?.show(event, {
|
||||||
|
projectId: dependencyProject.id,
|
||||||
|
incompatibleGameVersions: dependencyProject.game_versions.filter(
|
||||||
|
(gameVersion) => !baseGameVersions.has(gameVersion),
|
||||||
|
),
|
||||||
|
incompatibleLoaders: dependencyProject.loaders.filter((loader) => !baseLoaders.has(loader)),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteVersionMutation = useMutation({
|
const deleteVersionMutation = useMutation({
|
||||||
|
|||||||
Reference in New Issue
Block a user