File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
'use client';
import { useState } from 'react';
import { FoxFileUpload } from '@fox/core/components/fox-file-upload';
import { FoxHelperText } from '@fox/core/components/fox-helper-text';
import {
FILE_MODULE,
MAX_STREAM_UPLOAD_BYTES,
allowedExtensions,
fileModuleAccept,
isAllowedFileExtension,
} from '@/lib/domain/file-module';
const UPLOAD_ENDPOINT = `/contents/upload?module=${FILE_MODULE.vodContent.id}`;
interface ContentVideoFieldProps {
defaultFileId?: string | null;
}
/**
* 영상 콘텐츠 — 고르는 즉시 스트리밍 라우트로 올리고 파일 ID만 폼에 싣는다.
*
* 영상은 Server Action의 multipart 한도(10MB)를 넘기 때문에 폼 제출에 파일을 실을 수 없다.
* 업로드가 끝나기 전에 저장하면 영상 없이 저장되므로 그동안은 `videoUploading`을 실어 보내
* Server Action이 막게 한다.
*/
export function ContentVideoField({ defaultFileId }: ContentVideoFieldProps) {
const [fileId, setFileId] = useState(defaultFileId ?? '');
const [isUploading, setIsUploading] = useState(false);
const [error, setError] = useState('');
async function upload(picked: File[]) {
const file = picked[0];
if (!file) {
return;
}
setError('');
if (!isAllowedFileExtension(FILE_MODULE.vodContent, file.name)) {
setError(
`${allowedExtensions(FILE_MODULE.vodContent).join(', ')} 파일만 올릴 수 있습니다.`
);
return;
}
if (file.size > MAX_STREAM_UPLOAD_BYTES) {
setError(
`${Math.floor(MAX_STREAM_UPLOAD_BYTES / 1_000_000)}MB 이하만 올릴 수 있습니다.`
);
return;
}
const body = new FormData();
body.set('file', file);
setIsUploading(true);
try {
const response = await fetch(UPLOAD_ENDPOINT, { method: 'POST', body });
const payload = (await response.json()) as { id?: string; message?: string };
if (!response.ok || !payload.id) {
setError(payload.message ?? '영상을 업로드하지 못했습니다.');
return;
}
setFileId(payload.id);
} catch {
setError('영상을 업로드하지 못했습니다.');
} finally {
setIsUploading(false);
}
}
return (
<>
<FoxFileUpload
mode="default"
accept={fileModuleAccept(FILE_MODULE.vodContent)}
selectLabel="파일선택"
placeholder="파일을 선택해 주세요."
disabled={isUploading}
onSelect={upload}
onRemove={() => setFileId('')}
/>
<input type="hidden" name="videoFileId" value={fileId} />
{isUploading && <input type="hidden" name="videoUploading" value="1" />}
{isUploading && (
<FoxHelperText type="information" message="영상을 올리는 중입니다…" />
)}
{error && <FoxHelperText type="danger" message={error} />}
</>
);
}