@brightcove/components-embed-code-modal 0.8.1 → 0.8.3

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 (164) hide show
  1. package/.eslintignore +7 -0
  2. package/.eslintrc +23 -0
  3. package/.husky/pre-commit +4 -0
  4. package/.nvmrc +1 -0
  5. package/.prettierignore +7 -0
  6. package/.prettierrc +5 -0
  7. package/.release-it.json +40 -0
  8. package/.whitesource +3 -0
  9. package/CHANGELOG.md +342 -0
  10. package/CODEOWNERS +37 -0
  11. package/PULL_REQUEST_TEMPLATE.md +17 -0
  12. package/__tests__/EmbedCode.test.tsx +51 -0
  13. package/__tests__/InteractivityProject.test.tsx +50 -0
  14. package/__tests__/PlayerPreview.test.tsx +23 -0
  15. package/__tests__/TabsHeader.test.tsx +19 -0
  16. package/__tests__/TabsSection.test.tsx +46 -0
  17. package/__tests__/TitleWithTooltip.test.tsx +45 -0
  18. package/__tests__/VideoPlayer.test.tsx +38 -0
  19. package/__tests__/util.test.ts +89 -0
  20. package/babel.config.json +17 -0
  21. package/etc/wildcard.brightcove.com.key +27 -0
  22. package/etc/wildcard.brightcove.com.pem +96 -0
  23. package/jest/jest.setup.js +62 -0
  24. package/jest/jest.stub.js +4 -0
  25. package/jest.config.js +24 -0
  26. package/package.json +15 -5
  27. package/scripts/create-dist-pkg-prod.js +18 -0
  28. package/src/index.ts +16 -0
  29. package/src/js/EmbedCodeModal.tsx +88 -0
  30. package/src/js/components/CustomSettingsDropdown.tsx +184 -0
  31. package/src/js/components/EmbedCopySection.tsx +130 -0
  32. package/src/js/components/ModalContainer.tsx +274 -0
  33. package/src/js/components/NoPlayerModal.tsx +42 -0
  34. package/src/js/components/PlayerPreview.tsx +116 -0
  35. package/src/js/components/TabsHeader.tsx +31 -0
  36. package/src/js/components/TabsSection.tsx +74 -0
  37. package/src/js/components/TitleWithTooltip.tsx +39 -0
  38. package/src/js/components/VideoLinks.tsx +83 -0
  39. package/src/js/components/VideoPlayer.tsx +303 -0
  40. package/src/js/components/{index.d.ts → index.ts} +0 -1
  41. package/src/js/constants/baseUrls.ts +4 -0
  42. package/src/js/constants/embed.ts +12 -0
  43. package/src/js/constants/player.ts +32 -0
  44. package/src/js/constants/pluginsRegistry.ts +30 -0
  45. package/src/js/hooks/useGetPlayers.ts +12 -0
  46. package/src/js/i18n.ts +43 -0
  47. package/src/js/services/players-api.ts +26 -0
  48. package/src/js/services/url-shortener.ts +10 -0
  49. package/src/js/types/EmbedCodeModalProps.ts +72 -0
  50. package/src/js/types/EmbedCodeProps.ts +13 -0
  51. package/src/js/types/brightcoveEmbedCode.d.ts +18 -0
  52. package/src/js/types/players.ts +64 -0
  53. package/src/js/types/react-i18next.d.ts +8 -0
  54. package/src/js/types/studioModule.ts +7 -0
  55. package/src/js/utils/addPluginsDataToPlayers.ts +59 -0
  56. package/src/js/utils/call.ts +31 -0
  57. package/src/js/utils/dimensions.ts +53 -0
  58. package/src/js/utils/featureSwitches.ts +16 -0
  59. package/src/js/utils/util.ts +153 -0
  60. package/src/module.tsx +246 -0
  61. package/src/styles/CustomSettingsDropdown.sass +42 -0
  62. package/src/styles/EmbedCode.sass +30 -0
  63. package/src/styles/EmbedCodeModal.sass +76 -0
  64. package/src/styles/base.sass +1 -0
  65. package/src/styles/theme.sass +11 -0
  66. package/src/styles/typings.td.ts +9 -0
  67. package/src/translations/en.json +53 -0
  68. package/src/translations/es.json +53 -0
  69. package/src/translations/fr.json +53 -0
  70. package/src/translations/ja.json +53 -0
  71. package/src/translations/ko.json +53 -0
  72. package/src/translations/zh.json +53 -0
  73. package/src/types/studio.ts +197 -0
  74. package/tsconfig.json +34 -0
  75. package/webpack.config.js +97 -0
  76. package/__tests__/EmbedCode.test.d.ts +0 -2
  77. package/__tests__/EmbedCode.test.d.ts.map +0 -1
  78. package/__tests__/InteractivityProject.test.d.ts +0 -2
  79. package/__tests__/InteractivityProject.test.d.ts.map +0 -1
  80. package/__tests__/PlayerPreview.test.d.ts +0 -2
  81. package/__tests__/PlayerPreview.test.d.ts.map +0 -1
  82. package/__tests__/PlayersDropdown.test.d.ts +0 -2
  83. package/__tests__/PlayersDropdown.test.d.ts.map +0 -1
  84. package/__tests__/TabsHeader.test.d.ts +0 -2
  85. package/__tests__/TabsHeader.test.d.ts.map +0 -1
  86. package/__tests__/TabsSection.test.d.ts +0 -2
  87. package/__tests__/TabsSection.test.d.ts.map +0 -1
  88. package/__tests__/TitleWithTooltip.test.d.ts +0 -2
  89. package/__tests__/TitleWithTooltip.test.d.ts.map +0 -1
  90. package/__tests__/VideoPlayer.test.d.ts +0 -2
  91. package/__tests__/VideoPlayer.test.d.ts.map +0 -1
  92. package/__tests__/util.test.d.ts +0 -2
  93. package/__tests__/util.test.d.ts.map +0 -1
  94. package/index.js +0 -56
  95. package/index.js.map +0 -1
  96. package/src/index.d.ts +0 -5
  97. package/src/index.d.ts.map +0 -1
  98. package/src/js/EmbedCodeModal.d.ts +0 -17
  99. package/src/js/EmbedCodeModal.d.ts.map +0 -1
  100. package/src/js/components/ActivateInteractivityModal.d.ts +0 -3
  101. package/src/js/components/ActivateInteractivityModal.d.ts.map +0 -1
  102. package/src/js/components/CustomSettingsDropdown.d.ts +0 -11
  103. package/src/js/components/CustomSettingsDropdown.d.ts.map +0 -1
  104. package/src/js/components/EmbedCopySection.d.ts +0 -4
  105. package/src/js/components/EmbedCopySection.d.ts.map +0 -1
  106. package/src/js/components/ModalContainer.d.ts +0 -17
  107. package/src/js/components/ModalContainer.d.ts.map +0 -1
  108. package/src/js/components/NoPlayerModal.d.ts +0 -8
  109. package/src/js/components/NoPlayerModal.d.ts.map +0 -1
  110. package/src/js/components/PlayerPreview.d.ts +0 -4
  111. package/src/js/components/PlayerPreview.d.ts.map +0 -1
  112. package/src/js/components/PlayersDropdown.d.ts +0 -9
  113. package/src/js/components/PlayersDropdown.d.ts.map +0 -1
  114. package/src/js/components/TabsHeader.d.ts +0 -11
  115. package/src/js/components/TabsHeader.d.ts.map +0 -1
  116. package/src/js/components/TabsSection.d.ts +0 -15
  117. package/src/js/components/TabsSection.d.ts.map +0 -1
  118. package/src/js/components/TitleWithTooltip.d.ts +0 -8
  119. package/src/js/components/TitleWithTooltip.d.ts.map +0 -1
  120. package/src/js/components/VideoLinks.d.ts +0 -9
  121. package/src/js/components/VideoLinks.d.ts.map +0 -1
  122. package/src/js/components/VideoPlayer.d.ts +0 -17
  123. package/src/js/components/VideoPlayer.d.ts.map +0 -1
  124. package/src/js/components/index.d.ts.map +0 -1
  125. package/src/js/constants/baseUrls.d.ts +0 -5
  126. package/src/js/constants/baseUrls.d.ts.map +0 -1
  127. package/src/js/constants/embed.d.ts +0 -9
  128. package/src/js/constants/embed.d.ts.map +0 -1
  129. package/src/js/constants/player.d.ts +0 -27
  130. package/src/js/constants/player.d.ts.map +0 -1
  131. package/src/js/constants/pluginsRegistry.d.ts +0 -7
  132. package/src/js/constants/pluginsRegistry.d.ts.map +0 -1
  133. package/src/js/hooks/useGetPlayers.d.ts +0 -3
  134. package/src/js/hooks/useGetPlayers.d.ts.map +0 -1
  135. package/src/js/i18n.d.ts +0 -330
  136. package/src/js/i18n.d.ts.map +0 -1
  137. package/src/js/services/players-api.d.ts +0 -5
  138. package/src/js/services/players-api.d.ts.map +0 -1
  139. package/src/js/services/url-shortener.d.ts +0 -4
  140. package/src/js/services/url-shortener.d.ts.map +0 -1
  141. package/src/js/types/EmbedCodeModalProps.d.ts +0 -59
  142. package/src/js/types/EmbedCodeModalProps.d.ts.map +0 -1
  143. package/src/js/types/EmbedCodeProps.d.ts +0 -13
  144. package/src/js/types/EmbedCodeProps.d.ts.map +0 -1
  145. package/src/js/types/players.d.ts +0 -58
  146. package/src/js/types/players.d.ts.map +0 -1
  147. package/src/js/types/studioModule.d.ts +0 -8
  148. package/src/js/types/studioModule.d.ts.map +0 -1
  149. package/src/js/utils/addPluginsDataToPlayers.d.ts +0 -6
  150. package/src/js/utils/addPluginsDataToPlayers.d.ts.map +0 -1
  151. package/src/js/utils/call.d.ts +0 -14
  152. package/src/js/utils/call.d.ts.map +0 -1
  153. package/src/js/utils/dimensions.d.ts +0 -9
  154. package/src/js/utils/dimensions.d.ts.map +0 -1
  155. package/src/js/utils/featureSwitches.d.ts +0 -2
  156. package/src/js/utils/featureSwitches.d.ts.map +0 -1
  157. package/src/js/utils/util.d.ts +0 -67
  158. package/src/js/utils/util.d.ts.map +0 -1
  159. package/src/module.d.ts +0 -2
  160. package/src/module.d.ts.map +0 -1
  161. package/src/styles/typings.td.d.ts +0 -13
  162. package/src/styles/typings.td.d.ts.map +0 -1
  163. package/src/types/studio.d.ts +0 -202
  164. package/src/types/studio.d.ts.map +0 -1
@@ -0,0 +1,184 @@
1
+ import React, { useState } from 'react';
2
+ import {
3
+ RadioGroup,
4
+ Select,
5
+ Input,
6
+ Icon,
7
+ ICONS,
8
+ Fadeable,
9
+ } from '@brightcove/studio-components';
10
+ import { useTranslation } from 'react-i18next';
11
+
12
+ import css from 'styles/CustomSettingsDropdown.sass';
13
+ import {
14
+ EmbedCodeAspectRatio,
15
+ EmbedCodeProperties,
16
+ EmbedCodeUnits,
17
+ } from 'js/types/EmbedCodeModalProps';
18
+ import {
19
+ calculateDimensions,
20
+ calculateHeight,
21
+ shouldUpdateWidth,
22
+ } from 'js/utils/dimensions';
23
+
24
+ export enum Sizing {
25
+ FIXED = 'Fixed',
26
+ RESPONSIVE = 'Responsive',
27
+ }
28
+
29
+ export const CustomSettingsDropdown = ({
30
+ setEmbedProperties,
31
+ embedProperties,
32
+ }: {
33
+ embedProperties: EmbedCodeProperties;
34
+ setEmbedProperties: (value: EmbedCodeProperties) => void;
35
+ }) => {
36
+ const { t } = useTranslation();
37
+ const [isHidden, setIsHidden] = useState<boolean>(true);
38
+ const [prevAspectRatio, setPrevAspectRatio] = useState(
39
+ embedProperties.aspectRatio,
40
+ );
41
+
42
+ const handlePropertiesUpdate = (
43
+ name: string,
44
+ value: string | boolean | number,
45
+ ) => {
46
+ if (name === 'aspectRatio') {
47
+ const calculateNewWidth = shouldUpdateWidth(
48
+ prevAspectRatio,
49
+ value as EmbedCodeAspectRatio,
50
+ );
51
+
52
+ setPrevAspectRatio(value as EmbedCodeAspectRatio);
53
+
54
+ const dimensions = calculateDimensions(
55
+ calculateNewWidth,
56
+ value as EmbedCodeAspectRatio,
57
+ embedProperties.width,
58
+ embedProperties.height,
59
+ );
60
+
61
+ setEmbedProperties({
62
+ ...embedProperties,
63
+ aspectRatio: value as EmbedCodeAspectRatio,
64
+ width: dimensions.width as number,
65
+ height: dimensions.height,
66
+ });
67
+
68
+ return;
69
+ } else if (
70
+ name === 'width' &&
71
+ embedProperties.aspectRatio !== EmbedCodeAspectRatio.CUSTOM
72
+ ) {
73
+ const height = calculateHeight(
74
+ value as number,
75
+ embedProperties.aspectRatio,
76
+ );
77
+
78
+ setEmbedProperties({
79
+ ...embedProperties,
80
+ width: value as number,
81
+ height,
82
+ });
83
+
84
+ return;
85
+ }
86
+
87
+ setEmbedProperties({
88
+ ...embedProperties,
89
+ [name]: value,
90
+ });
91
+ };
92
+
93
+ return (
94
+ <div className={css.featuresDropdown}>
95
+ <div className={css.topArea} onClick={() => setIsHidden(!isHidden)}>
96
+ <div className={css.title}>Custom settings</div>
97
+ <div className={css.right}>
98
+ <div className={css.description}>Sizing & ratio</div>
99
+ <Icon
100
+ name={isHidden ? ICONS.CARET_DOWN : ICONS.CARET_UP}
101
+ className={css.icon}
102
+ />
103
+ </div>
104
+ </div>
105
+ <Fadeable show={!isHidden} className={css.hiddenArea}>
106
+ <div className={css.hiddenAreaContainer}>
107
+ <RadioGroup
108
+ inline
109
+ name='sizing'
110
+ label={t('customSettings.sizing')}
111
+ value={
112
+ embedProperties.responsive ? Sizing.RESPONSIVE : Sizing.FIXED
113
+ }
114
+ options={[Sizing.FIXED, Sizing.RESPONSIVE]}
115
+ onChange={(value: string) =>
116
+ handlePropertiesUpdate('responsive', value === Sizing.RESPONSIVE)
117
+ }
118
+ testName='embed-sizing-radio-group'
119
+ />
120
+ <Select
121
+ label={t('customSettings.aspectRatio')}
122
+ value={embedProperties.aspectRatio}
123
+ options={[
124
+ EmbedCodeAspectRatio.SIXTEEN_NINE,
125
+ EmbedCodeAspectRatio.NINE_SIXTEEN,
126
+ EmbedCodeAspectRatio.ONE_ONE,
127
+ EmbedCodeAspectRatio.FOUR_THREE,
128
+ EmbedCodeAspectRatio.CUSTOM,
129
+ ]}
130
+ className={css.aspectRatioSelect}
131
+ onChange={(value: string) =>
132
+ handlePropertiesUpdate('aspectRatio', value)
133
+ }
134
+ testName='embed-aspect-ratio-select'
135
+ />
136
+ <div className={css.sizeEdit}>
137
+ <Input
138
+ label={t('customSettings.playerWidth')}
139
+ value={embedProperties.width}
140
+ onChange={(value: string) =>
141
+ handlePropertiesUpdate('width', value)
142
+ }
143
+ error={
144
+ embedProperties.width <= 0
145
+ ? 'Can only be positive integers'
146
+ : ''
147
+ }
148
+ testName='embed-width-input'
149
+ />
150
+ <Input
151
+ label={t('customSettings.playerHeight')}
152
+ value={embedProperties.height}
153
+ onChange={(value: string) =>
154
+ handlePropertiesUpdate('height', value)
155
+ }
156
+ disabled={
157
+ embedProperties.aspectRatio !== EmbedCodeAspectRatio.CUSTOM
158
+ }
159
+ error={
160
+ embedProperties.height <= 0
161
+ ? 'Can only be positive integers'
162
+ : ''
163
+ }
164
+ testName='embed-height-input'
165
+ />
166
+ <Select
167
+ label={t('customSettings.units')}
168
+ value={embedProperties.units}
169
+ options={[
170
+ EmbedCodeUnits.PX,
171
+ EmbedCodeUnits.EM,
172
+ EmbedCodeUnits.PERCENT,
173
+ ]}
174
+ onChange={(value: string) =>
175
+ handlePropertiesUpdate('units', value)
176
+ }
177
+ testName='embed-units-select'
178
+ />
179
+ </div>
180
+ </div>
181
+ </Fadeable>
182
+ </div>
183
+ );
184
+ };
@@ -0,0 +1,130 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+
4
+ import brightcoveEmbedCode from '@brightcove/player-embed-code';
5
+ import {
6
+ Flex,
7
+ FlexItem,
8
+ CopyField,
9
+ PlainTextArea,
10
+ } from '@brightcove/studio-components';
11
+
12
+ import { EmbedCodeProps } from 'js/types/EmbedCodeProps';
13
+ import { getUrlBase, getBrightcove, decodeHtmlEntities } from 'js/utils/util';
14
+
15
+ import css from 'styles/EmbedCode.sass';
16
+ import {
17
+ EmbedCodeAspectRatio,
18
+ EmbedCodeUnits,
19
+ } from 'js/types/EmbedCodeModalProps';
20
+
21
+ interface EmbedCodeConfig {
22
+ accountId: string;
23
+ responsive: boolean;
24
+ width: number;
25
+ height: number;
26
+ iframe: boolean;
27
+ videoId?: string;
28
+ interactivityProjectId?: string;
29
+ playerId: string;
30
+ urlBase: string;
31
+ playlistIsHorizontal: boolean;
32
+ units: EmbedCodeUnits;
33
+ aspectRatio?: EmbedCodeAspectRatio;
34
+ }
35
+
36
+ const DEFAULT_HEIGHT = 540;
37
+ const DEFAULT_WIDTH = 960;
38
+ const defaultConfig = {
39
+ responsive: false,
40
+ width: DEFAULT_WIDTH,
41
+ height: DEFAULT_HEIGHT,
42
+ urlBase: getUrlBase(),
43
+ playlistIsHorizontal: true,
44
+ units: EmbedCodeUnits.PX,
45
+ aspectRatio: EmbedCodeAspectRatio.FLUID,
46
+ };
47
+
48
+ const createEmbedConfigObject = ({
49
+ embedCodeProperties,
50
+ accountId,
51
+ iframe,
52
+ videoId,
53
+ playerId,
54
+ interactivityProjectId,
55
+ livePlaybackToken,
56
+ adConfigId,
57
+ }: EmbedCodeProps): EmbedCodeConfig => {
58
+ const config = {
59
+ accountId,
60
+ iframe,
61
+ videoId,
62
+ playerId,
63
+ livePlaybackToken,
64
+ adConfigId,
65
+ interactivityProjectId,
66
+ };
67
+ return Object.assign(
68
+ {},
69
+ { ...defaultConfig, ...config, ...embedCodeProperties },
70
+ );
71
+ };
72
+
73
+ export const EmbedCode = (config: EmbedCodeProps) => {
74
+ const { t } = useTranslation();
75
+ const [embedCode, setEmbedCode] = useState('');
76
+
77
+ useEffect(() => {
78
+ const embedConfigObj = createEmbedConfigObject(config);
79
+
80
+ if (embedConfigObj.aspectRatio) {
81
+ embedConfigObj.aspectRatio =
82
+ embedConfigObj.aspectRatio === EmbedCodeAspectRatio.CUSTOM
83
+ ? EmbedCodeAspectRatio.FLUID
84
+ : embedConfigObj.aspectRatio;
85
+ }
86
+
87
+ const embedCode = decodeHtmlEntities(brightcoveEmbedCode(embedConfigObj));
88
+ setEmbedCode(embedCode);
89
+ }, [config]);
90
+
91
+ const HelperTextNodeNew = (
92
+ <Flex>
93
+ <FlexItem flexGrow='1' className={css.helperText}>
94
+ {t('tabs.helperText')}
95
+ </FlexItem>
96
+ </Flex>
97
+ );
98
+
99
+ return (
100
+ <>
101
+ <CopyField
102
+ className={css.copyInput}
103
+ helperNode={HelperTextNodeNew}
104
+ Component={PlainTextArea}
105
+ componentProps={{
106
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
107
+ // @ts-ignore
108
+ disabled: true,
109
+ disableResizing: true,
110
+ className: css.textAreaDimensions,
111
+ onChange: () => void 0,
112
+ }}
113
+ onCopy={() => {
114
+ getBrightcove().Toaster.success({
115
+ title: t('toasterTitle'),
116
+ message: t('toasterMessage'),
117
+ });
118
+ }}
119
+ onError={() => {
120
+ getBrightcove().Toaster.error({
121
+ message: t('toasterErrorMessage'),
122
+ });
123
+ }}
124
+ value={embedCode}
125
+ hideButton={true}
126
+ testName='embed-copy-section'
127
+ />
128
+ </>
129
+ );
130
+ };
@@ -0,0 +1,274 @@
1
+ import React, { useState, useCallback, useEffect, useMemo } from 'react';
2
+
3
+ import { useTranslation } from 'react-i18next';
4
+ import useGetPlayers from 'js/hooks/useGetPlayers';
5
+ import { Modal, Flex, Button } from '@brightcove/studio-components';
6
+
7
+ import {
8
+ EmbedCodeModalTypes,
9
+ PlayerPayload,
10
+ NoInteractivityPlayerModalTypes,
11
+ EmbedCodeProperties,
12
+ } from 'js/types/EmbedCodeModalProps';
13
+ import { EmbedCodeModalProps } from 'js/EmbedCodeModal';
14
+ import { addPluginsDataToPlayers } from 'js/utils/addPluginsDataToPlayers';
15
+ import { PlayerSelectItem, PlayersObject } from 'js/types/players';
16
+ import { createPlayer } from 'js/services/players-api';
17
+ import { getStudioModule, getBrightcove, ROLES } from 'js/utils/util';
18
+ import { DEFAULT_INTERACTIVITY_PLAYER } from 'js/constants/player';
19
+
20
+ import css from 'styles/EmbedCodeModal.sass';
21
+ import { VideoPlayer } from './VideoPlayer';
22
+ import { PlayerPreview } from './PlayerPreview';
23
+ import { getAccountId } from '../utils/call';
24
+ import { NoPlayerModal } from './NoPlayerModal';
25
+
26
+ interface ModalContainerProps extends EmbedCodeModalProps {
27
+ handleSetComponentKey: () => void;
28
+ handleSetHasCreatedPlayer: (value: boolean) => void;
29
+ hasCreatedPlayer: boolean;
30
+ embedCodeProperties?: EmbedCodeProperties;
31
+ isOutstreamPlayer?: boolean;
32
+ onDVRClick?: (newValue: boolean) => void;
33
+ dvrTime?: number;
34
+ showLowLatency?: boolean;
35
+ onLowLatencyClick?: (newValue: boolean) => void;
36
+ }
37
+
38
+ export const ModalContainer = ({
39
+ handleSetComponentKey,
40
+ data,
41
+ embedCodeModalType = EmbedCodeModalTypes.VIDEO,
42
+ handleSetHasCreatedPlayer,
43
+ hasCreatedPlayer,
44
+ embedCodeProperties,
45
+ isOutstreamPlayer,
46
+ showDVR,
47
+ onDVRClick,
48
+ dvrTime,
49
+ showLowLatency,
50
+ onLowLatencyClick,
51
+ }: ModalContainerProps) => {
52
+ const { t } = useTranslation();
53
+ const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
54
+ const { isFetching, data: playersData } = useGetPlayers(
55
+ embedCodeModalType !== EmbedCodeModalTypes.PLAYER,
56
+ );
57
+ const [players, setPlayers] = useState<PlayersObject>({});
58
+ const [playerListForSelection, setPlayerListForSelection] = useState<
59
+ PlayerSelectItem[]
60
+ >([]);
61
+ const [hasPublishingRole, setHasPublishingRole] = useState(false);
62
+ const [buttonSpinner, setButtonSpinner] = useState(false);
63
+
64
+ useEffect(() => {
65
+ const { roles } = getStudioModule().getCurrentUserAccount();
66
+ if (roles.length && roles.includes(ROLES.PUBLISHING)) {
67
+ setHasPublishingRole(true);
68
+ }
69
+ }, []);
70
+
71
+ useEffect(() => {
72
+ if (embedCodeModalType === EmbedCodeModalTypes.PLAYER) {
73
+ return;
74
+ }
75
+ if (!isFetching && playersData && (playersData?.items.length ?? 0) > 0) {
76
+ const { playerListForSelection, players = {} } =
77
+ addPluginsDataToPlayers(playersData);
78
+ let filteredPlayerListForSelection = playerListForSelection;
79
+
80
+ if (embedCodeModalType === EmbedCodeModalTypes.PROJECT) {
81
+ filteredPlayerListForSelection = playerListForSelection.filter(
82
+ (player) => players[player.value].hasInteractivityPlugin,
83
+ );
84
+ }
85
+
86
+ setPlayers(players);
87
+ setPlayerListForSelection(filteredPlayerListForSelection);
88
+ }
89
+ }, [isFetching, playersData, embedCodeModalType]);
90
+
91
+ const hasInteractivityEnabledPlayer = useMemo(
92
+ () =>
93
+ Object.values(players).some((player) => player.hasInteractivityPlugin),
94
+ [players],
95
+ );
96
+
97
+ useEffect(() => {
98
+ if (data) {
99
+ setIsModalOpen(true);
100
+ }
101
+ }, [data]);
102
+
103
+ const closeModal = useCallback(() => {
104
+ setIsModalOpen(false);
105
+ handleSetHasCreatedPlayer(false);
106
+ }, [handleSetHasCreatedPlayer]);
107
+
108
+ const createInteractivityPlayer = () => {
109
+ const { name, registry_id, version, options } =
110
+ DEFAULT_INTERACTIVITY_PLAYER;
111
+
112
+ setButtonSpinner(true);
113
+
114
+ const payload = {
115
+ data: {
116
+ name,
117
+ configuration: {
118
+ plugins: [
119
+ {
120
+ registry_id,
121
+ version,
122
+ options,
123
+ },
124
+ ],
125
+ },
126
+ },
127
+ };
128
+
129
+ createPlayer(payload)
130
+ .then((res) => {
131
+ if (res && res.id) {
132
+ handleSetHasCreatedPlayer(true);
133
+ handleSetComponentKey();
134
+ }
135
+ })
136
+ .catch((reason) => {
137
+ console.error(reason);
138
+ getBrightcove().Toaster.error({
139
+ title: t('createPlayerFailedTitle'),
140
+ message: t('createPlayerFailedMessage'),
141
+ });
142
+ })
143
+ .finally(() => {
144
+ setButtonSpinner(false);
145
+ });
146
+ };
147
+
148
+ let content: JSX.Element | null = null;
149
+ let modalTitle = t('modalHeader');
150
+ let isLargeModal = true;
151
+ let buttons = [
152
+ <Button
153
+ key={'close-button'}
154
+ text={t('done')}
155
+ onClick={closeModal}
156
+ testName={'close-modal-btn'}
157
+ />,
158
+ ];
159
+
160
+ if (!isFetching) {
161
+ switch (embedCodeModalType) {
162
+ case EmbedCodeModalTypes.LIVE:
163
+ case EmbedCodeModalTypes.VIDEO:
164
+ content =
165
+ 'videoId' in data ? (
166
+ <VideoPlayer
167
+ video={data}
168
+ embedCodeModalType={embedCodeModalType}
169
+ players={players}
170
+ playerListForSelection={playerListForSelection}
171
+ showDVR={showDVR}
172
+ onDVRClick={onDVRClick}
173
+ dvrTime={dvrTime}
174
+ showLowLatency={showLowLatency}
175
+ onLowLatencyClick={onLowLatencyClick}
176
+ />
177
+ ) : null;
178
+ break;
179
+ case EmbedCodeModalTypes.PLAYER:
180
+ {
181
+ const accountId = data.accountId || getAccountId();
182
+ content =
183
+ 'id' in data && accountId ? (
184
+ <PlayerPreview
185
+ {...(data as PlayerPayload)}
186
+ accountId={accountId}
187
+ embedCodeProperties={embedCodeProperties}
188
+ isOutstreamPlayer={isOutstreamPlayer}
189
+ />
190
+ ) : null;
191
+ }
192
+ break;
193
+ case EmbedCodeModalTypes.PROJECT:
194
+ if (!hasInteractivityEnabledPlayer && !hasPublishingRole) {
195
+ modalTitle = t('activateModal');
196
+ isLargeModal = false;
197
+ buttons = [
198
+ <Button
199
+ key={'close-button'}
200
+ text={t('close')}
201
+ onClick={closeModal}
202
+ testName={'close-modal-done-btn'}
203
+ />,
204
+ ];
205
+ content = (
206
+ <NoPlayerModal
207
+ modalType={NoInteractivityPlayerModalTypes.NO_PERMISSION}
208
+ />
209
+ );
210
+ } else if (!hasInteractivityEnabledPlayer) {
211
+ modalTitle = t('noPlayerModalHeader');
212
+ isLargeModal = false;
213
+ buttons = [
214
+ <Button
215
+ key={'cancel-button'}
216
+ text={t('cancel')}
217
+ onClick={closeModal}
218
+ testName={'cancel-button'}
219
+ look='tertiary'
220
+ />,
221
+ <Button
222
+ key={'create-player-button'}
223
+ text={t('createPlayerNow')}
224
+ spinner={buttonSpinner}
225
+ onClick={createInteractivityPlayer}
226
+ testName={'create-player-button'}
227
+ />,
228
+ ];
229
+ content = (
230
+ <NoPlayerModal
231
+ modalType={NoInteractivityPlayerModalTypes.NO_PLAYER}
232
+ />
233
+ );
234
+ } else {
235
+ content =
236
+ 'videoId' in data && 'interactivityProjectId' in data ? (
237
+ <VideoPlayer
238
+ video={data}
239
+ embedCodeModalType={embedCodeModalType}
240
+ playerListForSelection={playerListForSelection}
241
+ players={players}
242
+ hasCreatedPlayer={hasCreatedPlayer}
243
+ />
244
+ ) : null;
245
+ }
246
+ break;
247
+ default: {
248
+ console.warn(`EmbedModal with ${embedCodeModalType} is not supported`);
249
+ content = null;
250
+ break;
251
+ }
252
+ }
253
+ }
254
+
255
+ if (!content) {
256
+ console.warn(
257
+ `EmbedModal called with wrong props for type: ${embedCodeModalType}`,
258
+ );
259
+ return null;
260
+ }
261
+
262
+ return (
263
+ <Modal
264
+ testName='embed-modal'
265
+ title={modalTitle}
266
+ isOpen={isModalOpen}
267
+ large={isLargeModal}
268
+ onClose={closeModal}
269
+ buttons={buttons}
270
+ >
271
+ <Flex className={css.videoDetailsContainer}>{content}</Flex>
272
+ </Modal>
273
+ );
274
+ };
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { Flex } from '@brightcove/studio-components';
4
+ import { getPlayerManagerModuleUrl } from 'js/utils/util';
5
+ import { NoInteractivityPlayerModalTypes } from 'js/types/EmbedCodeModalProps';
6
+
7
+ interface NoPlayerProps {
8
+ modalType: NoInteractivityPlayerModalTypes;
9
+ }
10
+
11
+ export const NoPlayerModal = ({ modalType }: NoPlayerProps) => {
12
+ const { t } = useTranslation();
13
+
14
+ const createPlayersModuleLink = (text: string) => {
15
+ return <a href={getPlayerManagerModuleUrl()}>{text}</a>;
16
+ };
17
+
18
+ return (
19
+ <Flex flexDirection='column'>
20
+ <p>
21
+ {modalType === NoInteractivityPlayerModalTypes.NO_PLAYER ? (
22
+ t('noPlayerModalContent1')
23
+ ) : (
24
+ <>
25
+ {t('activateModalContent1')}
26
+ {createPlayersModuleLink(t('activateModalContent2'))}.
27
+ </>
28
+ )}
29
+ <br />
30
+ {modalType === NoInteractivityPlayerModalTypes.NO_PLAYER
31
+ ? t('noPlayerModalContent2')
32
+ : null}
33
+ </p>
34
+
35
+ <>
36
+ {modalType === NoInteractivityPlayerModalTypes.NO_PLAYER ? (
37
+ <p>{createPlayersModuleLink(t('toPlayersModuleLink'))}</p>
38
+ ) : null}
39
+ </>
40
+ </Flex>
41
+ );
42
+ };