feat: if only dependency project is listed, find latest compatible dependency version to download in version page

This commit is contained in:
tdgao
2026-07-02 13:30:39 -07:00
parent 62748137aa
commit 0b69acb3aa
@@ -311,11 +311,25 @@
<template #dependencyActions="{ dependency }">
<ButtonStyled circular>
<nuxt-link
v-if="createDependencyLink(dependency)"
v-if="
createDependencyLink({
project: dependency.project,
version: dependency.version ?? getDependencyVersion(dependency.dependency),
})
"
v-tooltip="
formatMessage(dependency.version ? messages.viewVersion : messages.viewProject)
formatMessage(
(dependency.version ?? getDependencyVersion(dependency.dependency))
? messages.viewVersion
: messages.viewProject,
)
"
:to="
createDependencyLink({
project: dependency.project,
version: dependency.version ?? getDependencyVersion(dependency.dependency),
})
"
:to="createDependencyLink(dependency)"
target="_blank"
>
<ExternalIcon />
@@ -324,26 +338,29 @@
<ButtonStyled circular color="brand" color-fill="text">
<a
v-if="
dependency.version && dependency.dependency.dependency_type !== 'incompatible'
(dependency.version ?? getDependencyVersion(dependency.dependency)) &&
dependency.dependency.dependency_type !== 'incompatible'
"
v-tooltip="
dependencyVersionPrimaryFiles[dependency.version.id]
? dependencyVersionPrimaryFiles[dependency.version.id].filename +
' (' +
formatBytes(dependencyVersionPrimaryFiles[dependency.version.id].size) +
')'
: formatMessage(messages.noPrimaryFile)
"
:href="
createProjectDownloadUrl(
dependencyVersionPrimaryFiles[dependency.version.id].url,
{
reason: 'dependency',
},
getDependencyPrimaryFileTooltip(
dependency.version ?? getDependencyVersion(dependency.dependency),
)
"
:download="dependencyVersionPrimaryFiles[dependency.version.id].filename"
:disabled="dependencyVersionPrimaryFiles[dependency.version.id].url === undefined"
:href="
getDependencyDownloadUrl(
dependency.version ?? getDependencyVersion(dependency.dependency),
)
"
:download="
getDependencyPrimaryFile(
dependency.version ?? getDependencyVersion(dependency.dependency),
)?.filename
"
:disabled="
!getDependencyPrimaryFile(
dependency.version ?? getDependencyVersion(dependency.dependency),
)?.url
"
>
<DownloadIcon />
</a>
@@ -658,6 +675,73 @@ watch(
const enrichment = computed(() => contextDependencies.value ?? undefined)
const projectOnlyDependencyProjectIds = computed(() => {
const projectIds = new Set<string>()
for (const dependency of version.value?.dependencies ?? []) {
if (
dependency.project_id &&
!dependency.version_id &&
dependency.dependency_type !== 'incompatible'
) {
projectIds.add(dependency.project_id)
}
}
return [...projectIds]
})
const dependencyResolutionLoaders = computed(() => {
if (!version.value) return []
if (version.value.loaders.includes('mrpack')) {
return (version.value.mrpack_loaders ?? []).filter((loader) => loader !== 'minecraft')
}
return version.value.loaders
})
const { data: projectOnlyDependencyVersions } = useQuery({
queryKey: computed(
() =>
[
'version-page',
'project-only-dependency-versions',
version.value?.id,
version.value?.game_versions ?? [],
dependencyResolutionLoaders.value,
projectOnlyDependencyProjectIds.value,
] as const,
),
queryFn: async () => {
const currentVersion = version.value!
const entries = await Promise.all(
projectOnlyDependencyProjectIds.value.map(async (projectId) => {
let versions: Labrinth.Versions.v2.Version[] = []
try {
versions = await client.labrinth.versions_v2.getProjectVersions(projectId, {
game_versions: currentVersion.game_versions,
loaders: dependencyResolutionLoaders.value,
include_changelog: false,
limit: 1,
})
} catch {
return [projectId, undefined] as const
}
return [projectId, versions[0]] as const
}),
)
const versionsByProjectId: Record<string, Labrinth.Versions.v2.Version> = {}
for (const [projectId, dependencyVersion] of entries) {
if (dependencyVersion) {
versionsByProjectId[projectId] = dependencyVersion
}
}
return versionsByProjectId
},
enabled: computed(() => !!version.value && projectOnlyDependencyProjectIds.value.length > 0),
staleTime: STALE_TIME,
})
const primaryFile = computed(
() => version.value?.files?.find((file) => file.primary) ?? version.value?.files?.[0],
)
@@ -823,6 +907,10 @@ const messages = defineMessages({
id: 'version.download.no-primary-file',
defaultMessage: 'Error: No primary file found',
},
downloadVersion: {
id: 'version.download.version',
defaultMessage: 'Download {version} ({size})',
},
edit: {
id: 'version.edit.button',
defaultMessage: 'Edit',
@@ -951,13 +1039,20 @@ const copyFileHash = async (
file: Labrinth.Versions.v3.VersionFile,
method: Labrinth.Versions.v3.FileHashType,
) => {
await navigator.clipboard.writeText(file.hashes[method])
await copyToClipboard(file.hashes[method])
}
async function copyToClipboard(text: string) {
await navigator.clipboard.writeText(text)
}
const dependencyVersionPrimaryFiles = computed(() => {
const versions = enrichment.value?.versions
const versions = [
...(enrichment.value?.versions ?? []),
...Object.values(projectOnlyDependencyVersions.value ?? {}),
]
const primaryFileMap: Record<string, Labrinth.Versions.v2.VersionFile> = {}
versions?.forEach((depVersion) => {
versions.forEach((depVersion) => {
const depPrimaryFile = depVersion.files.find((file) => file.primary) ?? depVersion.files[0]
primaryFileMap[depVersion.id] = depPrimaryFile
@@ -965,6 +1060,37 @@ const dependencyVersionPrimaryFiles = computed(() => {
return primaryFileMap
})
function getDependencyVersion(dependency: Labrinth.Versions.v3.Dependency) {
if (dependency.version_id || !dependency.project_id) {
return undefined
}
return projectOnlyDependencyVersions.value?.[dependency.project_id]
}
function getDependencyPrimaryFile(version?: Labrinth.Versions.v2.Version) {
return version ? dependencyVersionPrimaryFiles.value[version.id] : undefined
}
function getDependencyPrimaryFileTooltip(version?: Labrinth.Versions.v2.Version) {
const dependencyPrimaryFile = getDependencyPrimaryFile(version)
return dependencyPrimaryFile
? formatMessage(messages.downloadVersion, {
version: version?.name || version?.version_number || dependencyPrimaryFile.filename,
size: formatBytes(dependencyPrimaryFile.size),
})
: formatMessage(messages.noPrimaryFile)
}
function getDependencyDownloadUrl(version?: Labrinth.Versions.v2.Version) {
const dependencyPrimaryFile = getDependencyPrimaryFile(version)
return dependencyPrimaryFile?.url
? createProjectDownloadUrl(dependencyPrimaryFile.url, {
reason: 'dependency',
})
: undefined
}
function createDependencyLink(context: {
project?: Labrinth.Projects.v2.Project
version?: Labrinth.Versions.v2.Version