mirror of
https://github.com/modrinth/code.git
synced 2026-08-30 11:36:05 +00:00
feat+refactor: add nested dependencies route and move logic into related subcomponents
This commit is contained in:
@@ -1,15 +1,20 @@
|
||||
<template>
|
||||
<div v-if="dependencies.length > 0" class="flex flex-col gap-2">
|
||||
<h3 class="m-0 text-sm font-bold text-contrast">
|
||||
<div v-if="dependencyRows.length > 0" class="flex flex-col gap-2">
|
||||
<h3 v-if="showTitle" class="m-0 text-sm font-bold text-contrast">
|
||||
{{ formatMessage(messages.dependenciesTitle) }}
|
||||
</h3>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div v-for="dependency in dependencies" :key="dependency.key" class="flex flex-col gap-1.5">
|
||||
<div v-for="dependency in dependencyRows" :key="dependency.key" class="flex flex-col gap-1.5">
|
||||
<div
|
||||
class="grid min-h-9 grid-cols-[minmax(0,1fr)_min-content] items-center gap-2 rounded-xl bg-button-bg px-3 py-2 text-primary"
|
||||
>
|
||||
<span class="flex min-w-0 items-center gap-2">
|
||||
<Avatar v-if="dependency.icon" :src="dependency.icon" :alt="dependency.name" size="20px" />
|
||||
<Avatar
|
||||
v-if="dependency.icon"
|
||||
:src="dependency.icon"
|
||||
:alt="dependency.name"
|
||||
size="20px"
|
||||
/>
|
||||
<PackageIcon v-else aria-hidden="true" class="size-5 flex-shrink-0 text-secondary" />
|
||||
<a
|
||||
v-if="dependency.projectHref"
|
||||
@@ -40,50 +45,15 @@
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
<div
|
||||
v-for="subDependency in dependency.subDependencies"
|
||||
:key="subDependency.key"
|
||||
class="grid grid-cols-[1.5rem_minmax(0,1fr)] items-center gap-1 pl-5"
|
||||
v-if="dependency.subDependencies.length > 0"
|
||||
class="grid grid-cols-[1.5rem_minmax(0,1fr)] items-start gap-1 pl-5"
|
||||
>
|
||||
<RightArrowIcon aria-hidden="true" class="size-4 text-secondary" />
|
||||
<div
|
||||
class="grid min-h-9 grid-cols-[minmax(0,1fr)_min-content] items-center gap-2 rounded-xl bg-button-bg px-3 py-2 text-primary"
|
||||
>
|
||||
<span class="flex min-w-0 items-center gap-2">
|
||||
<Avatar
|
||||
v-if="subDependency.icon"
|
||||
:src="subDependency.icon"
|
||||
:alt="subDependency.name"
|
||||
size="20px"
|
||||
/>
|
||||
<PackageIcon v-else aria-hidden="true" class="size-5 flex-shrink-0 text-secondary" />
|
||||
<a
|
||||
v-if="subDependency.projectHref"
|
||||
:href="subDependency.projectHref"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="min-w-0 truncate font-semibold text-contrast no-underline hover:underline"
|
||||
>
|
||||
{{ subDependency.name }}
|
||||
</a>
|
||||
<span v-else class="min-w-0 truncate font-semibold text-contrast">
|
||||
{{ subDependency.name }}
|
||||
</span>
|
||||
<TagItem class="shrink-0 border !border-solid border-surface-5">
|
||||
{{ subDependency.typeLabel }}
|
||||
</TagItem>
|
||||
</span>
|
||||
<ButtonStyled v-if="subDependency.downloadHref" circular type="transparent">
|
||||
<a
|
||||
v-tooltip="'Download'"
|
||||
:href="subDependency.downloadHref"
|
||||
:download="subDependency.filename"
|
||||
:aria-label="`Download ${subDependency.name}`"
|
||||
@click="emit('download')"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" class="size-5 text-secondary" />
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
<RightArrowIcon aria-hidden="true" class="mt-2.5 size-4 text-secondary" />
|
||||
<DownloadDependencies
|
||||
:dependencies="dependency.subDependencies"
|
||||
:show-title="false"
|
||||
@download="emit('download')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -92,22 +62,205 @@
|
||||
|
||||
<script setup>
|
||||
import { DownloadIcon, PackageIcon, RightArrowIcon } from '@modrinth/assets'
|
||||
import { Avatar, ButtonStyled, defineMessages, TagItem, useVIntl } from '@modrinth/ui'
|
||||
import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
defineMessages,
|
||||
injectModrinthClient,
|
||||
TagItem,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
import { computed } from 'vue'
|
||||
|
||||
defineOptions({
|
||||
name: 'DownloadDependencies',
|
||||
})
|
||||
|
||||
defineProps({
|
||||
const props = defineProps({
|
||||
dependencies: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
default: null,
|
||||
},
|
||||
project: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
selectedVersion: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
currentGameVersion: {
|
||||
type: [String, Boolean],
|
||||
default: null,
|
||||
},
|
||||
currentPlatform: {
|
||||
type: [String, Boolean],
|
||||
default: null,
|
||||
},
|
||||
downloadReason: {
|
||||
type: String,
|
||||
default: 'standalone',
|
||||
},
|
||||
showTitle: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['download'])
|
||||
const client = injectModrinthClient()
|
||||
const { createProjectDownloadUrl } = useCdnDownloadContext()
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const dependencyResolutionPreferences = computed(() => ({
|
||||
game_versions: props.currentGameVersion ? [props.currentGameVersion] : [],
|
||||
loaders: props.currentPlatform ? [props.currentPlatform] : [],
|
||||
}))
|
||||
|
||||
const { data: dependencyResolution } = useQuery({
|
||||
queryKey: computed(() => [
|
||||
'project-download-modal',
|
||||
'content-resolve',
|
||||
props.project?.id,
|
||||
props.selectedVersion?.id,
|
||||
props.project?.project_type,
|
||||
dependencyResolutionPreferences.value,
|
||||
]),
|
||||
queryFn: () =>
|
||||
client.labrinth.content_v3.resolve({
|
||||
project_id: props.project.id,
|
||||
version_id: props.selectedVersion.id,
|
||||
content_type: resolveContentType(props.project.project_type),
|
||||
selected: dependencyResolutionPreferences.value,
|
||||
target: dependencyResolutionPreferences.value,
|
||||
}),
|
||||
enabled: computed(() => !!props.project && !!props.selectedVersion),
|
||||
})
|
||||
|
||||
const dependencyVersionIds = computed(() => {
|
||||
return [
|
||||
...new Set(
|
||||
(dependencyResolution.value?.dependencies || []).map((dependency) => dependency.version_id),
|
||||
),
|
||||
]
|
||||
})
|
||||
|
||||
const { data: dependencyVersions } = useQuery({
|
||||
queryKey: computed(() => [
|
||||
'project-download-modal',
|
||||
'resolved-versions',
|
||||
dependencyVersionIds.value,
|
||||
]),
|
||||
queryFn: () => client.labrinth.versions_v3.getVersions(dependencyVersionIds.value),
|
||||
enabled: computed(() => dependencyVersionIds.value.length > 0),
|
||||
})
|
||||
|
||||
const dependencyVersionById = computed(() => {
|
||||
const map = new Map()
|
||||
for (const version of dependencyVersions.value || []) {
|
||||
if (!version) continue
|
||||
map.set(version.id, version)
|
||||
}
|
||||
return map
|
||||
})
|
||||
|
||||
const dependencyProjectIds = computed(() => {
|
||||
return [
|
||||
...new Set(
|
||||
(dependencyResolution.value?.dependencies || []).map((dependency) => dependency.project_id),
|
||||
),
|
||||
]
|
||||
})
|
||||
|
||||
const { data: dependencyProjects } = useQuery({
|
||||
queryKey: computed(() => [
|
||||
'project-download-modal',
|
||||
'resolved-projects',
|
||||
dependencyProjectIds.value,
|
||||
]),
|
||||
queryFn: () => client.labrinth.projects_v2.getMultiple(dependencyProjectIds.value),
|
||||
enabled: computed(() => dependencyProjectIds.value.length > 0),
|
||||
})
|
||||
|
||||
const dependencyProjectById = computed(() => {
|
||||
const map = new Map()
|
||||
for (const project of dependencyProjects.value || []) {
|
||||
map.set(project.id, project)
|
||||
}
|
||||
return map
|
||||
})
|
||||
|
||||
const dependenciesByParentVersionId = computed(() => {
|
||||
const map = new Map()
|
||||
|
||||
for (const dependency of dependencyResolution.value?.dependencies || []) {
|
||||
if (!dependency.dependent_on_version_id) continue
|
||||
|
||||
const dependencies = map.get(dependency.dependent_on_version_id) || []
|
||||
dependencies.push(dependency)
|
||||
map.set(dependency.dependent_on_version_id, dependencies)
|
||||
}
|
||||
|
||||
return map
|
||||
})
|
||||
|
||||
const resolvedDependencyRows = computed(() => {
|
||||
const primaryVersionId =
|
||||
dependencyResolution.value?.primary.version_id || props.selectedVersion?.id
|
||||
if (!primaryVersionId) return []
|
||||
|
||||
return (dependenciesByParentVersionId.value.get(primaryVersionId) || []).map((dependency) =>
|
||||
createDependencyRow(dependency),
|
||||
)
|
||||
})
|
||||
|
||||
const dependencyRows = computed(() => props.dependencies || resolvedDependencyRows.value)
|
||||
|
||||
function primaryFileForVersion(version) {
|
||||
return version?.files?.find((file) => file.primary) || version?.files?.[0]
|
||||
}
|
||||
|
||||
function createDependencyRow(dependency) {
|
||||
const version = dependencyVersionById.value.get(dependency.version_id)
|
||||
const project = dependencyProjectById.value.get(dependency.project_id)
|
||||
const primaryFile = primaryFileForVersion(version)
|
||||
const name =
|
||||
project?.title ||
|
||||
version?.name ||
|
||||
version?.version_number ||
|
||||
dependency.version_id ||
|
||||
dependency.project_id ||
|
||||
'Dependency'
|
||||
|
||||
return {
|
||||
key: `${dependency.project_id}-${dependency.version_id}`,
|
||||
name,
|
||||
icon: project?.icon_url,
|
||||
projectHref: project ? `/${project.project_type}/${project.slug || project.id}` : undefined,
|
||||
downloadHref: primaryFile ? getDownloadUrl(primaryFile.url) : undefined,
|
||||
filename: primaryFile?.filename,
|
||||
typeLabel: 'Required',
|
||||
subDependencies: (dependenciesByParentVersionId.value.get(dependency.version_id) || []).map(
|
||||
(subDependency) => createDependencyRow(subDependency),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
function resolveContentType(projectType) {
|
||||
return ['mod', 'plugin', 'datapack', 'resourcepack', 'shader', 'modpack'].includes(projectType)
|
||||
? projectType
|
||||
: 'mod'
|
||||
}
|
||||
|
||||
function getDownloadUrl(url) {
|
||||
return createProjectDownloadUrl(url, {
|
||||
reason: props.downloadReason,
|
||||
gameVersion: props.currentGameVersion ?? undefined,
|
||||
loader: props.currentPlatform ?? undefined,
|
||||
})
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
dependenciesTitle: {
|
||||
id: 'project.download.dependencies-title',
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
:no-options-message="formatMessage(messages.noGameVersionsFound)"
|
||||
trigger-class="!rounded-xl !bg-button-bg !px-3 !py-2"
|
||||
dropdown-class="!rounded-xl"
|
||||
@update:model-value="emit('selectGameVersion', $event)"
|
||||
@search-input="emit('update:versionFilter', $event)"
|
||||
@close="emit('update:versionFilter', '')"
|
||||
@update:model-value="selectGameVersion"
|
||||
@search-input="versionFilter = $event"
|
||||
@close="versionFilter = ''"
|
||||
>
|
||||
<template #option="{ item, isSelected }">
|
||||
<div
|
||||
@@ -37,7 +37,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #dropdown-footer>
|
||||
<div v-if="showVersionsCheckbox" class="border-0 border-t border-solid border-surface-5 p-3">
|
||||
<div
|
||||
v-if="showVersionsCheckbox"
|
||||
class="border-0 border-t border-solid border-surface-5 p-3"
|
||||
>
|
||||
<Checkbox
|
||||
v-model="showAllVersionsModel"
|
||||
:label="formatMessage(messages.showAllVersions)"
|
||||
@@ -54,7 +57,7 @@
|
||||
:placeholder="formatMessage(messages.selectPlatform)"
|
||||
trigger-class="!rounded-xl !bg-button-bg !px-3 !py-2"
|
||||
dropdown-class="!rounded-xl"
|
||||
@update:model-value="emit('selectPlatform', $event)"
|
||||
@update:model-value="selectPlatform"
|
||||
>
|
||||
<template #option="{ item, isSelected }">
|
||||
<div
|
||||
@@ -111,13 +114,30 @@
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
<p v-else-if="currentPlatform && currentGameVersion && !versionsLoading && versions.length > 0">
|
||||
{{
|
||||
formatMessage(messages.noVersionsAvailable, {
|
||||
gameVersion: currentGameVersion,
|
||||
platform: currentPlatformText,
|
||||
})
|
||||
}}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { DownloadIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled, Checkbox, Combobox, defineMessages, useVIntl } from '@modrinth/ui'
|
||||
import {
|
||||
ButtonStyled,
|
||||
Checkbox,
|
||||
Combobox,
|
||||
defineMessages,
|
||||
getTagMessage,
|
||||
useDebugLogger,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import VersionChannelTag from '@modrinth/ui/src/components/version/VersionChannelTag.vue'
|
||||
import { computed } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
||||
defineOptions({
|
||||
name: 'DownloadProject',
|
||||
@@ -128,78 +148,262 @@ const props = defineProps({
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
gameVersionOptions: {
|
||||
versions: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
currentGameVersion: {
|
||||
type: [String, Boolean],
|
||||
default: null,
|
||||
},
|
||||
possibleGameVersions: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
currentPlatformText: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
showVersionsCheckbox: {
|
||||
versionsLoading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showAllVersions: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
tags: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
versionFilter: {
|
||||
downloadReason: {
|
||||
type: String,
|
||||
default: '',
|
||||
default: 'standalone',
|
||||
},
|
||||
currentPlatform: {
|
||||
type: [String, Boolean],
|
||||
default: null,
|
||||
},
|
||||
platformOptions: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
possiblePlatforms: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
selectedVersion: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
selectedPrimaryFile: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
selectedPrimaryFileDownloadUrl: {
|
||||
initialGameVersion: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
initialPlatform: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
resetKey: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits([
|
||||
'download',
|
||||
'selectGameVersion',
|
||||
'selectPlatform',
|
||||
'update:showAllVersions',
|
||||
'update:versionFilter',
|
||||
])
|
||||
const emit = defineEmits(['download', 'selectGameVersion', 'selectPlatform', 'update:selection'])
|
||||
const { formatMessage } = useVIntl()
|
||||
const { createProjectDownloadUrl } = useCdnDownloadContext()
|
||||
const debug = useDebugLogger('DownloadProject')
|
||||
|
||||
const userSelectedGameVersion = ref(props.initialGameVersion)
|
||||
const userSelectedPlatform = ref(props.initialPlatform)
|
||||
const showAllVersions = ref(defaultShowAllVersions())
|
||||
const versionFilter = ref('')
|
||||
|
||||
const showAllVersionsModel = computed({
|
||||
get() {
|
||||
return props.showAllVersions
|
||||
return showAllVersions.value
|
||||
},
|
||||
set(value) {
|
||||
emit('update:showAllVersions', value)
|
||||
showAllVersions.value = value
|
||||
},
|
||||
})
|
||||
|
||||
const currentGameVersion = computed(() => {
|
||||
return (
|
||||
userSelectedGameVersion.value ||
|
||||
(props.project.game_versions.length === 1 && props.project.game_versions[0])
|
||||
)
|
||||
})
|
||||
|
||||
const possibleGameVersions = computed(() => {
|
||||
return props.versions
|
||||
.filter((x) => !currentPlatform.value || x.loaders.includes(currentPlatform.value))
|
||||
.flatMap((x) => x.game_versions)
|
||||
})
|
||||
|
||||
const possiblePlatforms = computed(() => {
|
||||
return props.versions
|
||||
.filter((x) => !currentGameVersion.value || x.game_versions.includes(currentGameVersion.value))
|
||||
.flatMap((x) => x.loaders)
|
||||
})
|
||||
|
||||
const currentPlatform = computed(() => {
|
||||
return (
|
||||
userSelectedPlatform.value || (props.project.loaders.length === 1 && props.project.loaders[0])
|
||||
)
|
||||
})
|
||||
|
||||
const currentPlatformText = computed(() => {
|
||||
if (!currentPlatform.value) return null
|
||||
return formatMessage(getTagMessage(currentPlatform.value, 'loader'))
|
||||
})
|
||||
|
||||
const releaseVersions = computed(() => {
|
||||
const set = new Set()
|
||||
for (const gameVersion of props.tags.gameVersions || []) {
|
||||
if (gameVersion?.version && gameVersion.version_type === 'release') {
|
||||
set.add(gameVersion.version)
|
||||
}
|
||||
}
|
||||
return set
|
||||
})
|
||||
|
||||
const nonReleaseVersions = computed(() => {
|
||||
const set = new Set()
|
||||
for (const gameVersion of props.tags.gameVersions || []) {
|
||||
if (gameVersion?.version && gameVersion.version_type !== 'release') {
|
||||
set.add(gameVersion.version)
|
||||
}
|
||||
}
|
||||
return set
|
||||
})
|
||||
|
||||
const showVersionsCheckbox = computed(() => {
|
||||
let hasRelease = false
|
||||
let hasNonRelease = false
|
||||
|
||||
for (const version of props.project.game_versions) {
|
||||
if (isReleaseGameVersion(version)) {
|
||||
hasRelease = true
|
||||
} else {
|
||||
hasNonRelease = true
|
||||
}
|
||||
|
||||
if (hasRelease && hasNonRelease) return true
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
const filteredGameVersions = computed(() => {
|
||||
return props.project.game_versions
|
||||
.filter(
|
||||
(x) =>
|
||||
(versionFilter.value && x.includes(versionFilter.value)) ||
|
||||
(!versionFilter.value && (showAllVersions.value || isReleaseGameVersion(x))),
|
||||
)
|
||||
.slice()
|
||||
.reverse()
|
||||
})
|
||||
|
||||
const gameVersionOptions = computed(() => {
|
||||
return filteredGameVersions.value.map((gameVersion) => ({
|
||||
value: gameVersion,
|
||||
label: gameVersion,
|
||||
}))
|
||||
})
|
||||
|
||||
const platformOptions = computed(() => {
|
||||
return props.project.loaders
|
||||
.slice()
|
||||
.reverse()
|
||||
.map((platform) => ({
|
||||
value: platform,
|
||||
label: formatMessage(getTagMessage(platform, 'loader')),
|
||||
}))
|
||||
})
|
||||
|
||||
const filteredVersions = computed(() => {
|
||||
const result = props.versions.filter(
|
||||
(x) =>
|
||||
x.game_versions?.includes(currentGameVersion.value) &&
|
||||
(x.loaders?.includes(currentPlatform.value) || props.project.project_type === 'resourcepack'),
|
||||
)
|
||||
debug('filteredVersions', {
|
||||
total: props.versions.length,
|
||||
filtered: result.length,
|
||||
currentGameVersion: currentGameVersion.value,
|
||||
currentPlatform: currentPlatform.value,
|
||||
sampleLoaders: props.versions.slice(0, 3).map((v) => v.loaders),
|
||||
})
|
||||
return result
|
||||
})
|
||||
|
||||
const filteredRelease = computed(() => {
|
||||
return filteredVersions.value.find((x) => x.version_type === 'release')
|
||||
})
|
||||
|
||||
const filteredBeta = computed(() => {
|
||||
return filteredVersions.value.find(
|
||||
(x) =>
|
||||
x.version_type === 'beta' &&
|
||||
(!filteredRelease.value ||
|
||||
dayjs(x.date_published).isAfter(dayjs(filteredRelease.value.date_published))),
|
||||
)
|
||||
})
|
||||
|
||||
const filteredAlpha = computed(() => {
|
||||
return filteredVersions.value.find(
|
||||
(x) =>
|
||||
x.version_type === 'alpha' &&
|
||||
(!filteredRelease.value ||
|
||||
dayjs(x.date_published).isAfter(dayjs(filteredRelease.value.date_published))) &&
|
||||
(!filteredBeta.value ||
|
||||
dayjs(x.date_published).isAfter(dayjs(filteredBeta.value.date_published))),
|
||||
)
|
||||
})
|
||||
|
||||
const selectedVersion = computed(() => {
|
||||
return filteredRelease.value || filteredBeta.value || filteredAlpha.value
|
||||
})
|
||||
|
||||
const selectedPrimaryFile = computed(() => {
|
||||
return (
|
||||
selectedVersion.value?.files?.find((file) => file.primary) || selectedVersion.value?.files?.[0]
|
||||
)
|
||||
})
|
||||
|
||||
const selectedPrimaryFileDownloadUrl = computed(() => {
|
||||
if (!selectedPrimaryFile.value) return null
|
||||
return getDownloadUrl(selectedPrimaryFile.value.url)
|
||||
})
|
||||
|
||||
watch(
|
||||
[currentGameVersion, currentPlatform, selectedVersion, selectedPrimaryFile],
|
||||
() => {
|
||||
emit('update:selection', {
|
||||
currentGameVersion: currentGameVersion.value,
|
||||
currentPlatform: currentPlatform.value,
|
||||
selectedVersion: selectedVersion.value,
|
||||
selectedPrimaryFile: selectedPrimaryFile.value,
|
||||
})
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.resetKey,
|
||||
() => {
|
||||
userSelectedGameVersion.value = null
|
||||
userSelectedPlatform.value = null
|
||||
showAllVersions.value = defaultShowAllVersions()
|
||||
versionFilter.value = ''
|
||||
},
|
||||
)
|
||||
|
||||
function selectGameVersion(gameVersion) {
|
||||
userSelectedGameVersion.value = gameVersion
|
||||
emit('selectGameVersion', gameVersion)
|
||||
}
|
||||
|
||||
function selectPlatform(platform) {
|
||||
userSelectedPlatform.value = platform
|
||||
emit('selectPlatform', platform)
|
||||
}
|
||||
|
||||
function getDownloadUrl(url) {
|
||||
return createProjectDownloadUrl(url, {
|
||||
reason: props.downloadReason,
|
||||
gameVersion: currentGameVersion.value ?? undefined,
|
||||
loader: currentPlatform.value ?? undefined,
|
||||
})
|
||||
}
|
||||
|
||||
function isReleaseGameVersion(version) {
|
||||
if (releaseVersions.value.has(version)) return true
|
||||
if (nonReleaseVersions.value.has(version)) return false
|
||||
return true
|
||||
}
|
||||
|
||||
function defaultShowAllVersions() {
|
||||
return (
|
||||
props.project.game_versions.length > 0 &&
|
||||
props.project.game_versions.every((projectVersion) => {
|
||||
const gameVersion = props.tags.gameVersions?.find((x) => x.version === projectVersion)
|
||||
return gameVersion?.version_type && gameVersion.version_type !== 'release'
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
gameVersionUnsupportedTooltip: {
|
||||
id: 'project.download.game-version-unsupported-tooltip',
|
||||
@@ -213,6 +417,10 @@ const messages = defineMessages({
|
||||
id: 'project.download.no-game-versions-found',
|
||||
defaultMessage: 'No game versions found',
|
||||
},
|
||||
noVersionsAvailable: {
|
||||
id: 'project.download.no-versions-available',
|
||||
defaultMessage: 'No versions available for {gameVersion} and {platform}.',
|
||||
},
|
||||
platformUnsupportedTooltip: {
|
||||
id: 'project.download.platform-unsupported-tooltip',
|
||||
defaultMessage: '{title} does not support {platform} for {gameVersion}',
|
||||
|
||||
@@ -11,43 +11,27 @@
|
||||
<InstallWithModrinthApp :project="project" :tags="tags" />
|
||||
<DownloadProject
|
||||
:project="project"
|
||||
:game-version-options="gameVersionOptions"
|
||||
:current-game-version="currentGameVersion"
|
||||
:possible-game-versions="possibleGameVersions"
|
||||
:current-platform-text="currentPlatformText"
|
||||
:show-versions-checkbox="showVersionsCheckbox"
|
||||
:show-all-versions="showAllVersions"
|
||||
:version-filter="versionFilter"
|
||||
:current-platform="currentPlatform"
|
||||
:platform-options="platformOptions"
|
||||
:possible-platforms="possiblePlatforms"
|
||||
:selected-version="selectedVersion"
|
||||
:selected-primary-file="selectedPrimaryFile"
|
||||
:selected-primary-file-download-url="selectedPrimaryFileDownloadUrl"
|
||||
:versions="versions"
|
||||
:versions-loading="versionsLoading"
|
||||
:tags="tags"
|
||||
:download-reason="downloadReason"
|
||||
:initial-game-version="initialGameVersion"
|
||||
:initial-platform="initialPlatform"
|
||||
:reset-key="downloadProjectResetKey"
|
||||
@select-game-version="selectGameVersion"
|
||||
@select-platform="selectPlatform"
|
||||
@update:show-all-versions="showAllVersions = $event"
|
||||
@update:version-filter="versionFilter = $event"
|
||||
@update:selection="projectDownloadSelection = $event"
|
||||
@download="onDownload"
|
||||
/>
|
||||
<div class="flex flex-col gap-4">
|
||||
<p
|
||||
v-if="
|
||||
currentPlatform &&
|
||||
currentGameVersion &&
|
||||
!selectedVersion &&
|
||||
!versionsLoading &&
|
||||
versions.length > 0
|
||||
"
|
||||
>
|
||||
{{
|
||||
formatMessage(messages.noVersionsAvailable, {
|
||||
gameVersion: currentGameVersion,
|
||||
platform: currentPlatformText,
|
||||
})
|
||||
}}
|
||||
</p>
|
||||
<DownloadDependencies :dependencies="dependencyRows" @download="onDownload" />
|
||||
<DownloadDependencies
|
||||
:project="project"
|
||||
:selected-version="selectedVersion"
|
||||
:current-game-version="currentGameVersion"
|
||||
:current-platform="currentPlatform"
|
||||
:download-reason="downloadReason"
|
||||
@download="onDownload"
|
||||
/>
|
||||
<div v-if="additionalFiles.length > 0" class="flex flex-col gap-2">
|
||||
<h3 class="m-0 text-sm font-bold text-contrast">
|
||||
{{ formatMessage(messages.additionalFilesTitle) }}
|
||||
@@ -97,16 +81,12 @@ import { DownloadIcon, FileIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Avatar,
|
||||
defineMessages,
|
||||
getTagMessage,
|
||||
injectModrinthClient,
|
||||
NewModal,
|
||||
ServersPromo,
|
||||
useDebugLogger,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
import dayjs from 'dayjs'
|
||||
import { computed, nextTick, ref, watch } from 'vue'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
||||
import { navigateTo } from '#app'
|
||||
import { saveFeatureFlags } from '~/composables/featureFlags.ts'
|
||||
@@ -146,389 +126,38 @@ const emit = defineEmits(['download'])
|
||||
|
||||
const route = useRoute()
|
||||
const flags = useFeatureFlags()
|
||||
const client = injectModrinthClient()
|
||||
const { createProjectDownloadUrl } = useCdnDownloadContext()
|
||||
const { formatMessage } = useVIntl()
|
||||
const debug = useDebugLogger('DownloadModal')
|
||||
|
||||
const modal = ref()
|
||||
const modalOpen = ref(false)
|
||||
const userSelectedGameVersion = ref(null)
|
||||
const userSelectedPlatform = ref(null)
|
||||
const showAllVersions = ref(false)
|
||||
const versionFilter = ref('')
|
||||
|
||||
const currentGameVersion = computed(() => {
|
||||
return (
|
||||
userSelectedGameVersion.value ||
|
||||
(props.project.game_versions.length === 1 && props.project.game_versions[0])
|
||||
)
|
||||
const downloadProjectResetKey = ref(0)
|
||||
const projectDownloadSelection = ref({
|
||||
currentGameVersion: null,
|
||||
currentPlatform: null,
|
||||
selectedVersion: null,
|
||||
selectedPrimaryFile: null,
|
||||
})
|
||||
|
||||
const possibleGameVersions = computed(() => {
|
||||
return props.versions
|
||||
.filter((x) => !currentPlatform.value || x.loaders.includes(currentPlatform.value))
|
||||
.flatMap((x) => x.game_versions)
|
||||
})
|
||||
const { version, loader } = route.query
|
||||
const initialGameVersion = ref(
|
||||
typeof version === 'string' && props.project.game_versions.includes(version) ? version : null,
|
||||
)
|
||||
const initialPlatform = ref(
|
||||
typeof loader === 'string' && props.project.loaders.includes(loader) ? loader : null,
|
||||
)
|
||||
|
||||
const possiblePlatforms = computed(() => {
|
||||
return props.versions
|
||||
.filter((x) => !currentGameVersion.value || x.game_versions.includes(currentGameVersion.value))
|
||||
.flatMap((x) => x.loaders)
|
||||
})
|
||||
|
||||
const currentPlatform = computed(() => {
|
||||
return (
|
||||
userSelectedPlatform.value || (props.project.loaders.length === 1 && props.project.loaders[0])
|
||||
)
|
||||
})
|
||||
|
||||
const currentPlatformText = computed(() => {
|
||||
if (!currentPlatform.value) return null
|
||||
return formatMessage(getTagMessage(currentPlatform.value, 'loader'))
|
||||
})
|
||||
|
||||
const releaseVersions = computed(() => {
|
||||
const set = new Set()
|
||||
for (const gameVersion of props.tags.gameVersions || []) {
|
||||
if (gameVersion?.version && gameVersion.version_type === 'release') {
|
||||
set.add(gameVersion.version)
|
||||
}
|
||||
}
|
||||
return set
|
||||
})
|
||||
|
||||
const nonReleaseVersions = computed(() => {
|
||||
const set = new Set()
|
||||
for (const gameVersion of props.tags.gameVersions || []) {
|
||||
if (gameVersion?.version && gameVersion.version_type !== 'release') {
|
||||
set.add(gameVersion.version)
|
||||
}
|
||||
}
|
||||
return set
|
||||
})
|
||||
|
||||
const showVersionsCheckbox = computed(() => {
|
||||
let hasRelease = false
|
||||
let hasNonRelease = false
|
||||
|
||||
for (const version of props.project.game_versions) {
|
||||
if (isReleaseGameVersion(version)) {
|
||||
hasRelease = true
|
||||
} else {
|
||||
hasNonRelease = true
|
||||
}
|
||||
|
||||
if (hasRelease && hasNonRelease) return true
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
const filteredGameVersions = computed(() => {
|
||||
return props.project.game_versions
|
||||
.filter(
|
||||
(x) =>
|
||||
(versionFilter.value && x.includes(versionFilter.value)) ||
|
||||
(!versionFilter.value && (showAllVersions.value || isReleaseGameVersion(x))),
|
||||
)
|
||||
.slice()
|
||||
.reverse()
|
||||
})
|
||||
|
||||
const gameVersionOptions = computed(() => {
|
||||
return filteredGameVersions.value.map((gameVersion) => ({
|
||||
value: gameVersion,
|
||||
label: gameVersion,
|
||||
}))
|
||||
})
|
||||
|
||||
const platformOptions = computed(() => {
|
||||
return props.project.loaders
|
||||
.slice()
|
||||
.reverse()
|
||||
.map((platform) => ({
|
||||
value: platform,
|
||||
label: formatMessage(getTagMessage(platform, 'loader')),
|
||||
}))
|
||||
})
|
||||
|
||||
const filteredVersions = computed(() => {
|
||||
const result = props.versions.filter(
|
||||
(x) =>
|
||||
x.game_versions?.includes(currentGameVersion.value) &&
|
||||
(x.loaders?.includes(currentPlatform.value) || props.project.project_type === 'resourcepack'),
|
||||
)
|
||||
debug('filteredVersions', {
|
||||
total: props.versions.length,
|
||||
filtered: result.length,
|
||||
currentGameVersion: currentGameVersion.value,
|
||||
currentPlatform: currentPlatform.value,
|
||||
sampleLoaders: props.versions.slice(0, 3).map((v) => v.loaders),
|
||||
})
|
||||
return result
|
||||
})
|
||||
|
||||
const filteredRelease = computed(() => {
|
||||
return filteredVersions.value.find((x) => x.version_type === 'release')
|
||||
})
|
||||
|
||||
const filteredBeta = computed(() => {
|
||||
return filteredVersions.value.find(
|
||||
(x) =>
|
||||
x.version_type === 'beta' &&
|
||||
(!filteredRelease.value ||
|
||||
dayjs(x.date_published).isAfter(dayjs(filteredRelease.value.date_published))),
|
||||
)
|
||||
})
|
||||
|
||||
const filteredAlpha = computed(() => {
|
||||
return filteredVersions.value.find(
|
||||
(x) =>
|
||||
x.version_type === 'alpha' &&
|
||||
(!filteredRelease.value ||
|
||||
dayjs(x.date_published).isAfter(dayjs(filteredRelease.value.date_published))) &&
|
||||
(!filteredBeta.value ||
|
||||
dayjs(x.date_published).isAfter(dayjs(filteredBeta.value.date_published))),
|
||||
)
|
||||
})
|
||||
|
||||
const selectedVersion = computed(() => {
|
||||
return filteredRelease.value || filteredBeta.value || filteredAlpha.value
|
||||
})
|
||||
|
||||
const selectedPrimaryFile = computed(() => {
|
||||
return (
|
||||
selectedVersion.value?.files?.find((file) => file.primary) || selectedVersion.value?.files?.[0]
|
||||
)
|
||||
})
|
||||
|
||||
const selectedPrimaryFileDownloadUrl = computed(() => {
|
||||
if (!selectedPrimaryFile.value) return null
|
||||
return getDownloadUrl(selectedPrimaryFile.value.url)
|
||||
})
|
||||
const currentGameVersion = computed(() => projectDownloadSelection.value.currentGameVersion)
|
||||
const currentPlatform = computed(() => projectDownloadSelection.value.currentPlatform)
|
||||
const selectedVersion = computed(() => projectDownloadSelection.value.selectedVersion)
|
||||
const selectedPrimaryFile = computed(() => projectDownloadSelection.value.selectedPrimaryFile)
|
||||
|
||||
const additionalFiles = computed(() => {
|
||||
if (!selectedVersion.value || !selectedPrimaryFile.value) return []
|
||||
return selectedVersion.value.files.filter((file) => file !== selectedPrimaryFile.value)
|
||||
})
|
||||
|
||||
const selectedDependencyVersionIds = computed(() => {
|
||||
if (!selectedVersion.value) return []
|
||||
|
||||
return [
|
||||
...new Set(
|
||||
selectedVersion.value.dependencies.map((dependency) => dependency.version_id).filter(Boolean),
|
||||
),
|
||||
]
|
||||
})
|
||||
|
||||
const { data: dependencyVersions } = useQuery({
|
||||
queryKey: computed(() => [
|
||||
'project-download-modal',
|
||||
'versions',
|
||||
selectedDependencyVersionIds.value,
|
||||
]),
|
||||
queryFn: () => client.labrinth.versions_v3.getVersions(selectedDependencyVersionIds.value),
|
||||
enabled: computed(() => selectedDependencyVersionIds.value.length > 0),
|
||||
})
|
||||
|
||||
const fallbackDependencyProjectIds = computed(() => {
|
||||
if (!selectedVersion.value) return []
|
||||
|
||||
return [
|
||||
...new Set(
|
||||
selectedVersion.value.dependencies
|
||||
.filter(
|
||||
(dependency) =>
|
||||
['required', 'optional'].includes(dependency.dependency_type) &&
|
||||
!dependency.version_id &&
|
||||
dependency.project_id,
|
||||
)
|
||||
.map((dependency) => dependency.project_id),
|
||||
),
|
||||
]
|
||||
})
|
||||
|
||||
const { data: fallbackDependencyVersions } = useQuery({
|
||||
queryKey: computed(() => [
|
||||
'project-download-modal',
|
||||
'fallback-versions',
|
||||
fallbackDependencyProjectIds.value,
|
||||
]),
|
||||
queryFn: async () =>
|
||||
Promise.all(
|
||||
fallbackDependencyProjectIds.value.map(async (projectId) => {
|
||||
const versions = await client.labrinth.versions_v3.getProjectVersions(projectId, {
|
||||
include_changelog: false,
|
||||
limit: 1,
|
||||
})
|
||||
|
||||
return versions[0]
|
||||
}),
|
||||
),
|
||||
enabled: computed(() => fallbackDependencyProjectIds.value.length > 0),
|
||||
})
|
||||
|
||||
const subDependencyVersionIds = computed(() => {
|
||||
const ids = new Set()
|
||||
|
||||
for (const version of [
|
||||
...(dependencyVersions.value || []),
|
||||
...(fallbackDependencyVersions.value || []),
|
||||
]) {
|
||||
if (!version) continue
|
||||
for (const dependency of version.dependencies || []) {
|
||||
if (dependency.version_id) ids.add(dependency.version_id)
|
||||
}
|
||||
}
|
||||
|
||||
return [...ids]
|
||||
})
|
||||
|
||||
const { data: subDependencyVersions } = useQuery({
|
||||
queryKey: computed(() => [
|
||||
'project-download-modal',
|
||||
'sub-versions',
|
||||
subDependencyVersionIds.value,
|
||||
]),
|
||||
queryFn: () => client.labrinth.versions_v3.getVersions(subDependencyVersionIds.value),
|
||||
enabled: computed(() => subDependencyVersionIds.value.length > 0),
|
||||
})
|
||||
|
||||
const fallbackSubDependencyProjectIds = computed(() => {
|
||||
const ids = new Set()
|
||||
|
||||
for (const version of [
|
||||
...(dependencyVersions.value || []),
|
||||
...(fallbackDependencyVersions.value || []),
|
||||
]) {
|
||||
if (!version) continue
|
||||
for (const dependency of version.dependencies || []) {
|
||||
if (
|
||||
['required', 'optional'].includes(dependency.dependency_type) &&
|
||||
!dependency.version_id &&
|
||||
dependency.project_id
|
||||
) {
|
||||
ids.add(dependency.project_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [...ids]
|
||||
})
|
||||
|
||||
const { data: fallbackSubDependencyVersions } = useQuery({
|
||||
queryKey: computed(() => [
|
||||
'project-download-modal',
|
||||
'fallback-sub-versions',
|
||||
fallbackSubDependencyProjectIds.value,
|
||||
]),
|
||||
queryFn: async () =>
|
||||
Promise.all(
|
||||
fallbackSubDependencyProjectIds.value.map(async (projectId) => {
|
||||
const versions = await client.labrinth.versions_v3.getProjectVersions(projectId, {
|
||||
include_changelog: false,
|
||||
limit: 1,
|
||||
})
|
||||
|
||||
return versions[0]
|
||||
}),
|
||||
),
|
||||
enabled: computed(() => fallbackSubDependencyProjectIds.value.length > 0),
|
||||
})
|
||||
|
||||
const dependencyVersionById = computed(() => {
|
||||
const map = new Map()
|
||||
for (const version of [
|
||||
...(dependencyVersions.value || []),
|
||||
...(subDependencyVersions.value || []),
|
||||
...(fallbackDependencyVersions.value || []),
|
||||
...(fallbackSubDependencyVersions.value || []),
|
||||
]) {
|
||||
if (!version) continue
|
||||
map.set(version.id, version)
|
||||
}
|
||||
return map
|
||||
})
|
||||
|
||||
const fallbackDependencyVersionByProjectId = computed(() => {
|
||||
const map = new Map()
|
||||
for (const version of [
|
||||
...(fallbackDependencyVersions.value || []),
|
||||
...(fallbackSubDependencyVersions.value || []),
|
||||
]) {
|
||||
if (!version) continue
|
||||
map.set(version.project_id, version)
|
||||
}
|
||||
return map
|
||||
})
|
||||
|
||||
const selectedDependencyProjectIds = computed(() => {
|
||||
const ids = new Set()
|
||||
|
||||
for (const dependency of selectedVersion.value?.dependencies || []) {
|
||||
if (dependency.project_id) ids.add(dependency.project_id)
|
||||
const version = dependency.version_id
|
||||
? dependencyVersionById.value.get(dependency.version_id)
|
||||
: null
|
||||
if (version?.project_id) ids.add(version.project_id)
|
||||
}
|
||||
|
||||
for (const version of [
|
||||
...(dependencyVersions.value || []),
|
||||
...(fallbackDependencyVersions.value || []),
|
||||
]) {
|
||||
if (!version) continue
|
||||
for (const dependency of version.dependencies || []) {
|
||||
if (dependency.project_id) ids.add(dependency.project_id)
|
||||
const dependencyVersion = dependency.version_id
|
||||
? dependencyVersionById.value.get(dependency.version_id)
|
||||
: null
|
||||
if (dependencyVersion?.project_id) ids.add(dependencyVersion.project_id)
|
||||
}
|
||||
}
|
||||
|
||||
for (const version of [
|
||||
...(fallbackDependencyVersions.value || []),
|
||||
...(fallbackSubDependencyVersions.value || []),
|
||||
]) {
|
||||
if (version?.project_id) ids.add(version.project_id)
|
||||
}
|
||||
|
||||
return [...ids]
|
||||
})
|
||||
|
||||
const { data: dependencyProjects } = useQuery({
|
||||
queryKey: computed(() => [
|
||||
'project-download-modal',
|
||||
'projects',
|
||||
selectedDependencyProjectIds.value,
|
||||
]),
|
||||
queryFn: () => client.labrinth.projects_v2.getMultiple(selectedDependencyProjectIds.value),
|
||||
enabled: computed(() => selectedDependencyProjectIds.value.length > 0),
|
||||
})
|
||||
|
||||
const dependencyProjectById = computed(() => {
|
||||
const map = new Map()
|
||||
for (const project of dependencyProjects.value || []) {
|
||||
map.set(project.id, project)
|
||||
}
|
||||
return map
|
||||
})
|
||||
|
||||
const dependencyRows = computed(() => {
|
||||
if (!selectedVersion.value) return []
|
||||
|
||||
return selectedVersion.value.dependencies
|
||||
.filter((dependency) => ['required', 'optional'].includes(dependency.dependency_type))
|
||||
.sort(
|
||||
(a, b) =>
|
||||
dependencyTypeSortOrder(a.dependency_type) - dependencyTypeSortOrder(b.dependency_type),
|
||||
)
|
||||
.map((dependency) => createDependencyRow(dependency))
|
||||
})
|
||||
|
||||
const messages = defineMessages({
|
||||
downloadTitle: {
|
||||
id: 'project.download.title',
|
||||
@@ -538,32 +167,8 @@ const messages = defineMessages({
|
||||
id: 'project.download.additional-files-title',
|
||||
defaultMessage: 'Additional files',
|
||||
},
|
||||
noVersionsAvailable: {
|
||||
id: 'project.download.no-versions-available',
|
||||
defaultMessage: 'No versions available for {gameVersion} and {platform}.',
|
||||
},
|
||||
})
|
||||
|
||||
function dependencyTypeLabel(type) {
|
||||
return (
|
||||
{
|
||||
required: 'Required',
|
||||
optional: 'Optional',
|
||||
embedded: 'Embedded',
|
||||
incompatible: 'Incompatible',
|
||||
}[type] || type
|
||||
)
|
||||
}
|
||||
|
||||
function dependencyTypeSortOrder(type) {
|
||||
return (
|
||||
{
|
||||
required: 0,
|
||||
optional: 1,
|
||||
}[type] ?? 2
|
||||
)
|
||||
}
|
||||
|
||||
function fileTypeLabel(type) {
|
||||
return (
|
||||
{
|
||||
@@ -574,56 +179,6 @@ function fileTypeLabel(type) {
|
||||
)
|
||||
}
|
||||
|
||||
function primaryFileForVersion(version) {
|
||||
return version?.files?.find((file) => file.primary) || version?.files?.[0]
|
||||
}
|
||||
|
||||
function createDependencyRow(dependency, includeSubDependencies = true) {
|
||||
const explicitVersion = dependency.version_id
|
||||
? dependencyVersionById.value.get(dependency.version_id)
|
||||
: null
|
||||
// todo: need algorithm for picking version.
|
||||
// try to match same game version and loader. and if cannot, then disable button and have tooltip saying cannot find any compatible versions to download.
|
||||
const version =
|
||||
explicitVersion ||
|
||||
(dependency.project_id
|
||||
? fallbackDependencyVersionByProjectId.value.get(dependency.project_id)
|
||||
: null)
|
||||
const projectId = dependency.project_id || version?.project_id
|
||||
const project = projectId ? dependencyProjectById.value.get(projectId) : null
|
||||
const primaryFile = primaryFileForVersion(version)
|
||||
const name =
|
||||
project?.title ||
|
||||
dependency.file_name ||
|
||||
version?.name ||
|
||||
version?.version_number ||
|
||||
dependency.version_id ||
|
||||
dependency.project_id ||
|
||||
'Dependency'
|
||||
|
||||
return {
|
||||
key: `${dependency.dependency_type}-${dependency.version_id ?? dependency.project_id ?? dependency.file_name ?? name}`,
|
||||
name,
|
||||
icon: project?.icon_url,
|
||||
projectHref: project ? `/${project.project_type}/${project.slug || project.id}` : undefined,
|
||||
downloadHref: primaryFile ? getDownloadUrl(primaryFile.url) : undefined,
|
||||
filename: primaryFile?.filename,
|
||||
typeLabel: dependencyTypeLabel(dependency.dependency_type),
|
||||
subDependencies: includeSubDependencies
|
||||
? (version?.dependencies || [])
|
||||
.filter((subDependency) =>
|
||||
['required', 'optional'].includes(subDependency.dependency_type),
|
||||
)
|
||||
.sort(
|
||||
(a, b) =>
|
||||
dependencyTypeSortOrder(a.dependency_type) -
|
||||
dependencyTypeSortOrder(b.dependency_type),
|
||||
)
|
||||
.map((subDependency) => createDependencyRow(subDependency, false))
|
||||
: [],
|
||||
}
|
||||
}
|
||||
|
||||
function getDownloadUrl(url) {
|
||||
return createProjectDownloadUrl(url, {
|
||||
reason: props.downloadReason,
|
||||
@@ -632,22 +187,16 @@ function getDownloadUrl(url) {
|
||||
})
|
||||
}
|
||||
|
||||
function isReleaseGameVersion(version) {
|
||||
if (releaseVersions.value.has(version)) return true
|
||||
if (nonReleaseVersions.value.has(version)) return false
|
||||
return true
|
||||
}
|
||||
|
||||
function updateDownloadQuery() {
|
||||
function updateDownloadQuery({ gameVersion, platform }) {
|
||||
navigateTo(
|
||||
{
|
||||
query: {
|
||||
...route.query,
|
||||
...(userSelectedGameVersion.value && {
|
||||
version: userSelectedGameVersion.value,
|
||||
...(gameVersion && {
|
||||
version: gameVersion,
|
||||
}),
|
||||
...(userSelectedPlatform.value && {
|
||||
loader: userSelectedPlatform.value,
|
||||
...(platform && {
|
||||
loader: platform,
|
||||
}),
|
||||
},
|
||||
hash: route.hash,
|
||||
@@ -657,13 +206,17 @@ function updateDownloadQuery() {
|
||||
}
|
||||
|
||||
function selectGameVersion(gameVersion) {
|
||||
userSelectedGameVersion.value = gameVersion
|
||||
updateDownloadQuery()
|
||||
updateDownloadQuery({
|
||||
gameVersion,
|
||||
platform: currentPlatform.value,
|
||||
})
|
||||
}
|
||||
|
||||
function selectPlatform(platform) {
|
||||
userSelectedPlatform.value = platform
|
||||
updateDownloadQuery()
|
||||
updateDownloadQuery({
|
||||
gameVersion: currentGameVersion.value,
|
||||
platform,
|
||||
})
|
||||
}
|
||||
|
||||
function onShow() {
|
||||
@@ -687,22 +240,13 @@ function show(event) {
|
||||
function hide(event) {
|
||||
if (!modal.value || !modalOpen.value) return
|
||||
modal.value?.hide(event)
|
||||
userSelectedPlatform.value = null
|
||||
userSelectedGameVersion.value = null
|
||||
showAllVersions.value = false
|
||||
downloadProjectResetKey.value += 1
|
||||
}
|
||||
|
||||
function onDownload() {
|
||||
emit('download')
|
||||
}
|
||||
|
||||
function onVersionNavigate(url) {
|
||||
hide()
|
||||
nextTick(() => {
|
||||
navigateTo(url)
|
||||
})
|
||||
}
|
||||
|
||||
function openFromHash() {
|
||||
if (!modal.value || modalOpen.value || route.hash !== '#download') return
|
||||
|
||||
@@ -710,23 +254,6 @@ function openFromHash() {
|
||||
show()
|
||||
}
|
||||
|
||||
const { version, loader } = route.query
|
||||
|
||||
if (
|
||||
props.project.game_versions.length > 0 &&
|
||||
props.project.game_versions.every((projectVersion) => !isReleaseGameVersion(projectVersion))
|
||||
) {
|
||||
showAllVersions.value = true
|
||||
}
|
||||
|
||||
if (version !== undefined && props.project.game_versions.includes(version)) {
|
||||
userSelectedGameVersion.value = version
|
||||
}
|
||||
|
||||
if (loader !== undefined && props.project.loaders.includes(loader)) {
|
||||
userSelectedPlatform.value = loader
|
||||
}
|
||||
|
||||
if (route.hash === '#download' || version !== undefined || loader !== undefined) {
|
||||
debug('eager loadVersions from setup', { hash: route.hash, version, loader })
|
||||
props.loadVersions()
|
||||
|
||||
Reference in New Issue
Block a user