feat: add download metadata to website (#6034)
* feat: add download metadata to website * add to project cards
This commit is contained in:
@@ -370,18 +370,21 @@
|
||||
<VersionSummary
|
||||
v-if="filteredRelease"
|
||||
:version="filteredRelease"
|
||||
:decorate-download-url="decorateModalDownloadUrl"
|
||||
@on-download="onDownload"
|
||||
@on-navigate="onVersionNavigate"
|
||||
/>
|
||||
<VersionSummary
|
||||
v-if="filteredBeta"
|
||||
:version="filteredBeta"
|
||||
:decorate-download-url="decorateModalDownloadUrl"
|
||||
@on-download="onDownload"
|
||||
@on-navigate="onVersionNavigate"
|
||||
/>
|
||||
<VersionSummary
|
||||
v-if="filteredAlpha"
|
||||
:version="filteredAlpha"
|
||||
:decorate-download-url="decorateModalDownloadUrl"
|
||||
@on-download="onDownload"
|
||||
@on-navigate="onVersionNavigate"
|
||||
/>
|
||||
@@ -1082,6 +1085,7 @@ import {
|
||||
OpenInAppModal,
|
||||
OverflowMenu,
|
||||
PopoutMenu,
|
||||
PROJECT_DEP_MARKER_QUERY,
|
||||
ProjectBackgroundGradient,
|
||||
ProjectEnvironmentModal,
|
||||
ProjectHeader,
|
||||
@@ -1108,7 +1112,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
|
||||
import { useLocalStorage } from '@vueuse/core'
|
||||
import dayjs from 'dayjs'
|
||||
import { Tooltip } from 'floating-vue'
|
||||
import { nextTick, useTemplateRef, watch } from 'vue'
|
||||
import { nextTick, readonly, ref, useTemplateRef, watch } from 'vue'
|
||||
|
||||
import { navigateTo } from '#app'
|
||||
import Accordion from '~/components/ui/Accordion.vue'
|
||||
@@ -1137,6 +1141,7 @@ definePageMeta({
|
||||
|
||||
const data = useNuxtApp()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const signInRouteObj = computed(() => getSignInRouteObj(route))
|
||||
const config = useRuntimeConfig()
|
||||
const moderationQueue = useModerationQueue()
|
||||
@@ -1146,6 +1151,23 @@ const { addNotification } = notifications
|
||||
const auth = await useAuth()
|
||||
const user = await useUser()
|
||||
|
||||
const { createProjectDownloadUrl } = useCdnDownloadContext()
|
||||
|
||||
const downloadReason = ref('standalone')
|
||||
|
||||
function absorbDepQuery() {
|
||||
if (route.query.dep === PROJECT_DEP_MARKER_QUERY.dep) {
|
||||
downloadReason.value = 'dependency'
|
||||
if (import.meta.client) {
|
||||
const newQuery = { ...route.query }
|
||||
delete newQuery.dep
|
||||
void router.replace({ path: route.path, query: newQuery, hash: route.hash })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => route.query.dep, absorbDepQuery, { immediate: true })
|
||||
|
||||
const tags = useGeneratedState()
|
||||
const flags = useFeatureFlags()
|
||||
const cosmetics = useCosmetics()
|
||||
@@ -1210,6 +1232,14 @@ const currentPlatformText = computed(() => {
|
||||
return formatMessage(getTagMessage(currentPlatform.value, 'loader'))
|
||||
})
|
||||
|
||||
function decorateModalDownloadUrl(url) {
|
||||
return createProjectDownloadUrl(url, {
|
||||
reason: downloadReason.value,
|
||||
gameVersion: currentGameVersion.value ?? undefined,
|
||||
loader: currentPlatform.value ?? undefined,
|
||||
})
|
||||
}
|
||||
|
||||
const releaseVersions = computed(() => {
|
||||
const set = new Set()
|
||||
for (const gv of tags.value.gameVersions || []) {
|
||||
@@ -1715,15 +1745,27 @@ const serverRequiredContent = computed(() => {
|
||||
icon: content.project_icon,
|
||||
onclickName:
|
||||
content.project_id && content.project_id !== projectId.value
|
||||
? () => navigateTo(`/project/${content.project_id}`)
|
||||
? () => {
|
||||
navigateTo({
|
||||
path: `/project/${content.project_id}`,
|
||||
query: { ...PROJECT_DEP_MARKER_QUERY },
|
||||
})
|
||||
}
|
||||
: undefined,
|
||||
onclickVersion:
|
||||
content.project_id && content.project_id !== projectId.value
|
||||
? () =>
|
||||
navigateTo(`/project/${content.project_id}/version/${serverModpackVersion.value?.id}`)
|
||||
? () => {
|
||||
navigateTo({
|
||||
path: `/project/${content.project_id}/version/${serverModpackVersion.value?.id}`,
|
||||
query: { ...PROJECT_DEP_MARKER_QUERY },
|
||||
})
|
||||
}
|
||||
: undefined,
|
||||
onclickDownload: primaryFile?.url
|
||||
? () => navigateTo(primaryFile.url, { external: true })
|
||||
? () =>
|
||||
navigateTo(createProjectDownloadUrl(primaryFile.url, { reason: 'dependency' }), {
|
||||
external: true,
|
||||
})
|
||||
: undefined,
|
||||
showCustomModpackTooltip: content.project_id === projectId.value,
|
||||
}
|
||||
@@ -2703,6 +2745,7 @@ provideProjectPageContext({
|
||||
// Lazy dependencies loading
|
||||
dependencies,
|
||||
dependenciesLoading: computed(() => dependenciesLoading.value),
|
||||
cdnDownloadReason: readonly(downloadReason),
|
||||
|
||||
// Invalidate all project queries (auto-refetches active ones)
|
||||
invalidate: invalidateProject,
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
<ButtonStyled color="brand" type="transparent">
|
||||
<a
|
||||
class="ml-auto"
|
||||
:href="version.primaryFile?.url"
|
||||
:href="createDownloadUrl(version)"
|
||||
:title="`Download ${version.name}`"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
@@ -98,7 +98,9 @@ import {
|
||||
import VersionFilterControl from '@modrinth/ui/src/components/version/VersionFilterControl.vue'
|
||||
import { renderHighlightedString } from '@modrinth/utils'
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
import { onMounted } from 'vue'
|
||||
import { onMounted, watch } from 'vue'
|
||||
|
||||
const { createProjectDownloadUrl, updateVersionsFilterContext } = useCdnDownloadContext()
|
||||
|
||||
const formatDate = useFormatDateTime({
|
||||
month: 'short',
|
||||
@@ -106,7 +108,8 @@ const formatDate = useFormatDateTime({
|
||||
year: 'numeric',
|
||||
})
|
||||
|
||||
const { projectV2, versions, versionsLoading, loadVersions } = injectProjectPageContext()
|
||||
const { projectV2, versions, versionsLoading, loadVersions, cdnDownloadReason } =
|
||||
injectProjectPageContext()
|
||||
|
||||
// Load versions on mount (client-side)
|
||||
onMounted(() => {
|
||||
@@ -216,6 +219,23 @@ function updateQuery(newQueries) {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [route.query.g, route.query.l],
|
||||
() => {
|
||||
updateVersionsFilterContext(
|
||||
queryAsStringArray(route.query.g),
|
||||
queryAsStringArray(route.query.l),
|
||||
)
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
function createDownloadUrl(version) {
|
||||
return createProjectDownloadUrl(getPrimaryFile(version).url, {
|
||||
reason: cdnDownloadReason.value,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
id: 'download',
|
||||
color: 'primary',
|
||||
hoverFilled: true,
|
||||
link: getPrimaryFile(version).url,
|
||||
link: createDownloadUrl(version),
|
||||
action: () => {
|
||||
emit('onDownload')
|
||||
},
|
||||
@@ -340,7 +340,7 @@ import {
|
||||
ProjectPageVersions,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { useTemplateRef } from 'vue'
|
||||
import { useTemplateRef, watch } from 'vue'
|
||||
|
||||
import CreateProjectVersionModal from '~/components/ui/create-project-version/CreateProjectVersionModal.vue'
|
||||
import { getSignInRouteObj } from '~/composables/auth.js'
|
||||
@@ -348,6 +348,8 @@ import { reportVersion } from '~/utils/report-helpers.ts'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const { createProjectDownloadUrl, updateVersionsFilterContext } = useCdnDownloadContext()
|
||||
|
||||
const client = injectModrinthClient()
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const { formatMessage } = useVIntl()
|
||||
@@ -357,6 +359,7 @@ const {
|
||||
versions,
|
||||
invalidate,
|
||||
loadVersions,
|
||||
cdnDownloadReason,
|
||||
} = injectProjectPageContext()
|
||||
|
||||
// Load versions on mount (client-side)
|
||||
@@ -401,6 +404,23 @@ function getPrimaryFile(version: Labrinth.Versions.v3.Version) {
|
||||
return version.files.find((x) => x.primary) || version.files[0]
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [route.query.g, route.query.l],
|
||||
() => {
|
||||
updateVersionsFilterContext(
|
||||
queryAsStringArray(route.query.g),
|
||||
queryAsStringArray(route.query.l),
|
||||
)
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
function createDownloadUrl(version: Labrinth.Versions.v3.Version) {
|
||||
return createProjectDownloadUrl(getPrimaryFile(version).url, {
|
||||
reason: cdnDownloadReason.value,
|
||||
})
|
||||
}
|
||||
|
||||
async function copyToClipboard(text: string) {
|
||||
await navigator.clipboard.writeText(text)
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
<ButtonStyled v-if="primaryFile && !currentMember" color="brand">
|
||||
<a
|
||||
v-tooltip="primaryFile.filename + ' (' + formatBytes(primaryFile.size) + ')'"
|
||||
:href="primaryFile.url"
|
||||
:href="decoratedPrimaryFileUrl"
|
||||
@click="emit('onDownload')"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
@@ -213,14 +213,19 @@
|
||||
:key="index"
|
||||
class="dependency"
|
||||
:class="{ 'button-transparent': !isEditing }"
|
||||
@click="!isEditing ? router.push(dependency.link) : {}"
|
||||
@click="!isEditing ? navigateToDependency(dependency) : {}"
|
||||
>
|
||||
<Avatar
|
||||
:src="dependency.project ? dependency.project.icon_url : null"
|
||||
alt="dependency-icon"
|
||||
size="sm"
|
||||
/>
|
||||
<nuxt-link v-if="!isEditing" :to="dependency.link" class="info">
|
||||
<nuxt-link
|
||||
v-if="!isEditing"
|
||||
:to="{ path: dependency.link, query: PROJECT_DEP_MARKER_QUERY }"
|
||||
class="info"
|
||||
@click.stop
|
||||
>
|
||||
<span class="project-title">
|
||||
{{ dependency.project ? dependency.project.title : 'Unknown Project' }}
|
||||
</span>
|
||||
@@ -299,7 +304,7 @@
|
||||
</span>
|
||||
<ButtonStyled>
|
||||
<a
|
||||
:href="file.url"
|
||||
:href="decorateDownloadUrl(file.url)"
|
||||
class="raised-button"
|
||||
:title="`Download ${file.filename}`"
|
||||
tabindex="0"
|
||||
@@ -435,6 +440,7 @@ import {
|
||||
injectNotificationManager,
|
||||
injectProjectPageContext,
|
||||
MultiSelect,
|
||||
PROJECT_DEP_MARKER_QUERY,
|
||||
StyledInput,
|
||||
useFormatDateTime,
|
||||
} from '@modrinth/ui'
|
||||
@@ -461,6 +467,7 @@ const auth = await useAuth()
|
||||
const tags = useGeneratedState()
|
||||
const flags = useFeatureFlags()
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const { createProjectDownloadUrl } = useCdnDownloadContext()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
@@ -481,6 +488,7 @@ const {
|
||||
dependenciesLoading: contextDependenciesLoading,
|
||||
loadDependencies,
|
||||
invalidate,
|
||||
cdnDownloadReason,
|
||||
} = injectProjectPageContext()
|
||||
|
||||
// Load versions and dependencies in parallel
|
||||
@@ -752,6 +760,21 @@ const sortedDeps = computed(() => {
|
||||
)
|
||||
})
|
||||
|
||||
const decoratedPrimaryFileUrl = computed(() =>
|
||||
createProjectDownloadUrl(primaryFile.value?.url, { reason: cdnDownloadReason.value }),
|
||||
)
|
||||
|
||||
function decorateDownloadUrl(url: string) {
|
||||
return createProjectDownloadUrl(url, { reason: cdnDownloadReason.value })
|
||||
}
|
||||
|
||||
function navigateToDependency(dependency: { link: string }) {
|
||||
return router.push({
|
||||
path: dependency.link,
|
||||
query: { ...PROJECT_DEP_MARKER_QUERY },
|
||||
})
|
||||
}
|
||||
|
||||
const environment = computed(
|
||||
() => ENVIRONMENTS_COPY[version.value.environment as keyof typeof ENVIRONMENTS_COPY],
|
||||
)
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<ButtonStyled circular type="transparent">
|
||||
<a
|
||||
v-tooltip="`Download`"
|
||||
:href="getPrimaryFile(version).url"
|
||||
:href="createDownloadUrl(version)"
|
||||
class="hover:!bg-button-bg [&>svg]:!text-green"
|
||||
aria-label="Download"
|
||||
@click="emit('onDownload')"
|
||||
@@ -100,7 +100,7 @@
|
||||
id: 'download',
|
||||
color: 'primary',
|
||||
hoverFilled: true,
|
||||
link: getPrimaryFile(version).url,
|
||||
link: createDownloadUrl(version),
|
||||
action: () => {
|
||||
emit('onDownload')
|
||||
},
|
||||
@@ -266,7 +266,7 @@ import {
|
||||
OverflowMenu,
|
||||
ProjectPageVersions,
|
||||
} from '@modrinth/ui'
|
||||
import { onMounted, useTemplateRef } from 'vue'
|
||||
import { onMounted, useTemplateRef, watch } from 'vue'
|
||||
|
||||
import CreateProjectVersionModal from '~/components/ui/create-project-version/CreateProjectVersionModal.vue'
|
||||
import { getSignInRouteObj } from '~/composables/auth.js'
|
||||
@@ -274,6 +274,8 @@ import { reportVersion } from '~/utils/report-helpers.ts'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const { createProjectDownloadUrl, updateVersionsFilterContext } = useCdnDownloadContext()
|
||||
|
||||
const tags = useGeneratedState()
|
||||
const flags = useFeatureFlags()
|
||||
const auth = await useAuth()
|
||||
@@ -287,6 +289,7 @@ const {
|
||||
versions,
|
||||
versionsLoading,
|
||||
loadVersions,
|
||||
cdnDownloadReason,
|
||||
} = injectProjectPageContext()
|
||||
|
||||
// Load versions on mount (client-side)
|
||||
@@ -316,6 +319,23 @@ function getPrimaryFile(version) {
|
||||
return version.files.find((x) => x.primary) || version.files[0]
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [route.query.g, route.query.l],
|
||||
() => {
|
||||
updateVersionsFilterContext(
|
||||
queryAsStringArray(route.query.g),
|
||||
queryAsStringArray(route.query.l),
|
||||
)
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
function createDownloadUrl(version) {
|
||||
return createProjectDownloadUrl(getPrimaryFile(version).url, {
|
||||
reason: cdnDownloadReason.value,
|
||||
})
|
||||
}
|
||||
|
||||
async function copyToClipboard(text) {
|
||||
await navigator.clipboard.writeText(text)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
BrowsePageLayout,
|
||||
BrowseSidebar,
|
||||
CreationFlowModal,
|
||||
PROJECT_DEP_MARKER_QUERY,
|
||||
defineMessages,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
@@ -40,6 +41,8 @@ import type { DisplayLocation, DisplayMode } from '~/plugins/cosmetics.ts'
|
||||
const { formatMessage } = useVIntl()
|
||||
const debug = useDebugLogger('Discover')
|
||||
|
||||
const { updateDiscoverFilterContext } = useCdnDownloadContext()
|
||||
|
||||
const client = injectModrinthClient()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
@@ -401,7 +404,13 @@ function getServerModpackContent(project: Labrinth.Search.v3.ResultSearchProject
|
||||
name: project_name,
|
||||
icon: project_icon ?? undefined,
|
||||
onclick:
|
||||
project_id !== project.project_id ? () => navigateTo(`/project/${project_id}`) : undefined,
|
||||
project_id !== project.project_id
|
||||
? () =>
|
||||
navigateTo({
|
||||
path: `/project/${project_id}`,
|
||||
query: { ...PROJECT_DEP_MARKER_QUERY },
|
||||
})
|
||||
: undefined,
|
||||
showCustomModpackTooltip: project_id === project.project_id,
|
||||
}
|
||||
}
|
||||
@@ -639,6 +648,15 @@ const searchState = useBrowseSearch({
|
||||
displayMode: resultsDisplayMode,
|
||||
})
|
||||
|
||||
watch(
|
||||
() =>
|
||||
searchState.isServerType.value
|
||||
? searchState.serverCurrentFilters.value
|
||||
: searchState.currentFilters.value,
|
||||
(filters) => updateDiscoverFilterContext(filters),
|
||||
{ deep: true, immediate: true },
|
||||
)
|
||||
|
||||
watch(
|
||||
[
|
||||
() => searchState.query.value,
|
||||
|
||||
@@ -307,6 +307,7 @@ import {
|
||||
injectModrinthClient,
|
||||
NavTabs,
|
||||
OverflowMenu,
|
||||
PROJECT_DEP_MARKER_QUERY,
|
||||
ProjectCard,
|
||||
ProjectCardList,
|
||||
useCompactNumber,
|
||||
@@ -489,7 +490,10 @@ function getServerModpackContent(project: ProjectV3) {
|
||||
onclick:
|
||||
project_id !== project.id
|
||||
? () => {
|
||||
navigateTo(`/project/${project_id}`)
|
||||
navigateTo({
|
||||
path: `/project/${project_id}`,
|
||||
query: { ...PROJECT_DEP_MARKER_QUERY },
|
||||
})
|
||||
}
|
||||
: undefined,
|
||||
showCustomModpackTooltip: project_id === project.id,
|
||||
|
||||
Reference in New Issue
Block a user