sanity-plugin-mux-input 3.0.5 → 4.0.1

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 +20 -92
  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,78 +0,0 @@
1
- import {Card} from '@sanity/ui'
2
- import {memo, Suspense} from 'react'
3
-
4
- import {useAccessControl} from '../hooks/useAccessControl'
5
- import {useAssetDocumentValues} from '../hooks/useAssetDocumentValues'
6
- import {useClient} from '../hooks/useClient'
7
- import {useDialogState} from '../hooks/useDialogState'
8
- import {useMuxPolling} from '../hooks/useMuxPolling'
9
- import {useSecretsDocumentValues} from '../hooks/useSecretsDocumentValues'
10
- import type {MuxInputProps, PluginConfig} from '../util/types'
11
- import {ConfigureApiDialog} from './ConfigureApi'
12
- import ErrorBoundaryCard from './ErrorBoundaryCard'
13
- import {InputFallback} from './Input.styled'
14
- import Onboard from './Onboard'
15
- import Uploader from './Uploader'
16
-
17
- export interface InputProps extends MuxInputProps {
18
- config: PluginConfig
19
- }
20
- const Input = (props: InputProps) => {
21
- const client = useClient()
22
- const secretDocumentValues = useSecretsDocumentValues()
23
- const assetDocumentValues = useAssetDocumentValues(props.value?.asset)
24
- const poll = useMuxPolling(props.readOnly ? undefined : assetDocumentValues?.value || undefined)
25
- const [dialogState, setDialogState] = useDialogState()
26
- const {hasConfigAccess} = useAccessControl(props.config)
27
-
28
- const error = secretDocumentValues.error || assetDocumentValues.error || poll.error /*||
29
- // @TODO move errored logic to Uploader, where handleRemoveVideo can be called
30
- (assetDocumentValues.value?.status === 'errored'
31
- ? new Error(assetDocumentValues.value.data?.errors?.messages?.join(' '))
32
- : undefined)
33
- // */
34
- if (error) {
35
- // @TODO deal with it more gracefully
36
- throw error
37
- }
38
- const isLoading = secretDocumentValues.isLoading || assetDocumentValues.isLoading
39
-
40
- return (
41
- <Card>
42
- <ErrorBoundaryCard schemaType={props.schemaType}>
43
- <Suspense fallback={<InputFallback />}>
44
- {isLoading ? (
45
- <InputFallback />
46
- ) : (
47
- <>
48
- {secretDocumentValues.value.needsSetup && !assetDocumentValues.value ? (
49
- <Onboard setDialogState={setDialogState} config={props.config} />
50
- ) : (
51
- <Uploader
52
- {...props}
53
- config={props.config}
54
- onChange={props.onChange}
55
- client={client}
56
- secrets={secretDocumentValues.value.secrets}
57
- asset={assetDocumentValues.value}
58
- dialogState={dialogState}
59
- setDialogState={setDialogState}
60
- needsSetup={secretDocumentValues.value.needsSetup}
61
- />
62
- )}
63
-
64
- {dialogState === 'secrets' && hasConfigAccess && (
65
- <ConfigureApiDialog
66
- setDialogState={setDialogState}
67
- secrets={secretDocumentValues.value.secrets}
68
- />
69
- )}
70
- </>
71
- )}
72
- </Suspense>
73
- </ErrorBoundaryCard>
74
- </Card>
75
- )
76
- }
77
-
78
- export default memo(Input)
@@ -1,41 +0,0 @@
1
- import {Dialog} from '@sanity/ui'
2
- import {useCallback, useId} from 'react'
3
- import {styled} from 'styled-components'
4
-
5
- import type {SetDialogState} from '../hooks/useDialogState'
6
- import SelectAsset, {type Props as SelectAssetProps} from './SelectAsset'
7
-
8
- /** To prevent Content Layout Shift (CLS), ensure that the dialog always occupies the entire available height. */
9
- const StyledDialog = styled(Dialog)`
10
- > div[data-ui='DialogCard'] > div[data-ui='Card'] {
11
- height: 100%;
12
- }
13
- `
14
-
15
- export default function InputBrowser({
16
- setDialogState,
17
- asset,
18
- onChange,
19
- config,
20
- }: Pick<SelectAssetProps, 'onChange' | 'asset' | 'config'> & {
21
- setDialogState: SetDialogState
22
- }) {
23
- const id = `InputBrowser${useId()}`
24
- const handleClose = useCallback(() => setDialogState(false), [setDialogState])
25
- return (
26
- <StyledDialog
27
- __unstable_autoFocus
28
- header="Select video"
29
- id={id}
30
- onClose={handleClose}
31
- width={2}
32
- >
33
- <SelectAsset
34
- config={config}
35
- asset={asset}
36
- onChange={onChange}
37
- setDialogState={setDialogState}
38
- />
39
- </StyledDialog>
40
- )
41
- }
@@ -1,42 +0,0 @@
1
- import {useTheme_v2} from '@sanity/ui'
2
- import {type CSSProperties, useId, useMemo} from 'react'
3
-
4
- export interface Props {
5
- height?: number
6
- }
7
- export default function MuxLogo({height = 26}: Props) {
8
- const id = useId()
9
- const theme = useTheme_v2()
10
- const fillColor = theme.color._dark ? 'white' : 'black'
11
- const titleId = useMemo(() => `${id}-title`, [id])
12
-
13
- const pathStyle: CSSProperties = {
14
- fillRule: 'nonzero',
15
- }
16
-
17
- return (
18
- <svg
19
- aria-labelledby={titleId}
20
- style={{height: `${height}px`}}
21
- viewBox="0 0 1600 500"
22
- version="1.1"
23
- xmlns="http://www.w3.org/2000/svg"
24
- xmlSpace="preserve"
25
- >
26
- <g id="Layer-1" fill={fillColor}>
27
- <path
28
- d="M994.287,93.486c-17.121,-0 -31,-13.879 -31,-31c0,-17.121 13.879,-31 31,-31c17.121,-0 31,13.879 31,31c0,17.121 -13.879,31 -31,31m0,-93.486c-34.509,-0 -62.484,27.976 -62.484,62.486l0,187.511c0,68.943 -56.09,125.033 -125.032,125.033c-68.942,-0 -125.03,-56.09 -125.03,-125.033l0,-187.511c0,-34.51 -27.976,-62.486 -62.485,-62.486c-34.509,-0 -62.484,27.976 -62.484,62.486l0,187.511c0,137.853 112.149,250.003 249.999,250.003c137.851,-0 250.001,-112.15 250.001,-250.003l0,-187.511c0,-34.51 -27.976,-62.486 -62.485,-62.486"
29
- style={pathStyle}
30
- />
31
- <path
32
- d="M1537.51,468.511c-17.121,-0 -31,-13.879 -31,-31c0,-17.121 13.879,-31 31,-31c17.121,-0 31,13.879 31,31c0,17.121 -13.879,31 -31,31m-275.883,-218.509l-143.33,143.329c-24.402,24.402 -24.402,63.966 0,88.368c24.402,24.402 63.967,24.402 88.369,-0l143.33,-143.329l143.328,143.329c24.402,24.4 63.967,24.402 88.369,-0c24.403,-24.402 24.403,-63.966 0.001,-88.368l-143.33,-143.329l0.001,-0.004l143.329,-143.329c24.402,-24.402 24.402,-63.965 0,-88.367c-24.402,-24.402 -63.967,-24.402 -88.369,-0l-143.329,143.328l-143.329,-143.328c-24.402,-24.401 -63.967,-24.402 -88.369,-0c-24.402,24.402 -24.402,63.965 0,88.367l143.329,143.329l0,0.004Z"
33
- style={pathStyle}
34
- />
35
- <path
36
- d="M437.511,468.521c-17.121,-0 -31,-13.879 -31,-31c0,-17.121 13.879,-31 31,-31c17.121,-0 31,13.879 31,31c0,17.121 -13.879,31 -31,31m23.915,-463.762c-23.348,-9.672 -50.226,-4.327 -68.096,13.544l-143.331,143.329l-143.33,-143.329c-17.871,-17.871 -44.747,-23.216 -68.096,-13.544c-23.349,9.671 -38.574,32.455 -38.574,57.729l0,375.026c0,34.51 27.977,62.486 62.487,62.486c34.51,-0 62.486,-27.976 62.486,-62.486l0,-224.173l80.843,80.844c24.404,24.402 63.965,24.402 88.369,-0l80.843,-80.844l0,224.173c0,34.51 27.976,62.486 62.486,62.486c34.51,-0 62.486,-27.976 62.486,-62.486l0,-375.026c0,-25.274 -15.224,-48.058 -38.573,-57.729"
37
- style={pathStyle}
38
- />
39
- </g>
40
- </svg>
41
- )
42
- }
@@ -1,65 +0,0 @@
1
- import {PlugIcon} from '@sanity/icons'
2
- import {Button, Card, Flex, Grid, Heading, Inline, Text} from '@sanity/ui'
3
- import {useCallback} from 'react'
4
-
5
- import {useAccessControl} from '../hooks/useAccessControl'
6
- import type {SetDialogState} from '../hooks/useDialogState'
7
- import {type PluginConfig} from '../util/types'
8
- import MuxLogo from './MuxLogo'
9
-
10
- interface OnboardProps {
11
- setDialogState: SetDialogState
12
- config: PluginConfig
13
- }
14
-
15
- export default function Onboard(props: OnboardProps) {
16
- const {setDialogState} = props
17
- const handleOpen = useCallback(() => setDialogState('secrets'), [setDialogState])
18
- const {hasConfigAccess} = useAccessControl(props.config)
19
-
20
- return (
21
- <>
22
- <div style={{padding: 2}}>
23
- <Card
24
- display="flex"
25
- sizing="border"
26
- style={{
27
- aspectRatio: '16/9',
28
- width: '100%',
29
- boxShadow: 'var(--card-bg-color) 0 0 0 2px',
30
- }}
31
- paddingX={[2, 3, 4, 4]}
32
- radius={1}
33
- tone="transparent"
34
- >
35
- <Flex justify="flex-start" align="center">
36
- <Grid columns={1} gap={[2, 3, 4, 4]}>
37
- <Inline paddingY={1}>
38
- <div style={{height: '32px'}}>
39
- <MuxLogo />
40
- </div>
41
- </Inline>
42
- <Inline paddingY={1}>
43
- <Heading size={[0, 1, 2, 2]}>
44
- Upload and preview videos directly from your studio.
45
- </Heading>
46
- </Inline>
47
- <Inline paddingY={1}>
48
- {hasConfigAccess ? (
49
- <Button mode="ghost" icon={PlugIcon} text="Configure API" onClick={handleOpen} />
50
- ) : (
51
- <Card padding={[3, 3, 3]} radius={2} shadow={1} tone="critical">
52
- <Text>
53
- You do not have access to configure the Mux API. Please contact your
54
- administrator.
55
- </Text>
56
- </Card>
57
- )}
58
- </Inline>
59
- </Grid>
60
- </Flex>
61
- </Card>
62
- </div>
63
- </>
64
- )
65
- }
@@ -1,54 +0,0 @@
1
- import {ChevronLeftIcon, ChevronRightIcon} from '@sanity/icons'
2
- import {Button, Label} from '@sanity/ui'
3
- import {type Dispatch, type SetStateAction, useEffect} from 'react'
4
-
5
- const PageSelector = (props: {
6
- page: number
7
- setPage: Dispatch<SetStateAction<number>>
8
- total: number
9
- }) => {
10
- const page = props.page
11
- const setPage = props.setPage
12
-
13
- useEffect(() => {
14
- // Constraint in bounds.
15
- const clamped = Math.min(props.total - 1, Math.max(0, page))
16
- if (page !== clamped) {
17
- setPage(clamped)
18
- }
19
- }, [page, props.total, setPage])
20
-
21
- return (
22
- <>
23
- <Button
24
- icon={ChevronLeftIcon}
25
- mode="bleed"
26
- padding={3}
27
- style={{cursor: 'pointer'}}
28
- disabled={page <= 0}
29
- onClick={() => {
30
- setPage((p) => {
31
- return Math.min(props.total - 1, Math.max(0, p - 1))
32
- })
33
- }}
34
- />
35
- <Label muted>
36
- Page {page + 1}/{props.total}
37
- </Label>
38
- <Button
39
- icon={ChevronRightIcon}
40
- mode="bleed"
41
- padding={3}
42
- style={{cursor: 'pointer'}}
43
- disabled={page >= props.total - 1}
44
- onClick={() => {
45
- setPage((p) => {
46
- return Math.min(props.total - 1, Math.max(0, p + 1))
47
- })
48
- }}
49
- />
50
- </>
51
- )
52
- }
53
-
54
- export default PageSelector
@@ -1,11 +0,0 @@
1
- import {styled} from 'styled-components'
2
-
3
- export const TopControls = styled.div`
4
- position: absolute;
5
- top: 0;
6
- right: 0;
7
- justify-content: flex-end;
8
- button {
9
- height: auto;
10
- }
11
- `
@@ -1,117 +0,0 @@
1
- import {Card, Text} from '@sanity/ui'
2
- import React, {useEffect, useMemo, useRef} from 'react'
3
-
4
- import {useCancelUpload} from '../hooks/useCancelUpload'
5
- import type {MuxInputProps, PluginConfig, VideoAssetDocument} from '../util/types'
6
- import {TopControls} from './Player.styled'
7
- import {UploadProgress} from './UploadProgress'
8
- import VideoPlayer from './VideoPlayer'
9
-
10
- interface Props extends Pick<MuxInputProps, 'onChange' | 'readOnly'> {
11
- buttons?: React.ReactNode
12
- asset: VideoAssetDocument
13
- config?: PluginConfig
14
- }
15
-
16
- const Player = ({asset, buttons, readOnly, onChange, config}: Props) => {
17
- const isLoading = useMemo<boolean | string>(() => {
18
- if (asset?.status === 'preparing') {
19
- return 'Preparing the video'
20
- }
21
- if (asset?.status === 'waiting_for_upload') {
22
- return 'Waiting for upload to start'
23
- }
24
- if (asset?.status === 'waiting') {
25
- return 'Processing upload'
26
- }
27
- if (asset?.status === 'ready') {
28
- return false
29
- }
30
- if (typeof asset?.status === 'undefined') {
31
- return false
32
- }
33
-
34
- return true
35
- }, [asset])
36
- // oxlint-disable-next-line react/react-compiler
37
- const isPreparingStaticRenditions = useMemo<boolean>(() => {
38
- // Legacy: If static_renditions has a status field, it was created with mp4_support (deprecated)
39
- // We don't process this old format, just return false
40
- // Note: 'disabled' status is valid in the new format when no renditions were requested
41
- if (
42
- asset?.data?.static_renditions?.status &&
43
- asset?.data?.static_renditions?.status !== 'disabled'
44
- ) {
45
- return false
46
- }
47
-
48
- // Check if any file in static_renditions is still preparing
49
- const files = asset?.data?.static_renditions?.files
50
- if (!files || files.length === 0) {
51
- return false
52
- }
53
- return files.some((file) => file.status === 'preparing')
54
- }, [asset?.data?.static_renditions?.status, asset?.data?.static_renditions?.files])
55
- const playRef = useRef<HTMLDivElement>(null)
56
- const muteRef = useRef<HTMLDivElement>(null)
57
- const handleCancelUpload = useCancelUpload(asset, onChange)
58
-
59
- useEffect(() => {
60
- const style = document.createElement('style')
61
- style.innerHTML = 'button svg { vertical-align: middle; }'
62
-
63
- if (playRef.current?.shadowRoot) {
64
- playRef.current.shadowRoot.appendChild(style)
65
- }
66
- if (muteRef?.current?.shadowRoot) {
67
- muteRef.current.shadowRoot.appendChild(style.cloneNode(true))
68
- }
69
- }, [])
70
-
71
- useEffect(() => {
72
- if (asset?.status === 'errored') {
73
- handleCancelUpload()
74
- // @TODO use better error handling
75
- throw new Error(asset.data?.errors?.messages?.join(' '))
76
- }
77
- }, [asset.data?.errors?.messages, asset?.status, handleCancelUpload])
78
-
79
- if (!asset || !asset.status) {
80
- return null
81
- }
82
-
83
- if (isLoading) {
84
- return (
85
- <UploadProgress
86
- progress={100}
87
- filename={asset?.filename}
88
- text={(isLoading !== true && isLoading) || 'Waiting for Mux to complete the upload'}
89
- onCancel={readOnly ? undefined : () => handleCancelUpload()}
90
- />
91
- )
92
- }
93
-
94
- return (
95
- <VideoPlayer asset={asset} hlsConfig={config?.hlsConfig}>
96
- {buttons && <TopControls slot="top-chrome">{buttons}</TopControls>}
97
- {isPreparingStaticRenditions && (
98
- <Card
99
- padding={2}
100
- radius={1}
101
- style={{
102
- background: 'var(--card-fg-color)',
103
- position: 'absolute',
104
- top: '0.5em',
105
- left: '0.5em',
106
- }}
107
- >
108
- <Text size={1} style={{color: 'var(--card-bg-color)'}}>
109
- MUX is preparing static renditions, please stand by
110
- </Text>
111
- </Card>
112
- )}
113
- </VideoPlayer>
114
- )
115
- }
116
-
117
- export default Player
@@ -1,191 +0,0 @@
1
- import {
2
- EllipsisHorizontalIcon,
3
- ImageIcon,
4
- LockIcon,
5
- PlugIcon,
6
- ResetIcon,
7
- SearchIcon,
8
- SyncIcon,
9
- TranslateIcon,
10
- UploadIcon,
11
- } from '@sanity/icons'
12
- import {
13
- Box,
14
- Button,
15
- Card,
16
- Inline,
17
- Label,
18
- Menu,
19
- MenuDivider,
20
- MenuItem,
21
- Popover,
22
- Text,
23
- Tooltip,
24
- useClickOutsideEvent,
25
- } from '@sanity/ui'
26
- import {memo, useCallback, useEffect, useMemo, useState} from 'react'
27
- import {PatchEvent, unset} from 'sanity'
28
- import {styled} from 'styled-components'
29
-
30
- import {useAccessControl} from '../hooks/useAccessControl'
31
- import {type DialogState, type SetDialogState} from '../hooks/useDialogState'
32
- import {useResyncAsset} from '../hooks/useResyncAsset'
33
- import {getPlaybackPolicy} from '../util/getPlaybackPolicy'
34
- import type {MuxInputProps, PluginConfig, VideoAssetDocument} from '../util/types'
35
- import {FileInputMenuItem} from './FileInputMenuItem'
36
-
37
- const LockCard = styled(Card)`
38
- position: absolute;
39
- top: 0;
40
- left: 0;
41
- opacity: 0.6;
42
- mix-blend-mode: screen;
43
- background: transparent;
44
- `
45
-
46
- const LockButton = styled(Button)`
47
- background: transparent;
48
- color: white;
49
- `
50
-
51
- // @TODO: add support for audio type (asset._type) when uploading an audio file so we can hide the thumbnail option.
52
- const isVideoAsset = (asset: VideoAssetDocument) => {
53
- return asset._type === 'mux.videoAsset'
54
- }
55
-
56
- function PlayerActionsMenu(
57
- props: Pick<MuxInputProps, 'onChange' | 'readOnly'> & {
58
- asset: VideoAssetDocument
59
- onSelect: (files: File[]) => void
60
- dialogState: DialogState
61
- setDialogState: SetDialogState
62
- config: PluginConfig
63
- accept: string
64
- },
65
- ) {
66
- const {asset, readOnly, dialogState, setDialogState, onChange, onSelect, accept} = props
67
- const [open, setOpen] = useState(false)
68
- const [menuElement, setMenuRef] = useState<HTMLDivElement | null>(null)
69
- const isSigned = useMemo(() => getPlaybackPolicy(asset)?.policy === 'signed', [asset])
70
- const {hasConfigAccess} = useAccessControl(props.config)
71
- const {resyncAsset, isResyncing} = useResyncAsset({showToast: true})
72
-
73
- const onReset = useCallback(() => onChange(PatchEvent.from(unset([]))), [onChange])
74
-
75
- const handleResync = useCallback(async () => {
76
- setOpen(false)
77
- await resyncAsset(asset)
78
- }, [resyncAsset, asset])
79
-
80
- useEffect(() => {
81
- if (open && dialogState) {
82
- // oxlint-disable-next-line react/react-compiler
83
- setOpen(false)
84
- }
85
- }, [dialogState, open])
86
-
87
- useClickOutsideEvent(
88
- () => setOpen(false),
89
- () => [menuElement],
90
- )
91
-
92
- return (
93
- <Inline space={1} padding={2}>
94
- {isSigned && (
95
- <Tooltip
96
- animate
97
- content={
98
- <Box padding={2}>
99
- <Text muted size={1}>
100
- Signed playback policy
101
- </Text>
102
- </Box>
103
- }
104
- placement="right"
105
- portal
106
- >
107
- <LockCard radius={2} margin={2} scheme="dark" tone="positive">
108
- <LockButton icon={LockIcon} mode="bleed" tone="positive" />
109
- </LockCard>
110
- </Tooltip>
111
- )}
112
- <Popover
113
- animate
114
- content={
115
- <Menu ref={setMenuRef}>
116
- <Box padding={2}>
117
- <Label muted size={1}>
118
- Replace
119
- </Label>
120
- </Box>
121
- <FileInputMenuItem
122
- accept={accept}
123
- icon={UploadIcon}
124
- onSelect={onSelect}
125
- text="Upload"
126
- disabled={readOnly}
127
- fontSize={1}
128
- />
129
- <MenuItem
130
- icon={SearchIcon}
131
- text="Browse"
132
- onClick={() => setDialogState('select-video')}
133
- />
134
- {isVideoAsset(asset) && (
135
- <>
136
- <MenuItem
137
- icon={ImageIcon}
138
- text="Thumbnail"
139
- onClick={() => setDialogState('edit-thumbnail')}
140
- />
141
- <MenuItem
142
- icon={TranslateIcon}
143
- text="Captions"
144
- onClick={() => setDialogState('edit-captions')}
145
- />
146
- <MenuItem
147
- icon={SyncIcon}
148
- text="Resync from Mux"
149
- onClick={handleResync}
150
- disabled={readOnly || isResyncing}
151
- />
152
- </>
153
- )}
154
- <MenuDivider />
155
- {hasConfigAccess && (
156
- <>
157
- <MenuItem
158
- icon={PlugIcon}
159
- text="Configure API"
160
- onClick={() => setDialogState('secrets')}
161
- />
162
- <MenuDivider />
163
- </>
164
- )}
165
- <MenuItem
166
- tone="critical"
167
- icon={ResetIcon}
168
- text="Clear field"
169
- onClick={onReset}
170
- disabled={readOnly}
171
- />
172
- </Menu>
173
- }
174
- portal
175
- open={open}
176
- >
177
- <Button
178
- icon={EllipsisHorizontalIcon}
179
- mode="ghost"
180
- fontSize={1}
181
- onClick={() => {
182
- setDialogState(false)
183
- setOpen(true)
184
- }}
185
- />
186
- </Popover>
187
- </Inline>
188
- )
189
- }
190
-
191
- export default memo(PlayerActionsMenu)