diff --git a/packages/ui/src/components/project/ProjectPageVersions.vue b/packages/ui/src/components/project/ProjectPageVersions.vue index 012fea3115..353fe5f3a4 100644 --- a/packages/ui/src/components/project/ProjectPageVersions.vue +++ b/packages/ui/src/components/project/ProjectPageVersions.vue @@ -115,14 +115,17 @@ @@ -311,16 +318,18 @@
- {{ gameVersion }} + {{ gameVersionGroup.label }}
- {{ gameVersion }} + {{ gameVersionGroup.label }}
@@ -457,7 +467,12 @@ import { VersionChannelIndicator, VersionFilterControl, } from '@modrinth/ui' -import { formatVersionsForDisplay, type GameVersionTag, type Version } from '@modrinth/utils' +import { + type GameVersionTag, + getVersionGroupsForDisplay, + type Version, + type VersionDisplayGroup, +} from '@modrinth/utils' import { Menu } from 'floating-vue' import { computed, type Ref, ref } from 'vue' import { useRoute, useRouter } from 'vue-router' @@ -616,8 +631,8 @@ function hasNoModLoader(loaders: string[]): boolean { ) } -function getDisplayGameVersions(version: DisplayVersion): string[] { - return formatVersionsForDisplay(version.game_versions, props.gameVersions) +function getDisplayGameVersions(version: DisplayVersion): VersionDisplayGroup[] { + return getVersionGroupsForDisplay(version.game_versions, props.gameVersions) } function getFilterTooltip(filter: string): string { diff --git a/packages/ui/src/components/version/VersionFilterControl.vue b/packages/ui/src/components/version/VersionFilterControl.vue index 09a77a7bdd..88a32ae463 100644 --- a/packages/ui/src/components/version/VersionFilterControl.vue +++ b/packages/ui/src/components/version/VersionFilterControl.vue @@ -243,31 +243,35 @@ if (selectedGameVersions.value.some((version) => !isReleaseGameVersion(version)) showSnapshots.value = true } -async function toggleFilters(type: FilterType, filters: Filter[]) { - for (const filter of filters) { - await toggleFilter(type, filter, true) +function selectedFiltersOfType(type: FilterType) { + if (type === 'channel') { + return selectedChannels + } else if (type === 'gameVersion') { + return selectedGameVersions + } else { + return selectedPlatforms } +} + +function toggleFilters(type: FilterType, filters: Filter[]) { + const selected = selectedFiltersOfType(type) + const allSelected = filters.every((filter) => selected.value.includes(filter)) + + selected.value = allSelected + ? selected.value.filter((x) => !filters.includes(x)) + : [...selected.value, ...filters.filter((filter) => !selected.value.includes(filter))] updateFilters() } -async function toggleFilter(type: FilterType, filter: Filter, bulk = false) { - if (type === 'channel') { - selectedChannels.value = selectedChannels.value.includes(filter) - ? selectedChannels.value.filter((x) => x !== filter) - : [...selectedChannels.value, filter] - } else if (type === 'gameVersion') { - selectedGameVersions.value = selectedGameVersions.value.includes(filter) - ? selectedGameVersions.value.filter((x) => x !== filter) - : [...selectedGameVersions.value, filter] - } else if (type === 'platform') { - selectedPlatforms.value = selectedPlatforms.value.includes(filter) - ? selectedPlatforms.value.filter((x) => x !== filter) - : [...selectedPlatforms.value, filter] - } - if (!bulk) { - updateFilters() - } +function toggleFilter(type: FilterType, filter: Filter) { + const selected = selectedFiltersOfType(type) + + selected.value = selected.value.includes(filter) + ? selected.value.filter((x) => x !== filter) + : [...selected.value, filter] + + updateFilters() } function updateSelectedGameVersions(versions: string[]) { @@ -302,7 +306,7 @@ function updateShowSnapshots(value: boolean, _event?: MouseEvent) { } } -async function clearFilters() { +function clearFilters() { selectedChannels.value = [] selectedGameVersions.value = [] selectedPlatforms.value = [] diff --git a/packages/utils/projects.ts b/packages/utils/projects.ts index 3176c940a4..36bb49e299 100644 --- a/packages/utils/projects.ts +++ b/packages/utils/projects.ts @@ -66,6 +66,11 @@ export type PlatformTag = { supported_project_types: DisplayProjectType[] } +export type VersionDisplayGroup = { + label: string + versions: string[] +} + export function getVersionsToDisplay(project, allGameVersions: GameVersionTag[]) { return formatVersionsForDisplay(project.game_versions.slice(), allGameVersions) } @@ -74,6 +79,13 @@ export function formatVersionsForDisplay( gameVersions: string[], allGameVersions: GameVersionTag[], ) { + return getVersionGroupsForDisplay(gameVersions, allGameVersions).map((group) => group.label) +} + +export function getVersionGroupsForDisplay( + gameVersions: string[], + allGameVersions: GameVersionTag[], +): VersionDisplayGroup[] { const inputVersions = gameVersions.slice() const allVersions = allGameVersions.slice() @@ -112,26 +124,33 @@ export function formatVersionsForDisplay( ) const projectVersionsGrouped = groupVersions(releaseVersions, true) - const releaseVersionsAsRanges = projectVersionsGrouped.map(({ major, minor }) => { - if (minor.length === 1) { - return formatMinecraftMinorVersion(major, minor[0]) - } + const releaseVersionsAsRanges: VersionDisplayGroup[] = projectVersionsGrouped.map( + ({ major, minor }) => { + const versions = minor.map((minorVersion) => formatMinecraftMinorVersion(major, minorVersion)) + + if (minor.length === 1) { + return { label: versions[0], versions } + } - const range = allReleasesGrouped.find((x) => x.major === major) + const range = allReleasesGrouped.find((x) => x.major === major) - if (range?.minor.every((value, index) => value === minor[index])) { - return `${major}.x` - } + if (range?.minor.every((value, index) => value === minor[index])) { + return { label: `${major}.x`, versions } + } - return `${formatMinecraftMinorVersion(major, minor[0])}–${formatMinecraftMinorVersion(major, minor[minor.length - 1])}` - }) + return { + label: `${formatMinecraftMinorVersion(major, minor[0])}–${formatMinecraftMinorVersion(major, minor[minor.length - 1])}`, + versions, + } + }, + ) const legacyVersionsAsRanges = groupConsecutiveIndices( inputVersions.filter((projVer) => allLegacy.some((gameVer) => gameVer.version === projVer)), allLegacy, ) - let output = [...legacyVersionsAsRanges] + let output: VersionDisplayGroup[] = [...legacyVersionsAsRanges] // show all snapshots if there's no release versions if (releaseVersionsAsRanges.length === 0) { @@ -141,14 +160,14 @@ export function formatVersionsForDisplay( const snapshotVersionsAsRanges = snapshotVersions.length > 3 ? groupConsecutiveIndices(snapshotVersions, allSnapshots) - : snapshotVersions + : snapshotVersions.map((version) => ({ label: version, versions: [version] })) output = [...snapshotVersionsAsRanges, ...output] } else { output = [...releaseVersionsAsRanges, ...output] } if (releaseVersionsAsRanges.length > 0 && latestSnapshot) { - output = [latestSnapshot, ...output] + output = [{ label: latestSnapshot, versions: [latestSnapshot] }, ...output] } return output } @@ -188,7 +207,10 @@ function groupVersions(versions: string[], consecutive = false) { .reverse() } -function groupConsecutiveIndices(versions: string[], referenceList: GameVersionTag[]) { +function groupConsecutiveIndices( + versions: string[], + referenceList: GameVersionTag[], +): VersionDisplayGroup[] { if (!versions || versions.length === 0) { return [] } @@ -202,21 +224,23 @@ function groupConsecutiveIndices(versions: string[], referenceList: GameVersionT .slice() .sort((a, b) => referenceMap.get(a) - referenceMap.get(b)) - const ranges: string[] = [] - let start = sortedList[0] - let previous = sortedList[0] - - for (let i = 1; i < sortedList.length; i++) { - const current = sortedList[i] - if (referenceMap.get(current) !== referenceMap.get(previous) + 1) { - ranges.push(validateRange(`${previous}–${start}`)) - start = current + const ranges: VersionDisplayGroup[] = [] + let rangeStartIndex = 0 + + for (let i = 1; i <= sortedList.length; i++) { + if ( + i === sortedList.length || + referenceMap.get(sortedList[i]) !== referenceMap.get(sortedList[i - 1]) + 1 + ) { + const members = sortedList.slice(rangeStartIndex, i) + ranges.push({ + label: validateRange(`${members[members.length - 1]}–${members[0]}`), + versions: members, + }) + rangeStartIndex = i } - previous = current } - ranges.push(validateRange(`${previous}–${start}`)) - return ranges }