// Reusable upload form: file picker, metadata, thumbnail, progress, actions. // Used by both /dashboard/upload (new upload) and /dashboard/videos (edit). import { Button } from "@/components/ui/button"; import { humanBytes, type useUpload } from "@/hooks/use-upload"; import { LICENSE_OPTIONS } from "@/lib/content-licenses"; import { CONTENT_WARNINGS } from "@/lib/content-warnings"; import { Link } from "@tanstack/react-router"; import { AlertCircle, ArrowUp, CheckCircle2, ImagePlus, LoaderCircle, Video, X, } from "lucide-react"; import { useCallback, useRef } from "react"; type UploadHook = ReturnType; interface UploadFormProps { /** The upload hook instance. The parent owns this so it can pre-fill state for edits. */ upload: UploadHook; /** When set, the form is in "edit" mode: hides file picker, shows update/delete actions. */ editingVideoUri?: string; /** Called when the user wants to cancel editing. */ onCancelEdit?: () => void; /** Called when the user clicks "Update Video". Receives the hook instance so the parent can drive the API call. */ onUpdate?: (upload: UploadHook) => Promise; /** Called when the user clicks "Delete Video". */ onDelete?: () => Promise; /** Whether an update or delete is in progress (disables buttons). */ isUpdating?: boolean; isDeleting?: boolean; } export function UploadForm({ upload, editingVideoUri, onCancelEdit, onUpdate, onDelete, isUpdating, isDeleting, }: UploadFormProps) { const fileInputRef = useRef(null); const thumbnailInputRef = useRef(null); const handleFileChange = useCallback( (e: React.ChangeEvent) => { const f = e.target.files?.[0] ?? null; upload.selectFile(f); e.target.value = ""; }, [upload], ); const handleThumbnailChange = useCallback( (e: React.ChangeEvent) => { const f = e.target.files?.[0] ?? null; upload.selectThumbnail(f); e.target.value = ""; }, [upload], ); const isEdit = !!editingVideoUri; return (
{/* ── left column: metadata ────────────────────────────────────── */}
{/* title + description */}

Details

upload.setTitle(e.target.value)} maxLength={140} placeholder="Give your video a title" className="w-full rounded-md border border-(--color-border) bg-(--color-bg) px-3 py-2 text-sm text-(--color-fg) placeholder:text-(--color-fg-muted)" />