mirror of
https://github.com/modrinth/code.git
synced 2026-09-04 22:10:15 +00:00
fix: file uploading state admonition
This commit is contained in:
@@ -229,7 +229,6 @@ async function handleDownloadFile(path: string, _fileName: string) {
|
|||||||
const uploadState = ref<UploadState>({
|
const uploadState = ref<UploadState>({
|
||||||
isUploading: false,
|
isUploading: false,
|
||||||
currentFileName: null,
|
currentFileName: null,
|
||||||
currentFileProgress: 0,
|
|
||||||
uploadedBytes: 0,
|
uploadedBytes: 0,
|
||||||
totalBytes: 0,
|
totalBytes: 0,
|
||||||
completedFiles: 0,
|
completedFiles: 0,
|
||||||
@@ -241,8 +240,7 @@ async function handleUploadFiles(files: File[]) {
|
|||||||
|
|
||||||
uploadState.value = {
|
uploadState.value = {
|
||||||
isUploading: true,
|
isUploading: true,
|
||||||
currentFileName: '',
|
currentFileName: files[0]?.name ?? null,
|
||||||
currentFileProgress: 0,
|
|
||||||
uploadedBytes: 0,
|
uploadedBytes: 0,
|
||||||
totalBytes: files.reduce((sum, f) => sum + f.size, 0),
|
totalBytes: files.reduce((sum, f) => sum + f.size, 0),
|
||||||
completedFiles: 0,
|
completedFiles: 0,
|
||||||
@@ -258,7 +256,6 @@ async function handleUploadFiles(files: File[]) {
|
|||||||
await writeFileBytes(targetPath, new Uint8Array(buffer))
|
await writeFileBytes(targetPath, new Uint8Array(buffer))
|
||||||
uploadState.value.completedFiles++
|
uploadState.value.completedFiles++
|
||||||
uploadState.value.uploadedBytes += file.size
|
uploadState.value.uploadedBytes += file.size
|
||||||
uploadState.value.currentFileProgress = 1
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
addNotification({
|
addNotification({
|
||||||
|
|||||||
@@ -96,13 +96,16 @@ export abstract class XHRUploadClient extends AbstractModrinthClient {
|
|||||||
return new Promise<T>((resolve, reject) => {
|
return new Promise<T>((resolve, reject) => {
|
||||||
const xhr = new XMLHttpRequest()
|
const xhr = new XMLHttpRequest()
|
||||||
const metadata = context.metadata as UploadMetadata
|
const metadata = context.metadata as UploadMetadata
|
||||||
|
const fallbackTotal = this.getUploadPayloadSize(metadata)
|
||||||
|
|
||||||
xhr.upload.addEventListener('progress', (e) => {
|
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 = {
|
const progress: UploadProgress = {
|
||||||
loaded: e.loaded,
|
loaded,
|
||||||
total: e.total,
|
total,
|
||||||
progress: e.loaded / e.total,
|
progress: loaded / total,
|
||||||
}
|
}
|
||||||
progressCallbacks.forEach((cb) => cb(progress))
|
progressCallbacks.forEach((cb) => cb(progress))
|
||||||
}
|
}
|
||||||
@@ -146,6 +149,18 @@ export abstract class XHRUploadClient extends AbstractModrinthClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getUploadPayloadSize(metadata: UploadMetadata): number {
|
||||||
|
if ('file' in metadata) {
|
||||||
|
return metadata.file.size
|
||||||
|
}
|
||||||
|
|
||||||
|
let total = 0
|
||||||
|
metadata.formData.forEach((value) => {
|
||||||
|
total += value instanceof Blob ? value.size : new Blob([value]).size
|
||||||
|
})
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
protected createUploadError(xhr: XMLHttpRequest): ModrinthApiError {
|
protected createUploadError(xhr: XMLHttpRequest): ModrinthApiError {
|
||||||
let responseData: unknown
|
let responseData: unknown
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -93,7 +93,6 @@ export interface UploadHandle<T> {
|
|||||||
export interface UploadState {
|
export interface UploadState {
|
||||||
isUploading: boolean
|
isUploading: boolean
|
||||||
currentFileName: string | null
|
currentFileName: string | null
|
||||||
currentFileProgress: number
|
|
||||||
uploadedBytes: number
|
uploadedBytes: number
|
||||||
totalBytes: number
|
totalBytes: number
|
||||||
completedFiles: number
|
completedFiles: number
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<Admonition type="info" :progress="overallProgress" progress-color="blue">
|
<Admonition type="info" :progress="displayProgress" progress-color="blue">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<UploadIcon class="h-6 w-6 flex-none text-brand-blue" />
|
<UploadIcon class="h-6 w-6 flex-none text-brand-blue" />
|
||||||
</template>
|
</template>
|
||||||
<template #header>
|
<template #header>
|
||||||
{{
|
{{ headerText }}
|
||||||
state.currentFileName
|
|
||||||
? `Uploading ${state.currentFileName} (${state.completedFiles}/${state.totalFiles})`
|
|
||||||
: `Uploading files (${state.completedFiles}/${state.totalFiles})`
|
|
||||||
}}
|
|
||||||
</template>
|
</template>
|
||||||
<span class="text-secondary">
|
<span class="text-secondary">
|
||||||
{{ formatBytes(state.uploadedBytes) }} / {{ formatBytes(state.totalBytes) }} ({{
|
{{ formatBytes(displayUploadedBytes) }} / {{ formatBytes(state.totalBytes) }} ({{
|
||||||
Math.round(overallProgress * 100)
|
Math.round(displayProgress * 100)
|
||||||
}}%)
|
}}%)
|
||||||
</span>
|
</span>
|
||||||
<template v-if="cancelUpload" #top-right-actions>
|
<template v-if="cancelUpload" #top-right-actions>
|
||||||
@@ -39,9 +35,32 @@ const ctx = injectModrinthServerContext()
|
|||||||
const state = computed(() => ctx.uploadState.value)
|
const state = computed(() => ctx.uploadState.value)
|
||||||
const cancelUpload = computed(() => ctx.cancelUpload.value)
|
const cancelUpload = computed(() => ctx.cancelUpload.value)
|
||||||
|
|
||||||
const overallProgress = computed(() => {
|
const headerText = computed(() => {
|
||||||
const s = state.value
|
const s = state.value
|
||||||
if (!s.isUploading || s.totalFiles === 0) return 0
|
if (s.currentFileName) {
|
||||||
return Math.min((s.completedFiles + s.currentFileProgress) / s.totalFiles, 1)
|
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>
|
</script>
|
||||||
|
|||||||
@@ -349,7 +349,6 @@ export function useServerManageCoreRuntime(options: UseServerManageCoreRuntimeOp
|
|||||||
const uploadState = ref<UploadState>({
|
const uploadState = ref<UploadState>({
|
||||||
isUploading: false,
|
isUploading: false,
|
||||||
currentFileName: null,
|
currentFileName: null,
|
||||||
currentFileProgress: 0,
|
|
||||||
uploadedBytes: 0,
|
uploadedBytes: 0,
|
||||||
totalBytes: 0,
|
totalBytes: 0,
|
||||||
completedFiles: 0,
|
completedFiles: 0,
|
||||||
|
|||||||
@@ -702,31 +702,49 @@ function handleUploadFiles() {
|
|||||||
input.onchange = async () => {
|
input.onchange = async () => {
|
||||||
if (!input.files) return
|
if (!input.files) return
|
||||||
const files = Array.from(input.files)
|
const files = Array.from(input.files)
|
||||||
|
if (files.length === 0) return
|
||||||
const wid = worldId.value
|
const wid = worldId.value
|
||||||
if (!wid) return
|
if (!wid) return
|
||||||
|
|
||||||
|
const totalBytes = files.reduce((sum, f) => sum + f.size, 0)
|
||||||
uploadState.value = {
|
uploadState.value = {
|
||||||
isUploading: true,
|
isUploading: true,
|
||||||
currentFileName: null,
|
currentFileName: files[0]?.name ?? null,
|
||||||
currentFileProgress: 0,
|
|
||||||
uploadedBytes: 0,
|
uploadedBytes: 0,
|
||||||
totalBytes: files.reduce((sum, f) => sum + f.size, 0),
|
totalBytes,
|
||||||
completedFiles: 0,
|
completedFiles: 0,
|
||||||
totalFiles: files.length,
|
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
|
||||||
|
}
|
||||||
|
|
||||||
const handle = client.kyros.content_v1.uploadAddonFile(wid, files, {
|
const handle = client.kyros.content_v1.uploadAddonFile(wid, files, {
|
||||||
onProgress: (p) => {
|
onProgress: ({ loaded, total }) => updateUploadProgress(loaded, total),
|
||||||
uploadState.value.currentFileProgress = p.progress
|
|
||||||
uploadState.value.uploadedBytes = p.loaded
|
|
||||||
uploadState.value.totalBytes = p.total
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
cancelUpload.value = () => handle.cancel()
|
cancelUpload.value = () => handle.cancel()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await handle.promise
|
await handle.promise
|
||||||
|
uploadState.value.uploadedBytes = totalBytes
|
||||||
uploadState.value.completedFiles = files.length
|
uploadState.value.completedFiles = files.length
|
||||||
|
uploadState.value.currentFileName = files[files.length - 1]?.name ?? null
|
||||||
await contentQuery.refetch()
|
await contentQuery.refetch()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof Error && err.message === 'Upload cancelled') return
|
if (err instanceof Error && err.message === 'Upload cancelled') return
|
||||||
@@ -740,7 +758,6 @@ function handleUploadFiles() {
|
|||||||
uploadState.value = {
|
uploadState.value = {
|
||||||
isUploading: false,
|
isUploading: false,
|
||||||
currentFileName: null,
|
currentFileName: null,
|
||||||
currentFileProgress: 0,
|
|
||||||
uploadedBytes: 0,
|
uploadedBytes: 0,
|
||||||
totalBytes: 0,
|
totalBytes: 0,
|
||||||
completedFiles: 0,
|
completedFiles: 0,
|
||||||
|
|||||||
@@ -374,7 +374,6 @@ async function uploadFiles(files: File[]) {
|
|||||||
uploadState.value = {
|
uploadState.value = {
|
||||||
isUploading: true,
|
isUploading: true,
|
||||||
currentFileName: files[0].name,
|
currentFileName: files[0].name,
|
||||||
currentFileProgress: 0,
|
|
||||||
uploadedBytes: 0,
|
uploadedBytes: 0,
|
||||||
totalBytes,
|
totalBytes,
|
||||||
completedFiles: 0,
|
completedFiles: 0,
|
||||||
@@ -389,13 +388,11 @@ async function uploadFiles(files: File[]) {
|
|||||||
const filePath = `${currentPath.value}/${file.name}`.replace('//', '/')
|
const filePath = `${currentPath.value}/${file.name}`.replace('//', '/')
|
||||||
|
|
||||||
uploadState.value.currentFileName = file.name
|
uploadState.value.currentFileName = file.name
|
||||||
uploadState.value.currentFileProgress = 0
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const uploader = client.kyros.files_v0.uploadFile(filePath, file, {
|
const uploader = client.kyros.files_v0.uploadFile(filePath, file, {
|
||||||
onProgress: ({ progress }) => {
|
onProgress: ({ loaded }) => {
|
||||||
uploadState.value.currentFileProgress = progress
|
uploadState.value.uploadedBytes = completedBytes + Math.min(loaded, file.size)
|
||||||
uploadState.value.uploadedBytes = completedBytes + Math.round(file.size * progress)
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
activeUploadCancel = () => uploader.cancel()
|
activeUploadCancel = () => uploader.cancel()
|
||||||
@@ -420,7 +417,6 @@ async function uploadFiles(files: File[]) {
|
|||||||
uploadState.value = {
|
uploadState.value = {
|
||||||
isUploading: false,
|
isUploading: false,
|
||||||
currentFileName: null,
|
currentFileName: null,
|
||||||
currentFileProgress: 0,
|
|
||||||
uploadedBytes: 0,
|
uploadedBytes: 0,
|
||||||
totalBytes: 0,
|
totalBytes: 0,
|
||||||
completedFiles: 0,
|
completedFiles: 0,
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ const meta = {
|
|||||||
const uploadState = ref<UploadState>({
|
const uploadState = ref<UploadState>({
|
||||||
isUploading: false,
|
isUploading: false,
|
||||||
currentFileName: null,
|
currentFileName: null,
|
||||||
currentFileProgress: 0,
|
|
||||||
uploadedBytes: 0,
|
uploadedBytes: 0,
|
||||||
totalBytes: 0,
|
totalBytes: 0,
|
||||||
completedFiles: 0,
|
completedFiles: 0,
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ const meta = {
|
|||||||
const uploadState = ref<UploadState>({
|
const uploadState = ref<UploadState>({
|
||||||
isUploading: true,
|
isUploading: true,
|
||||||
currentFileName: 'resourcepack.zip',
|
currentFileName: 'resourcepack.zip',
|
||||||
currentFileProgress: 0.2,
|
|
||||||
uploadedBytes: 20_000,
|
uploadedBytes: 20_000,
|
||||||
totalBytes: 100_000,
|
totalBytes: 100_000,
|
||||||
completedFiles: 1,
|
completedFiles: 1,
|
||||||
|
|||||||
Reference in New Issue
Block a user