Add upload to show functionality in MediaPanel: introduce file selection for uploading media files directly to a show, enhancing the upload store to support show associations. Update translations for new upload options and UI elements.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { create } from 'zustand'
|
||||
import { queryClient } from '@/shared/api/query-client'
|
||||
import { addEpisode } from '@/features/admin/shows/api'
|
||||
import { toast } from '@/shared/ui/toast-store'
|
||||
import { listMedia, uploadMedia } from './api'
|
||||
|
||||
export type UploadItem = {
|
||||
@@ -14,16 +16,19 @@ type UploadStore = {
|
||||
active: boolean
|
||||
minimized: boolean
|
||||
skipped: number
|
||||
enqueue: (files: File[]) => Promise<void>
|
||||
enqueue: (files: File[], options?: EnqueueOptions) => Promise<void>
|
||||
cancel: (id: string) => void
|
||||
cancelAll: () => void
|
||||
toggleMinimize: () => void
|
||||
dismiss: () => void
|
||||
}
|
||||
|
||||
/** Доп-опции загрузки: привязка загружаемых файлов к шоу (добавляются сериями после аплоада). */
|
||||
export type EnqueueOptions = { showId?: string }
|
||||
|
||||
// Очередь и флаг живут вне React — загрузка продолжается при любой навигации.
|
||||
let counter = 0
|
||||
const queue: { id: string; file: File }[] = []
|
||||
const queue: { id: string; file: File; showId?: string }[] = []
|
||||
const controllers = new Map<string, AbortController>()
|
||||
let running = false
|
||||
|
||||
@@ -43,9 +48,23 @@ async function pump() {
|
||||
controllers.set(job.id, controller)
|
||||
patch(job.id, { status: 'uploading', percent: 0 })
|
||||
try {
|
||||
await uploadMedia(job.file, (percent) => patch(job.id, { percent }), controller.signal)
|
||||
const created = await uploadMedia(
|
||||
job.file,
|
||||
(percent) => patch(job.id, { percent }),
|
||||
controller.signal,
|
||||
)
|
||||
patch(job.id, { status: 'done', percent: 100 })
|
||||
void queryClient.invalidateQueries({ queryKey: ['admin', 'media'] })
|
||||
|
||||
// Привязка к шоу: добавляем свежий ассет серией (порядок — как в очереди).
|
||||
if (job.showId) {
|
||||
try {
|
||||
await addEpisode(job.showId, created.id)
|
||||
void queryClient.invalidateQueries({ queryKey: ['admin', 'shows'] })
|
||||
} catch {
|
||||
toast.error(`${job.file.name}: не удалось добавить в шоу`)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Отмена (AbortError) — тихо: элемент уже убран из списка. Прочее — помечаем ошибкой.
|
||||
if (!(error instanceof DOMException && error.name === 'AbortError'))
|
||||
@@ -65,7 +84,7 @@ export const useUploadStore = create<UploadStore>((set) => ({
|
||||
minimized: false,
|
||||
skipped: 0,
|
||||
|
||||
enqueue: async (files) => {
|
||||
enqueue: async (files, options) => {
|
||||
// Пропускаем дубликаты: уже в библиотеке (не проваленные) и уже в текущей очереди.
|
||||
let existing = new Set<string>()
|
||||
try {
|
||||
@@ -90,7 +109,7 @@ export const useUploadStore = create<UploadStore>((set) => ({
|
||||
|
||||
const newItems: UploadItem[] = toAdd.map((file) => {
|
||||
const id = `u${++counter}`
|
||||
queue.push({ id, file })
|
||||
queue.push({ id, file, showId: options?.showId })
|
||||
return { id, name: file.name, percent: 0, status: 'queued' }
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user