@brightcove/components-embed-code-modal 1.0.7 → 1.0.8

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 (181) hide show
  1. package/.eslintignore +7 -0
  2. package/.eslintrc +23 -0
  3. package/.github/workflows/ci.yml +23 -0
  4. package/.husky/pre-commit +4 -0
  5. package/.nvmrc +1 -0
  6. package/.prettierignore +7 -0
  7. package/.prettierrc +5 -0
  8. package/.release-it.json +40 -0
  9. package/.whitesource +3 -0
  10. package/CHANGELOG.md +387 -0
  11. package/CODEOWNERS +37 -0
  12. package/PULL_REQUEST_TEMPLATE.md +17 -0
  13. package/__tests__/EmbedCode.test.tsx +51 -0
  14. package/__tests__/InteractivityProject.test.tsx +50 -0
  15. package/__tests__/PlayerPreview.test.tsx +23 -0
  16. package/__tests__/TabsHeader.test.tsx +19 -0
  17. package/__tests__/TabsSection.test.tsx +46 -0
  18. package/__tests__/TitleWithTooltip.test.tsx +45 -0
  19. package/__tests__/VideoPlayer.test.tsx +38 -0
  20. package/__tests__/embedCodeGenerator.test.ts +111 -0
  21. package/__tests__/util.test.ts +89 -0
  22. package/babel.config.json +17 -0
  23. package/build/README.md +109 -0
  24. package/build/package.json +100 -0
  25. package/etc/wildcard.brightcove.com.key +27 -0
  26. package/etc/wildcard.brightcove.com.pem +96 -0
  27. package/jest/jest.setup.js +62 -0
  28. package/jest/jest.stub.js +4 -0
  29. package/jest.config.js +24 -0
  30. package/package.json +15 -5
  31. package/scripts/create-dist-pkg-prod.js +18 -0
  32. package/src/index.ts +17 -0
  33. package/src/js/EmbedCodeModal.tsx +105 -0
  34. package/src/js/components/CustomSettingsDropdown.tsx +184 -0
  35. package/src/js/components/EmbedCopySection.tsx +130 -0
  36. package/src/js/components/ModalContainer.tsx +367 -0
  37. package/src/js/components/NoPlayerModal.tsx +42 -0
  38. package/src/js/components/PlayerPreview.tsx +116 -0
  39. package/src/js/components/TabsHeader.tsx +31 -0
  40. package/src/js/components/TabsSection.tsx +74 -0
  41. package/src/js/components/TitleWithTooltip.tsx +39 -0
  42. package/src/js/components/VideoLinks.tsx +83 -0
  43. package/src/js/components/VideoPlayer.tsx +324 -0
  44. package/src/js/components/index.ts +9 -0
  45. package/src/js/components/prism/PrismLiveModal.tsx +162 -0
  46. package/src/js/components/prism/PrismPlayerControls.tsx +343 -0
  47. package/src/js/components/prism/PrismPlayerPreview.tsx +98 -0
  48. package/src/js/components/prism/index.ts +3 -0
  49. package/src/js/constants/baseUrls.ts +4 -0
  50. package/src/js/constants/embed.ts +12 -0
  51. package/src/js/constants/player.ts +32 -0
  52. package/src/js/constants/pluginsRegistry.ts +30 -0
  53. package/src/js/hooks/useGetPlayers.ts +12 -0
  54. package/src/js/i18n.ts +43 -0
  55. package/src/js/services/players-api.ts +26 -0
  56. package/src/js/services/url-shortener.ts +10 -0
  57. package/src/js/types/EmbedCodeModalProps.ts +72 -0
  58. package/src/js/types/EmbedCodeProps.ts +13 -0
  59. package/src/js/types/brightcoveEmbedCode.d.ts +18 -0
  60. package/src/js/types/players.ts +64 -0
  61. package/src/js/types/react-i18next.d.ts +8 -0
  62. package/src/js/types/studioModule.ts +7 -0
  63. package/src/js/utils/addPluginsDataToPlayers.ts +59 -0
  64. package/src/js/utils/call.ts +31 -0
  65. package/src/js/utils/dimensions.ts +53 -0
  66. package/src/js/utils/embedCodeGenerator.ts +120 -0
  67. package/src/js/utils/featureSwitches.ts +16 -0
  68. package/src/js/utils/util.ts +153 -0
  69. package/src/module.tsx +280 -0
  70. package/src/styles/CustomSettingsDropdown.sass +42 -0
  71. package/src/styles/EmbedCode.sass +30 -0
  72. package/src/styles/EmbedCodeModal.sass +76 -0
  73. package/src/styles/base.sass +1 -0
  74. package/src/styles/prism/PrismLiveModal.sass +155 -0
  75. package/src/styles/theme.sass +11 -0
  76. package/src/styles/typings.td.ts +9 -0
  77. package/src/translations/en.json +61 -0
  78. package/src/translations/es.json +61 -0
  79. package/src/translations/fr.json +61 -0
  80. package/src/translations/ja.json +61 -0
  81. package/src/translations/ko.json +61 -0
  82. package/src/translations/zh.json +61 -0
  83. package/src/types/studio.ts +197 -0
  84. package/tsconfig.json +34 -0
  85. package/webpack.config.js +117 -0
  86. /package/{__tests__ → build/__tests__}/EmbedCode.test.d.ts +0 -0
  87. /package/{__tests__ → build/__tests__}/EmbedCode.test.d.ts.map +0 -0
  88. /package/{__tests__ → build/__tests__}/InteractivityProject.test.d.ts +0 -0
  89. /package/{__tests__ → build/__tests__}/InteractivityProject.test.d.ts.map +0 -0
  90. /package/{__tests__ → build/__tests__}/PlayerPreview.test.d.ts +0 -0
  91. /package/{__tests__ → build/__tests__}/PlayerPreview.test.d.ts.map +0 -0
  92. /package/{__tests__ → build/__tests__}/TabsHeader.test.d.ts +0 -0
  93. /package/{__tests__ → build/__tests__}/TabsHeader.test.d.ts.map +0 -0
  94. /package/{__tests__ → build/__tests__}/TabsSection.test.d.ts +0 -0
  95. /package/{__tests__ → build/__tests__}/TabsSection.test.d.ts.map +0 -0
  96. /package/{__tests__ → build/__tests__}/TitleWithTooltip.test.d.ts +0 -0
  97. /package/{__tests__ → build/__tests__}/TitleWithTooltip.test.d.ts.map +0 -0
  98. /package/{__tests__ → build/__tests__}/VideoPlayer.test.d.ts +0 -0
  99. /package/{__tests__ → build/__tests__}/VideoPlayer.test.d.ts.map +0 -0
  100. /package/{__tests__ → build/__tests__}/embedCodeGenerator.test.d.ts +0 -0
  101. /package/{__tests__ → build/__tests__}/embedCodeGenerator.test.d.ts.map +0 -0
  102. /package/{__tests__ → build/__tests__}/util.test.d.ts +0 -0
  103. /package/{__tests__ → build/__tests__}/util.test.d.ts.map +0 -0
  104. /package/{index.js → build/index.js} +0 -0
  105. /package/{index.js.map → build/index.js.map} +0 -0
  106. /package/{src → build/src}/index.d.ts +0 -0
  107. /package/{src → build/src}/index.d.ts.map +0 -0
  108. /package/{src → build/src}/js/EmbedCodeModal.d.ts +0 -0
  109. /package/{src → build/src}/js/EmbedCodeModal.d.ts.map +0 -0
  110. /package/{src → build/src}/js/components/CustomSettingsDropdown.d.ts +0 -0
  111. /package/{src → build/src}/js/components/CustomSettingsDropdown.d.ts.map +0 -0
  112. /package/{src → build/src}/js/components/EmbedCopySection.d.ts +0 -0
  113. /package/{src → build/src}/js/components/EmbedCopySection.d.ts.map +0 -0
  114. /package/{src → build/src}/js/components/ModalContainer.d.ts +0 -0
  115. /package/{src → build/src}/js/components/ModalContainer.d.ts.map +0 -0
  116. /package/{src → build/src}/js/components/NoPlayerModal.d.ts +0 -0
  117. /package/{src → build/src}/js/components/NoPlayerModal.d.ts.map +0 -0
  118. /package/{src → build/src}/js/components/PlayerPreview.d.ts +0 -0
  119. /package/{src → build/src}/js/components/PlayerPreview.d.ts.map +0 -0
  120. /package/{src → build/src}/js/components/TabsHeader.d.ts +0 -0
  121. /package/{src → build/src}/js/components/TabsHeader.d.ts.map +0 -0
  122. /package/{src → build/src}/js/components/TabsSection.d.ts +0 -0
  123. /package/{src → build/src}/js/components/TabsSection.d.ts.map +0 -0
  124. /package/{src → build/src}/js/components/TitleWithTooltip.d.ts +0 -0
  125. /package/{src → build/src}/js/components/TitleWithTooltip.d.ts.map +0 -0
  126. /package/{src → build/src}/js/components/VideoLinks.d.ts +0 -0
  127. /package/{src → build/src}/js/components/VideoLinks.d.ts.map +0 -0
  128. /package/{src → build/src}/js/components/VideoPlayer.d.ts +0 -0
  129. /package/{src → build/src}/js/components/VideoPlayer.d.ts.map +0 -0
  130. /package/{src → build/src}/js/components/index.d.ts +0 -0
  131. /package/{src → build/src}/js/components/index.d.ts.map +0 -0
  132. /package/{src → build/src}/js/components/prism/PrismLiveModal.d.ts +0 -0
  133. /package/{src → build/src}/js/components/prism/PrismLiveModal.d.ts.map +0 -0
  134. /package/{src → build/src}/js/components/prism/PrismPlayerControls.d.ts +0 -0
  135. /package/{src → build/src}/js/components/prism/PrismPlayerControls.d.ts.map +0 -0
  136. /package/{src → build/src}/js/components/prism/PrismPlayerPreview.d.ts +0 -0
  137. /package/{src → build/src}/js/components/prism/PrismPlayerPreview.d.ts.map +0 -0
  138. /package/{src → build/src}/js/components/prism/index.d.ts +0 -0
  139. /package/{src → build/src}/js/components/prism/index.d.ts.map +0 -0
  140. /package/{src → build/src}/js/constants/baseUrls.d.ts +0 -0
  141. /package/{src → build/src}/js/constants/baseUrls.d.ts.map +0 -0
  142. /package/{src → build/src}/js/constants/embed.d.ts +0 -0
  143. /package/{src → build/src}/js/constants/embed.d.ts.map +0 -0
  144. /package/{src → build/src}/js/constants/player.d.ts +0 -0
  145. /package/{src → build/src}/js/constants/player.d.ts.map +0 -0
  146. /package/{src → build/src}/js/constants/pluginsRegistry.d.ts +0 -0
  147. /package/{src → build/src}/js/constants/pluginsRegistry.d.ts.map +0 -0
  148. /package/{src → build/src}/js/hooks/useGetPlayers.d.ts +0 -0
  149. /package/{src → build/src}/js/hooks/useGetPlayers.d.ts.map +0 -0
  150. /package/{src → build/src}/js/i18n.d.ts +0 -0
  151. /package/{src → build/src}/js/i18n.d.ts.map +0 -0
  152. /package/{src → build/src}/js/services/players-api.d.ts +0 -0
  153. /package/{src → build/src}/js/services/players-api.d.ts.map +0 -0
  154. /package/{src → build/src}/js/services/url-shortener.d.ts +0 -0
  155. /package/{src → build/src}/js/services/url-shortener.d.ts.map +0 -0
  156. /package/{src → build/src}/js/types/EmbedCodeModalProps.d.ts +0 -0
  157. /package/{src → build/src}/js/types/EmbedCodeModalProps.d.ts.map +0 -0
  158. /package/{src → build/src}/js/types/EmbedCodeProps.d.ts +0 -0
  159. /package/{src → build/src}/js/types/EmbedCodeProps.d.ts.map +0 -0
  160. /package/{src → build/src}/js/types/players.d.ts +0 -0
  161. /package/{src → build/src}/js/types/players.d.ts.map +0 -0
  162. /package/{src → build/src}/js/types/studioModule.d.ts +0 -0
  163. /package/{src → build/src}/js/types/studioModule.d.ts.map +0 -0
  164. /package/{src → build/src}/js/utils/addPluginsDataToPlayers.d.ts +0 -0
  165. /package/{src → build/src}/js/utils/addPluginsDataToPlayers.d.ts.map +0 -0
  166. /package/{src → build/src}/js/utils/call.d.ts +0 -0
  167. /package/{src → build/src}/js/utils/call.d.ts.map +0 -0
  168. /package/{src → build/src}/js/utils/dimensions.d.ts +0 -0
  169. /package/{src → build/src}/js/utils/dimensions.d.ts.map +0 -0
  170. /package/{src → build/src}/js/utils/embedCodeGenerator.d.ts +0 -0
  171. /package/{src → build/src}/js/utils/embedCodeGenerator.d.ts.map +0 -0
  172. /package/{src → build/src}/js/utils/featureSwitches.d.ts +0 -0
  173. /package/{src → build/src}/js/utils/featureSwitches.d.ts.map +0 -0
  174. /package/{src → build/src}/js/utils/util.d.ts +0 -0
  175. /package/{src → build/src}/js/utils/util.d.ts.map +0 -0
  176. /package/{src → build/src}/module.d.ts +0 -0
  177. /package/{src → build/src}/module.d.ts.map +0 -0
  178. /package/{src → build/src}/styles/typings.td.d.ts +0 -0
  179. /package/{src → build/src}/styles/typings.td.d.ts.map +0 -0
  180. /package/{src → build/src}/types/studio.d.ts +0 -0
  181. /package/{src → build/src}/types/studio.d.ts.map +0 -0
@@ -0,0 +1,367 @@
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
+ LivePayload,
13
+ } from 'js/types/EmbedCodeModalProps';
14
+ import { EmbedCodeModalProps } from 'js/EmbedCodeModal';
15
+ import { addPluginsDataToPlayers } from 'js/utils/addPluginsDataToPlayers';
16
+ import { PlayerSelectItem, PlayersObject } from 'js/types/players';
17
+ import { createPlayer } from 'js/services/players-api';
18
+ import { getStudioModule, getBrightcove, ROLES } from 'js/utils/util';
19
+ import { DEFAULT_INTERACTIVITY_PLAYER } from 'js/constants/player';
20
+
21
+ import css from 'styles/EmbedCodeModal.sass';
22
+ import { VideoPlayer } from './VideoPlayer';
23
+ import { PlayerPreview } from './PlayerPreview';
24
+ import { getAccountId } from '../utils/call';
25
+ import { NoPlayerModal } from './NoPlayerModal';
26
+ import { PrismLiveModal } from './prism';
27
+
28
+ interface ModalContainerProps extends EmbedCodeModalProps {
29
+ handleSetComponentKey: () => void;
30
+ handleSetHasCreatedPlayer: (value: boolean) => void;
31
+ hasCreatedPlayer: boolean;
32
+ embedCodeProperties?: EmbedCodeProperties;
33
+ isOutstreamPlayer?: boolean;
34
+ onDVRClick?: (newValue: boolean) => void;
35
+ dvrTime?: number;
36
+ showLowLatency?: boolean;
37
+ onLowLatencyClick?: (newValue: boolean) => void;
38
+ ssai?: boolean;
39
+ adConfigOptions?: Array<{ label: string; value: string }>;
40
+ onAdConfigChange?: (value: string) => void;
41
+ theme?: 'blueSteel' | 'prism';
42
+ }
43
+
44
+ export const ModalContainer = ({
45
+ handleSetComponentKey,
46
+ data,
47
+ embedCodeModalType = EmbedCodeModalTypes.VIDEO,
48
+ handleSetHasCreatedPlayer,
49
+ hasCreatedPlayer,
50
+ embedCodeProperties,
51
+ isOutstreamPlayer,
52
+ showDVR,
53
+ onDVRClick,
54
+ dvrTime,
55
+ showLowLatency,
56
+ onLowLatencyClick,
57
+ ssai,
58
+ adConfigOptions = [],
59
+ onAdConfigChange,
60
+ theme,
61
+ }: ModalContainerProps) => {
62
+ const { t } = useTranslation();
63
+ const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
64
+ const { isFetching, data: playersData } = useGetPlayers(
65
+ embedCodeModalType !== EmbedCodeModalTypes.PLAYER,
66
+ );
67
+ const [players, setPlayers] = useState<PlayersObject>({});
68
+ const [playerListForSelection, setPlayerListForSelection] = useState<
69
+ PlayerSelectItem[]
70
+ >([]);
71
+ const [hasPublishingRole, setHasPublishingRole] = useState(false);
72
+ const [buttonSpinner, setButtonSpinner] = useState(false);
73
+ const [adConfigValue, setAdConfigValue] = useState('');
74
+ const [hasInitialized, setHasInitialized] = useState(false);
75
+
76
+ const handleAdConfigChange = (value: string) => {
77
+ setAdConfigValue(value);
78
+ if (onAdConfigChange) {
79
+ onAdConfigChange(value);
80
+ }
81
+ };
82
+
83
+ useEffect(() => {
84
+ if (adConfigOptions.length > 0) {
85
+ const initialValue = adConfigOptions[0].value;
86
+
87
+ // Only initialize if we haven't initialized yet, or if the current value is not in the new options
88
+ if (
89
+ !hasInitialized ||
90
+ !adConfigOptions.some((option) => option.value === adConfigValue)
91
+ ) {
92
+ setAdConfigValue(initialValue);
93
+ setHasInitialized(true);
94
+
95
+ if (onAdConfigChange) {
96
+ onAdConfigChange(initialValue);
97
+ }
98
+ }
99
+ }
100
+ }, [adConfigOptions, hasInitialized, onAdConfigChange, adConfigValue]);
101
+
102
+ useEffect(() => {
103
+ const { roles } = getStudioModule().getCurrentUserAccount();
104
+ if (roles.length && roles.includes(ROLES.PUBLISHING)) {
105
+ setHasPublishingRole(true);
106
+ }
107
+ }, []);
108
+
109
+ useEffect(() => {
110
+ if (embedCodeModalType === EmbedCodeModalTypes.PLAYER) {
111
+ return;
112
+ }
113
+ if (!isFetching && playersData && (playersData?.items.length ?? 0) > 0) {
114
+ const { playerListForSelection, players = {} } =
115
+ addPluginsDataToPlayers(playersData);
116
+ let filteredPlayerListForSelection = playerListForSelection;
117
+
118
+ if (embedCodeModalType === EmbedCodeModalTypes.PROJECT) {
119
+ filteredPlayerListForSelection = playerListForSelection.filter(
120
+ (player) => players[player.value].hasInteractivityPlugin,
121
+ );
122
+ }
123
+
124
+ setPlayers(players);
125
+ setPlayerListForSelection(filteredPlayerListForSelection);
126
+ }
127
+ }, [isFetching, playersData, embedCodeModalType]);
128
+
129
+ const hasInteractivityEnabledPlayer = useMemo(
130
+ () =>
131
+ Object.values(players).some((player) => player.hasInteractivityPlugin),
132
+ [players],
133
+ );
134
+
135
+ useEffect(() => {
136
+ if (data) {
137
+ setIsModalOpen(true);
138
+ }
139
+ }, [data]);
140
+
141
+ const closeModal = useCallback(() => {
142
+ setIsModalOpen(false);
143
+ handleSetHasCreatedPlayer(false);
144
+ }, [handleSetHasCreatedPlayer]);
145
+
146
+ const createInteractivityPlayer = () => {
147
+ const { name, registry_id, version, options } =
148
+ DEFAULT_INTERACTIVITY_PLAYER;
149
+
150
+ setButtonSpinner(true);
151
+
152
+ const payload = {
153
+ data: {
154
+ name,
155
+ configuration: {
156
+ plugins: [
157
+ {
158
+ registry_id,
159
+ version,
160
+ options,
161
+ },
162
+ ],
163
+ },
164
+ },
165
+ };
166
+
167
+ createPlayer(payload)
168
+ .then((res) => {
169
+ if (res && res.id) {
170
+ handleSetHasCreatedPlayer(true);
171
+ handleSetComponentKey();
172
+ }
173
+ })
174
+ .catch((reason) => {
175
+ console.error(reason);
176
+ getBrightcove().Toaster.error({
177
+ title: t('createPlayerFailedTitle'),
178
+ message: t('createPlayerFailedMessage'),
179
+ });
180
+ })
181
+ .finally(() => {
182
+ setButtonSpinner(false);
183
+ });
184
+ };
185
+
186
+ let content: JSX.Element | null = null;
187
+ let modalTitle = t('modalHeader');
188
+ let isLargeModal = true;
189
+ let buttons = [
190
+ <Button
191
+ key={'close-button'}
192
+ text={t('done')}
193
+ onClick={closeModal}
194
+ testName={'close-modal-btn'}
195
+ />,
196
+ ];
197
+
198
+ if (
199
+ embedCodeModalType === EmbedCodeModalTypes.LIVE &&
200
+ theme === 'prism' &&
201
+ 'videoId' in data
202
+ ) {
203
+ return (
204
+ <PrismLiveModal
205
+ isOpen={isModalOpen}
206
+ onClose={closeModal}
207
+ video={data as LivePayload}
208
+ players={players}
209
+ playerListForSelection={playerListForSelection}
210
+ showDVR={showDVR}
211
+ onDVRClick={onDVRClick}
212
+ dvrTime={dvrTime}
213
+ showLowLatency={showLowLatency}
214
+ onLowLatencyClick={onLowLatencyClick}
215
+ adConfigValue={adConfigValue}
216
+ onAdConfigChange={handleAdConfigChange}
217
+ ssai={ssai}
218
+ adConfigOptions={adConfigOptions}
219
+ />
220
+ );
221
+ }
222
+
223
+ switch (embedCodeModalType) {
224
+ case EmbedCodeModalTypes.LIVE:
225
+ content =
226
+ 'videoId' in data ? (
227
+ <VideoPlayer
228
+ video={data}
229
+ embedCodeModalType={embedCodeModalType}
230
+ players={players}
231
+ playerListForSelection={playerListForSelection}
232
+ showDVR={showDVR}
233
+ onDVRClick={onDVRClick}
234
+ dvrTime={dvrTime}
235
+ showLowLatency={showLowLatency}
236
+ onLowLatencyClick={onLowLatencyClick}
237
+ adConfigValue={adConfigValue}
238
+ onAdConfigChange={handleAdConfigChange}
239
+ ssai={ssai}
240
+ adConfigOptions={adConfigOptions}
241
+ />
242
+ ) : null;
243
+ break;
244
+ case EmbedCodeModalTypes.VIDEO:
245
+ content =
246
+ 'videoId' in data ? (
247
+ <VideoPlayer
248
+ video={data}
249
+ embedCodeModalType={embedCodeModalType}
250
+ players={players}
251
+ playerListForSelection={playerListForSelection}
252
+ showDVR={showDVR}
253
+ onDVRClick={onDVRClick}
254
+ dvrTime={dvrTime}
255
+ showLowLatency={showLowLatency}
256
+ onLowLatencyClick={onLowLatencyClick}
257
+ />
258
+ ) : null;
259
+ break;
260
+ case EmbedCodeModalTypes.PLAYER:
261
+ {
262
+ const accountId = data.accountId || getAccountId();
263
+ content =
264
+ 'id' in data && accountId ? (
265
+ <PlayerPreview
266
+ {...(data as PlayerPayload)}
267
+ accountId={accountId}
268
+ embedCodeProperties={embedCodeProperties}
269
+ isOutstreamPlayer={isOutstreamPlayer}
270
+ />
271
+ ) : null;
272
+ }
273
+ break;
274
+ case EmbedCodeModalTypes.PROJECT:
275
+ if (!isFetching) {
276
+ if (!hasInteractivityEnabledPlayer && !hasPublishingRole) {
277
+ modalTitle = t('activateModal');
278
+ isLargeModal = false;
279
+ buttons = [
280
+ <Button
281
+ key={'close-button'}
282
+ text={t('close')}
283
+ onClick={closeModal}
284
+ testName={'close-modal-done-btn'}
285
+ />,
286
+ ];
287
+ content = (
288
+ <NoPlayerModal
289
+ modalType={NoInteractivityPlayerModalTypes.NO_PERMISSION}
290
+ />
291
+ );
292
+ } else if (!hasInteractivityEnabledPlayer) {
293
+ modalTitle = t('noPlayerModalHeader');
294
+ isLargeModal = false;
295
+ buttons = [
296
+ <Button
297
+ key={'cancel-button'}
298
+ text={t('cancel')}
299
+ onClick={closeModal}
300
+ testName={'cancel-button'}
301
+ look='tertiary'
302
+ />,
303
+ <Button
304
+ key={'create-player-button'}
305
+ text={t('createPlayerNow')}
306
+ spinner={buttonSpinner}
307
+ onClick={createInteractivityPlayer}
308
+ testName={'create-player-button'}
309
+ />,
310
+ ];
311
+ content = (
312
+ <NoPlayerModal
313
+ modalType={NoInteractivityPlayerModalTypes.NO_PLAYER}
314
+ />
315
+ );
316
+ } else {
317
+ content =
318
+ 'videoId' in data && 'interactivityProjectId' in data ? (
319
+ <VideoPlayer
320
+ video={data}
321
+ embedCodeModalType={embedCodeModalType}
322
+ playerListForSelection={playerListForSelection}
323
+ players={players}
324
+ hasCreatedPlayer={hasCreatedPlayer}
325
+ />
326
+ ) : null;
327
+ }
328
+ } else {
329
+ content =
330
+ 'videoId' in data && 'interactivityProjectId' in data ? (
331
+ <VideoPlayer
332
+ video={data}
333
+ embedCodeModalType={embedCodeModalType}
334
+ playerListForSelection={playerListForSelection}
335
+ players={players}
336
+ hasCreatedPlayer={hasCreatedPlayer}
337
+ />
338
+ ) : null;
339
+ }
340
+ break;
341
+ default: {
342
+ console.warn(`EmbedModal with ${embedCodeModalType} is not supported`);
343
+ content = null;
344
+ break;
345
+ }
346
+ }
347
+
348
+ if (!content) {
349
+ console.warn(
350
+ `EmbedModal called with wrong props for type: ${embedCodeModalType}`,
351
+ );
352
+ return null;
353
+ }
354
+
355
+ return (
356
+ <Modal
357
+ testName='embed-modal'
358
+ title={modalTitle}
359
+ isOpen={isModalOpen}
360
+ large={isLargeModal}
361
+ onClose={closeModal}
362
+ buttons={buttons}
363
+ >
364
+ <Flex className={css.videoDetailsContainer}>{content}</Flex>
365
+ </Modal>
366
+ );
367
+ };
@@ -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
+ };
@@ -0,0 +1,116 @@
1
+ import React, { useMemo } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import cx from 'classnames';
4
+ import { Hideable } from '@brightcove/studio-components';
5
+ import ReactPlayerLoader from '@brightcove/react-player-loader';
6
+
7
+ import {
8
+ getPreviewPlayerUrl,
9
+ getUrlBase,
10
+ getVideoPreviewLink,
11
+ } from 'js/utils/util';
12
+ import {
13
+ bcHostedPlugins,
14
+ CAMPAIGN_PLUGIN_NAME,
15
+ VIEWER_ID_KEY_NAME,
16
+ BC_MAP_TRACKING_NAME,
17
+ } from 'js/constants/pluginsRegistry';
18
+ import { PlayerPayload } from '../types/EmbedCodeModalProps';
19
+
20
+ import { TabsSection, VideoLinks } from 'js/components';
21
+
22
+ import css from 'styles/EmbedCodeModal.sass';
23
+
24
+ interface Plugin {
25
+ name: string;
26
+ options?: Record<string, unknown>;
27
+ }
28
+
29
+ export const PlayerPreview = ({
30
+ id,
31
+ configuration,
32
+ accountId = '',
33
+ embedCodeProperties,
34
+ isOutstreamPlayer,
35
+ }: PlayerPayload) => {
36
+ const { t } = useTranslation();
37
+ const previewPlayerUrl = getPreviewPlayerUrl(accountId, id);
38
+ const playerUrl = getVideoPreviewLink({ accountId, playerId: id });
39
+
40
+ const {
41
+ hasCustomPlugins,
42
+ isCampaignPlayer,
43
+ isViewerIdSource,
44
+ isBcMapTracking,
45
+ } = useMemo((): {
46
+ isCampaignPlayer: boolean;
47
+ hasCustomPlugins: boolean;
48
+ isViewerIdSource: boolean;
49
+ isBcMapTracking: boolean;
50
+ } => {
51
+ let hasBCHostedPlugin = false;
52
+ let isCampaignPlayer = false;
53
+ let isViewerIdSource = false;
54
+ let isBcMapTracking = false;
55
+ let plugins: Plugin[] = [];
56
+
57
+ // TODO: refactor with addPluginsDataToPlayers function to reuse code
58
+ if (configuration) {
59
+ plugins = (configuration.plugins as Plugin[]) ?? plugins;
60
+ for (const plugin of plugins) {
61
+ const { name } = plugin;
62
+ hasBCHostedPlugin = bcHostedPlugins.includes(name);
63
+ isCampaignPlayer =
64
+ isCampaignPlayer || plugin.name === CAMPAIGN_PLUGIN_NAME;
65
+ isBcMapTracking =
66
+ isBcMapTracking || plugin.name === BC_MAP_TRACKING_NAME;
67
+ }
68
+ isViewerIdSource =
69
+ (configuration[VIEWER_ID_KEY_NAME] as boolean) || false;
70
+ }
71
+
72
+ return {
73
+ hasCustomPlugins: !hasBCHostedPlugin && plugins.length > 0,
74
+ isCampaignPlayer,
75
+ isViewerIdSource,
76
+ isBcMapTracking,
77
+ };
78
+ }, [configuration]);
79
+
80
+ return (
81
+ <>
82
+ <div className={css.leftContent}>
83
+ <ReactPlayerLoader
84
+ accountId={accountId}
85
+ baseUrl={getUrlBase()}
86
+ attrs={{ className: css.playerLoader }}
87
+ embedType='iframe'
88
+ refNode='player-container'
89
+ playerUrl={previewPlayerUrl}
90
+ />
91
+ <Hideable show={hasCustomPlugins}>
92
+ <span className={css.customPluginNotice}>
93
+ {t('customPluginNotice')}
94
+ </span>
95
+ </Hideable>
96
+ {isOutstreamPlayer ? null : (
97
+ <VideoLinks previewUrl={previewPlayerUrl} playerUrl={playerUrl} />
98
+ )}
99
+ </div>
100
+ <div
101
+ className={cx(css.rightContent, {
102
+ [css.rightContentFullWidth]: !!isOutstreamPlayer,
103
+ })}
104
+ >
105
+ <TabsSection
106
+ isCampaign={isCampaignPlayer}
107
+ isViewerIdSource={isViewerIdSource}
108
+ isBcMapTracking={isBcMapTracking}
109
+ playerId={id}
110
+ accountId={accountId}
111
+ embedCodeProperties={embedCodeProperties}
112
+ />
113
+ </div>
114
+ </>
115
+ );
116
+ };
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import { Flex, Tabs, TabList, Tab } from '@brightcove/studio-components';
3
+ import { TabsSelectedId } from 'js/types/players';
4
+
5
+ interface TabsHeaderProps {
6
+ currentTab: TabsSelectedId;
7
+ handleTabChange: () => void;
8
+ iframeLabel: string;
9
+ jsLabel: string;
10
+ }
11
+ export const TabsHeader = ({
12
+ currentTab,
13
+ handleTabChange,
14
+ iframeLabel,
15
+ jsLabel,
16
+ }: TabsHeaderProps) => {
17
+ return (
18
+ <Flex justifyContent='space-between' alignItems='baseline'>
19
+ <Tabs selectedId={currentTab} onSelect={handleTabChange}>
20
+ <TabList>
21
+ <Tab tabId='iframe' testName={'iframe-tab'}>
22
+ {iframeLabel}
23
+ </Tab>
24
+ <Tab tabId='javascript' testName={'js-tab'}>
25
+ {jsLabel}
26
+ </Tab>
27
+ </TabList>
28
+ </Tabs>
29
+ </Flex>
30
+ );
31
+ };
@@ -0,0 +1,74 @@
1
+ import React, { useState } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { TabPanel } from '@brightcove/studio-components';
4
+ import { TabsSelectedId } from 'js/types/players';
5
+ import css from 'styles/EmbedCodeModal.sass';
6
+ import { TabsHeader, EmbedCode } from 'js/components';
7
+ import { EmbedCodeProperties } from 'js/types/EmbedCodeModalProps';
8
+
9
+ export const TabsSection = ({
10
+ isCampaign,
11
+ isViewerIdSource,
12
+ isBcMapTracking,
13
+ ...rest
14
+ }: {
15
+ playerId: string;
16
+ isCampaign: boolean;
17
+ isViewerIdSource: boolean;
18
+ isBcMapTracking: boolean;
19
+ videoId?: string;
20
+ accountId: string;
21
+ interactivityProjectId?: string;
22
+ livePlaybackToken?: string;
23
+ adConfigId?: string;
24
+ embedCodeProperties?: EmbedCodeProperties;
25
+ }) => {
26
+ const { t } = useTranslation();
27
+ const [currentTab, setCurrentTab] = useState<TabsSelectedId>('iframe');
28
+
29
+ const isIframe = currentTab === 'iframe';
30
+
31
+ const handleTabChange = (id?: string | number) => setCurrentTab(id);
32
+
33
+ return (
34
+ <>
35
+ <TabsHeader
36
+ currentTab={currentTab}
37
+ handleTabChange={handleTabChange}
38
+ jsLabel={t('tabs.labels.javascript')}
39
+ iframeLabel={t('tabs.labels.iframe')}
40
+ />
41
+ <TabPanel
42
+ testName='embed-tab-content'
43
+ className={css.tabPanelContent}
44
+ render={() => {
45
+ if (isIframe) {
46
+ if (isCampaign) {
47
+ return (
48
+ <span className={css.redHelperText}>
49
+ {t('tabs.helperTextWarning')}
50
+ </span>
51
+ );
52
+ } else if (isViewerIdSource) {
53
+ return (
54
+ <span className={css.redHelperText}>
55
+ {t('tabs.viewerIdTextWarning')}
56
+ </span>
57
+ );
58
+ } else if (isBcMapTracking) {
59
+ return (
60
+ <span className={css.redHelperText}>
61
+ {t('tabs.bcMapTrackingTextWarning')}
62
+ </span>
63
+ );
64
+ } else {
65
+ return <EmbedCode iframe={isIframe} {...rest} />;
66
+ }
67
+ } else {
68
+ return <EmbedCode iframe={isIframe} {...rest} />;
69
+ }
70
+ }}
71
+ />
72
+ </>
73
+ );
74
+ };
@@ -0,0 +1,39 @@
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
+ import { Flex, ToolTip } from '@brightcove/studio-components';
3
+ import css from 'styles/EmbedCodeModal.sass';
4
+ import { truncateVideoTitle } from '../utils/util';
5
+
6
+ interface TitleWithTooltipProps {
7
+ name: string;
8
+ id: string;
9
+ }
10
+ export const TitleWithTooltip = ({ name, id }: TitleWithTooltipProps) => {
11
+ const [isOverflowing, setIsOverflowing] = useState(false);
12
+
13
+ const titleMemo = useMemo(
14
+ () => (name ? truncateVideoTitle(name) : ''),
15
+ [name],
16
+ );
17
+
18
+ useEffect(() => {
19
+ if (name) {
20
+ setIsOverflowing(titleMemo !== name);
21
+ }
22
+ }, [titleMemo, name]);
23
+
24
+ return (
25
+ <Flex flexDirection='column' className={css.mediaData}>
26
+ <ToolTip
27
+ placement='top'
28
+ portal
29
+ block
30
+ content={name}
31
+ active={isOverflowing}
32
+ testName='embed-video-tooltip'
33
+ >
34
+ <div className={css.mediaTitle}>{titleMemo}</div>
35
+ </ToolTip>
36
+ <div className={css.mediaId}>{id}</div>
37
+ </Flex>
38
+ );
39
+ };