mirror of
https://github.com/modrinth/code.git
synced 2026-08-30 11:36:05 +00:00
feat: implement incompatible options due to base project
This commit is contained in:
@@ -17,20 +17,12 @@
|
||||
>
|
||||
<template #option="{ item, isSelected }">
|
||||
<div
|
||||
v-tooltip="
|
||||
!possibleGameVersions.includes(item.value)
|
||||
? formatMessage(messages.gameVersionUnsupportedTooltip, {
|
||||
title: project.title,
|
||||
gameVersion: item.value,
|
||||
platform: currentPlatformText,
|
||||
})
|
||||
: null
|
||||
"
|
||||
v-tooltip="gameVersionOptionTooltip(item.value)"
|
||||
class="flex w-full items-center justify-between gap-2"
|
||||
:class="{
|
||||
'text-brand-red opacity-40': !possibleGameVersions.includes(item.value),
|
||||
'text-brand-red opacity-40': isGameVersionUnavailable(item.value),
|
||||
'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>
|
||||
@@ -61,20 +53,12 @@
|
||||
>
|
||||
<template #option="{ item, isSelected }">
|
||||
<div
|
||||
v-tooltip="
|
||||
!possiblePlatforms.includes(item.value)
|
||||
? formatMessage(messages.platformUnsupportedTooltip, {
|
||||
title: project.title,
|
||||
platform: item.label,
|
||||
gameVersion: currentGameVersion,
|
||||
})
|
||||
: null
|
||||
"
|
||||
v-tooltip="platformOptionTooltip(item.value, item.label)"
|
||||
class="flex w-full items-center justify-between gap-2"
|
||||
:class="{
|
||||
'text-brand-red opacity-40': !possiblePlatforms.includes(item.value),
|
||||
'text-brand-red opacity-40': isPlatformUnavailable(item.value),
|
||||
'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>
|
||||
@@ -166,6 +150,8 @@ const props = withDefaults(
|
||||
downloadReason?: CdnDownloadReason
|
||||
initialGameVersion?: string | null
|
||||
initialPlatform?: string | null
|
||||
incompatibleGameVersions?: string[]
|
||||
incompatibleLoaders?: string[]
|
||||
resetKey?: number
|
||||
}>(),
|
||||
{
|
||||
@@ -173,6 +159,8 @@ const props = withDefaults(
|
||||
downloadReason: 'standalone',
|
||||
initialGameVersion: null,
|
||||
initialPlatform: null,
|
||||
incompatibleGameVersions: () => [],
|
||||
incompatibleLoaders: () => [],
|
||||
resetKey: 0,
|
||||
},
|
||||
)
|
||||
@@ -193,6 +181,9 @@ const userSelectedPlatform = ref<string | null>(props.initialPlatform)
|
||||
const showAllVersions = ref(defaultShowAllVersions())
|
||||
const versionFilter = ref('')
|
||||
|
||||
const incompatibleGameVersionsSet = computed(() => new Set(props.incompatibleGameVersions))
|
||||
const incompatibleLoadersSet = computed(() => new Set(props.incompatibleLoaders))
|
||||
|
||||
const showAllVersionsModel = computed({
|
||||
get() {
|
||||
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({
|
||||
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: {
|
||||
id: 'project.download.game-version-unsupported-tooltip',
|
||||
defaultMessage: '{title} does not support {gameVersion} for {platform}',
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
:download-reason="downloadReason"
|
||||
:initial-game-version="initialGameVersion"
|
||||
:initial-platform="initialPlatform"
|
||||
:incompatible-game-versions="showOptions.incompatibleGameVersions"
|
||||
:incompatible-loaders="showOptions.incompatibleLoaders"
|
||||
:reset-key="downloadProjectResetKey"
|
||||
@select-game-version="selectGameVersion"
|
||||
@select-platform="selectPlatform"
|
||||
@@ -89,7 +91,7 @@ import {
|
||||
} from '@modrinth/ui'
|
||||
import type { DisplayProjectType } from '@modrinth/utils'
|
||||
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 { saveFeatureFlags } from '~/composables/featureFlags.ts'
|
||||
@@ -116,6 +118,18 @@ type NewModalRef = {
|
||||
hide: () => void
|
||||
}
|
||||
|
||||
type ProjectDownloadModalShowOptions = {
|
||||
projectId?: string
|
||||
incompatibleGameVersions?: string[]
|
||||
incompatibleLoaders?: string[]
|
||||
}
|
||||
|
||||
type ResolvedProjectDownloadModalShowOptions = {
|
||||
projectId?: string
|
||||
incompatibleGameVersions: string[]
|
||||
incompatibleLoaders: string[]
|
||||
}
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
projectId?: string
|
||||
@@ -143,8 +157,11 @@ const debug = useDebugLogger('DownloadModal')
|
||||
const modal = ref<NewModalRef | null>(null)
|
||||
const modalOpen = ref(false)
|
||||
const showProjectId = ref<string | null>(null)
|
||||
const showOptions = ref<ResolvedProjectDownloadModalShowOptions>(getDefaultShowOptions())
|
||||
const downloadProjectResetKey = ref(0)
|
||||
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)
|
||||
|
||||
@@ -320,6 +337,7 @@ function selectPlatform(platform: string) {
|
||||
}
|
||||
|
||||
function onShow() {
|
||||
clearCloseStateResetTimeout()
|
||||
modalOpen.value = true
|
||||
debug('on-show fired')
|
||||
versionsEnabled.value = true
|
||||
@@ -331,17 +349,30 @@ function onShow() {
|
||||
function onHide() {
|
||||
const hadShowProjectId = !!showProjectId.value
|
||||
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) {
|
||||
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
|
||||
showProjectId.value = projectId ?? null
|
||||
clearCloseStateResetTimeout()
|
||||
showOptions.value = {
|
||||
...getDefaultShowOptions(),
|
||||
...options,
|
||||
}
|
||||
showProjectId.value = showOptions.value.projectId ?? null
|
||||
await nextTick()
|
||||
if (!(await loadProjectForModal(!!projectId))) return
|
||||
if (!(await loadProjectForModal(!!showOptions.value.projectId))) return
|
||||
modalOpen.value = true
|
||||
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) {
|
||||
if (!routeProjectId.value) return false
|
||||
if (!forceRefetch && projectRaw.value) return true
|
||||
@@ -411,5 +456,7 @@ watch(routeProjectId, () => {
|
||||
downloadProjectResetKey.value += 1
|
||||
})
|
||||
|
||||
onUnmounted(clearCloseStateResetTimeout)
|
||||
|
||||
defineExpose({ show, hide })
|
||||
</script>
|
||||
|
||||
@@ -374,7 +374,7 @@
|
||||
v-else-if="dependency.project"
|
||||
v-tooltip="formatMessage(messages.downloadProject)"
|
||||
:aria-label="formatMessage(messages.downloadProject)"
|
||||
@click="openDependencyDownloadModal(dependency.project.id, $event)"
|
||||
@click="openDependencyDownloadModal(dependency.project, $event)"
|
||||
>
|
||||
<DownloadIcon />
|
||||
</button>
|
||||
@@ -804,8 +804,20 @@ function handleOpenEditVersionModal(versionId: string, projectId: string, stageI
|
||||
editModal.value?.openEditVersionModal(versionId, projectId, stageId)
|
||||
}
|
||||
|
||||
function openDependencyDownloadModal(projectId: string, event: MouseEvent) {
|
||||
dependencyDownloadModal.value?.show(event, projectId)
|
||||
function openDependencyDownloadModal(
|
||||
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({
|
||||
|
||||
Reference in New Issue
Block a user