mirror of
https://github.com/modrinth/code.git
synced 2026-08-28 18:45:15 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8642134a61 | ||
|
|
680301ed9d |
@@ -229,7 +229,6 @@ async function handleDownloadFile(path: string, _fileName: string) {
|
||||
const uploadState = ref<UploadState>({
|
||||
isUploading: false,
|
||||
currentFileName: null,
|
||||
currentFileProgress: 0,
|
||||
uploadedBytes: 0,
|
||||
totalBytes: 0,
|
||||
completedFiles: 0,
|
||||
@@ -241,8 +240,7 @@ async function handleUploadFiles(files: File[]) {
|
||||
|
||||
uploadState.value = {
|
||||
isUploading: true,
|
||||
currentFileName: '',
|
||||
currentFileProgress: 0,
|
||||
currentFileName: files[0]?.name ?? null,
|
||||
uploadedBytes: 0,
|
||||
totalBytes: files.reduce((sum, f) => sum + f.size, 0),
|
||||
completedFiles: 0,
|
||||
@@ -258,7 +256,6 @@ async function handleUploadFiles(files: File[]) {
|
||||
await writeFileBytes(targetPath, new Uint8Array(buffer))
|
||||
uploadState.value.completedFiles++
|
||||
uploadState.value.uploadedBytes += file.size
|
||||
uploadState.value.currentFileProgress = 1
|
||||
}
|
||||
} catch (e) {
|
||||
addNotification({
|
||||
|
||||
@@ -96,13 +96,17 @@ export abstract class XHRUploadClient extends AbstractModrinthClient {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest()
|
||||
const metadata = context.metadata as UploadMetadata
|
||||
const fallbackTotal = this.getUploadPayloadSize(metadata)
|
||||
const abortUpload = () => xhr.abort()
|
||||
|
||||
xhr.upload.addEventListener('progress', (e) => {
|
||||
if (e.lengthComputable) {
|
||||
const total = e.lengthComputable ? e.total : fallbackTotal
|
||||
if (total > 0) {
|
||||
const loaded = Math.min(e.loaded, total)
|
||||
const progress: UploadProgress = {
|
||||
loaded: e.loaded,
|
||||
total: e.total,
|
||||
progress: e.loaded / e.total,
|
||||
loaded,
|
||||
total,
|
||||
progress: loaded / total,
|
||||
}
|
||||
progressCallbacks.forEach((cb) => cb(progress))
|
||||
}
|
||||
@@ -122,6 +126,12 @@ export abstract class XHRUploadClient extends AbstractModrinthClient {
|
||||
|
||||
xhr.addEventListener('error', () => reject(new ModrinthApiError('Upload failed')))
|
||||
xhr.addEventListener('abort', () => reject(new ModrinthApiError('Upload cancelled')))
|
||||
abortController.signal.addEventListener('abort', abortUpload, { once: true })
|
||||
|
||||
if (abortController.signal.aborted) {
|
||||
reject(new ModrinthApiError('Upload cancelled'))
|
||||
return
|
||||
}
|
||||
|
||||
// build URL with params (unlike $fetch, XHR doesn't handle params automatically)
|
||||
let url = context.url
|
||||
@@ -140,12 +150,23 @@ export abstract class XHRUploadClient extends AbstractModrinthClient {
|
||||
}
|
||||
|
||||
// Send either FormData or file depending on what was provided
|
||||
const data = 'formData' in metadata ? metadata.formData : metadata.file
|
||||
const data = metadata.formData instanceof FormData ? metadata.formData : metadata.file
|
||||
xhr.send(data)
|
||||
abortController.signal.addEventListener('abort', () => xhr.abort())
|
||||
})
|
||||
}
|
||||
|
||||
private getUploadPayloadSize(metadata: UploadMetadata): number {
|
||||
if (metadata.formData instanceof FormData) {
|
||||
let total = 0
|
||||
metadata.formData.forEach((value) => {
|
||||
total += value instanceof Blob ? value.size : new Blob([value]).size
|
||||
})
|
||||
return total
|
||||
}
|
||||
|
||||
return metadata.file instanceof Blob ? metadata.file.size : 0
|
||||
}
|
||||
|
||||
protected createUploadError(xhr: XMLHttpRequest): ModrinthApiError {
|
||||
let responseData: unknown
|
||||
try {
|
||||
|
||||
@@ -93,7 +93,6 @@ export interface UploadHandle<T> {
|
||||
export interface UploadState {
|
||||
isUploading: boolean
|
||||
currentFileName: string | null
|
||||
currentFileProgress: number
|
||||
uploadedBytes: number
|
||||
totalBytes: number
|
||||
completedFiles: number
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
<template>
|
||||
<Admonition type="info" :progress="overallProgress" progress-color="blue">
|
||||
<Admonition type="info" :progress="displayProgress" progress-color="blue">
|
||||
<template #icon>
|
||||
<UploadIcon class="h-6 w-6 flex-none text-brand-blue" />
|
||||
</template>
|
||||
<template #header>
|
||||
{{
|
||||
state.currentFileName
|
||||
? `Uploading ${state.currentFileName} (${state.completedFiles}/${state.totalFiles})`
|
||||
: `Uploading files (${state.completedFiles}/${state.totalFiles})`
|
||||
}}
|
||||
{{ headerText }}
|
||||
</template>
|
||||
<span class="text-secondary">
|
||||
{{ formatBytes(state.uploadedBytes) }} / {{ formatBytes(state.totalBytes) }} ({{
|
||||
Math.round(overallProgress * 100)
|
||||
{{ formatBytes(displayUploadedBytes) }} / {{ formatBytes(state.totalBytes) }} ({{
|
||||
Math.round(displayProgress * 100)
|
||||
}}%)
|
||||
</span>
|
||||
<template v-if="cancelUpload" #top-right-actions>
|
||||
@@ -39,9 +35,32 @@ const ctx = injectModrinthServerContext()
|
||||
const state = computed(() => ctx.uploadState.value)
|
||||
const cancelUpload = computed(() => ctx.cancelUpload.value)
|
||||
|
||||
const overallProgress = computed(() => {
|
||||
const headerText = computed(() => {
|
||||
const s = state.value
|
||||
if (!s.isUploading || s.totalFiles === 0) return 0
|
||||
return Math.min((s.completedFiles + s.currentFileProgress) / s.totalFiles, 1)
|
||||
if (s.currentFileName) {
|
||||
return `Uploading ${s.currentFileName} (${currentFileNumber.value}/${s.totalFiles})`
|
||||
}
|
||||
return `Uploading files (${s.completedFiles}/${s.totalFiles})`
|
||||
})
|
||||
|
||||
const currentFileNumber = computed(() => {
|
||||
const s = state.value
|
||||
if (s.totalFiles === 0) return 0
|
||||
return Math.min(s.completedFiles + 1, s.totalFiles)
|
||||
})
|
||||
|
||||
const displayUploadedBytes = computed(() => {
|
||||
const s = state.value
|
||||
if (s.totalBytes <= 0) return s.uploadedBytes
|
||||
return Math.min(s.uploadedBytes, s.totalBytes)
|
||||
})
|
||||
|
||||
const displayProgress = computed(() => {
|
||||
const s = state.value
|
||||
if (!s.isUploading) return 0
|
||||
if (s.totalBytes > 0) {
|
||||
return displayUploadedBytes.value / s.totalBytes
|
||||
}
|
||||
return 0
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -349,7 +349,6 @@ export function useServerManageCoreRuntime(options: UseServerManageCoreRuntimeOp
|
||||
const uploadState = ref<UploadState>({
|
||||
isUploading: false,
|
||||
currentFileName: null,
|
||||
currentFileProgress: 0,
|
||||
uploadedBytes: 0,
|
||||
totalBytes: 0,
|
||||
completedFiles: 0,
|
||||
|
||||
@@ -702,31 +702,53 @@ function handleUploadFiles() {
|
||||
input.onchange = async () => {
|
||||
if (!input.files) return
|
||||
const files = Array.from(input.files)
|
||||
if (files.length === 0) return
|
||||
const wid = worldId.value
|
||||
if (!wid) return
|
||||
|
||||
const totalBytes = files.reduce((sum, f) => sum + f.size, 0)
|
||||
uploadState.value = {
|
||||
isUploading: true,
|
||||
currentFileName: null,
|
||||
currentFileProgress: 0,
|
||||
currentFileName: files[0]?.name ?? null,
|
||||
uploadedBytes: 0,
|
||||
totalBytes: files.reduce((sum, f) => sum + f.size, 0),
|
||||
totalBytes,
|
||||
completedFiles: 0,
|
||||
totalFiles: files.length,
|
||||
}
|
||||
|
||||
const cumulativeFileBytes = files.reduce<number[]>((out, file) => {
|
||||
out.push((out[out.length - 1] ?? 0) + file.size)
|
||||
return out
|
||||
}, [])
|
||||
|
||||
function updateUploadProgress(loaded: number, total: number) {
|
||||
const uploadedBytes =
|
||||
total > 0 ? Math.round((Math.min(loaded, total) / total) * totalBytes) : loaded
|
||||
const clampedUploadedBytes = Math.min(uploadedBytes, totalBytes)
|
||||
const completedFiles = cumulativeFileBytes.filter(
|
||||
(bytes) => bytes <= clampedUploadedBytes,
|
||||
).length
|
||||
const currentFileIndex = Math.min(completedFiles, files.length - 1)
|
||||
|
||||
uploadState.value.uploadedBytes = clampedUploadedBytes
|
||||
uploadState.value.completedFiles = completedFiles
|
||||
uploadState.value.currentFileName = files[currentFileIndex]?.name ?? null
|
||||
if (clampedUploadedBytes >= totalBytes) {
|
||||
cancelUpload.value = null
|
||||
}
|
||||
}
|
||||
|
||||
const handle = client.kyros.content_v1.uploadAddonFile(wid, files, {
|
||||
onProgress: (p) => {
|
||||
uploadState.value.currentFileProgress = p.progress
|
||||
uploadState.value.uploadedBytes = p.loaded
|
||||
uploadState.value.totalBytes = p.total
|
||||
},
|
||||
onProgress: ({ loaded, total }) => updateUploadProgress(loaded, total),
|
||||
})
|
||||
cancelUpload.value = () => handle.cancel()
|
||||
|
||||
try {
|
||||
await handle.promise
|
||||
cancelUpload.value = null
|
||||
uploadState.value.uploadedBytes = totalBytes
|
||||
uploadState.value.completedFiles = files.length
|
||||
uploadState.value.currentFileName = files[files.length - 1]?.name ?? null
|
||||
await contentQuery.refetch()
|
||||
} catch (err) {
|
||||
if (err instanceof Error && err.message === 'Upload cancelled') return
|
||||
@@ -740,7 +762,6 @@ function handleUploadFiles() {
|
||||
uploadState.value = {
|
||||
isUploading: false,
|
||||
currentFileName: null,
|
||||
currentFileProgress: 0,
|
||||
uploadedBytes: 0,
|
||||
totalBytes: 0,
|
||||
completedFiles: 0,
|
||||
|
||||
@@ -374,7 +374,6 @@ async function uploadFiles(files: File[]) {
|
||||
uploadState.value = {
|
||||
isUploading: true,
|
||||
currentFileName: files[0].name,
|
||||
currentFileProgress: 0,
|
||||
uploadedBytes: 0,
|
||||
totalBytes,
|
||||
completedFiles: 0,
|
||||
@@ -389,13 +388,11 @@ async function uploadFiles(files: File[]) {
|
||||
const filePath = `${currentPath.value}/${file.name}`.replace('//', '/')
|
||||
|
||||
uploadState.value.currentFileName = file.name
|
||||
uploadState.value.currentFileProgress = 0
|
||||
|
||||
try {
|
||||
const uploader = client.kyros.files_v0.uploadFile(filePath, file, {
|
||||
onProgress: ({ progress }) => {
|
||||
uploadState.value.currentFileProgress = progress
|
||||
uploadState.value.uploadedBytes = completedBytes + Math.round(file.size * progress)
|
||||
onProgress: ({ loaded }) => {
|
||||
uploadState.value.uploadedBytes = completedBytes + Math.min(loaded, file.size)
|
||||
},
|
||||
})
|
||||
activeUploadCancel = () => uploader.cancel()
|
||||
@@ -420,7 +417,6 @@ async function uploadFiles(files: File[]) {
|
||||
uploadState.value = {
|
||||
isUploading: false,
|
||||
currentFileName: null,
|
||||
currentFileProgress: 0,
|
||||
uploadedBytes: 0,
|
||||
totalBytes: 0,
|
||||
completedFiles: 0,
|
||||
|
||||
@@ -46,7 +46,6 @@ const meta = {
|
||||
const uploadState = ref<UploadState>({
|
||||
isUploading: false,
|
||||
currentFileName: null,
|
||||
currentFileProgress: 0,
|
||||
uploadedBytes: 0,
|
||||
totalBytes: 0,
|
||||
completedFiles: 0,
|
||||
|
||||
@@ -52,7 +52,6 @@ const meta = {
|
||||
const uploadState = ref<UploadState>({
|
||||
isUploading: true,
|
||||
currentFileName: 'resourcepack.zip',
|
||||
currentFileProgress: 0.2,
|
||||
uploadedBytes: 20_000,
|
||||
totalBytes: 100_000,
|
||||
completedFiles: 1,
|
||||
|
||||
Reference in New Issue
Block a user