sanity-plugin-mux-input 3.0.4 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/README.md +0 -2
  2. package/dist/index.d.ts +134 -193
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +2893 -4417
  5. package/dist/index.js.map +1 -1
  6. package/package.json +33 -43
  7. package/dist/index.cjs +0 -7270
  8. package/dist/index.cjs.map +0 -1
  9. package/dist/index.d.cts +0 -347
  10. package/sanity.json +0 -8
  11. package/src/_exports/index.ts +0 -73
  12. package/src/actions/assets.ts +0 -152
  13. package/src/actions/secrets.ts +0 -111
  14. package/src/actions/upload.ts +0 -310
  15. package/src/clients/upChunkObservable.ts +0 -54
  16. package/src/components/AddCaptionDialog.tsx +0 -440
  17. package/src/components/CaptionsDialog.tsx +0 -23
  18. package/src/components/ConfigureApi.styled.tsx +0 -19
  19. package/src/components/ConfigureApi.tsx +0 -296
  20. package/src/components/DraggableWatermark.tsx +0 -877
  21. package/src/components/EditCaptionDialog.tsx +0 -510
  22. package/src/components/EditThumbnailDialog.tsx +0 -122
  23. package/src/components/ErrorBoundaryCard.tsx +0 -96
  24. package/src/components/FileInputButton.tsx +0 -54
  25. package/src/components/FileInputMenuItem.styled.tsx +0 -36
  26. package/src/components/FileInputMenuItem.tsx +0 -85
  27. package/src/components/FormField.tsx +0 -38
  28. package/src/components/IconInfo.tsx +0 -22
  29. package/src/components/ImportVideosFromMux.tsx +0 -342
  30. package/src/components/Input.styled.tsx +0 -22
  31. package/src/components/Input.tsx +0 -78
  32. package/src/components/InputBrowser.tsx +0 -41
  33. package/src/components/InputError.tsx +0 -17
  34. package/src/components/MuxLogo.tsx +0 -42
  35. package/src/components/Onboard.tsx +0 -65
  36. package/src/components/PageSelector.tsx +0 -54
  37. package/src/components/Player.styled.tsx +0 -55
  38. package/src/components/Player.tsx +0 -117
  39. package/src/components/PlayerActionsMenu.tsx +0 -190
  40. package/src/components/ResyncMetadata.tsx +0 -280
  41. package/src/components/SelectAsset.tsx +0 -39
  42. package/src/components/SelectSortOptions.tsx +0 -45
  43. package/src/components/SpinnerBox.tsx +0 -16
  44. package/src/components/StudioTool.tsx +0 -24
  45. package/src/components/TextTracksEditor.tsx +0 -117
  46. package/src/components/TextTracksManager.tsx +0 -737
  47. package/src/components/UploadConfiguration.tsx +0 -694
  48. package/src/components/UploadPlaceholder.tsx +0 -88
  49. package/src/components/UploadProgress.tsx +0 -80
  50. package/src/components/Uploader.styled.tsx +0 -66
  51. package/src/components/Uploader.tsx +0 -498
  52. package/src/components/VideoDetails/DeleteDialog.tsx +0 -147
  53. package/src/components/VideoDetails/VideoDetails.tsx +0 -358
  54. package/src/components/VideoDetails/VideoReferences.tsx +0 -63
  55. package/src/components/VideoDetails/useVideoDetails.ts +0 -103
  56. package/src/components/VideoInBrowser.tsx +0 -245
  57. package/src/components/VideoMetadata.tsx +0 -45
  58. package/src/components/VideoPlayer.tsx +0 -235
  59. package/src/components/VideoThumbnail.tsx +0 -138
  60. package/src/components/VideosBrowser.tsx +0 -100
  61. package/src/components/documentPreview/DocumentPreview.tsx +0 -83
  62. package/src/components/documentPreview/DraftStatus.tsx +0 -34
  63. package/src/components/documentPreview/MissingSchemaType.tsx +0 -32
  64. package/src/components/documentPreview/PaneItemPreview.tsx +0 -74
  65. package/src/components/documentPreview/PublishedStatus.tsx +0 -35
  66. package/src/components/documentPreview/TimeAgo.tsx +0 -12
  67. package/src/components/documentPreview/paneItemTypes.ts +0 -7
  68. package/src/components/icons/Audio.tsx +0 -13
  69. package/src/components/icons/Resolution.tsx +0 -12
  70. package/src/components/icons/StopWatch.tsx +0 -20
  71. package/src/components/icons/ToolIcon.tsx +0 -19
  72. package/src/components/uploadConfiguration/PlaybackPolicy.tsx +0 -133
  73. package/src/components/uploadConfiguration/PlaybackPolicyOption.tsx +0 -76
  74. package/src/components/uploadConfiguration/PlaybackPolicyWarning.tsx +0 -29
  75. package/src/components/uploadConfiguration/ResolutionTierSelector.tsx +0 -72
  76. package/src/components/uploadConfiguration/StaticRenditionSelector.tsx +0 -180
  77. package/src/components/withFocusRing/helpers.ts +0 -24
  78. package/src/components/withFocusRing/index.ts +0 -1
  79. package/src/components/withFocusRing/withFocusRing.ts +0 -30
  80. package/src/context/DialogStateContext.tsx +0 -36
  81. package/src/context/DrmPlaybackWarningContext.tsx +0 -93
  82. package/src/hooks/useAccessControl.ts +0 -13
  83. package/src/hooks/useAssetDocumentValues.ts +0 -11
  84. package/src/hooks/useAssets.ts +0 -68
  85. package/src/hooks/useCancelUpload.ts +0 -22
  86. package/src/hooks/useClient.ts +0 -8
  87. package/src/hooks/useDialogState.ts +0 -11
  88. package/src/hooks/useDocReferences.ts +0 -21
  89. package/src/hooks/useFetchFileSize.ts +0 -54
  90. package/src/hooks/useImportMuxAssets.ts +0 -132
  91. package/src/hooks/useInView.ts +0 -42
  92. package/src/hooks/useMediaMetadata.ts +0 -103
  93. package/src/hooks/useMuxAssets.ts +0 -179
  94. package/src/hooks/useMuxPolling.ts +0 -49
  95. package/src/hooks/useResyncAsset.ts +0 -110
  96. package/src/hooks/useResyncMuxMetadata.ts +0 -176
  97. package/src/hooks/useSaveSecrets.ts +0 -78
  98. package/src/hooks/useSecretsDocumentValues.ts +0 -38
  99. package/src/hooks/useSecretsFormState.ts +0 -47
  100. package/src/plugin.tsx +0 -31
  101. package/src/sanity-ui.d.ts +0 -5
  102. package/src/schema.ts +0 -196
  103. package/src/util/addKeysToMuxData.ts +0 -30
  104. package/src/util/areSecretsSignable.ts +0 -5
  105. package/src/util/asserters.ts +0 -36
  106. package/src/util/assetTitlePlaceholder.ts +0 -31
  107. package/src/util/constants.ts +0 -15
  108. package/src/util/convertWatermarkToMux.ts +0 -160
  109. package/src/util/createSearchFilter.ts +0 -76
  110. package/src/util/createUrlParamsObject.ts +0 -29
  111. package/src/util/extractFiles.ts +0 -67
  112. package/src/util/formatBytes.ts +0 -32
  113. package/src/util/formatDriveShareLink.ts +0 -64
  114. package/src/util/formatSeconds.ts +0 -49
  115. package/src/util/generateJwt.ts +0 -57
  116. package/src/util/getAnimatedPosterSrc.ts +0 -26
  117. package/src/util/getPlaybackPolicy.ts +0 -69
  118. package/src/util/getPosterSrc.ts +0 -28
  119. package/src/util/getStoryboardSrc.ts +0 -27
  120. package/src/util/getVideoMetadata.ts +0 -23
  121. package/src/util/getVideoSrc.ts +0 -23
  122. package/src/util/isSigned.ts +0 -20
  123. package/src/util/parsers.ts +0 -5
  124. package/src/util/pluginVersion.ts +0 -1
  125. package/src/util/readSecrets.ts +0 -38
  126. package/src/util/roundPxString.ts +0 -16
  127. package/src/util/textTracks.ts +0 -222
  128. package/src/util/tryWithSuspend.ts +0 -22
  129. package/src/util/types.ts +0 -596
  130. package/v2-incompatible.js +0 -11
@@ -1,498 +0,0 @@
1
- import {ErrorOutlineIcon} from '@sanity/icons'
2
- import {Button, CardTone, Flex, Text, useToast} from '@sanity/ui'
3
- import React, {useCallback, useEffect, useReducer, useRef, useState} from 'react'
4
- import {type Observable, Subject, Subscription} from 'rxjs'
5
- import {takeUntil, tap} from 'rxjs/operators'
6
- import type {SanityClient} from 'sanity'
7
- import {PatchEvent, set, setIfMissing} from 'sanity'
8
-
9
- import {uploadFile, uploadUrl} from '../actions/upload'
10
- import {DialogStateProvider} from '../context/DialogStateContext'
11
- import {type DialogState, type SetDialogState} from '../hooks/useDialogState'
12
- import {isServerError, isValidUrl} from '../util/asserters'
13
- import {extractDroppedFiles} from '../util/extractFiles'
14
- import {hasPlaybackPolicy} from '../util/getPlaybackPolicy'
15
- import type {
16
- MuxInputProps,
17
- MuxNewAssetSettings,
18
- PluginConfig,
19
- Secrets,
20
- VideoAssetDocument,
21
- } from '../util/types'
22
- import InputBrowser from './InputBrowser'
23
- import Player from './Player'
24
- import PlayerActionsMenu from './PlayerActionsMenu'
25
- import UploadConfiguration from './UploadConfiguration'
26
- import {UploadCard} from './Uploader.styled'
27
- import UploadPlaceholder from './UploadPlaceholder'
28
- import {UploadProgress} from './UploadProgress'
29
-
30
- interface Props extends Pick<MuxInputProps, 'onChange' | 'readOnly'> {
31
- config: PluginConfig
32
- client: SanityClient
33
- secrets: Secrets
34
- asset: VideoAssetDocument | null | undefined
35
- dialogState: DialogState
36
- setDialogState: SetDialogState
37
- needsSetup: boolean
38
- }
39
-
40
- export type StagedUpload = {type: 'file'; files: FileList | File[]} | {type: 'url'; url: string}
41
- type UploadStatus = {
42
- progress: number
43
- file?: {name: string | undefined; type: string}
44
- uuid?: string
45
- url?: string
46
- }
47
-
48
- interface State {
49
- stagedUpload: StagedUpload | null
50
- uploadStatus: UploadStatus | null
51
- error: Error | null
52
- }
53
-
54
- const INITIAL_STATE: State = {
55
- stagedUpload: null,
56
- uploadStatus: null,
57
- error: null,
58
- }
59
-
60
- type UploadFileEvent = ReturnType<typeof uploadFile> extends Observable<infer T> ? T : never
61
- type UploadUrlEvent = ReturnType<typeof uploadUrl> extends Observable<infer T> ? T : never
62
- type UploaderStateAction =
63
- | {action: 'stageUpload'; input: NonNullable<State['stagedUpload']>}
64
- | {action: 'commitUpload'}
65
- | ({action: 'progressInfo'} & (
66
- | Extract<UploadFileEvent, {type: 'uuid' | 'file'}>
67
- | Extract<UploadUrlEvent, {type: 'url'}>
68
- ))
69
- | {action: 'progress'; percent: number}
70
- | {action: 'error'; error: Error; settings: MuxNewAssetSettings}
71
- | {action: 'complete' | 'reset'}
72
-
73
- /**
74
- * The main interface for inputting a Mux Video. It handles staging an upload
75
- * file, setting its configuration, displaying upload progress, and showing
76
- * the preview player.
77
- */
78
- export default function Uploader(props: Props) {
79
- const toast = useToast()
80
- const containerRef = useRef<HTMLDivElement>(null)
81
-
82
- const dragEnteredEls = useRef<EventTarget[]>([])
83
- const [dragState, setDragState] = useState<'valid' | 'invalid' | null>(null)
84
-
85
- const cancelUploadButton = useRef(
86
- (() => {
87
- const events$ = new Subject()
88
- return {
89
- observable: events$.asObservable(),
90
- handleClick: ((event) => events$.next(event)) as React.MouseEventHandler<HTMLButtonElement>,
91
- }
92
- })()
93
- ).current
94
-
95
- const uploadRef = useRef<Subscription | null>(null)
96
- const uploadingDocumentId = useRef<string | null>(null)
97
- const [state, dispatch] = useReducer(
98
- (prev: State, action: UploaderStateAction) => {
99
- switch (action.action) {
100
- case 'stageUpload':
101
- return Object.assign({}, INITIAL_STATE, {stagedUpload: action.input})
102
- case 'commitUpload':
103
- return Object.assign({}, prev, {uploadStatus: {progress: 0}})
104
- case 'progressInfo': {
105
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
106
- const {type, action: _, ...payload} = action
107
- return Object.assign({}, prev, {
108
- uploadStatus: {
109
- ...prev.uploadStatus,
110
- progress: prev.uploadStatus!.progress,
111
- ...payload,
112
- },
113
- } satisfies Pick<typeof prev, 'uploadStatus'>)
114
- }
115
- case 'progress':
116
- return Object.assign({}, prev, {
117
- uploadStatus: {
118
- ...prev.uploadStatus,
119
- progress: action.percent,
120
- },
121
- } satisfies Pick<typeof prev, 'uploadStatus'>)
122
- case 'reset':
123
- case 'complete':
124
- // Clear upload observable on completion
125
- uploadRef.current?.unsubscribe()
126
- uploadRef.current = null
127
- uploadingDocumentId.current = null
128
- return INITIAL_STATE
129
- case 'error': {
130
- // Clear upload observable on error
131
- uploadRef.current?.unsubscribe()
132
- uploadRef.current = null
133
- uploadingDocumentId.current = null
134
-
135
- let error = action.error
136
- if (isServerError(action.error) && hasPlaybackPolicy(action.settings, 'drm')) {
137
- error = new Error(
138
- 'Unknown Error while uploading DRM protected content. Make sure your DRM configuration ID is valid and set correctly'
139
- )
140
- }
141
-
142
- return Object.assign({}, INITIAL_STATE, {error: error})
143
- }
144
- default:
145
- return prev
146
- }
147
- },
148
- {
149
- stagedUpload: null,
150
- uploadStatus: null,
151
- error: null,
152
- }
153
- )
154
-
155
- // Make sure we close out the upload observer on dismount
156
- // and cleanup orphaned documents if upload was in progress
157
- useEffect(() => {
158
- const cleanup = () => {
159
- // Cancel subscription
160
- if (uploadRef.current && !uploadRef.current.closed) {
161
- uploadRef.current.unsubscribe()
162
- }
163
-
164
- // Delete orphaned document if upload was in progress and document is different from the saved asset
165
- if (uploadingDocumentId.current && props.asset?._id !== uploadingDocumentId.current) {
166
- const docId = uploadingDocumentId.current
167
- uploadingDocumentId.current = null
168
-
169
- props.client.delete(docId).catch((err) => {
170
- console.warn('Failed to cleanup orphaned upload document:', err)
171
- })
172
- }
173
- }
174
-
175
- const handleBeforeUnload = () => {
176
- cleanup()
177
- }
178
-
179
- window.addEventListener('beforeunload', handleBeforeUnload)
180
- window.addEventListener('pagehide', handleBeforeUnload)
181
-
182
- return () => {
183
- window.removeEventListener('beforeunload', handleBeforeUnload)
184
- window.removeEventListener('pagehide', handleBeforeUnload)
185
- cleanup()
186
- }
187
- }, [props.client, props.asset?._id])
188
-
189
- /* -------------------------------------------------------------------------- */
190
- /* Uploading */
191
- /* -------------------------------------------------------------------------- */
192
-
193
- /**
194
- * Begins a file or URL upload with the staged files or URL.
195
- *
196
- * Should only be called from the UploadConfiguration component, which provides
197
- * the Mux configuration for the direct asset upload.
198
- *
199
- * @param settings The Mux new_asset_settings object to send to Sanity
200
- * @param watermark Optional watermark configuration
201
- * @returns
202
- */
203
- const startUpload = (
204
- settings: MuxNewAssetSettings,
205
- watermark?: import('../util/types').WatermarkConfig
206
- ) => {
207
- const {stagedUpload} = state
208
- if (!stagedUpload || uploadRef.current) return
209
- dispatch({action: 'commitUpload'})
210
- let uploadObservable: Observable<UploadFileEvent | UploadUrlEvent>
211
- // eslint-disable-next-line default-case
212
- switch (stagedUpload.type) {
213
- case 'url':
214
- uploadObservable = uploadUrl({
215
- client: props.client,
216
- url: stagedUpload.url,
217
- settings,
218
- watermark,
219
- })
220
- break
221
- case 'file':
222
- uploadObservable = uploadFile({
223
- client: props.client,
224
- file: stagedUpload.files[0],
225
- settings,
226
- watermark,
227
- }).pipe(
228
- takeUntil(
229
- cancelUploadButton.observable.pipe(
230
- tap(() => {
231
- if (uploadingDocumentId.current) {
232
- props.client.delete(uploadingDocumentId.current)
233
- uploadingDocumentId.current = null
234
- }
235
- })
236
- )
237
- )
238
- )
239
- break
240
- }
241
- uploadRef.current = uploadObservable.subscribe({
242
- next: (event) => {
243
- switch (event.type) {
244
- case 'uuid':
245
- // Track the document ID for cleanup on unmount
246
- uploadingDocumentId.current = event.uuid
247
- dispatch({action: 'progressInfo', ...event})
248
- break
249
- case 'file':
250
- case 'url':
251
- dispatch({action: 'progressInfo', ...event})
252
- break
253
- case 'progress':
254
- dispatch({action: 'progress', percent: event.percent})
255
- break
256
- case 'success':
257
- dispatch({action: 'progress', percent: 100})
258
- uploadingDocumentId.current = null
259
- props.onChange(
260
- PatchEvent.from([
261
- setIfMissing({asset: {}}),
262
- set({_type: 'reference', _weak: true, _ref: event.asset._id}, ['asset']),
263
- ])
264
- )
265
- break
266
- case 'pause':
267
- case 'resume':
268
- default:
269
- break
270
- }
271
- },
272
- complete: () => dispatch({action: 'complete'}),
273
- error: (error) => dispatch({action: 'error', error, settings}),
274
- })
275
- }
276
-
277
- const invalidFileToast = useCallback(() => {
278
- toast.push({
279
- status: 'error',
280
- title: `Invalid file type. Accepted types: ${props.config.acceptedMimeTypes?.join(', ')}`,
281
- })
282
- }, [props.config.acceptedMimeTypes, toast])
283
-
284
- /**
285
- * Validates if any file in the provided FileList or File array has an unsupported MIME type
286
- * @param files - FileList or File array to validate
287
- * @returns true if any file has an invalid MIME type, false if all files are valid
288
- */
289
- const isInvalidFile = (files: FileList | File[]) => {
290
- const isInvalid = Array.from(files).some((file) => {
291
- return !props.config.acceptedMimeTypes?.some((acceptedType) => {
292
- // Convert mime type pattern to regex (e.g., 'audio/*' -> /^audio\/.*$/)
293
- const pattern = `^${acceptedType.replace('*', '.*')}$`
294
- return new RegExp(pattern).test(file.type)
295
- })
296
- })
297
-
298
- return isInvalid
299
- }
300
-
301
- /* -------------------------- Upload Initialization ------------------------- */
302
- // The below populate the uploadInput state field, which then triggers the
303
- // upload configuration, or the startUpload function if no config is required.
304
-
305
- // Stages an upload from the file selector
306
- const handleUpload = (files: FileList | File[]) => {
307
- if (isInvalidFile(files)) return
308
- dispatch({
309
- action: 'stageUpload',
310
- input: {type: 'file', files},
311
- })
312
- }
313
-
314
- // Stages and validates an upload from pasting an asset URL
315
- const handlePaste: React.ClipboardEventHandler<HTMLInputElement> = (event) => {
316
- const target = event.target as HTMLElement
317
-
318
- // Ignore paste coming from the VTT URL input
319
- if (target.closest('#vtt-url')) {
320
- return
321
- }
322
-
323
- event.preventDefault()
324
- event.stopPropagation()
325
- const clipboardData =
326
- event.clipboardData || (window as Window & {clipboardData?: DataTransfer}).clipboardData
327
- const url = clipboardData?.getData('text')?.trim()
328
- if (!isValidUrl(url)) {
329
- toast.push({status: 'error', title: 'Invalid URL for Mux video input.'})
330
- return
331
- }
332
- dispatch({action: 'stageUpload', input: {type: 'url', url: url}})
333
- }
334
-
335
- // Stages and validates an upload from dragging+dropping files or folders
336
- const handleDrop: React.DragEventHandler<HTMLDivElement> = (event) => {
337
- event.preventDefault()
338
- event.stopPropagation()
339
- if (dragState === 'invalid') {
340
- invalidFileToast()
341
- setDragState(null)
342
- return
343
- }
344
- setDragState(null)
345
- extractDroppedFiles(event.nativeEvent.dataTransfer!).then((files) => {
346
- dispatch({
347
- action: 'stageUpload',
348
- input: {type: 'file', files},
349
- })
350
- })
351
- }
352
-
353
- /* ------------------------------- Drag State ------------------------------- */
354
-
355
- const handleDragOver: React.DragEventHandler<HTMLDivElement> = (event) => {
356
- event.preventDefault()
357
- event.stopPropagation()
358
- }
359
-
360
- const handleDragEnter: React.DragEventHandler<HTMLDivElement> = (event) => {
361
- event.stopPropagation()
362
- dragEnteredEls.current.push(event.target)
363
- const type = event.dataTransfer.items?.[0]?.type
364
- const mimeTypes = props.config.acceptedMimeTypes
365
-
366
- // Check if the dragged file type matches any of the accepted mime types
367
- const isValidType = mimeTypes?.some((acceptedType) => {
368
- // Convert mime type pattern to regex (e.g., 'video/*' -> /^video\/.*$/)
369
- const pattern = `^${acceptedType.replace('*', '.*')}$`
370
- return new RegExp(pattern).test(type)
371
- })
372
-
373
- setDragState(isValidType ? 'valid' : 'invalid')
374
- }
375
-
376
- const handleDragLeave: React.DragEventHandler<HTMLDivElement> = (event) => {
377
- event.stopPropagation()
378
- const idx = dragEnteredEls.current.indexOf(event.target)
379
- if (idx > -1) {
380
- dragEnteredEls.current.splice(idx, 1)
381
- }
382
- if (dragEnteredEls.current.length === 0) {
383
- setDragState(null)
384
- }
385
- }
386
-
387
- /* -------------------------------- Rendering ------------------------------- */
388
-
389
- // Upload has errored
390
- if (state.error !== null) {
391
- const error = state.error
392
- return (
393
- <Flex gap={3} direction="column" justify="center" align="center">
394
- <Text size={5} muted>
395
- <ErrorOutlineIcon />
396
- </Text>
397
- <Text>Something went wrong</Text>
398
- {error instanceof Error && error.message && (
399
- <Text size={1} muted weight="semibold" style={{textAlign: 'center'}}>
400
- {error.message}
401
- </Text>
402
- )}
403
- <Button text="Upload another file" onClick={() => dispatch({action: 'reset'})} />
404
- </Flex>
405
- )
406
- }
407
-
408
- // Upload is in progress
409
- if (state.uploadStatus !== null) {
410
- const {uploadStatus} = state
411
- return (
412
- <UploadProgress
413
- onCancel={cancelUploadButton.handleClick}
414
- progress={uploadStatus.progress}
415
- filename={uploadStatus.file?.name || uploadStatus.url}
416
- />
417
- )
418
- }
419
-
420
- // Upload needs configuration
421
- if (state.stagedUpload !== null) {
422
- return (
423
- <UploadConfiguration
424
- stagedUpload={state.stagedUpload}
425
- pluginConfig={props.config}
426
- secrets={props.secrets}
427
- startUpload={startUpload}
428
- onClose={() => dispatch({action: 'reset'})}
429
- />
430
- )
431
- }
432
-
433
- // Default: No staged upload
434
- let tone: CardTone | undefined
435
- if (dragState) tone = dragState === 'valid' ? 'positive' : 'critical'
436
-
437
- const acceptMimeString = props.config?.acceptedMimeTypes?.length
438
- ? props.config.acceptedMimeTypes.join(',')
439
- : 'video/*, audio/*'
440
-
441
- return (
442
- <>
443
- <UploadCard
444
- tone={tone}
445
- onDrop={handleDrop}
446
- onDragOver={handleDragOver}
447
- onDragLeave={handleDragLeave}
448
- onDragEnter={handleDragEnter}
449
- onPaste={handlePaste}
450
- ref={containerRef}
451
- >
452
- {props.asset ? (
453
- <DialogStateProvider
454
- dialogState={props.dialogState}
455
- setDialogState={props.setDialogState}
456
- >
457
- <Player
458
- readOnly={props.readOnly}
459
- asset={props.asset}
460
- onChange={props.onChange}
461
- config={props.config}
462
- buttons={
463
- <PlayerActionsMenu
464
- accept={acceptMimeString}
465
- asset={props.asset}
466
- dialogState={props.dialogState}
467
- setDialogState={props.setDialogState}
468
- onChange={props.onChange}
469
- onSelect={handleUpload}
470
- readOnly={props.readOnly}
471
- config={props.config}
472
- />
473
- }
474
- />
475
- </DialogStateProvider>
476
- ) : (
477
- <UploadPlaceholder
478
- accept={acceptMimeString}
479
- hovering={dragState !== null}
480
- onSelect={handleUpload}
481
- readOnly={!!props.readOnly}
482
- setDialogState={props.setDialogState}
483
- needsSetup={props.needsSetup}
484
- config={props.config}
485
- />
486
- )}
487
- </UploadCard>
488
- {props.dialogState === 'select-video' && (
489
- <InputBrowser
490
- config={props.config}
491
- asset={props.asset}
492
- onChange={props.onChange}
493
- setDialogState={props.setDialogState}
494
- />
495
- )}
496
- </>
497
- )
498
- }
@@ -1,147 +0,0 @@
1
- import {TrashIcon} from '@sanity/icons'
2
- import {Box, Button, Card, Checkbox, Dialog, Flex, Heading, Stack, Text, useToast} from '@sanity/ui'
3
- import {useEffect, useState} from 'react'
4
- import type {SanityDocument} from 'sanity'
5
-
6
- import {deleteAsset} from '../../actions/assets'
7
- import {useClient} from '../../hooks/useClient'
8
- import {DIALOGS_Z_INDEX} from '../../util/constants'
9
- import type {VideoAssetDocument} from '../../util/types'
10
- import SpinnerBox from '../SpinnerBox'
11
- import VideoReferences from './VideoReferences'
12
-
13
- export default function DeleteDialog({
14
- asset,
15
- references,
16
- referencesLoading,
17
- cancelDelete,
18
- succeededDeleting,
19
- }: {
20
- asset: VideoAssetDocument
21
- references?: SanityDocument[]
22
- referencesLoading: boolean
23
- cancelDelete: () => void
24
- succeededDeleting: () => void
25
- }) {
26
- const client = useClient()
27
- const [state, setState] = useState<
28
- 'processing_deletion' | 'checkingReferences' | 'error_deleting' | 'cantDelete' | 'confirm'
29
- >('checkingReferences')
30
- const [deleteOnMux, setDeleteOnMux] = useState(true)
31
- const toast = useToast()
32
-
33
- useEffect(() => {
34
- if (state !== 'checkingReferences' || referencesLoading) return
35
-
36
- setState(references?.length ? 'cantDelete' : 'confirm')
37
- }, [state, references, referencesLoading])
38
-
39
- async function confirmDelete() {
40
- if (state !== 'confirm') return
41
-
42
- setState('processing_deletion')
43
- const worked = await deleteAsset({client, asset, deleteOnMux})
44
- if (worked === true) {
45
- toast.push({title: 'Successfully deleted video', status: 'success'})
46
- succeededDeleting()
47
- } else if (worked === 'failed-mux') {
48
- toast.push({
49
- title: 'Deleted video in Sanity',
50
- description: "But it wasn't deleted in Mux",
51
- status: 'warning',
52
- })
53
- succeededDeleting()
54
- } else {
55
- toast.push({title: 'Failed deleting video', status: 'error'})
56
-
57
- setState('error_deleting')
58
- }
59
- }
60
-
61
- return (
62
- <Dialog
63
- animate
64
- header={'Delete video'}
65
- zOffset={DIALOGS_Z_INDEX}
66
- id="deleting-video-details-dialog"
67
- onClose={cancelDelete}
68
- onClickOutside={cancelDelete}
69
- width={1}
70
- position="fixed"
71
- >
72
- <Card
73
- padding={3}
74
- style={{
75
- minHeight: '150px',
76
- display: 'flex',
77
- alignItems: 'center',
78
- justifyContent: 'center',
79
- }}
80
- >
81
- <Stack space={3}>
82
- {state === 'checkingReferences' && (
83
- <>
84
- <Heading size={2}>Checking if video can be deleted</Heading>
85
- <SpinnerBox />
86
- </>
87
- )}
88
- {state === 'cantDelete' && (
89
- <>
90
- <Heading size={2}>Video can&apos;t be deleted</Heading>
91
- <Text size={2} style={{marginBottom: '2rem'}}>
92
- There are {references?.length} document{references && references.length > 0 && 's'}{' '}
93
- pointing to this video. Remove their references to this file or delete them before
94
- proceeding.
95
- </Text>
96
- <VideoReferences references={references} isLoaded={!referencesLoading} />
97
- </>
98
- )}
99
- {state === 'confirm' && (
100
- <>
101
- <Heading size={2}>Are you sure you want to delete this video?</Heading>
102
- <Text size={2}>This action is irreversible</Text>
103
- <Stack space={4} marginY={4}>
104
- <Flex align="center" as="label">
105
- <Checkbox
106
- checked={deleteOnMux}
107
- onChange={() => setDeleteOnMux((prev) => !prev)}
108
- />
109
- <Text style={{margin: '0 10px'}}>Delete asset on Mux</Text>
110
- </Flex>
111
- <Flex align="center" as="label">
112
- <Checkbox disabled checked />
113
- <Text style={{margin: '0 10px'}}>Delete video from dataset</Text>
114
- </Flex>
115
- <Box>
116
- <Button
117
- icon={TrashIcon}
118
- fontSize={2}
119
- padding={3}
120
- text="Delete video"
121
- tone="critical"
122
- onClick={confirmDelete}
123
- disabled={['processing_deletion', 'checkingReferences', 'cantDelete'].some(
124
- (s) => s === state
125
- )}
126
- />
127
- </Box>
128
- </Stack>
129
- </>
130
- )}
131
- {state === 'processing_deletion' && (
132
- <>
133
- <Heading size={2}>Deleting video...</Heading>
134
- <SpinnerBox />
135
- </>
136
- )}
137
- {state === 'error_deleting' && (
138
- <>
139
- <Heading size={2}>Something went wrong!</Heading>
140
- <Text size={2}>Try deleting the video again by clicking the button below</Text>
141
- </>
142
- )}
143
- </Stack>
144
- </Card>
145
- </Dialog>
146
- )
147
- }