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,72 +0,0 @@
1
- import {Flex, Radio, Text} from '@sanity/ui'
2
- import {type ActionDispatch} from 'react'
3
- import {FormField} from 'sanity'
4
-
5
- import {type UploadConfig} from '../../util/types'
6
- import {type UploadConfigurationStateAction} from '../UploadConfiguration'
7
-
8
- export const RESOLUTION_TIERS = [
9
- {value: '1080p', label: '1080p'},
10
- {value: '1440p', label: '1440p (2k)'},
11
- {value: '2160p', label: '2160p (4k)'},
12
- ] as const satisfies {value: UploadConfig['max_resolution_tier']; label: string}[]
13
-
14
- export const ResolutionTierSelector = ({
15
- id,
16
- config,
17
- dispatch,
18
- maxSupportedResolution,
19
- }: {
20
- id: string
21
- config: UploadConfig
22
- dispatch: ActionDispatch<[action: UploadConfigurationStateAction]>
23
- maxSupportedResolution: number
24
- }) => {
25
- return (
26
- <FormField
27
- path={[]}
28
- title="Resolution Tier"
29
- description={
30
- <>
31
- The maximum{' '}
32
- <a
33
- href="https://docs.mux.com/api-reference#video/operation/create-direct-upload"
34
- target="_blank"
35
- rel="noopener noreferrer"
36
- >
37
- resolution_tier
38
- </a>{' '}
39
- your asset is encoded, stored, and streamed at.
40
- </>
41
- }
42
- >
43
- <Flex gap={3} wrap={'wrap'}>
44
- {RESOLUTION_TIERS.map(({value, label}, index) => {
45
- const inputId = `${id}--type-${value}`
46
-
47
- if (index > maxSupportedResolution) return null
48
-
49
- return (
50
- <Flex key={value} align="center" gap={2}>
51
- <Radio
52
- checked={config.max_resolution_tier === value}
53
- name="asset-resolutiontier"
54
- onChange={(e) =>
55
- dispatch({
56
- action: 'max_resolution_tier',
57
- value: e.currentTarget.value as UploadConfig['max_resolution_tier'],
58
- })
59
- }
60
- value={value}
61
- id={inputId}
62
- />
63
- <Text as="label" htmlFor={inputId}>
64
- {label}
65
- </Text>
66
- </Flex>
67
- )
68
- })}
69
- </Flex>
70
- </FormField>
71
- )
72
- }
@@ -1,180 +0,0 @@
1
- import {Checkbox, Flex, Label, Radio, Stack, Text} from '@sanity/ui'
2
- import {type ActionDispatch, useMemo, useState} from 'react'
3
- import {FormField} from 'sanity'
4
-
5
- import {type StaticRenditionResolution, type UploadConfig} from '../../util/types'
6
- import {type UploadConfigurationStateAction} from '../UploadConfiguration'
7
-
8
- const ADVANCED_RESOLUTIONS: {value: StaticRenditionResolution; label: string}[] = [
9
- {value: '270p', label: '270p'},
10
- {value: '360p', label: '360p'},
11
- {value: '480p', label: '480p'},
12
- {value: '540p', label: '540p'},
13
- {value: '720p', label: '720p'},
14
- {value: '1080p', label: '1080p'},
15
- {value: '1440p', label: '1440p'},
16
- {value: '2160p', label: '2160p'},
17
- ]
18
-
19
- export const StaticRenditionSelector = ({
20
- id,
21
- config,
22
- dispatch,
23
- }: {
24
- id: string
25
- config: UploadConfig
26
- dispatch: ActionDispatch<[action: UploadConfigurationStateAction]>
27
- }) => {
28
- // Determine if user is in advanced mode based on selected renditions
29
- const isAdvancedMode = useMemo(() => {
30
- const specificResolutions = config.static_renditions.filter(
31
- (r) => r !== 'highest' && r !== 'audio-only',
32
- )
33
- return specificResolutions.length > 0
34
- }, [config.static_renditions])
35
-
36
- const [renditionMode, setRenditionMode] = useState<'standard' | 'advanced'>(
37
- isAdvancedMode ? 'advanced' : 'standard',
38
- )
39
-
40
- // Helper to toggle a rendition
41
- const toggleRendition = (rendition: StaticRenditionResolution) => {
42
- const current = config.static_renditions
43
- const hasRendition = current.includes(rendition)
44
-
45
- if (hasRendition) {
46
- dispatch({
47
- action: 'static_renditions',
48
- value: current.filter((r) => r !== rendition),
49
- })
50
- } else {
51
- dispatch({
52
- action: 'static_renditions',
53
- value: [...current, rendition],
54
- })
55
- }
56
- }
57
-
58
- // When switching modes, clear renditions that don't apply
59
- const handleModeChange = (mode: 'standard' | 'advanced') => {
60
- setRenditionMode(mode)
61
- if (mode === 'standard') {
62
- // Remove specific resolutions, keep only highest and audio-only
63
- dispatch({
64
- action: 'static_renditions',
65
- value: config.static_renditions.filter((r) => r === 'highest' || r === 'audio-only'),
66
- })
67
- } else {
68
- // Remove highest, keep specific resolutions and audio-only
69
- dispatch({
70
- action: 'static_renditions',
71
- value: config.static_renditions.filter((r) => r !== 'highest'),
72
- })
73
- }
74
- }
75
- return (
76
- <Stack space={3}>
77
- <FormField
78
- path={[]}
79
- title="Static Renditions"
80
- description="Generate downloadable MP4 or M4A files. Note: Mux will not upscale to produce MP4 renditions - renditions that would cause upscaling are skipped."
81
- >
82
- <Stack space={3}>
83
- {/* Mode Selector */}
84
- <Flex gap={3}>
85
- <Flex align="center" gap={2}>
86
- <Radio
87
- checked={renditionMode === 'standard'}
88
- name="rendition-mode"
89
- onChange={() => handleModeChange('standard')}
90
- value="standard"
91
- id={`${id}--mode-standard`}
92
- />
93
- <Text as="label" htmlFor={`${id}--mode-standard`}>
94
- Standard
95
- </Text>
96
- </Flex>
97
- <Flex align="center" gap={2}>
98
- <Radio
99
- checked={renditionMode === 'advanced'}
100
- name="rendition-mode"
101
- onChange={() => handleModeChange('advanced')}
102
- value="advanced"
103
- id={`${id}--mode-advanced`}
104
- />
105
- <Text as="label" htmlFor={`${id}--mode-advanced`}>
106
- Advanced
107
- </Text>
108
- </Flex>
109
- </Flex>
110
-
111
- {/* Standard Mode Options */}
112
- {renditionMode === 'standard' && (
113
- <Stack space={2}>
114
- <Flex align="center" gap={2} padding={[0, 2]}>
115
- <Checkbox
116
- id={`${id}--highest`}
117
- style={{display: 'block'}}
118
- checked={config.static_renditions.includes('highest')}
119
- onChange={() => toggleRendition('highest')}
120
- />
121
- <Text as="label" htmlFor={`${id}--highest`}>
122
- Highest Resolution (up to 4K)
123
- </Text>
124
- </Flex>
125
- <Flex align="center" gap={2} padding={[0, 2]}>
126
- <Checkbox
127
- id={`${id}--audio-only-standard`}
128
- style={{display: 'block'}}
129
- checked={config.static_renditions.includes('audio-only')}
130
- onChange={() => toggleRendition('audio-only')}
131
- />
132
- <Text as="label" htmlFor={`${id}--audio-only-standard`}>
133
- Audio Only (M4A)
134
- </Text>
135
- </Flex>
136
- </Stack>
137
- )}
138
-
139
- {/* Advanced Mode Options */}
140
- {renditionMode === 'advanced' && (
141
- <Stack space={2}>
142
- <Label size={1} muted>
143
- Select specific resolutions:
144
- </Label>
145
- <Flex gap={2} wrap="wrap">
146
- {ADVANCED_RESOLUTIONS.map(({value, label}) => {
147
- const inputId = `${id}--resolution-${value}`
148
- return (
149
- <Flex key={value} align="center" gap={2}>
150
- <Checkbox
151
- id={inputId}
152
- style={{display: 'block'}}
153
- checked={config.static_renditions.includes(value)}
154
- onChange={() => toggleRendition(value)}
155
- />
156
- <Text as="label" htmlFor={inputId} size={1}>
157
- {label}
158
- </Text>
159
- </Flex>
160
- )
161
- })}
162
- </Flex>
163
- <Flex align="center" gap={2} padding={[2, 2, 0, 2]}>
164
- <Checkbox
165
- id={`${id}--audio-only-advanced`}
166
- style={{display: 'block'}}
167
- checked={config.static_renditions.includes('audio-only')}
168
- onChange={() => toggleRendition('audio-only')}
169
- />
170
- <Text as="label" htmlFor={`${id}--audio-only-advanced`}>
171
- Audio Only (M4A)
172
- </Text>
173
- </Flex>
174
- </Stack>
175
- )}
176
- </Stack>
177
- </FormField>
178
- </Stack>
179
- )
180
- }
@@ -1,24 +0,0 @@
1
- // todo: get these utils from @sanity/ui instead
2
- export function focusRingBorderStyle(border: {color: string; width: number}): string {
3
- return `inset 0 0 0 ${border.width}px ${border.color}`
4
- }
5
-
6
- export function focusRingStyle(opts: {
7
- base?: {bg: string}
8
- border?: {color: string; width: number}
9
- focusRing: {offset: number; width: number}
10
- }): string {
11
- const {base, border, focusRing} = opts
12
- const focusRingOutsetWidth = focusRing.offset + focusRing.width
13
- const focusRingInsetWidth = 0 - focusRing.offset
14
- const bgColor = base ? base.bg : 'var(--card-bg-color)'
15
-
16
- return [
17
- focusRingInsetWidth > 0 && `inset 0 0 0 ${focusRingInsetWidth}px var(--card-focus-ring-color)`,
18
- border && focusRingBorderStyle(border),
19
- focusRingInsetWidth < 0 && `0 0 0 ${0 - focusRingInsetWidth}px ${bgColor}`,
20
- focusRingOutsetWidth > 0 && `0 0 0 ${focusRingOutsetWidth}px var(--card-focus-ring-color)`,
21
- ]
22
- .filter(Boolean)
23
- .join(',')
24
- }
@@ -1 +0,0 @@
1
- export * from './withFocusRing'
@@ -1,30 +0,0 @@
1
- import {rem} from '@sanity/ui'
2
- import type {ComponentType} from 'react'
3
- import {css, styled} from 'styled-components'
4
-
5
- import {focusRingBorderStyle, focusRingStyle} from './helpers'
6
-
7
- export function withFocusRing<Props>(component: ComponentType<Props>) {
8
- return styled(component as unknown as any)<Props & {$border?: boolean}>((props) => {
9
- const border = {
10
- width: props.$border ? 1 : 0,
11
- color: 'var(--card-border-color)',
12
- }
13
-
14
- return css`
15
- --card-focus-box-shadow: ${focusRingBorderStyle(border)};
16
-
17
- border-radius: ${rem(props.theme.sanity.radius[1]!)};
18
- outline: none;
19
- box-shadow: var(--card-focus-box-shadow);
20
-
21
- &:focus {
22
- --card-focus-box-shadow: ${focusRingStyle({
23
- base: props.theme.sanity.color.base,
24
- border,
25
- focusRing: props.theme.sanity.focusRing,
26
- })};
27
- }
28
- `
29
- })
30
- }
@@ -1,33 +0,0 @@
1
- import React, {createContext, useContext, useMemo} from 'react'
2
-
3
- import {type DialogState, type SetDialogState} from '../hooks/useDialogState'
4
-
5
- type DialogStateContextProps = {
6
- dialogState: DialogState
7
- setDialogState: SetDialogState
8
- }
9
-
10
- const DialogStateContext = createContext<DialogStateContextProps>({
11
- dialogState: false,
12
- setDialogState: () => {
13
- return null
14
- },
15
- })
16
-
17
- interface DialogStateProviderProps extends DialogStateContextProps {
18
- children: React.ReactNode
19
- }
20
-
21
- export const DialogStateProvider = ({
22
- dialogState,
23
- setDialogState,
24
- children,
25
- }: DialogStateProviderProps) => {
26
- const value = useMemo(() => ({dialogState, setDialogState}), [dialogState, setDialogState])
27
- return <DialogStateContext.Provider value={value}>{children}</DialogStateContext.Provider>
28
- }
29
-
30
- export const useDialogStateContext = () => {
31
- const context = useContext(DialogStateContext)
32
- return context
33
- }
@@ -1,97 +0,0 @@
1
- import {Button, Card, Dialog, Stack, Text} from '@sanity/ui'
2
- import React, {createContext, useCallback, useContext, useMemo, useState} from 'react'
3
-
4
- import {type PluginConfig} from '../util/types'
5
-
6
- const LOCAL_STORAGE_HAS_SHOWN_WARNING_KEY = 'mux-plugin-has-shown-drm-playback-warning'
7
-
8
- type DrmPlaybackWarningContextContextProps = {
9
- hasShownWarning: boolean
10
- setHasWarnedAboutDrmPlayback: (b: boolean) => void
11
- }
12
-
13
- const DrmPlaybackWarningContext = createContext<DrmPlaybackWarningContextContextProps>({
14
- hasShownWarning: false,
15
- setHasWarnedAboutDrmPlayback: () => {
16
- return null
17
- },
18
- })
19
-
20
- interface DrmPlaybackWarningContextProviderProps {
21
- config?: PluginConfig
22
- children: React.ReactNode
23
- }
24
-
25
- export const DrmPlaybackWarningContextProvider = ({
26
- config,
27
- children,
28
- }: DrmPlaybackWarningContextProviderProps) => {
29
- const warningDisabled = config?.disableDrmPlaybackWarning ?? false
30
- const hasWarned: boolean =
31
- warningDisabled || window.localStorage.getItem(LOCAL_STORAGE_HAS_SHOWN_WARNING_KEY) === 'true'
32
- const [hasWarnedAboutDrmPlayback, setHasWarnedAboutDrmPlayback] = useState(hasWarned)
33
-
34
- const setHasShownWarning = useCallback((b: boolean) => {
35
- window.localStorage.setItem(LOCAL_STORAGE_HAS_SHOWN_WARNING_KEY, b.toString())
36
- setHasWarnedAboutDrmPlayback(b)
37
- }, [])
38
-
39
- const value = useMemo(
40
- () => ({
41
- hasShownWarning: hasWarnedAboutDrmPlayback,
42
- setHasWarnedAboutDrmPlayback: setHasShownWarning,
43
- }),
44
- [hasWarnedAboutDrmPlayback, setHasShownWarning],
45
- )
46
-
47
- return (
48
- <DrmPlaybackWarningContext.Provider value={value}>
49
- {children}
50
- </DrmPlaybackWarningContext.Provider>
51
- )
52
- }
53
-
54
- export const useDrmPlaybackWarningContext = () => {
55
- const context = useContext(DrmPlaybackWarningContext)
56
- return context
57
- }
58
-
59
- export const DRMWarningDialog = ({onClose}: {onClose: () => void}) => {
60
- const {setHasWarnedAboutDrmPlayback} = useDrmPlaybackWarningContext()
61
- const _onClose = () => {
62
- setHasWarnedAboutDrmPlayback(true)
63
- onClose()
64
- }
65
- return (
66
- <Dialog
67
- open
68
- id="drm-playback-warn"
69
- onClose={_onClose}
70
- header="DRM Playback Warning"
71
- footer={
72
- <Stack padding={3}>
73
- <Button mode="ghost" tone="primary" onClick={_onClose} text="Ok" />
74
- </Stack>
75
- }
76
- >
77
- <Stack space={3} padding={3}>
78
- <Card padding={[3, 3, 3]} radius={2}>
79
- <Stack space={3}>
80
- <Text size={1} weight="semibold">
81
- DRM-protected playback will generate a license with a small associated cost. The
82
- plugin will attempt to play signed or public playback IDs instead whenever possible.
83
- </Text>
84
- </Stack>
85
- </Card>
86
- <Card padding={[3, 3, 3]} radius={2} tone="suggest">
87
- <Stack space={3}>
88
- <Text size={1} weight="semibold">
89
- This is a one time warning. If it persists, you can disable it from your plugin
90
- configuration.
91
- </Text>
92
- </Stack>
93
- </Card>
94
- </Stack>
95
- </Dialog>
96
- )
97
- }
@@ -1,13 +0,0 @@
1
- import {useCurrentUser} from 'sanity'
2
-
3
- import {type PluginConfig} from '../util/types'
4
-
5
- export const useAccessControl = (config: PluginConfig) => {
6
- const user = useCurrentUser()
7
-
8
- const hasConfigAccess =
9
- !config?.allowedRolesForConfiguration?.length ||
10
- user?.roles?.some((role) => config.allowedRolesForConfiguration.includes(role.name))
11
-
12
- return {hasConfigAccess}
13
- }
@@ -1,11 +0,0 @@
1
- import {isReference} from 'sanity'
2
- import {useDocumentValues} from 'sanity'
3
-
4
- import type {Reference, VideoAssetDocument} from '../util/types'
5
-
6
- const path = ['assetId', 'data', 'playbackId', 'status', 'thumbTime', 'filename']
7
- export const useAssetDocumentValues = (asset: Reference | null | undefined) =>
8
- useDocumentValues<VideoAssetDocument | null | undefined>(
9
- isReference(asset) ? asset._ref : '',
10
- path,
11
- )
@@ -1,73 +0,0 @@
1
- import {useMemo, useState} from 'react'
2
- import {
3
- collate,
4
- createHookFromObservableFactory,
5
- type DocumentStore,
6
- useDocumentStore,
7
- } from 'sanity'
8
-
9
- import {SANITY_API_VERSION} from '../hooks/useClient'
10
- import {createSearchFilter} from '../util/createSearchFilter'
11
- import type {VideoAssetDocument} from '../util/types'
12
-
13
- export const ASSET_SORT_OPTIONS = {
14
- createdDesc: {groq: '_createdAt desc', label: 'Newest first'},
15
- createdAsc: {groq: '_createdAt asc', label: 'First created (oldest)'},
16
- filenameAsc: {groq: 'filename asc', label: 'By filename (A-Z)'},
17
- filenameDesc: {groq: 'filename desc', label: 'By filename (Z-A)'},
18
- }
19
-
20
- export type SortOption = keyof typeof ASSET_SORT_OPTIONS
21
-
22
- const useAssetDocuments = createHookFromObservableFactory<
23
- VideoAssetDocument[],
24
- {
25
- documentStore: DocumentStore
26
- sort: SortOption
27
- searchQuery: string
28
- }
29
- >(({documentStore, sort, searchQuery}) => {
30
- const search = createSearchFilter(searchQuery)
31
- const filter = [`_type == "mux.videoAsset"`, ...search.filter].filter(Boolean).join(' && ')
32
-
33
- const sortFragment = ASSET_SORT_OPTIONS[sort].groq
34
- return documentStore.listenQuery(
35
- /* groq */ `*[${filter}] | order(${sortFragment})`,
36
- search.params,
37
- {
38
- apiVersion: SANITY_API_VERSION,
39
- },
40
- )
41
- })
42
-
43
- export default function useAssets() {
44
- const documentStore = useDocumentStore()
45
- const [sort, setSort] = useState<SortOption>('createdDesc')
46
- const [searchQuery, setSearchQuery] = useState('')
47
-
48
- const [assetDocuments = [], isLoading] = useAssetDocuments(
49
- useMemo(() => ({documentStore, sort, searchQuery}), [documentStore, sort, searchQuery]),
50
- )
51
-
52
- const assets = useMemo(
53
- () =>
54
- // Avoid displaying both drafts & published assets by collating them together and giving preference to drafts
55
- collate<VideoAssetDocument>(assetDocuments).map(
56
- (collated) =>
57
- ({
58
- ...(collated.draft || collated.published),
59
- _id: collated.id,
60
- }) as VideoAssetDocument,
61
- ),
62
- [assetDocuments],
63
- )
64
-
65
- return {
66
- assets,
67
- isLoading,
68
- sort,
69
- searchQuery,
70
- setSort,
71
- setSearchQuery,
72
- }
73
- }
@@ -1,22 +0,0 @@
1
- import {useCallback} from 'react'
2
- import {PatchEvent, unset} from 'sanity'
3
-
4
- import {deleteAssetOnMux} from '../actions/assets'
5
- import {useClient} from '../hooks/useClient'
6
- import type {MuxInputProps, VideoAssetDocument} from '../util/types'
7
-
8
- export const useCancelUpload = (asset: VideoAssetDocument, onChange: MuxInputProps['onChange']) => {
9
- const client = useClient()
10
- return useCallback(() => {
11
- if (!asset) {
12
- return
13
- }
14
- onChange(PatchEvent.from(unset()))
15
- if (asset.assetId) {
16
- void deleteAssetOnMux(client, asset.assetId)
17
- }
18
- if (asset._id) {
19
- void client.delete(asset._id)
20
- }
21
- }, [asset, client, onChange])
22
- }
@@ -1,8 +0,0 @@
1
- // As it's required to specify the API Version this custom hook ensures it's all using the same version
2
- import {useClient as useSanityClient} from 'sanity'
3
-
4
- export const SANITY_API_VERSION = '2024-03-05'
5
-
6
- export function useClient() {
7
- return useSanityClient({apiVersion: SANITY_API_VERSION})
8
- }
@@ -1,11 +0,0 @@
1
- // Handy little state machine to simplify managing which root level dialog to open
2
-
3
- import {useState} from 'react'
4
-
5
- export type DialogState = 'secrets' | 'select-video' | 'edit-thumbnail' | 'edit-captions' | false
6
-
7
- export function useDialogState() {
8
- return useState<DialogState>(false)
9
- }
10
-
11
- export type SetDialogState = ReturnType<typeof useDialogState>[1]
@@ -1,21 +0,0 @@
1
- import {createHookFromObservableFactory, type DocumentStore, type SanityDocument} from 'sanity'
2
-
3
- import {SANITY_API_VERSION} from './useClient'
4
-
5
- const useDocReferences = createHookFromObservableFactory<
6
- SanityDocument[],
7
- {
8
- documentStore: DocumentStore
9
- id: string
10
- }
11
- >(({documentStore, id}) => {
12
- return documentStore.listenQuery(
13
- /* groq */ '*[references($id)]{_id, _type, _rev, _updatedAt, _createdAt}',
14
- {id},
15
- {
16
- apiVersion: SANITY_API_VERSION,
17
- },
18
- )
19
- })
20
-
21
- export default useDocReferences
@@ -1,55 +0,0 @@
1
- import {useEffect, useState} from 'react'
2
-
3
- import {type StagedUpload} from '../components/Uploader'
4
-
5
- export function useFetchFileSize(stagedUpload: StagedUpload, maxFileSize?: number) {
6
- const [fileSize, setFileSize] = useState<number | null>(null)
7
- const [isLoadingFileSize, setIsLoadingFileSize] = useState(false)
8
- const [canSkipFileSizeValidation, setCanSkipFileSizeValidation] = useState(false)
9
-
10
- useEffect(() => {
11
- // Fetch URL Upload file size
12
- if (stagedUpload.type === 'url') {
13
- // oxlint-disable-next-line react/react-compiler
14
- setIsLoadingFileSize(false)
15
- setCanSkipFileSizeValidation(false)
16
- setFileSize(null)
17
- const url = stagedUpload.url
18
-
19
- // Get file size from URL
20
- const fetchFileSize = async () => {
21
- setIsLoadingFileSize(true)
22
- try {
23
- const response = await fetch(url, {method: 'HEAD'})
24
- const contentLength = response.headers.get('content-length')
25
- const newFileSize = contentLength ? parseInt(contentLength, 10) : null
26
-
27
- setIsLoadingFileSize(false)
28
- if (newFileSize) {
29
- setFileSize(newFileSize)
30
- }
31
- if (newFileSize === null && maxFileSize !== undefined) {
32
- // Size unknown but size limit is configured - skip file size validation
33
- setCanSkipFileSizeValidation(true)
34
- }
35
- } catch {
36
- console.warn('Could not validate file size from URL')
37
- // Skip validation of file size, but still validate duration
38
- setCanSkipFileSizeValidation(true)
39
- setIsLoadingFileSize(false)
40
- }
41
- }
42
-
43
- void fetchFileSize()
44
- }
45
- if (stagedUpload.type === 'file') {
46
- setFileSize(stagedUpload.files[0]!.size)
47
- }
48
- }, [maxFileSize, stagedUpload, stagedUpload.type])
49
-
50
- return {
51
- fileSize,
52
- isLoadingFileSize,
53
- canSkipFileSizeValidation,
54
- }
55
- }