import { ref } from 'vue' import { redirectToLogin, redirectUnverified } from '@/composables/useApi' import type { MediaItem } from '@/composables/useMedia' const UPLOAD_URL = '/v1/media/' const HTTP_CREATED = 201 const HTTP_BAD_REQUEST = 400 const HTTP_UNAUTHORIZED = 401 const HTTP_FORBIDDEN = 403 const HTTP_TOO_LARGE = 413 const HTTP_UNSUPPORTED = 415 export interface UploadProgress { loaded: number total: number } export type UploadOutcome = | { kind: 'success'; item: MediaItem } | { kind: 'rejected'; reason: 'size' | 'type' } | { kind: 'handled' } | { kind: 'failed' } function readItem(responseText: string): MediaItem | null { try { return JSON.parse(responseText) as MediaItem } catch { return null } } function outcomeOf(request: XMLHttpRequest): UploadOutcome { switch (request.status) { case HTTP_CREATED: { const item = readItem(request.responseText) return item ? { kind: 'success', item } : { kind: 'failed' } } case HTTP_TOO_LARGE: return { kind: 'rejected', reason: 'size' } case HTTP_UNSUPPORTED: case HTTP_BAD_REQUEST: return { kind: 'rejected', reason: 'type' } case HTTP_UNAUTHORIZED: redirectToLogin() return { kind: 'handled' } case HTTP_FORBIDDEN: redirectUnverified() return { kind: 'handled' } default: return { kind: 'failed' } } } export function useImageUpload() { const progress = ref({ loaded: 0, total: 0 }) let activeRequest: XMLHttpRequest | null = null function abort() { activeRequest?.abort() activeRequest = null } function start(file: File): Promise { progress.value = { loaded: 0, total: file.size } return new Promise((resolve) => { const request = new XMLHttpRequest() activeRequest = request request.open('POST', UPLOAD_URL) request.withCredentials = true request.upload.addEventListener('progress', (event) => { progress.value = { loaded: event.loaded, total: event.total } }) request.addEventListener('load', () => { activeRequest = null resolve(outcomeOf(request)) }) request.addEventListener('error', () => { activeRequest = null resolve({ kind: 'failed' }) }) request.addEventListener('abort', () => { activeRequest = null }) const formData = new FormData() formData.append('file', file) request.send(formData) }) } return { progress, start, abort } }