Skip to content

Commit 61c8cd7

Browse files
tdgaoIMB11Prospector
authored
feat: manage project versions v2 (#5049)
* update add files copy and go to next step on just one file * rename and reorder stages * add metadata stage and update details stage * implement files inside metadata stage * use regular prettier instead of prettier eslint * remove changelog stage config * save button on details stage * update edit buttons in versions table * add collapse environment selector * implement dependencies list in metadata step * move dependencies into provider * add suggested dependencies to metadata stage * pnpm prepr * fix unused var * Revert "add collapse environment selector" This reverts commit f90fabc. * hide resource pack loader only when its the only loader * fix no dependencies for modpack * add breadcrumbs with hide breadcrumb option * wider stages * add proper horizonal scroll breadcrumbs * fix titles * handle save version in version page * remove box shadow * add notification provider to storybook * add drop area for versions to drop file right into page * fix mobile versions table buttons overflowing * pnpm prepr * fix drop file opening modal in wrong stage * implement invalid file for dropping files * allow horizontal scroll on breadcrumbs * update infer.js as best as possible * add create version button uploading version state * add extractVersionFromFilename for resource pack and datapack * allow jars for datapack project * detect multiple loaders when possible * iris means compatible with optifine too * infer environment on loader change as well * add tooltip * prevent navigate forward when cannot go to next step * larger breadcrumb click targets * hide loaders and mc versions stage until files added * fix max width in header * fix add files from metadata step jumping steps * define width in NewModal instead * disable remove dependency in metadata stage * switch metadata and details buttons positions * fix remove button spacing * do not allow duplicate suggested dependencies * fix version detection for fabric minecraft version semvar * better verion number detection based on filename * show resource pack loader but uneditable * remove vanilla shader detection * refactor: break up large infer.js into ts and modules * remove duplicated types * add fill missing from file name step * pnpm prepr * fix neoforge loader parse failing and not adding neoforge loader * add missing pack formats * handle new pack format * pnpm prepr * add another regex where it is version in anywhere in filename * only show resource pack or data pack options for filetype on datapack project * add redundant zip folder check * reject RP and DP if has redundant folder * fix hide stage in breadcrumb * add snapshot group key in case no release version. brings out 26.1 snapshots * pnpm prepr * open in group if has something selected * fix resource pack loader uneditable if accidentally selected on different project type * add new environment tags * add unknown and not applicable environment tags * pnpm prepr * use shared constant on labels * use ref for timeout * remove console logs * remove box shadow only for cm-content * feat: xhr upload + fix wrangler prettierignore * fix: upload content type fix * fix dependencies version width * fix already added dependencies logic * add changelog minheight * set progress percentage on button * add legacy fabric detection logic * lint * small update on create version button label --------- Co-authored-by: Calum H. (IMB11) <[email protected]> Co-authored-by: Prospector <[email protected]>
1 parent b46f6d0 commit 61c8cd7

File tree

64 files changed

+3170
-1694
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+3170
-1694
lines changed

.vscode/settings.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,21 @@
1111
"source.fixAll.eslint": "explicit",
1212
"source.organizeImports": "always"
1313
},
14-
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint",
14+
"editor.defaultFormatter": "esbenp.prettier-vscode",
1515
"[vue]": {
16-
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
16+
"editor.defaultFormatter": "esbenp.prettier-vscode"
1717
},
1818
"[typescript]": {
19-
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
19+
"editor.defaultFormatter": "esbenp.prettier-vscode"
2020
},
2121
"[javascript]": {
22-
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
22+
"editor.defaultFormatter": "esbenp.prettier-vscode"
2323
},
2424
"[scss]": {
25-
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
25+
"editor.defaultFormatter": "esbenp.prettier-vscode"
2626
},
2727
"[css]": {
28-
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
28+
"editor.defaultFormatter": "esbenp.prettier-vscode"
2929
},
3030
"[rust]": {
3131
"editor.defaultFormatter": "rust-lang.rust-analyzer"

apps/frontend/.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
**/dist
33
**/.output
44
**/.data
5+
**/.wrangler
56
src/generated/**
67
src/locales/**
78
src/public/news/feed

apps/frontend/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
"@nuxtjs/i18n": "^9.0.0",
2222
"@types/dompurify": "^3.0.5",
2323
"@types/iso-3166-2": "^1.0.4",
24+
"@types/js-yaml": "^4.0.9",
2425
"@types/node": "^20.1.0",
26+
"@types/semver": "^7.7.1",
2527
"autoprefixer": "^10.4.19",
2628
"glob": "^10.2.7",
2729
"nuxt": "^3.20.2",

apps/frontend/src/components/ui/create-project-version/CreateProjectVersionModal.vue

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,46 @@
11
<template>
2-
<MultiStageModal ref="modal" :stages="ctx.stageConfigs" :context="ctx" />
2+
<MultiStageModal
3+
ref="modal"
4+
:stages="ctx.stageConfigs"
5+
:context="ctx"
6+
:breadcrumbs="!editingVersion"
7+
@hide="() => (modalOpen = false)"
8+
/>
9+
<DropArea
10+
v-if="!modalOpen"
11+
:accept="acceptFileFromProjectType(projectV2.project_type)"
12+
@change="handleDropArea"
13+
/>
314
</template>
415

516
<script setup lang="ts">
617
import type { Labrinth } from '@modrinth/api-client'
718
import {
19+
DropArea,
820
injectModrinthClient,
921
injectNotificationManager,
1022
injectProjectPageContext,
1123
MultiStageModal,
1224
} from '@modrinth/ui'
25+
import { acceptFileFromProjectType } from '@modrinth/utils'
1326
import type { ComponentExposed } from 'vue-component-type-helpers'
1427
1528
import {
1629
createManageVersionContext,
1730
provideManageVersionContext,
1831
} from '~/providers/version/manage-version-modal'
1932
33+
const emit = defineEmits<{
34+
(e: 'save'): void
35+
}>()
36+
2037
const modal = useTemplateRef<ComponentExposed<typeof MultiStageModal>>('modal')
38+
const modalOpen = ref(false)
2139
22-
const ctx = createManageVersionContext(modal)
40+
const ctx = createManageVersionContext(modal, () => emit('save'))
2341
provideManageVersionContext(ctx)
2442
25-
const { newDraftVersion } = ctx
43+
const { newDraftVersion, editingVersion, handleNewFiles } = ctx
2644
2745
const { projectV2 } = injectProjectPageContext()
2846
const { addNotification } = injectNotificationManager()
@@ -64,6 +82,15 @@ function openCreateVersionModal(
6482
newDraftVersion(projectV2.value.id, version)
6583
modal.value?.setStage(stageId ?? 0)
6684
modal.value?.show()
85+
modalOpen.value = true
86+
}
87+
88+
async function handleDropArea(files: FileList) {
89+
newDraftVersion(projectV2.value.id, null)
90+
modal.value?.setStage(0)
91+
await handleNewFiles(Array.from(files))
92+
modal.value?.show()
93+
modalOpen.value = true
6794
}
6895
6996
defineExpose({

apps/frontend/src/components/ui/create-project-version/components/AddedDependencyRow.vue

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<template>
22
<div
3-
class="flex items-center justify-between gap-2 rounded-xl bg-button-bg px-4 py-1 text-button-text"
3+
class="flex h-11 items-center justify-between gap-2 rounded-xl bg-button-bg px-4 py-1 text-button-text"
44
>
55
<div class="grid max-w-[75%] grid-cols-[auto_1fr_auto] items-center gap-2">
66
<Avatar v-if="icon" :src="icon" alt="dependency-icon" size="20px" :no-shadow="true" />
@@ -9,22 +9,23 @@
99
{{ name || 'Unknown Project' }}
1010
</span>
1111

12-
<TagItem class="shrink-0 border !border-solid border-surface-5">
12+
<TagItem class="shrink-0 border !border-solid border-surface-5 capitalize">
1313
{{ dependencyType }}
1414
</TagItem>
1515
</div>
1616

1717
<span
1818
v-if="versionName"
1919
v-tooltip="versionName"
20-
class="max-w-[35%] truncate whitespace-nowrap font-medium"
20+
class="truncate whitespace-nowrap font-medium"
21+
:class="!hideRemove ? 'max-w-[35%]' : 'max-w-[50%]'"
2122
>
2223
{{ versionName }}
2324
</span>
2425

25-
<div class="flex items-center justify-end gap-1">
26+
<div v-if="!hideRemove" class="flex items-center justify-end gap-1">
2627
<ButtonStyled size="standard" :circular="true">
27-
<button aria-label="Remove file" class="!shadow-none" @click="emitRemove">
28+
<button aria-label="Remove file" class="-mr-2 !shadow-none" @click="emitRemove">
2829
<XIcon aria-hidden="true" />
2930
</button>
3031
</ButtonStyled>
@@ -42,12 +43,13 @@ const emit = defineEmits<{
4243
(e: 'remove'): void
4344
}>()
4445
45-
const { projectId, name, icon, dependencyType, versionName } = defineProps<{
46+
const { projectId, name, icon, dependencyType, versionName, hideRemove } = defineProps<{
4647
projectId: string
4748
name?: string
4849
icon?: string
4950
dependencyType: Labrinth.Versions.v2.DependencyType
5051
versionName?: string
52+
hideRemove?: boolean
5153
}>()
5254
5355
function emitRemove() {
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<template>
2+
<div v-if="addedDependencies.length" class="5 flex flex-col gap-2">
3+
<template v-for="(dependency, index) in addedDependencies">
4+
<AddedDependencyRow
5+
v-if="dependency"
6+
:key="index"
7+
:project-id="dependency.projectId"
8+
:name="dependency.name"
9+
:icon="dependency.icon"
10+
:dependency-type="dependency.dependencyType"
11+
:version-name="dependency.versionName"
12+
:hide-remove="disableRemove"
13+
@remove="() => removeDependency(index)"
14+
/>
15+
</template>
16+
<span v-if="!addedDependencies.length"> No dependencies added. </span>
17+
</div>
18+
</template>
19+
20+
<script setup lang="ts">
21+
import { injectManageVersionContext } from '~/providers/version/manage-version-modal'
22+
23+
import AddedDependencyRow from './AddedDependencyRow.vue'
24+
25+
const { disableRemove } = defineProps<{
26+
disableRemove?: boolean
27+
}>()
28+
29+
const { draftVersion, dependencyProjects, dependencyVersions, projectsFetchLoading } =
30+
injectManageVersionContext()
31+
32+
const addedDependencies = computed(() =>
33+
(draftVersion.value.dependencies || [])
34+
.map((dep) => {
35+
if (!dep.project_id) return null
36+
37+
const dependencyProject = dependencyProjects.value[dep.project_id]
38+
const versionName = dependencyVersions.value[dep.version_id || '']?.name ?? ''
39+
40+
if (!dependencyProject && projectsFetchLoading.value) return null
41+
42+
return {
43+
projectId: dep.project_id,
44+
name: dependencyProject?.name,
45+
icon: dependencyProject?.icon_url,
46+
dependencyType: dep.dependency_type,
47+
versionName,
48+
}
49+
})
50+
.filter(Boolean),
51+
)
52+
53+
const removeDependency = (index: number) => {
54+
if (!draftVersion.value.dependencies) return
55+
draftVersion.value.dependencies.splice(index, 1)
56+
}
57+
</script>

apps/frontend/src/components/ui/create-project-version/components/LoaderPicker.vue

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,17 @@ function groupLoaders(loaders: Labrinth.Tags.v2.Loader[]) {
118118
}
119119
120120
const groupedLoaders = computed(() => groupLoaders(loaders))
121+
122+
onMounted(() => {
123+
if (selectedLoaders.value.length === 0) return
124+
125+
// Find the first group that contains any of the selected loaders
126+
const groups = groupedLoaders.value
127+
for (const [groupName, loadersInGroup] of Object.entries(groups)) {
128+
if (loadersInGroup.some((loader) => selectedLoaders.value.includes(loader.name))) {
129+
loaderGroup.value = groupName as GroupLabels
130+
break
131+
}
132+
}
133+
})
121134
</script>

apps/frontend/src/components/ui/create-project-version/components/McVersionPicker.vue

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ import type { Labrinth } from '@modrinth/api-client'
6666
import { SearchIcon } from '@modrinth/assets'
6767
import { ButtonStyled, Chips } from '@modrinth/ui'
6868
import { useMagicKeys } from '@vueuse/core'
69-
import { computed, ref } from 'vue'
69+
import { computed, nextTick, onMounted, ref } from 'vue'
7070
7171
type GameVersion = Labrinth.Tags.v2.GameVersion
7272
@@ -147,16 +147,24 @@ function groupVersions(gameVersions: GameVersion[]) {
147147
)
148148
149149
const getGroupKey = (v: string) => v.split('.').slice(0, 2).join('.')
150+
151+
const getSnapshotGroupKey = (v: string) => {
152+
const cleanVersion = v.split('-')[0]
153+
return cleanVersion.split('.').slice(0, 2).join('.')
154+
}
155+
150156
const groups: Record<string, string[]> = {}
151157
152-
let currentGroupKey = getGroupKey(gameVersions.find((v) => v.major)?.version || '')
158+
let currentGroupKey = getSnapshotGroupKey(gameVersions.find((v) => v.major)?.version || '')
153159
154160
gameVersions.forEach((gameVersion) => {
155161
if (gameVersion.version_type === 'release') {
156162
currentGroupKey = getGroupKey(gameVersion.version)
157163
if (!groups[currentGroupKey]) groups[currentGroupKey] = []
158164
groups[currentGroupKey].push(gameVersion.version)
159165
} else {
166+
if (!currentGroupKey) currentGroupKey = getSnapshotGroupKey(gameVersion.version)
167+
160168
const key = `${currentGroupKey} ${DEV_RELEASE_KEY}`
161169
if (!groups[key]) groups[key] = []
162170
groups[key].push(gameVersion.version)
@@ -205,4 +213,27 @@ function compareGroupKeys(a: string, b: string) {
205213
function searchFilter(gameVersion: Labrinth.Tags.v2.GameVersion) {
206214
return gameVersion.version.toLowerCase().includes(searchQuery.value.toLowerCase())
207215
}
216+
217+
onMounted(async () => {
218+
if (props.modelValue.length === 0) return
219+
220+
// Open non-release tab if any non-release versions are selected
221+
const hasNonReleaseVersions = props.gameVersions.some(
222+
(v) => props.modelValue.includes(v.version) && v.version_type !== 'release',
223+
)
224+
225+
if (hasNonReleaseVersions) {
226+
versionType.value = 'all'
227+
}
228+
229+
await nextTick()
230+
const firstSelectedVersion = allVersionsFlat.value.find((v) => props.modelValue.includes(v))
231+
if (firstSelectedVersion) {
232+
const buttons = Array.from(document.querySelectorAll('button'))
233+
const element = buttons.find((btn) => btn.textContent?.trim() === firstSelectedVersion)
234+
if (element) {
235+
element.scrollIntoView({ behavior: 'smooth', block: 'center' })
236+
}
237+
}
238+
})
208239
</script>

apps/frontend/src/components/ui/create-project-version/components/SuggestedDependencies/SuggestedDependencies.vue

Lines changed: 21 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,24 @@
11
<template>
2-
<div v-if="visibleDependencies.length" class="flex flex-col gap-4">
3-
<span class="font-semibold text-contrast">Suggested dependencies</span>
4-
<div class="flex flex-col gap-2">
5-
<template v-for="(dependency, index) in visibleDependencies">
6-
<SuggestedDependency
7-
v-if="dependency"
8-
:key="index"
9-
:project-id="dependency.project_id"
10-
:name="dependency.name"
11-
:icon="dependency.icon"
12-
:dependency-type="dependency.dependency_type"
13-
:version-name="dependency.versionName"
14-
@on-add-suggestion="
15-
() =>
16-
handleAddSuggestion({
17-
dependency_type: dependency.dependency_type,
18-
project_id: dependency.project_id,
19-
version_id: dependency.version_id,
20-
})
21-
"
22-
/>
23-
</template>
24-
</div>
2+
<div v-if="visibleSuggestedDependencies.length" class="flex flex-col gap-2">
3+
<template v-for="(dependency, index) in visibleSuggestedDependencies">
4+
<SuggestedDependency
5+
v-if="dependency"
6+
:key="index"
7+
:project-id="dependency.project_id"
8+
:name="dependency.name"
9+
:icon="dependency.icon"
10+
:dependency-type="dependency.dependency_type"
11+
:version-name="dependency.versionName"
12+
@on-add-suggestion="
13+
() =>
14+
handleAddSuggestion({
15+
dependency_type: dependency.dependency_type,
16+
project_id: dependency.project_id,
17+
version_id: dependency.version_id,
18+
})
19+
"
20+
/>
21+
</template>
2522
</div>
2623
</template>
2724

@@ -32,28 +29,7 @@ import { injectManageVersionContext } from '~/providers/version/manage-version-m
3229
3330
import SuggestedDependency from './SuggestedDependency.vue'
3431
35-
export interface SuggestedDependency extends Labrinth.Versions.v3.Dependency {
36-
icon?: string
37-
name?: string
38-
versionName?: string
39-
}
40-
41-
const props = defineProps<{
42-
suggestedDependencies: SuggestedDependency[]
43-
}>()
44-
45-
const { draftVersion } = injectManageVersionContext()
46-
47-
const visibleDependencies = computed<SuggestedDependency[]>(() =>
48-
props.suggestedDependencies
49-
.filter(
50-
(dep) =>
51-
!draftVersion.value.dependencies?.some(
52-
(d) => d.project_id === dep.project_id && d.version_id === dep.version_id,
53-
),
54-
)
55-
.sort((a, b) => (a.name || '').localeCompare(b.name || '')),
56-
)
32+
const { visibleSuggestedDependencies } = injectManageVersionContext()
5733
5834
const emit = defineEmits<{
5935
(e: 'onAddSuggestion', dependency: Labrinth.Versions.v3.Dependency): void

0 commit comments

Comments
 (0)