You've already forked AstralRinth
forked from didirus/AstralRinth
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 f90fabc7a57ff201f26e1b628eeced8e6ef75865. * 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) <contact@cal.engineer> Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import type { Labrinth, UploadProgress } from '@modrinth/api-client'
|
||||
import { SaveIcon, SpinnerIcon } from '@modrinth/assets'
|
||||
import {
|
||||
createContext,
|
||||
@@ -55,6 +55,18 @@ export type VersionStage =
|
||||
| 'from-details-mc-versions'
|
||||
| 'from-details-environment'
|
||||
|
||||
export type SuggestedDependency = Labrinth.Versions.v3.Dependency & {
|
||||
name?: string
|
||||
icon?: string
|
||||
versionName?: string
|
||||
}
|
||||
|
||||
export interface PrimaryFile {
|
||||
name: string
|
||||
fileType?: string
|
||||
existing?: boolean
|
||||
}
|
||||
|
||||
export interface ManageVersionContextValue {
|
||||
// State
|
||||
draftVersion: Ref<Labrinth.Versions.v3.DraftVersion>
|
||||
@@ -64,16 +76,23 @@ export interface ManageVersionContextValue {
|
||||
projectType: Ref<Labrinth.Projects.v2.ProjectType | undefined>
|
||||
dependencyProjects: Ref<Record<string, Labrinth.Projects.v3.Project>>
|
||||
dependencyVersions: Ref<Record<string, Labrinth.Versions.v3.Version>>
|
||||
projectsFetchLoading: Ref<boolean>
|
||||
handlingNewFiles: Ref<boolean>
|
||||
suggestedDependencies: Ref<SuggestedDependency[]>
|
||||
visibleSuggestedDependencies: ComputedRef<SuggestedDependency[]>
|
||||
primaryFile: ComputedRef<PrimaryFile | null>
|
||||
|
||||
// Stage management
|
||||
stageConfigs: StageConfigInput<ManageVersionContextValue>[]
|
||||
isSubmitting: Ref<boolean>
|
||||
isUploading: Ref<boolean>
|
||||
uploadProgress: Ref<UploadProgress>
|
||||
modal: ShallowRef<ComponentExposed<typeof MultiStageModal> | null>
|
||||
|
||||
// Computed state
|
||||
editingVersion: ComputedRef<boolean>
|
||||
noLoadersProject: ComputedRef<boolean>
|
||||
noEnvironmentProject: ComputedRef<boolean>
|
||||
noDependenciesProject: ComputedRef<boolean>
|
||||
|
||||
// Stage helpers
|
||||
getNextLabel: (currentIndex?: number | null) => string
|
||||
@@ -81,11 +100,9 @@ export interface ManageVersionContextValue {
|
||||
|
||||
// Version methods
|
||||
newDraftVersion: (projectId: string, version?: Labrinth.Versions.v3.DraftVersion | null) => void
|
||||
setPrimaryFile: (index: number) => void
|
||||
setInferredVersionData: (
|
||||
file: File,
|
||||
project: Labrinth.Projects.v2.Project,
|
||||
) => Promise<InferredVersionInfo>
|
||||
handleNewFiles: (newFiles: File[]) => Promise<void>
|
||||
swapPrimaryFile: (index: number) => void
|
||||
replacePrimaryFile: (file: File) => Promise<void>
|
||||
getProject: (projectId: string) => Promise<Labrinth.Projects.v3.Project>
|
||||
getVersion: (versionId: string) => Promise<Labrinth.Versions.v3.Version>
|
||||
|
||||
@@ -129,24 +146,42 @@ const PROJECT_TYPE_LOADERS: Record<string, readonly string[]> = {
|
||||
modpack: ['mrpack'],
|
||||
} as const
|
||||
|
||||
export const fileTypeLabels: Record<Labrinth.Versions.v3.FileType | 'primary', string> = {
|
||||
primary: 'Primary',
|
||||
unknown: 'Other',
|
||||
'required-resource-pack': 'Required RP',
|
||||
'optional-resource-pack': 'Optional RP',
|
||||
'sources-jar': 'Sources JAR',
|
||||
'dev-jar': 'Dev JAR',
|
||||
'javadoc-jar': 'Javadoc JAR',
|
||||
signature: 'Signature',
|
||||
}
|
||||
|
||||
export const [injectManageVersionContext, provideManageVersionContext] =
|
||||
createContext<ManageVersionContextValue>('CreateProjectVersionModal')
|
||||
|
||||
export function createManageVersionContext(
|
||||
modal: ShallowRef<ComponentExposed<typeof MultiStageModal> | null>,
|
||||
onSave?: () => void,
|
||||
): ManageVersionContextValue {
|
||||
const { labrinth } = injectModrinthClient()
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const { refreshVersions } = injectProjectPageContext()
|
||||
const { refreshVersions, projectV2 } = injectProjectPageContext()
|
||||
|
||||
// State
|
||||
const draftVersion = ref<Labrinth.Versions.v3.DraftVersion>(structuredClone(EMPTY_DRAFT_VERSION))
|
||||
const filesToAdd = ref<Labrinth.Versions.v3.DraftVersionFile[]>([])
|
||||
const existingFilesToDelete = ref<Labrinth.Versions.v3.VersionFileHash['sha1'][]>([])
|
||||
const handlingNewFiles = ref(false)
|
||||
const inferredVersionData = ref<InferredVersionInfo>()
|
||||
const dependencyProjects = ref<Record<string, Labrinth.Projects.v3.Project>>({})
|
||||
const dependencyVersions = ref<Record<string, Labrinth.Versions.v3.Version>>({})
|
||||
const projectsFetchLoading = ref(false)
|
||||
const suggestedDependencies = ref<SuggestedDependency[]>([])
|
||||
|
||||
const isSubmitting = ref(false)
|
||||
const isUploading = ref(false)
|
||||
const uploadProgress = ref<UploadProgress>({ loaded: 0, total: 0, progress: 0 })
|
||||
|
||||
const projectType = computed<Labrinth.Projects.v2.ProjectType>(() => {
|
||||
const primaryFile = filesToAdd.value[0]?.file
|
||||
@@ -166,7 +201,7 @@ export function createManageVersionContext(
|
||||
if (loaders.some((loader) => PROJECT_TYPE_LOADERS.datapack.includes(loader))) {
|
||||
return 'datapack'
|
||||
}
|
||||
if (loaders.some((loader) => PROJECT_TYPE_LOADERS.resourcepack.includes(loader))) {
|
||||
if (loaders.length === 1 && loaders[0] === 'minecraft') {
|
||||
return 'resourcepack'
|
||||
}
|
||||
if (loaders.some((loader) => PROJECT_TYPE_LOADERS.shader.includes(loader))) {
|
||||
@@ -185,6 +220,30 @@ export function createManageVersionContext(
|
||||
// Computed state
|
||||
const editingVersion = computed(() => Boolean(draftVersion.value.version_id))
|
||||
|
||||
const visibleSuggestedDependencies = computed<SuggestedDependency[]>(() => {
|
||||
const existingDeps = draftVersion.value.dependencies ?? []
|
||||
const seenKeys = new Set<string>()
|
||||
|
||||
const isDuplicateSuggestion = (dep: SuggestedDependency) => {
|
||||
const key = `${dep.project_id ?? ''}:${dep.version_id ?? ''}`
|
||||
if (seenKeys.has(key)) return true
|
||||
seenKeys.add(key)
|
||||
return false
|
||||
}
|
||||
|
||||
const isAlreadyAdded = (dep: SuggestedDependency) =>
|
||||
existingDeps.some((existing) => {
|
||||
if (existing.project_id !== dep.project_id) return false
|
||||
if (!existing.version_id && !dep.version_id) return true
|
||||
return existing.version_id === dep.version_id
|
||||
})
|
||||
|
||||
return suggestedDependencies.value
|
||||
.filter((dep) => !isDuplicateSuggestion(dep))
|
||||
.filter((dep) => !isAlreadyAdded(dep))
|
||||
.sort((a, b) => (a.name ?? '').localeCompare(b.name ?? ''))
|
||||
})
|
||||
|
||||
// Version management methods
|
||||
function newDraftVersion(
|
||||
projectId: string,
|
||||
@@ -195,15 +254,50 @@ export function createManageVersionContext(
|
||||
filesToAdd.value = []
|
||||
existingFilesToDelete.value = []
|
||||
inferredVersionData.value = undefined
|
||||
// projectType.value = undefined
|
||||
}
|
||||
|
||||
function setPrimaryFile(index: number) {
|
||||
async function handleNewFiles(newFiles: File[]) {
|
||||
handlingNewFiles.value = true
|
||||
// detect primary file if no primary file is set
|
||||
const primaryFileIndex = primaryFile.value ? null : detectPrimaryFileIndex(newFiles)
|
||||
|
||||
newFiles.forEach((file) => filesToAdd.value.push({ file }))
|
||||
|
||||
if (primaryFileIndex !== null) {
|
||||
if (primaryFileIndex) swapPrimaryFile(primaryFileIndex)
|
||||
}
|
||||
|
||||
if (
|
||||
filesToAdd.value.length === 1 &&
|
||||
!editingVersion.value &&
|
||||
modal.value?.currentStageIndex === 0
|
||||
) {
|
||||
if (await rejectOnRedundantWrappedZip(filesToAdd.value[0].file)) return
|
||||
|
||||
await addDetectedData()
|
||||
modal.value?.nextStage()
|
||||
}
|
||||
|
||||
handlingNewFiles.value = false
|
||||
}
|
||||
|
||||
async function replacePrimaryFile(file: File) {
|
||||
if (file && !editingVersion.value) {
|
||||
filesToAdd.value[0] = { file }
|
||||
}
|
||||
if (await rejectOnRedundantWrappedZip(file)) return
|
||||
await addDetectedData()
|
||||
}
|
||||
|
||||
async function swapPrimaryFile(index: number) {
|
||||
const files = filesToAdd.value
|
||||
if (index <= 0 || index >= files.length) return
|
||||
files[0].fileType = 'unknown'
|
||||
files[index].fileType = 'unknown'
|
||||
;[files[0], files[index]] = [files[index], files[0]]
|
||||
|
||||
if (await rejectOnRedundantWrappedZip(files[0].file)) return
|
||||
await addDetectedData()
|
||||
}
|
||||
|
||||
const tags = useGeneratedState()
|
||||
@@ -241,6 +335,65 @@ export function createManageVersionContext(
|
||||
}
|
||||
}
|
||||
|
||||
async function checkRedundantWrappedZip(file: File): Promise<boolean> {
|
||||
const fileName = file.name.toLowerCase()
|
||||
if (!fileName.endsWith('.zip')) return false
|
||||
|
||||
const zip = await JSZip.loadAsync(file)
|
||||
const entries = Object.keys(zip.files).map((e) => e.toLowerCase())
|
||||
const filtered = entries.filter((e) => !e.startsWith('__macosx/') && !e.endsWith('.ds_store'))
|
||||
|
||||
const hasRootEntries = filtered.some((e) => !e.includes('/'))
|
||||
if (hasRootEntries) return false
|
||||
|
||||
const topLevelFolders = new Set(filtered.map((e) => e.split('/')[0]).filter(Boolean))
|
||||
if (topLevelFolders.size !== 1) return false
|
||||
|
||||
const [folderName] = [...topLevelFolders]
|
||||
|
||||
// Check if the inner folder contents indicate a datapack or resource pack
|
||||
const innerEntries = filtered.map((e) => e.substring(folderName.length + 1))
|
||||
const hasPackMcmeta = hasFile(innerEntries, 'pack.mcmeta')
|
||||
const hasAssets = hasDir(innerEntries, 'assets')
|
||||
const hasData = hasDir(innerEntries, 'data')
|
||||
|
||||
return hasPackMcmeta && (hasAssets || hasData)
|
||||
}
|
||||
|
||||
async function rejectOnRedundantWrappedZip(file: File): Promise<boolean> {
|
||||
if (await checkRedundantWrappedZip(file)) {
|
||||
newDraftVersion(projectV2.value.id)
|
||||
modal.value?.setStage('add-files')
|
||||
addNotification({
|
||||
title: 'Invalid ZIP structure',
|
||||
text: `The uploaded ZIP file "${file.name}" contains a redundant top-level folder. Please re-zip the contents directly without the extra folder layer.`,
|
||||
type: 'error',
|
||||
})
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
async function inferEnvironmentFromVersions(
|
||||
projectId: string,
|
||||
loaders: string[],
|
||||
): Promise<Labrinth.Projects.v3.Environment | undefined> {
|
||||
try {
|
||||
const versions = await labrinth.versions_v3.getProjectVersions(projectId, {
|
||||
loaders,
|
||||
})
|
||||
|
||||
if (versions.length > 0) {
|
||||
const mostRecentVersion = versions[0]
|
||||
const version = await labrinth.versions_v3.getVersion(mostRecentVersion.id)
|
||||
return version.environment !== 'unknown' ? version.environment : undefined
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching versions for environment inference:', error)
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
async function setInferredVersionData(
|
||||
file: File,
|
||||
project: Labrinth.Projects.v2.Project,
|
||||
@@ -251,19 +404,7 @@ export function createManageVersionContext(
|
||||
tags.value.gameVersions,
|
||||
)) as InferredVersionInfo
|
||||
|
||||
try {
|
||||
const versions = await labrinth.versions_v3.getProjectVersions(project.id, {
|
||||
loaders: inferred.loaders ?? [],
|
||||
})
|
||||
|
||||
if (versions.length > 0) {
|
||||
const mostRecentVersion = versions[0]
|
||||
const version = await labrinth.versions_v3.getVersion(mostRecentVersion.id)
|
||||
inferred.environment = version.environment !== 'unknown' ? version.environment : undefined
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching versions for environment inference:', error)
|
||||
}
|
||||
inferred.environment = await inferEnvironmentFromVersions(project.id, inferred.loaders ?? [])
|
||||
|
||||
const noLoaders = !inferred.loaders?.length
|
||||
|
||||
@@ -284,6 +425,12 @@ export function createManageVersionContext(
|
||||
return inferred
|
||||
}
|
||||
|
||||
// Stage visibility computeds (inlined)
|
||||
const noEnvironmentProject = computed(
|
||||
() => projectType.value !== 'mod' && projectType.value !== 'modpack',
|
||||
)
|
||||
const noDependenciesProject = computed(() => projectType.value === 'modpack')
|
||||
|
||||
const getProject = async (projectId: string) => {
|
||||
if (dependencyProjects.value[projectId]) {
|
||||
return dependencyProjects.value[projectId]
|
||||
@@ -302,16 +449,198 @@ export function createManageVersionContext(
|
||||
return version
|
||||
}
|
||||
|
||||
// Primary file computed
|
||||
const primaryFile = computed<PrimaryFile | null>(() => {
|
||||
const existingPrimaryFile = draftVersion.value.existing_files?.[0]
|
||||
if (existingPrimaryFile) {
|
||||
return {
|
||||
name: existingPrimaryFile.filename,
|
||||
fileType: existingPrimaryFile.file_type,
|
||||
existing: true,
|
||||
}
|
||||
}
|
||||
|
||||
const addedPrimaryFile = filesToAdd.value[0]
|
||||
if (addedPrimaryFile) {
|
||||
return {
|
||||
name: addedPrimaryFile.file.name,
|
||||
fileType: addedPrimaryFile.fileType,
|
||||
existing: false,
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
// File handling helpers
|
||||
function detectPrimaryFileIndex(files: File[]): number {
|
||||
const extensionPriority = ['.jar', '.zip', '.litemod', '.mrpack', '.mrpack-primary']
|
||||
|
||||
for (const ext of extensionPriority) {
|
||||
const matches = files.filter((file) => file.name.toLowerCase().endsWith(ext))
|
||||
if (matches.length > 0) {
|
||||
const shortest = matches.reduce((a, b) => (a.name.length < b.name.length ? a : b))
|
||||
return files.indexOf(shortest)
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
const addDetectedData = async () => {
|
||||
if (editingVersion.value) return
|
||||
|
||||
const primaryFileData = filesToAdd.value[0]?.file
|
||||
if (!primaryFileData) return
|
||||
|
||||
try {
|
||||
const inferredData = await setInferredVersionData(primaryFileData, projectV2.value)
|
||||
const mappedInferredData: Partial<Labrinth.Versions.v3.DraftVersion> = {
|
||||
...inferredData,
|
||||
name: inferredData.name || '',
|
||||
}
|
||||
|
||||
draftVersion.value = {
|
||||
...draftVersion.value,
|
||||
...mappedInferredData,
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error parsing version file data', err)
|
||||
}
|
||||
}
|
||||
|
||||
// Watch draft version dependencies to fetch project/version data
|
||||
watch(
|
||||
draftVersion,
|
||||
async (version) => {
|
||||
if (noDependenciesProject.value) return
|
||||
const deps = version.dependencies || []
|
||||
|
||||
for (const dep of deps) {
|
||||
try {
|
||||
if (dep?.project_id) await getProject(dep.project_id)
|
||||
if (dep?.version_id) await getVersion(dep.version_id)
|
||||
} catch (error: any) {
|
||||
addNotification({
|
||||
title: 'Could not fetch dependency data',
|
||||
text: error.data ? error.data.description : error,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
}
|
||||
projectsFetchLoading.value = false
|
||||
},
|
||||
{ immediate: true, deep: true },
|
||||
)
|
||||
|
||||
// Watch loaders to infer environment if not set
|
||||
watch(
|
||||
() => draftVersion.value.loaders,
|
||||
async (loaders) => {
|
||||
if (noEnvironmentProject.value) return
|
||||
if (draftVersion.value.environment) return
|
||||
if (!loaders?.length) return
|
||||
|
||||
const projectId = draftVersion.value.project_id
|
||||
if (!projectId) return
|
||||
|
||||
const environment = await inferEnvironmentFromVersions(projectId, loaders)
|
||||
if (environment && !draftVersion.value.environment) {
|
||||
draftVersion.value.environment = environment
|
||||
inferredVersionData.value = { ...inferredVersionData.value, environment }
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// Watch loaders to fetch suggested dependencies
|
||||
// Gets the most recent version that matches loaders and suggests its dependencies
|
||||
watch(
|
||||
() => draftVersion.value.loaders,
|
||||
async (loaders) => {
|
||||
if (noDependenciesProject.value) return
|
||||
try {
|
||||
suggestedDependencies.value = []
|
||||
|
||||
if (!loaders?.length) return
|
||||
|
||||
const projectId = draftVersion.value.project_id
|
||||
if (!projectId) return
|
||||
|
||||
try {
|
||||
const versions = await labrinth.versions_v3.getProjectVersions(projectId, {
|
||||
loaders,
|
||||
})
|
||||
|
||||
// Get the most recent matching version and extract its dependencies
|
||||
if (versions.length > 0) {
|
||||
const mostRecentVersion = versions[0]
|
||||
for (const dep of mostRecentVersion.dependencies) {
|
||||
suggestedDependencies.value.push({
|
||||
project_id: dep.project_id,
|
||||
version_id: dep.version_id,
|
||||
dependency_type: dep.dependency_type,
|
||||
file_name: dep.file_name,
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(`Failed to get versions for project ${projectId}:`, error)
|
||||
}
|
||||
|
||||
for (const dep of suggestedDependencies.value) {
|
||||
try {
|
||||
if (dep.project_id) {
|
||||
const proj = await getProject(dep.project_id)
|
||||
dep.name = proj.name
|
||||
dep.icon = proj.icon_url
|
||||
}
|
||||
|
||||
if (dep.version_id) {
|
||||
const version = await getVersion(dep.version_id)
|
||||
dep.versionName = version.name
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(`Failed to fetch project/version data for dependency:`, error)
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
addNotification({
|
||||
title: 'Could not fetch suggested dependencies',
|
||||
text: error.data ? error.data.description : error,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
// Submission handlers
|
||||
async function handleCreateVersion() {
|
||||
const version = toRaw(draftVersion.value)
|
||||
const files = toRaw(filesToAdd.value)
|
||||
isSubmitting.value = true
|
||||
isUploading.value = true
|
||||
|
||||
// Reset progress and navigate to uploading stage
|
||||
uploadProgress.value = { loaded: 0, total: 0, progress: 0 }
|
||||
|
||||
if (noEnvironmentProject.value) version.environment = undefined
|
||||
|
||||
try {
|
||||
await labrinth.versions_v3.createVersion(version, files, projectType.value ?? null)
|
||||
const uploadHandle = labrinth.versions_v3.createVersion(
|
||||
version,
|
||||
files,
|
||||
projectType.value ?? null,
|
||||
)
|
||||
|
||||
// Subscribe to progress updates
|
||||
uploadHandle.onProgress((progress) => {
|
||||
uploadProgress.value = progress
|
||||
})
|
||||
|
||||
// Wait for upload to complete
|
||||
await uploadHandle.promise
|
||||
|
||||
modal.value?.hide()
|
||||
addNotification({
|
||||
title: 'Project version created',
|
||||
@@ -319,13 +648,15 @@ export function createManageVersionContext(
|
||||
type: 'success',
|
||||
})
|
||||
await refreshVersions()
|
||||
onSave?.()
|
||||
} catch (err: any) {
|
||||
addNotification({
|
||||
title: 'An error occurred',
|
||||
title: 'Could not create project version',
|
||||
text: err.data ? err.data.description : err,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
isUploading.value = false
|
||||
isSubmitting.value = false
|
||||
}
|
||||
|
||||
@@ -336,6 +667,12 @@ export function createManageVersionContext(
|
||||
|
||||
isSubmitting.value = true
|
||||
|
||||
// Reset progress if we have files to upload
|
||||
if (files.length > 0) {
|
||||
isUploading.value = true
|
||||
uploadProgress.value = { loaded: 0, total: 0, progress: 0 }
|
||||
}
|
||||
|
||||
if (noEnvironmentProject.value) version.environment = undefined
|
||||
|
||||
try {
|
||||
@@ -362,7 +699,13 @@ export function createManageVersionContext(
|
||||
await labrinth.versions_v3.modifyVersion(version.version_id, data)
|
||||
|
||||
if (files.length > 0) {
|
||||
await labrinth.versions_v3.addFilesToVersion(version.version_id, files)
|
||||
const uploadHandle = labrinth.versions_v3.addFilesToVersion(version.version_id, files)
|
||||
|
||||
uploadHandle.onProgress((progress) => {
|
||||
uploadProgress.value = progress
|
||||
})
|
||||
|
||||
await uploadHandle.promise
|
||||
}
|
||||
|
||||
// Delete files that were marked for deletion
|
||||
@@ -379,6 +722,7 @@ export function createManageVersionContext(
|
||||
type: 'success',
|
||||
})
|
||||
await refreshVersions()
|
||||
onSave?.()
|
||||
} catch (err: any) {
|
||||
addNotification({
|
||||
title: 'An error occurred',
|
||||
@@ -386,15 +730,10 @@ export function createManageVersionContext(
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
isUploading.value = false
|
||||
isSubmitting.value = false
|
||||
}
|
||||
|
||||
// Stage visibility computeds (inlined)
|
||||
const noLoadersProject = computed(() => projectType.value === 'resourcepack')
|
||||
const noEnvironmentProject = computed(
|
||||
() => projectType.value !== 'mod' && projectType.value !== 'modpack',
|
||||
)
|
||||
|
||||
// Dynamic next button label
|
||||
function getNextLabel(currentIndex: number | null = null) {
|
||||
const currentStageIndex = currentIndex ? currentIndex : modal.value?.currentStageIndex || 0
|
||||
@@ -424,6 +763,8 @@ export function createManageVersionContext(
|
||||
return editingVersion.value ? 'Edit environment' : 'Add environment'
|
||||
case 'add-changelog':
|
||||
return editingVersion.value ? 'Edit changelog' : 'Add changelog'
|
||||
case 'metadata':
|
||||
return 'Edit metadata'
|
||||
default:
|
||||
return 'Next'
|
||||
}
|
||||
@@ -448,16 +789,23 @@ export function createManageVersionContext(
|
||||
projectType,
|
||||
dependencyProjects,
|
||||
dependencyVersions,
|
||||
handlingNewFiles,
|
||||
projectsFetchLoading,
|
||||
suggestedDependencies,
|
||||
visibleSuggestedDependencies,
|
||||
primaryFile,
|
||||
|
||||
// Stage management
|
||||
stageConfigs,
|
||||
isSubmitting,
|
||||
isUploading,
|
||||
uploadProgress,
|
||||
modal,
|
||||
|
||||
// Computed
|
||||
editingVersion,
|
||||
noLoadersProject,
|
||||
noEnvironmentProject,
|
||||
noDependenciesProject,
|
||||
|
||||
// Stage helpers
|
||||
getNextLabel,
|
||||
@@ -465,10 +813,11 @@ export function createManageVersionContext(
|
||||
|
||||
// Methods
|
||||
newDraftVersion,
|
||||
setPrimaryFile,
|
||||
setInferredVersionData,
|
||||
swapPrimaryFile,
|
||||
replacePrimaryFile,
|
||||
getProject,
|
||||
getVersion,
|
||||
handleNewFiles,
|
||||
handleCreateVersion,
|
||||
handleSaveVersionEdits,
|
||||
}
|
||||
|
||||
100
apps/frontend/src/providers/version/stages/add-files-stage.ts
Normal file
100
apps/frontend/src/providers/version/stages/add-files-stage.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { LeftArrowIcon, RightArrowIcon, XIcon } from '@modrinth/assets'
|
||||
import type { StageConfigInput } from '@modrinth/ui'
|
||||
import { markRaw } from 'vue'
|
||||
|
||||
import AddFilesStage from '~/components/ui/create-project-version/stages/AddFilesStage.vue'
|
||||
|
||||
import type { ManageVersionContextValue } from '../manage-version-modal'
|
||||
|
||||
export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
|
||||
id: 'add-files',
|
||||
stageContent: markRaw(AddFilesStage),
|
||||
title: (ctx) => (ctx.editingVersion.value ? 'Edit files' : 'Files'),
|
||||
nonProgressStage: (ctx) => ctx.editingVersion.value,
|
||||
cannotNavigateForward: (ctx) => {
|
||||
const hasFiles =
|
||||
ctx.filesToAdd.value.length !== 0 ||
|
||||
(ctx.draftVersion.value.existing_files?.length ?? 0) !== 0
|
||||
return !hasFiles || ctx.handlingNewFiles.value
|
||||
},
|
||||
leftButtonConfig: (ctx) => {
|
||||
const hasFiles =
|
||||
ctx.filesToAdd.value.length !== 0 ||
|
||||
(ctx.draftVersion.value.existing_files?.length ?? 0) !== 0
|
||||
|
||||
if (ctx.editingVersion.value)
|
||||
return {
|
||||
label: 'Cancel',
|
||||
icon: XIcon,
|
||||
onClick: () => ctx.modal.value?.hide(),
|
||||
}
|
||||
|
||||
if (!hasFiles || ctx.handlingNewFiles.value) return null
|
||||
|
||||
return {
|
||||
label: 'Cancel',
|
||||
icon: XIcon,
|
||||
onClick: () => ctx.modal.value?.hide(),
|
||||
}
|
||||
},
|
||||
rightButtonConfig: (ctx) => {
|
||||
const hasFiles =
|
||||
ctx.filesToAdd.value.length !== 0 ||
|
||||
(ctx.draftVersion.value.existing_files?.length ?? 0) !== 0
|
||||
|
||||
if (ctx.editingVersion.value)
|
||||
return {
|
||||
...ctx.saveButtonConfig(),
|
||||
label: 'Save files',
|
||||
disabled: ctx.isSubmitting.value,
|
||||
}
|
||||
|
||||
if (!hasFiles || ctx.handlingNewFiles.value) return null
|
||||
|
||||
return {
|
||||
label: ctx.getNextLabel(),
|
||||
icon: RightArrowIcon,
|
||||
iconPosition: 'after',
|
||||
disabled: !hasFiles,
|
||||
onClick: () => ctx.modal.value?.nextStage(),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export const fromDetailsStageConfig: StageConfigInput<ManageVersionContextValue> = {
|
||||
id: 'from-details-files',
|
||||
stageContent: markRaw(AddFilesStage),
|
||||
title: 'Edit files',
|
||||
nonProgressStage: true,
|
||||
leftButtonConfig: (ctx) => {
|
||||
const hasFiles =
|
||||
ctx.filesToAdd.value.length !== 0 ||
|
||||
(ctx.draftVersion.value.existing_files?.length ?? 0) !== 0
|
||||
|
||||
return {
|
||||
label: 'Back',
|
||||
icon: LeftArrowIcon,
|
||||
disabled: !hasFiles,
|
||||
onClick: () => ctx.modal.value?.setStage('metadata'),
|
||||
}
|
||||
},
|
||||
rightButtonConfig: (ctx) => {
|
||||
const hasFiles =
|
||||
ctx.filesToAdd.value.length !== 0 ||
|
||||
(ctx.draftVersion.value.existing_files?.length ?? 0) !== 0
|
||||
|
||||
return ctx.editingVersion.value
|
||||
? {
|
||||
...ctx.saveButtonConfig(),
|
||||
label: 'Save files',
|
||||
disabled: !hasFiles || ctx.isSubmitting.value,
|
||||
}
|
||||
: {
|
||||
label: 'Add details',
|
||||
icon: RightArrowIcon,
|
||||
iconPosition: 'after',
|
||||
disabled: !hasFiles,
|
||||
onClick: () => ctx.modal.value?.setStage('add-details'),
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
import { RightArrowIcon, XIcon } from '@modrinth/assets'
|
||||
import type { StageConfigInput } from '@modrinth/ui'
|
||||
import { markRaw } from 'vue'
|
||||
|
||||
import AddFilesStage from '~/components/ui/create-project-version/stages/AddFilesStage.vue'
|
||||
|
||||
import type { ManageVersionContextValue } from '../manage-version-modal'
|
||||
|
||||
export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
|
||||
id: 'add-files',
|
||||
stageContent: markRaw(AddFilesStage),
|
||||
title: (ctx) => (ctx.editingVersion.value ? 'Edit files' : 'Add files'),
|
||||
leftButtonConfig: (ctx) => {
|
||||
const hasFiles =
|
||||
ctx.filesToAdd.value.length !== 0 ||
|
||||
(ctx.draftVersion.value.existing_files?.length ?? 0) !== 0
|
||||
|
||||
if (ctx.editingVersion.value)
|
||||
return {
|
||||
label: 'Cancel',
|
||||
icon: XIcon,
|
||||
onClick: () => ctx.modal.value?.hide(),
|
||||
}
|
||||
|
||||
if (!hasFiles) return null
|
||||
|
||||
return {
|
||||
label: 'Cancel',
|
||||
icon: XIcon,
|
||||
onClick: () => ctx.modal.value?.hide(),
|
||||
}
|
||||
},
|
||||
rightButtonConfig: (ctx) => {
|
||||
const hasFiles =
|
||||
ctx.filesToAdd.value.length !== 0 ||
|
||||
(ctx.draftVersion.value.existing_files?.length ?? 0) !== 0
|
||||
|
||||
if (ctx.editingVersion.value)
|
||||
return {
|
||||
...ctx.saveButtonConfig(),
|
||||
label: 'Save files',
|
||||
disabled: ctx.isSubmitting.value,
|
||||
}
|
||||
|
||||
if (!hasFiles) return null
|
||||
|
||||
return {
|
||||
label: ctx.getNextLabel(),
|
||||
icon: RightArrowIcon,
|
||||
iconPosition: 'after',
|
||||
disabled: !hasFiles,
|
||||
onClick: () => ctx.modal.value?.nextStage(),
|
||||
}
|
||||
},
|
||||
nonProgressStage: (ctx) => ctx.editingVersion.value,
|
||||
}
|
||||
@@ -2,14 +2,15 @@ import { LeftArrowIcon, RightArrowIcon, XIcon } from '@modrinth/assets'
|
||||
import type { StageConfigInput } from '@modrinth/ui'
|
||||
import { markRaw } from 'vue'
|
||||
|
||||
import AddDetailsStage from '~/components/ui/create-project-version/stages/AddDetailsStage.vue'
|
||||
import DependenciesStage from '~/components/ui/create-project-version/stages/DependenciesStage.vue'
|
||||
|
||||
import type { ManageVersionContextValue } from '../manage-version-modal'
|
||||
|
||||
export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
|
||||
id: 'add-details',
|
||||
stageContent: markRaw(AddDetailsStage),
|
||||
title: (ctx) => (ctx.editingVersion.value ? 'Edit details' : 'Add details'),
|
||||
id: 'add-dependencies',
|
||||
stageContent: markRaw(DependenciesStage),
|
||||
title: (ctx) => (ctx.editingVersion.value ? 'Edit dependencies' : 'Dependencies'),
|
||||
skip: true,
|
||||
leftButtonConfig: (ctx) =>
|
||||
ctx.editingVersion.value
|
||||
? {
|
||||
@@ -24,17 +25,35 @@ export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
|
||||
},
|
||||
rightButtonConfig: (ctx) =>
|
||||
ctx.editingVersion.value
|
||||
? {
|
||||
...ctx.saveButtonConfig(),
|
||||
disabled:
|
||||
ctx.draftVersion.value.version_number.trim().length === 0 || ctx.isSubmitting.value,
|
||||
}
|
||||
? ctx.saveButtonConfig()
|
||||
: {
|
||||
label: ctx.getNextLabel(),
|
||||
icon: RightArrowIcon,
|
||||
iconPosition: 'after',
|
||||
disabled: ctx.draftVersion.value.version_number.trim().length === 0,
|
||||
onClick: () => ctx.modal.value?.nextStage(),
|
||||
},
|
||||
nonProgressStage: (ctx) => ctx.editingVersion.value,
|
||||
}
|
||||
|
||||
export const fromDetailsStageConfig: StageConfigInput<ManageVersionContextValue> = {
|
||||
id: 'from-details-dependencies',
|
||||
stageContent: markRaw(DependenciesStage),
|
||||
title: 'Edit dependencies',
|
||||
nonProgressStage: true,
|
||||
leftButtonConfig: (ctx) => ({
|
||||
label: 'Back',
|
||||
icon: LeftArrowIcon,
|
||||
onClick: () => ctx.modal.value?.setStage('metadata'),
|
||||
}),
|
||||
rightButtonConfig: (ctx) =>
|
||||
ctx.editingVersion.value
|
||||
? {
|
||||
...ctx.saveButtonConfig(),
|
||||
}
|
||||
: {
|
||||
label: 'Add details',
|
||||
icon: RightArrowIcon,
|
||||
iconPosition: 'after',
|
||||
onClick: () => ctx.modal.value?.setStage('add-details'),
|
||||
},
|
||||
}
|
||||
@@ -2,14 +2,15 @@ import { LeftArrowIcon, PlusIcon, SaveIcon, SpinnerIcon, XIcon } from '@modrinth
|
||||
import type { StageConfigInput } from '@modrinth/ui'
|
||||
import { markRaw } from 'vue'
|
||||
|
||||
import AddChangelogStage from '~/components/ui/create-project-version/stages/AddChangelogStage.vue'
|
||||
import DetailsStage from '~/components/ui/create-project-version/stages/DetailsStage.vue'
|
||||
|
||||
import type { ManageVersionContextValue } from '../manage-version-modal'
|
||||
|
||||
export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
|
||||
id: 'add-changelog',
|
||||
stageContent: markRaw(AddChangelogStage),
|
||||
title: (ctx) => (ctx.editingVersion.value ? 'Edit changelog' : 'Add changelog'),
|
||||
id: 'add-details',
|
||||
stageContent: markRaw(DetailsStage),
|
||||
title: (ctx) => (ctx.editingVersion.value ? 'Edit details' : 'Details'),
|
||||
maxWidth: '744px',
|
||||
leftButtonConfig: (ctx) =>
|
||||
ctx.editingVersion.value
|
||||
? {
|
||||
@@ -23,7 +24,13 @@ export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
|
||||
onClick: () => ctx.modal.value?.prevStage(),
|
||||
},
|
||||
rightButtonConfig: (ctx) => ({
|
||||
label: ctx.editingVersion.value ? 'Save changes' : 'Create version',
|
||||
label: ctx.editingVersion.value
|
||||
? 'Save changes'
|
||||
: ctx.isUploading.value
|
||||
? ctx.uploadProgress.value.progress >= 1
|
||||
? 'Creating version'
|
||||
: `Uploading version ${Math.round(ctx.uploadProgress.value.progress * 100)}%`
|
||||
: 'Create version',
|
||||
icon: ctx.isSubmitting.value ? SpinnerIcon : ctx.editingVersion.value ? SaveIcon : PlusIcon,
|
||||
iconPosition: 'before',
|
||||
iconClass: ctx.isSubmitting.value ? 'animate-spin' : undefined,
|
||||
@@ -2,18 +2,20 @@ import { LeftArrowIcon, RightArrowIcon } from '@modrinth/assets'
|
||||
import type { StageConfigInput } from '@modrinth/ui'
|
||||
import { markRaw } from 'vue'
|
||||
|
||||
import AddEnvironmentStage from '~/components/ui/create-project-version/stages/AddEnvironmentStage.vue'
|
||||
import EnvironmentStage from '~/components/ui/create-project-version/stages/EnvironmentStage.vue'
|
||||
|
||||
import type { ManageVersionContextValue } from '../manage-version-modal'
|
||||
|
||||
export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
|
||||
id: 'add-environment',
|
||||
stageContent: markRaw(AddEnvironmentStage),
|
||||
title: (ctx) => (ctx.editingVersion.value ? 'Edit environment' : 'Add environment'),
|
||||
stageContent: markRaw(EnvironmentStage),
|
||||
title: (ctx) => (ctx.editingVersion.value ? 'Edit environment' : 'Environment'),
|
||||
skip: (ctx) =>
|
||||
ctx.noEnvironmentProject.value ||
|
||||
(!ctx.editingVersion.value && !!ctx.inferredVersionData.value?.environment) ||
|
||||
(ctx.editingVersion.value && !!ctx.draftVersion.value.environment),
|
||||
hideStageInBreadcrumb: (ctx) => !ctx.primaryFile.value || ctx.handlingNewFiles.value,
|
||||
cannotNavigateForward: (ctx) => !ctx.draftVersion.value.environment,
|
||||
leftButtonConfig: (ctx) => ({
|
||||
label: 'Back',
|
||||
icon: LeftArrowIcon,
|
||||
@@ -30,14 +32,14 @@ export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
|
||||
|
||||
export const fromDetailsStageConfig: StageConfigInput<ManageVersionContextValue> = {
|
||||
id: 'from-details-environment',
|
||||
stageContent: markRaw(AddEnvironmentStage),
|
||||
stageContent: markRaw(EnvironmentStage),
|
||||
title: 'Edit environment',
|
||||
nonProgressStage: true,
|
||||
leftButtonConfig: (ctx) => ({
|
||||
label: 'Back',
|
||||
icon: LeftArrowIcon,
|
||||
disabled: !ctx.draftVersion.value.environment,
|
||||
onClick: () => ctx.modal.value?.setStage('add-details'),
|
||||
onClick: () => ctx.modal.value?.setStage('metadata'),
|
||||
}),
|
||||
rightButtonConfig: (ctx) =>
|
||||
ctx.editingVersion.value
|
||||
@@ -46,10 +48,10 @@ export const fromDetailsStageConfig: StageConfigInput<ManageVersionContextValue>
|
||||
disabled: !ctx.draftVersion.value.environment,
|
||||
}
|
||||
: {
|
||||
label: ctx.getNextLabel(2),
|
||||
label: 'Add details',
|
||||
icon: RightArrowIcon,
|
||||
iconPosition: 'after',
|
||||
disabled: !ctx.draftVersion.value.environment,
|
||||
onClick: () => ctx.modal.value?.setStage(2),
|
||||
onClick: () => ctx.modal.value?.setStage('add-details'),
|
||||
},
|
||||
}
|
||||
@@ -1,30 +1,39 @@
|
||||
import { stageConfig as addChangelogStageConfig } from './add-changelog'
|
||||
import { stageConfig as addDependenciesStageConfig } from './add-dependencies'
|
||||
import { stageConfig as addDetailsStageConfig } from './add-details'
|
||||
import {
|
||||
fromDetailsStageConfig as editEnvironmentStageConfig,
|
||||
stageConfig as addEnvironmentStageConfig,
|
||||
} from './add-environment'
|
||||
import { stageConfig as addFilesStageConfig } from './add-files'
|
||||
fromDetailsStageConfig as fromDetailsFilesStageConfig,
|
||||
stageConfig as addFilesStageConfig,
|
||||
} from './add-files-stage'
|
||||
import {
|
||||
fromDetailsStageConfig as editLoadersStageConfig,
|
||||
stageConfig as addLoadersStageConfig,
|
||||
} from './add-loaders'
|
||||
fromDetailsStageConfig as fromDetailsDependenciesStageConfig,
|
||||
stageConfig as dependenciesStageConfig,
|
||||
} from './dependencies-stage'
|
||||
import { stageConfig as detailsStageConfig } from './details-stage'
|
||||
import {
|
||||
fromDetailsStageConfig as editMcVersionsStageConfig,
|
||||
stageConfig as addMcVersionsStageConfig,
|
||||
} from './add-mc-versions'
|
||||
fromDetailsStageConfig as fromDetailsEnvironmentStageConfig,
|
||||
stageConfig as environmentStageConfig,
|
||||
} from './environment-stage'
|
||||
import {
|
||||
fromDetailsStageConfig as fromDetailsLoadersStageConfig,
|
||||
stageConfig as loadersStageConfig,
|
||||
} from './loaders-stage'
|
||||
import {
|
||||
fromDetailsStageConfig as fromDetailsMcVersionsStageConfig,
|
||||
stageConfig as mcVersionsStageConfig,
|
||||
} from './mc-versions-stage'
|
||||
import { stageConfig as metadataStageConfig } from './metadata-stage'
|
||||
|
||||
export const stageConfigs = [
|
||||
addFilesStageConfig,
|
||||
addDetailsStageConfig,
|
||||
addLoadersStageConfig,
|
||||
addMcVersionsStageConfig,
|
||||
addEnvironmentStageConfig,
|
||||
addDependenciesStageConfig,
|
||||
addChangelogStageConfig,
|
||||
loadersStageConfig,
|
||||
mcVersionsStageConfig,
|
||||
environmentStageConfig,
|
||||
dependenciesStageConfig,
|
||||
metadataStageConfig,
|
||||
detailsStageConfig,
|
||||
|
||||
// Non-progress stages for editing from details page
|
||||
editLoadersStageConfig,
|
||||
editMcVersionsStageConfig,
|
||||
editEnvironmentStageConfig,
|
||||
fromDetailsLoadersStageConfig,
|
||||
fromDetailsMcVersionsStageConfig,
|
||||
fromDetailsEnvironmentStageConfig,
|
||||
fromDetailsFilesStageConfig,
|
||||
fromDetailsDependenciesStageConfig,
|
||||
]
|
||||
|
||||
@@ -2,18 +2,18 @@ import { LeftArrowIcon, RightArrowIcon } from '@modrinth/assets'
|
||||
import type { StageConfigInput } from '@modrinth/ui'
|
||||
import { markRaw } from 'vue'
|
||||
|
||||
import AddLoadersStage from '~/components/ui/create-project-version/stages/AddLoadersStage.vue'
|
||||
import LoadersStage from '~/components/ui/create-project-version/stages/LoadersStage.vue'
|
||||
|
||||
import type { ManageVersionContextValue } from '../manage-version-modal'
|
||||
|
||||
export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
|
||||
id: 'add-loaders',
|
||||
stageContent: markRaw(AddLoadersStage),
|
||||
title: (ctx) => (ctx.editingVersion.value ? 'Edit loaders' : 'Add loaders'),
|
||||
stageContent: markRaw(LoadersStage),
|
||||
title: (ctx) => (ctx.editingVersion.value ? 'Edit loaders' : 'Loaders'),
|
||||
skip: (ctx) =>
|
||||
ctx.noLoadersProject.value ||
|
||||
(ctx.inferredVersionData.value?.loaders?.length ?? 0) > 0 ||
|
||||
ctx.editingVersion.value,
|
||||
(ctx.inferredVersionData.value?.loaders?.length ?? 0) > 0 || ctx.editingVersion.value,
|
||||
hideStageInBreadcrumb: (ctx) => !ctx.primaryFile.value || ctx.handlingNewFiles.value,
|
||||
cannotNavigateForward: (ctx) => ctx.draftVersion.value.loaders.length === 0,
|
||||
leftButtonConfig: (ctx) => ({
|
||||
label: 'Back',
|
||||
icon: LeftArrowIcon,
|
||||
@@ -30,14 +30,14 @@ export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
|
||||
|
||||
export const fromDetailsStageConfig: StageConfigInput<ManageVersionContextValue> = {
|
||||
id: 'from-details-loaders',
|
||||
stageContent: markRaw(AddLoadersStage),
|
||||
stageContent: markRaw(LoadersStage),
|
||||
title: 'Edit loaders',
|
||||
nonProgressStage: true,
|
||||
leftButtonConfig: (ctx) => ({
|
||||
label: 'Back',
|
||||
icon: LeftArrowIcon,
|
||||
disabled: ctx.draftVersion.value.loaders.length === 0,
|
||||
onClick: () => ctx.modal.value?.setStage('add-details'),
|
||||
onClick: () => ctx.modal.value?.setStage('metadata'),
|
||||
}),
|
||||
rightButtonConfig: (ctx) =>
|
||||
ctx.editingVersion.value
|
||||
@@ -46,10 +46,10 @@ export const fromDetailsStageConfig: StageConfigInput<ManageVersionContextValue>
|
||||
disabled: ctx.draftVersion.value.loaders.length === 0,
|
||||
}
|
||||
: {
|
||||
label: ctx.getNextLabel(2),
|
||||
label: 'Add details',
|
||||
icon: RightArrowIcon,
|
||||
iconPosition: 'after',
|
||||
disabled: ctx.draftVersion.value.loaders.length === 0,
|
||||
onClick: () => ctx.modal.value?.setStage(2),
|
||||
onClick: () => ctx.modal.value?.setStage('add-details'),
|
||||
},
|
||||
}
|
||||
@@ -2,16 +2,19 @@ import { LeftArrowIcon, RightArrowIcon } from '@modrinth/assets'
|
||||
import type { StageConfigInput } from '@modrinth/ui'
|
||||
import { markRaw } from 'vue'
|
||||
|
||||
import AddMcVersionsStage from '~/components/ui/create-project-version/stages/AddMcVersionsStage.vue'
|
||||
import McVersionsStage from '~/components/ui/create-project-version/stages/McVersionsStage.vue'
|
||||
|
||||
import type { ManageVersionContextValue } from '../manage-version-modal'
|
||||
|
||||
export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
|
||||
id: 'add-mc-versions',
|
||||
stageContent: markRaw(AddMcVersionsStage),
|
||||
title: (ctx) => (ctx.editingVersion.value ? 'Edit game versions' : 'Add game versions'),
|
||||
stageContent: markRaw(McVersionsStage),
|
||||
title: (ctx) => (ctx.editingVersion.value ? 'Edit game versions' : 'Game versions'),
|
||||
skip: (ctx) =>
|
||||
(ctx.inferredVersionData.value?.game_versions?.length ?? 0) > 0 || ctx.editingVersion.value,
|
||||
(ctx.inferredVersionData.value?.game_versions?.length ?? 0) > 0 || !ctx.primaryFile.value,
|
||||
hideStageInBreadcrumb: (ctx) => !ctx.primaryFile.value || ctx.handlingNewFiles.value,
|
||||
|
||||
cannotNavigateForward: (ctx) => ctx.draftVersion.value.game_versions.length === 0,
|
||||
leftButtonConfig: (ctx) => ({
|
||||
label: 'Back',
|
||||
icon: LeftArrowIcon,
|
||||
@@ -28,14 +31,14 @@ export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
|
||||
|
||||
export const fromDetailsStageConfig: StageConfigInput<ManageVersionContextValue> = {
|
||||
id: 'from-details-mc-versions',
|
||||
stageContent: markRaw(AddMcVersionsStage),
|
||||
stageContent: markRaw(McVersionsStage),
|
||||
title: 'Edit game versions',
|
||||
nonProgressStage: true,
|
||||
leftButtonConfig: (ctx) => ({
|
||||
label: 'Back',
|
||||
icon: LeftArrowIcon,
|
||||
disabled: ctx.draftVersion.value.game_versions.length === 0,
|
||||
onClick: () => ctx.modal.value?.setStage('add-details'),
|
||||
onClick: () => ctx.modal.value?.setStage('metadata'),
|
||||
}),
|
||||
rightButtonConfig: (ctx) =>
|
||||
ctx.editingVersion.value
|
||||
@@ -44,10 +47,10 @@ export const fromDetailsStageConfig: StageConfigInput<ManageVersionContextValue>
|
||||
disabled: ctx.draftVersion.value.game_versions.length === 0,
|
||||
}
|
||||
: {
|
||||
label: ctx.getNextLabel(2),
|
||||
label: 'Add details',
|
||||
icon: RightArrowIcon,
|
||||
iconPosition: 'after',
|
||||
disabled: ctx.draftVersion.value.game_versions.length === 0,
|
||||
onClick: () => ctx.modal.value?.setStage(2),
|
||||
onClick: () => ctx.modal.value?.setStage('add-details'),
|
||||
},
|
||||
}
|
||||
@@ -2,15 +2,14 @@ import { LeftArrowIcon, RightArrowIcon, XIcon } from '@modrinth/assets'
|
||||
import type { StageConfigInput } from '@modrinth/ui'
|
||||
import { markRaw } from 'vue'
|
||||
|
||||
import AddDependenciesStage from '~/components/ui/create-project-version/stages/AddDependenciesStage.vue'
|
||||
import MetadataStage from '~/components/ui/create-project-version/stages/MetadataStage.vue'
|
||||
|
||||
import type { ManageVersionContextValue } from '../manage-version-modal'
|
||||
|
||||
export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
|
||||
id: 'add-dependencies',
|
||||
stageContent: markRaw(AddDependenciesStage),
|
||||
title: (ctx) => (ctx.editingVersion.value ? 'Edit dependencies' : 'Add dependencies'),
|
||||
skip: (ctx) => ctx.projectType.value === 'modpack',
|
||||
id: 'metadata',
|
||||
stageContent: markRaw(MetadataStage),
|
||||
title: 'Metadata',
|
||||
leftButtonConfig: (ctx) =>
|
||||
ctx.editingVersion.value
|
||||
? {
|
||||
@@ -25,12 +24,13 @@ export const stageConfig: StageConfigInput<ManageVersionContextValue> = {
|
||||
},
|
||||
rightButtonConfig: (ctx) =>
|
||||
ctx.editingVersion.value
|
||||
? ctx.saveButtonConfig()
|
||||
? {
|
||||
...ctx.saveButtonConfig(),
|
||||
}
|
||||
: {
|
||||
label: ctx.getNextLabel(),
|
||||
icon: RightArrowIcon,
|
||||
iconPosition: 'after',
|
||||
onClick: () => ctx.modal.value?.nextStage(),
|
||||
},
|
||||
nonProgressStage: (ctx) => ctx.editingVersion.value,
|
||||
}
|
||||
Reference in New Issue
Block a user