diff --git a/src/actions/upload.ts b/src/actions/upload.ts index 06eb8e14..40b7fd39 100644 --- a/src/actions/upload.ts +++ b/src/actions/upload.ts @@ -5,7 +5,7 @@ import {catchError, mergeMap, mergeMapTo, switchMap} from 'rxjs/operators' import type {SanityClient} from 'sanity' import {createUpChunkObservable} from '../clients/upChunkObservable' -import type {MuxAsset, PluginConfig, UploadConfig} from '../util/types' +import type {MuxAsset, MuxNewAssetSettings, PluginConfig, UploadConfig} from '../util/types' import {getAsset} from './assets' import {testSecretsObservable} from './secrets' @@ -19,11 +19,11 @@ export function cancelUpload(client: SanityClient, uuid: string) { export function uploadUrl({ url, - uploadConfig, + settings, client, }: { url: string - uploadConfig: UploadConfig + settings: MuxNewAssetSettings client: SanityClient }) { return testUrl(url).pipe( @@ -36,14 +36,11 @@ export function uploadUrl({ return throwError(new Error('Invalid credentials')) } const uuid = generateUuid() - const muxBody = { - input: validUrl, - mp4_support: uploadConfig.mp4_support, - encoding_tier: uploadConfig.encoding_tier, - max_resolution_tier: uploadConfig.max_resolution_tier, - playback_policy: [uploadConfig.signed ? 'signed' : 'public'], - // @TODO: send tracks to backend - } + const muxBody = settings + if (!muxBody.input) muxBody.input = [{type: 'video'}] + muxBody.input[0].url = validUrl + console.log(muxBody) + const query = { muxBody: JSON.stringify(muxBody), filename: validUrl.split('/').slice(-1)[0], @@ -81,11 +78,11 @@ export function uploadUrl({ } export function uploadFile({ - uploadConfig, + settings, client, file, }: { - uploadConfig: UploadConfig + settings: MuxNewAssetSettings client: SanityClient file: File }) { @@ -99,13 +96,7 @@ export function uploadFile({ return throwError(new Error('Invalid credentials')) } const uuid = generateUuid() - const body = { - mp4_support: uploadConfig.mp4_support, - encoding_tier: uploadConfig.encoding_tier, - max_resolution_tier: uploadConfig.max_resolution_tier, - playback_policy: [uploadConfig.signed ? 'signed' : 'public'], - // @TODO: send tracks to backend - } + const body = settings return concat( of({type: 'uuid' as const, uuid}), @@ -115,13 +106,7 @@ export function uploadFile({ upload: { cors_origin: string id: string - new_asset_settings: Pick< - Required, - 'mp4_support' | 'encoding_tier' | 'max_resolution_tier' - > & { - passthrough: string - playback_policies: ['public' | 'signed'] - } + new_asset_settings: MuxNewAssetSettings status: 'waiting' timeout: number url: string diff --git a/src/components/FileInputArea.tsx b/src/components/FileInputArea.tsx new file mode 100644 index 00000000..f7c00f1f --- /dev/null +++ b/src/components/FileInputArea.tsx @@ -0,0 +1,92 @@ +import {PropsWithChildren, useRef, useState} from 'react' +import {Box, Button, Card, CardTone, Flex, Inline, Text} from '@sanity/ui' +import {FileInputButton} from './FileInputButton' +import {UploadIcon} from '@sanity/icons' +import {extractDroppedFiles} from '../util/extractFiles' + +interface FileInputAreaProps extends PropsWithChildren { + accept?: string + acceptMIMETypes?: string[] + label: React.ReactNode + onSelect: (files: FileList | File[]) => void +} + +export default function FileInputArea({ + label, + accept, + acceptMIMETypes, + onSelect, +}: FileInputAreaProps) { + const dragEnteredEls = useRef([]) + const [dragState, setDragState] = useState<'valid' | 'invalid' | null>(null) + + // Stages and validates an upload from dragging+dropping files or folders + const handleDrop: React.DragEventHandler = (event) => { + setDragState(null) + event.preventDefault() + event.stopPropagation() + extractDroppedFiles(event.nativeEvent.dataTransfer!).then(onSelect) + } + + /* ------------------------------- Drag State ------------------------------- */ + + const handleDragOver: React.DragEventHandler = (event) => { + event.preventDefault() + event.stopPropagation() + } + + const handleDragEnter: React.DragEventHandler = (event) => { + event.stopPropagation() + dragEnteredEls.current.push(event.target) + const type = event.dataTransfer.items?.[0]?.type + setDragState( + !acceptMIMETypes || acceptMIMETypes.some((mimeType) => type?.match(mimeType)) + ? 'valid' + : 'invalid' + ) + } + + const handleDragLeave: React.DragEventHandler = (event) => { + event.stopPropagation() + const idx = dragEnteredEls.current.indexOf(event.target) + if (idx > -1) { + dragEnteredEls.current.splice(idx, 1) + } + if (dragEnteredEls.current.length === 0) { + setDragState(null) + } + } + + let tone: CardTone = 'inherit' + if (dragState) tone = dragState === 'valid' ? 'positive' : 'critical' + return ( + + + + {label} + + + + + + + ) +} diff --git a/src/components/FileInputButton.tsx b/src/components/FileInputButton.tsx index a29c7ae5..618668f0 100644 --- a/src/components/FileInputButton.tsx +++ b/src/components/FileInputButton.tsx @@ -17,8 +17,9 @@ const Label = styled.label` export interface FileInputButtonProps extends ButtonProps { onSelect: (files: FileList) => void + accept?: string } -export const FileInputButton = ({onSelect, ...props}: FileInputButtonProps) => { +export const FileInputButton = ({onSelect, accept, ...props}: FileInputButtonProps) => { const inputId = `FileSelect${useId()}` const inputRef = useRef(null) const handleSelect = useCallback>( @@ -33,7 +34,7 @@ export const FileInputButton = ({onSelect, ...props}: FileInputButtonProps) => { return (