sanity-plugin-mux-input 3.0.5 → 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 (123) hide show
  1. package/dist/index.js +28 -28
  2. package/dist/index.js.map +1 -1
  3. package/package.json +5 -15
  4. package/dist/index.cjs +0 -5746
  5. package/dist/index.cjs.map +0 -1
  6. package/dist/index.d.cts +0 -288
  7. package/dist/index.d.cts.map +0 -1
  8. package/sanity.json +0 -8
  9. package/src/_exports/index.ts +0 -73
  10. package/src/actions/assets.ts +0 -152
  11. package/src/actions/secrets.ts +0 -110
  12. package/src/actions/upload.ts +0 -308
  13. package/src/clients/upChunkObservable.ts +0 -54
  14. package/src/components/AddCaptionDialog.tsx +0 -440
  15. package/src/components/CaptionsDialog.tsx +0 -23
  16. package/src/components/ConfigureApi.styled.tsx +0 -19
  17. package/src/components/ConfigureApi.tsx +0 -296
  18. package/src/components/DraggableWatermark.tsx +0 -885
  19. package/src/components/EditCaptionDialog.tsx +0 -511
  20. package/src/components/EditThumbnailDialog.tsx +0 -121
  21. package/src/components/ErrorBoundaryCard.tsx +0 -97
  22. package/src/components/FileInputButton.tsx +0 -54
  23. package/src/components/FileInputMenuItem.styled.tsx +0 -36
  24. package/src/components/FileInputMenuItem.tsx +0 -85
  25. package/src/components/FormField.tsx +0 -38
  26. package/src/components/IconInfo.tsx +0 -22
  27. package/src/components/ImportVideosFromMux.tsx +0 -339
  28. package/src/components/Input.styled.tsx +0 -22
  29. package/src/components/Input.tsx +0 -78
  30. package/src/components/InputBrowser.tsx +0 -41
  31. package/src/components/MuxLogo.tsx +0 -42
  32. package/src/components/Onboard.tsx +0 -65
  33. package/src/components/PageSelector.tsx +0 -54
  34. package/src/components/Player.styled.tsx +0 -11
  35. package/src/components/Player.tsx +0 -117
  36. package/src/components/PlayerActionsMenu.tsx +0 -191
  37. package/src/components/ResyncMetadata.tsx +0 -278
  38. package/src/components/SelectAsset.tsx +0 -39
  39. package/src/components/SelectSortOptions.tsx +0 -45
  40. package/src/components/SpinnerBox.tsx +0 -16
  41. package/src/components/StudioTool.tsx +0 -24
  42. package/src/components/TextTracksEditor.tsx +0 -117
  43. package/src/components/TextTracksManager.tsx +0 -738
  44. package/src/components/UploadConfiguration.tsx +0 -696
  45. package/src/components/UploadPlaceholder.tsx +0 -88
  46. package/src/components/UploadProgress.tsx +0 -80
  47. package/src/components/Uploader.styled.tsx +0 -65
  48. package/src/components/Uploader.tsx +0 -499
  49. package/src/components/VideoDetails/DeleteDialog.tsx +0 -148
  50. package/src/components/VideoDetails/VideoDetails.tsx +0 -358
  51. package/src/components/VideoDetails/VideoReferences.tsx +0 -63
  52. package/src/components/VideoDetails/useVideoDetails.ts +0 -103
  53. package/src/components/VideoInBrowser.tsx +0 -245
  54. package/src/components/VideoMetadata.tsx +0 -45
  55. package/src/components/VideoPlayer.tsx +0 -241
  56. package/src/components/VideoThumbnail.tsx +0 -139
  57. package/src/components/VideosBrowser.tsx +0 -100
  58. package/src/components/documentPreview/DocumentPreview.tsx +0 -84
  59. package/src/components/documentPreview/DraftStatus.tsx +0 -34
  60. package/src/components/documentPreview/MissingSchemaType.tsx +0 -32
  61. package/src/components/documentPreview/PaneItemPreview.tsx +0 -67
  62. package/src/components/documentPreview/PublishedStatus.tsx +0 -35
  63. package/src/components/documentPreview/TimeAgo.tsx +0 -12
  64. package/src/components/icons/Audio.tsx +0 -13
  65. package/src/components/icons/Resolution.tsx +0 -12
  66. package/src/components/icons/StopWatch.tsx +0 -20
  67. package/src/components/icons/ToolIcon.tsx +0 -19
  68. package/src/components/uploadConfiguration/PlaybackPolicy.tsx +0 -133
  69. package/src/components/uploadConfiguration/PlaybackPolicyOption.tsx +0 -76
  70. package/src/components/uploadConfiguration/PlaybackPolicyWarning.tsx +0 -29
  71. package/src/components/uploadConfiguration/ResolutionTierSelector.tsx +0 -72
  72. package/src/components/uploadConfiguration/StaticRenditionSelector.tsx +0 -180
  73. package/src/components/withFocusRing/helpers.ts +0 -24
  74. package/src/components/withFocusRing/index.ts +0 -1
  75. package/src/components/withFocusRing/withFocusRing.ts +0 -30
  76. package/src/context/DialogStateContext.tsx +0 -33
  77. package/src/context/DrmPlaybackWarningContext.tsx +0 -97
  78. package/src/hooks/useAccessControl.ts +0 -13
  79. package/src/hooks/useAssetDocumentValues.ts +0 -11
  80. package/src/hooks/useAssets.ts +0 -73
  81. package/src/hooks/useCancelUpload.ts +0 -22
  82. package/src/hooks/useClient.ts +0 -8
  83. package/src/hooks/useDialogState.ts +0 -11
  84. package/src/hooks/useDocReferences.ts +0 -21
  85. package/src/hooks/useFetchFileSize.ts +0 -55
  86. package/src/hooks/useImportMuxAssets.ts +0 -132
  87. package/src/hooks/useInView.ts +0 -41
  88. package/src/hooks/useMediaMetadata.ts +0 -104
  89. package/src/hooks/useMuxAssets.ts +0 -179
  90. package/src/hooks/useMuxPolling.ts +0 -52
  91. package/src/hooks/useResyncAsset.ts +0 -110
  92. package/src/hooks/useResyncMuxMetadata.ts +0 -169
  93. package/src/hooks/useSaveSecrets.ts +0 -78
  94. package/src/hooks/useSecretsDocumentValues.ts +0 -38
  95. package/src/hooks/useSecretsFormState.ts +0 -47
  96. package/src/plugin.tsx +0 -31
  97. package/src/sanity-ui.d.ts +0 -5
  98. package/src/schema.ts +0 -196
  99. package/src/util/addKeysToMuxData.ts +0 -30
  100. package/src/util/asserters.ts +0 -23
  101. package/src/util/assetTitlePlaceholder.ts +0 -31
  102. package/src/util/constants.ts +0 -15
  103. package/src/util/convertWatermarkToMux.ts +0 -160
  104. package/src/util/createSearchFilter.ts +0 -76
  105. package/src/util/createUrlParamsObject.ts +0 -29
  106. package/src/util/extractFiles.ts +0 -67
  107. package/src/util/formatBytes.ts +0 -31
  108. package/src/util/formatDriveShareLink.ts +0 -64
  109. package/src/util/formatSeconds.ts +0 -48
  110. package/src/util/generateJwt.ts +0 -57
  111. package/src/util/getAnimatedPosterSrc.ts +0 -26
  112. package/src/util/getPlaybackPolicy.ts +0 -69
  113. package/src/util/getPosterSrc.ts +0 -28
  114. package/src/util/getVideoMetadata.ts +0 -23
  115. package/src/util/getVideoSrc.ts +0 -23
  116. package/src/util/parsers.ts +0 -5
  117. package/src/util/pluginVersion.ts +0 -5
  118. package/src/util/readSecrets.ts +0 -38
  119. package/src/util/roundPxString.ts +0 -16
  120. package/src/util/textTracks.ts +0 -222
  121. package/src/util/tryWithSuspend.ts +0 -22
  122. package/src/util/types.ts +0 -566
  123. package/v2-incompatible.js +0 -11
@@ -1,499 +0,0 @@
1
- import {ErrorOutlineIcon} from '@sanity/icons'
2
- import {Button, type 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
- // oxlint-disable-next-line react/react-compiler
99
- (prev: State, action: UploaderStateAction) => {
100
- switch (action.action) {
101
- case 'stageUpload':
102
- return Object.assign({}, INITIAL_STATE, {stagedUpload: action.input})
103
- case 'commitUpload':
104
- return Object.assign({}, prev, {uploadStatus: {progress: 0}})
105
- case 'progressInfo': {
106
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
107
- const {type, action: _, ...payload} = action
108
- return Object.assign({}, prev, {
109
- uploadStatus: {
110
- ...prev.uploadStatus,
111
- progress: prev.uploadStatus!.progress,
112
- ...payload,
113
- },
114
- } satisfies Pick<typeof prev, 'uploadStatus'>)
115
- }
116
- case 'progress':
117
- return Object.assign({}, prev, {
118
- uploadStatus: {
119
- ...prev.uploadStatus,
120
- progress: action.percent,
121
- },
122
- } satisfies Pick<typeof prev, 'uploadStatus'>)
123
- case 'reset':
124
- case 'complete':
125
- // Clear upload observable on completion
126
- uploadRef.current?.unsubscribe()
127
- uploadRef.current = null
128
- uploadingDocumentId.current = null
129
- return INITIAL_STATE
130
- case 'error': {
131
- // Clear upload observable on error
132
- uploadRef.current?.unsubscribe()
133
- uploadRef.current = null
134
- uploadingDocumentId.current = null
135
-
136
- let error = action.error
137
- if (isServerError(action.error) && hasPlaybackPolicy(action.settings, 'drm')) {
138
- error = new Error(
139
- 'Unknown Error while uploading DRM protected content. Make sure your DRM configuration ID is valid and set correctly',
140
- )
141
- }
142
-
143
- return Object.assign({}, INITIAL_STATE, {error: error})
144
- }
145
- default:
146
- return prev
147
- }
148
- },
149
- {
150
- stagedUpload: null,
151
- uploadStatus: null,
152
- error: null,
153
- },
154
- )
155
-
156
- // Make sure we close out the upload observer on dismount
157
- // and cleanup orphaned documents if upload was in progress
158
- useEffect(() => {
159
- const cleanup = () => {
160
- // Cancel subscription
161
- if (uploadRef.current && !uploadRef.current.closed) {
162
- uploadRef.current.unsubscribe()
163
- }
164
-
165
- // Delete orphaned document if upload was in progress and document is different from the saved asset
166
- if (uploadingDocumentId.current && props.asset?._id !== uploadingDocumentId.current) {
167
- const docId = uploadingDocumentId.current
168
- uploadingDocumentId.current = null
169
-
170
- props.client.delete(docId).catch((err) => {
171
- console.warn('Failed to cleanup orphaned upload document:', err)
172
- })
173
- }
174
- }
175
-
176
- const handleBeforeUnload = () => {
177
- cleanup()
178
- }
179
-
180
- window.addEventListener('beforeunload', handleBeforeUnload)
181
- window.addEventListener('pagehide', handleBeforeUnload)
182
-
183
- return () => {
184
- window.removeEventListener('beforeunload', handleBeforeUnload)
185
- window.removeEventListener('pagehide', handleBeforeUnload)
186
- cleanup()
187
- }
188
- }, [props.client, props.asset?._id])
189
-
190
- /* -------------------------------------------------------------------------- */
191
- /* Uploading */
192
- /* -------------------------------------------------------------------------- */
193
-
194
- /**
195
- * Begins a file or URL upload with the staged files or URL.
196
- *
197
- * Should only be called from the UploadConfiguration component, which provides
198
- * the Mux configuration for the direct asset upload.
199
- *
200
- * @param settings The Mux new_asset_settings object to send to Sanity
201
- * @param watermark Optional watermark configuration
202
- * @returns
203
- */
204
- const startUpload = (
205
- settings: MuxNewAssetSettings,
206
- watermark?: import('../util/types').WatermarkConfig,
207
- ) => {
208
- const {stagedUpload} = state
209
- if (!stagedUpload || uploadRef.current) return
210
- dispatch({action: 'commitUpload'})
211
- let uploadObservable: Observable<UploadFileEvent | UploadUrlEvent>
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
- void 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
- void 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
- // oxlint-disable-next-line react/react-compiler
414
- onCancel={cancelUploadButton.handleClick}
415
- progress={uploadStatus.progress}
416
- filename={uploadStatus.file?.name || uploadStatus.url}
417
- />
418
- )
419
- }
420
-
421
- // Upload needs configuration
422
- if (state.stagedUpload !== null) {
423
- return (
424
- <UploadConfiguration
425
- stagedUpload={state.stagedUpload}
426
- pluginConfig={props.config}
427
- secrets={props.secrets}
428
- startUpload={startUpload}
429
- onClose={() => dispatch({action: 'reset'})}
430
- />
431
- )
432
- }
433
-
434
- // Default: No staged upload
435
- let tone: CardTone | undefined
436
- if (dragState) tone = dragState === 'valid' ? 'positive' : 'critical'
437
-
438
- const acceptMimeString = props.config?.acceptedMimeTypes?.length
439
- ? props.config.acceptedMimeTypes.join(',')
440
- : 'video/*, audio/*'
441
-
442
- return (
443
- <>
444
- <UploadCard
445
- tone={tone}
446
- onDrop={handleDrop}
447
- onDragOver={handleDragOver}
448
- onDragLeave={handleDragLeave}
449
- onDragEnter={handleDragEnter}
450
- onPaste={handlePaste}
451
- ref={containerRef}
452
- >
453
- {props.asset ? (
454
- <DialogStateProvider
455
- dialogState={props.dialogState}
456
- setDialogState={props.setDialogState}
457
- >
458
- <Player
459
- readOnly={props.readOnly}
460
- asset={props.asset}
461
- onChange={props.onChange}
462
- config={props.config}
463
- buttons={
464
- <PlayerActionsMenu
465
- accept={acceptMimeString}
466
- asset={props.asset}
467
- dialogState={props.dialogState}
468
- setDialogState={props.setDialogState}
469
- onChange={props.onChange}
470
- onSelect={handleUpload}
471
- readOnly={props.readOnly}
472
- config={props.config}
473
- />
474
- }
475
- />
476
- </DialogStateProvider>
477
- ) : (
478
- <UploadPlaceholder
479
- accept={acceptMimeString}
480
- hovering={dragState !== null}
481
- onSelect={handleUpload}
482
- readOnly={!!props.readOnly}
483
- setDialogState={props.setDialogState}
484
- needsSetup={props.needsSetup}
485
- config={props.config}
486
- />
487
- )}
488
- </UploadCard>
489
- {props.dialogState === 'select-video' && (
490
- <InputBrowser
491
- config={props.config}
492
- asset={props.asset}
493
- onChange={props.onChange}
494
- setDialogState={props.setDialogState}
495
- />
496
- )}
497
- </>
498
- )
499
- }
@@ -1,148 +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
- // oxlint-disable-next-line react/react-compiler
37
- setState(references?.length ? 'cantDelete' : 'confirm')
38
- }, [state, references, referencesLoading])
39
-
40
- async function confirmDelete() {
41
- if (state !== 'confirm') return
42
-
43
- setState('processing_deletion')
44
- const worked = await deleteAsset({client, asset, deleteOnMux})
45
- if (worked === true) {
46
- toast.push({title: 'Successfully deleted video', status: 'success'})
47
- succeededDeleting()
48
- } else if (worked === 'failed-mux') {
49
- toast.push({
50
- title: 'Deleted video in Sanity',
51
- description: "But it wasn't deleted in Mux",
52
- status: 'warning',
53
- })
54
- succeededDeleting()
55
- } else {
56
- toast.push({title: 'Failed deleting video', status: 'error'})
57
-
58
- setState('error_deleting')
59
- }
60
- }
61
-
62
- return (
63
- <Dialog
64
- animate
65
- header={'Delete video'}
66
- zOffset={DIALOGS_Z_INDEX}
67
- id="deleting-video-details-dialog"
68
- onClose={cancelDelete}
69
- onClickOutside={cancelDelete}
70
- width={1}
71
- position="fixed"
72
- >
73
- <Card
74
- padding={3}
75
- style={{
76
- minHeight: '150px',
77
- display: 'flex',
78
- alignItems: 'center',
79
- justifyContent: 'center',
80
- }}
81
- >
82
- <Stack space={3}>
83
- {state === 'checkingReferences' && (
84
- <>
85
- <Heading size={2}>Checking if video can be deleted</Heading>
86
- <SpinnerBox />
87
- </>
88
- )}
89
- {state === 'cantDelete' && (
90
- <>
91
- <Heading size={2}>Video can&apos;t be deleted</Heading>
92
- <Text size={2} style={{marginBottom: '2rem'}}>
93
- There are {references?.length} document{references && references.length > 0 && 's'}{' '}
94
- pointing to this video. Remove their references to this file or delete them before
95
- proceeding.
96
- </Text>
97
- <VideoReferences references={references} isLoaded={!referencesLoading} />
98
- </>
99
- )}
100
- {state === 'confirm' && (
101
- <>
102
- <Heading size={2}>Are you sure you want to delete this video?</Heading>
103
- <Text size={2}>This action is irreversible</Text>
104
- <Stack space={4} marginY={4}>
105
- <Flex align="center" as="label">
106
- <Checkbox
107
- checked={deleteOnMux}
108
- onChange={() => setDeleteOnMux((prev) => !prev)}
109
- />
110
- <Text style={{margin: '0 10px'}}>Delete asset on Mux</Text>
111
- </Flex>
112
- <Flex align="center" as="label">
113
- <Checkbox disabled checked />
114
- <Text style={{margin: '0 10px'}}>Delete video from dataset</Text>
115
- </Flex>
116
- <Box>
117
- <Button
118
- icon={TrashIcon}
119
- fontSize={2}
120
- padding={3}
121
- text="Delete video"
122
- tone="critical"
123
- onClick={confirmDelete}
124
- disabled={['processing_deletion', 'checkingReferences', 'cantDelete'].some(
125
- (s) => s === state,
126
- )}
127
- />
128
- </Box>
129
- </Stack>
130
- </>
131
- )}
132
- {state === 'processing_deletion' && (
133
- <>
134
- <Heading size={2}>Deleting video...</Heading>
135
- <SpinnerBox />
136
- </>
137
- )}
138
- {state === 'error_deleting' && (
139
- <>
140
- <Heading size={2}>Something went wrong!</Heading>
141
- <Text size={2}>Try deleting the video again by clicking the button below</Text>
142
- </>
143
- )}
144
- </Stack>
145
- </Card>
146
- </Dialog>
147
- )
148
- }