Files
modrinth/apps/frontend/src/composables/useCdnDownloadContext.ts
T
Calum H.andProspector bd97ace974 feat: hosting access tab (#5995)
* feat: implement access tab with dummy data

* fix: spacing

* feat: qa

* feat: implement backend

* qa: qa pass

* feat: fix user "search"

* fix: lint

* feat: change to bitfield

* feat: fix fields

* fix: lint

* fix: lint

* feat: hook up api

* feat: fix permissions

* feat: audit log table event start

* feat: better mobile mode for audit log table

* feat: i18n

* feat: qa

* feat: enforce permissions

* feat: email template start

* feat: qa

* fix: tooltip bug

* feat: qa

* impl: sse support in api-client

* feat: sse impl

* fix: desync path

* feat: time frame picker from analytics

* feat: QA

* fix: spacing

* fix: permisison audit log entries

* fix: hosting manage page shared server detection

* fix: lint

* feat: qa + lint

* feat: audit log table sort by time

* feat: finish frontend panel stuff

* fix: lint

* fix: backend alignment

* fix: lint

* fix: supress friend errors

* feat: qa

* fix: qa

* fix: lint

* fix: utils barrel

* fix: safari cookies in dev

* fix: pin nuxt

* feat: fixes + notif fix

* fix: notifications

* feat: qa

* fix: notification sync not happening immediately

* fix: qa

* fix: qa

* feat: qa

* blog + prepr

* feat: toast shit

* blog images

* thumbnail update one last time

* prepr

* feat: use reinvite route

* update images

* fix: reinvite stuff

* fix: lint

* fix: alignment of save bar

* fix: notif sizing

* fix: split up access

* fix: lint

* fix: lint

* fix: link

---------

Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com>
2026-06-04 15:58:01 +00:00

150 lines
3.6 KiB
TypeScript

import type { FilterValue } from '@modrinth/ui'
import { LOADER_FILTER_TYPES } from '@modrinth/ui'
const TEN_MINUTES = 600
export type DownloadContext = {
gameVersion?: string
loader?: string
reason?: 'standalone' | 'dependency' | 'modpack' | 'update'
}
export type FilterSelection = {
gameVersion?: string
loader?: string
}
const cookieDefaults = {
maxAge: TEN_MINUTES,
sameSite: 'lax' as const,
path: '/',
httpOnly: false,
}
function readCookieValue(value: string | null | undefined): string | undefined {
if (typeof value !== 'string' || !value) {
return undefined
}
return value
}
function newFilterSelection(
gameVersion: string | undefined,
loader: string | undefined,
): FilterSelection | null {
if (!gameVersion && !loader) {
return null
} else if (!gameVersion) {
return {
loader,
}
} else if (!loader) {
return {
gameVersion,
}
} else {
return {
gameVersion,
loader,
}
}
}
export function useCdnDownloadContext() {
const config = useRuntimeConfig()
const cookieOptions = {
...cookieDefaults,
secure: config.public.cookieSecure,
}
const filterGameVersionCookie = useCookie<string | null>('mr_download_filter_game_version', {
...cookieOptions,
default: () => null,
})
const filterLoaderCookie = useCookie<string | null>('mr_download_filter_loader', {
...cookieOptions,
default: () => null,
})
function createProjectDownloadUrl(originalUrl: string, context?: DownloadContext): string {
if (
typeof originalUrl !== 'string' ||
!originalUrl ||
!originalUrl.startsWith('https://cdn.modrinth.com')
) {
return originalUrl
}
const reason = context?.reason
const gameVersion = context?.gameVersion ?? readCookieValue(filterGameVersionCookie.value)
const loader = context?.loader ?? readCookieValue(filterLoaderCookie.value)
try {
const url = new URL(originalUrl)
if (reason) {
url.searchParams.set('mr_download_reason', reason)
}
if (gameVersion) {
url.searchParams.set('mr_game_version', gameVersion)
} else {
url.searchParams.delete('mr_game_version')
}
if (loader) {
url.searchParams.set('mr_loader', loader)
} else {
url.searchParams.delete('mr_loader')
}
return url.toString()
} catch {
return originalUrl
}
}
function persistFilterSelection(selection: FilterSelection | null) {
if (!selection) {
filterGameVersionCookie.value = null
filterLoaderCookie.value = null
return
}
filterGameVersionCookie.value = selection.gameVersion ?? null
filterLoaderCookie.value = selection.loader ?? null
}
function updateDiscoverFilterContext(filters: FilterValue[]) {
if (!import.meta.client) {
return
}
const versionFilters = [
...new Set(filters.filter((f) => f.type === 'game_version').map((f) => f.option)),
]
const loaderFilters = [
...new Set(
filters
.filter((f) => (LOADER_FILTER_TYPES as readonly string[]).includes(f.type))
.map((f) => f.option),
),
]
const gameVersion = versionFilters.length === 1 ? versionFilters[0] : undefined
const loader = loaderFilters.length === 1 ? loaderFilters[0] : undefined
persistFilterSelection(newFilterSelection(gameVersion, loader))
}
function updateVersionsFilterContext(gameVersions: string[], loaders: string[]) {
if (!import.meta.client) {
return
}
const gameVersion = gameVersions.length === 1 ? gameVersions[0] : undefined
const loader = loaders.length === 1 ? loaders[0] : undefined
persistFilterSelection(newFilterSelection(gameVersion, loader))
}
return {
createProjectDownloadUrl,
updateDiscoverFilterContext,
updateVersionsFilterContext,
}
}