@brightcove/components-embed-code-modal 1.1.2 → 1.1.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 (196) 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 +423 -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__/PrismLiveModal.test.tsx +85 -0
  17. package/__tests__/PrismPlayerControls.test.tsx +139 -0
  18. package/__tests__/PrismPlayerPreview.test.tsx +168 -0
  19. package/__tests__/TabsHeader.test.tsx +19 -0
  20. package/__tests__/TabsSection.test.tsx +46 -0
  21. package/__tests__/TitleWithTooltip.test.tsx +45 -0
  22. package/__tests__/VideoPlayer.test.tsx +105 -0
  23. package/__tests__/embedCodeGenerator.test.ts +111 -0
  24. package/__tests__/util.test.ts +89 -0
  25. package/babel.config.json +17 -0
  26. package/build/README.md +109 -0
  27. package/build/__tests__/PrismLiveModal.test.d.ts +2 -0
  28. package/build/__tests__/PrismLiveModal.test.d.ts.map +1 -0
  29. package/build/index.js +9 -0
  30. package/build/index.js.map +1 -0
  31. package/build/package.json +100 -0
  32. package/{src → build/src}/js/components/prism/PrismLiveModal.d.ts.map +1 -1
  33. package/{src → build/src}/js/components/prism/PrismPlayerControls.d.ts +2 -1
  34. package/{src → build/src}/js/components/prism/PrismPlayerControls.d.ts.map +1 -1
  35. package/{src → build/src}/js/components/prism/PrismPlayerPreview.d.ts +2 -1
  36. package/build/src/js/components/prism/PrismPlayerPreview.d.ts.map +1 -0
  37. package/build/src/js/hooks/useIsMobile.d.ts +3 -0
  38. package/build/src/js/hooks/useIsMobile.d.ts.map +1 -0
  39. package/{src → build/src}/js/i18n.d.ts +24 -0
  40. package/{src → build/src}/js/i18n.d.ts.map +1 -1
  41. package/etc/wildcard.brightcove.com.key +27 -0
  42. package/etc/wildcard.brightcove.com.pem +96 -0
  43. package/jest/jest.setup.js +90 -0
  44. package/jest/jest.stub.js +4 -0
  45. package/jest.config.js +24 -0
  46. package/package.json +17 -7
  47. package/scripts/create-dist-pkg-prod.js +19 -0
  48. package/src/index.ts +17 -0
  49. package/src/js/EmbedCodeModal.tsx +105 -0
  50. package/src/js/components/CustomSettingsDropdown.tsx +184 -0
  51. package/src/js/components/EmbedCopySection.tsx +130 -0
  52. package/src/js/components/ModalContainer.tsx +367 -0
  53. package/src/js/components/NoPlayerModal.tsx +42 -0
  54. package/src/js/components/PlayerPreview.tsx +116 -0
  55. package/src/js/components/TabsHeader.tsx +31 -0
  56. package/src/js/components/TabsSection.tsx +74 -0
  57. package/src/js/components/TitleWithTooltip.tsx +39 -0
  58. package/src/js/components/VideoLinks.tsx +83 -0
  59. package/src/js/components/VideoPlayer.tsx +324 -0
  60. package/src/js/components/index.ts +9 -0
  61. package/src/js/components/prism/PrismLiveModal.tsx +196 -0
  62. package/src/js/components/prism/PrismPlayerControls.tsx +356 -0
  63. package/src/js/components/prism/PrismPlayerPreview.tsx +200 -0
  64. package/src/js/components/prism/index.ts +3 -0
  65. package/src/js/constants/baseUrls.ts +4 -0
  66. package/src/js/constants/embed.ts +12 -0
  67. package/src/js/constants/player.ts +32 -0
  68. package/src/js/constants/pluginsRegistry.ts +30 -0
  69. package/src/js/hooks/useGetPlayers.ts +12 -0
  70. package/src/js/hooks/useIsMobile.ts +33 -0
  71. package/src/js/i18n.ts +43 -0
  72. package/src/js/services/players-api.ts +26 -0
  73. package/src/js/services/url-shortener.ts +10 -0
  74. package/src/js/types/EmbedCodeModalProps.ts +72 -0
  75. package/src/js/types/EmbedCodeProps.ts +13 -0
  76. package/src/js/types/brightcoveEmbedCode.d.ts +18 -0
  77. package/src/js/types/players.ts +64 -0
  78. package/src/js/types/react-i18next.d.ts +8 -0
  79. package/src/js/types/studioModule.ts +7 -0
  80. package/src/js/utils/addPluginsDataToPlayers.ts +59 -0
  81. package/src/js/utils/call.ts +31 -0
  82. package/src/js/utils/dimensions.ts +53 -0
  83. package/src/js/utils/embedCodeGenerator.ts +120 -0
  84. package/src/js/utils/featureSwitches.ts +16 -0
  85. package/src/js/utils/util.ts +153 -0
  86. package/src/module.tsx +280 -0
  87. package/src/styles/CustomSettingsDropdown.sass +42 -0
  88. package/src/styles/EmbedCode.sass +30 -0
  89. package/src/styles/EmbedCodeModal.sass +76 -0
  90. package/src/styles/base.sass +1 -0
  91. package/src/styles/prism/PrismLiveModal.sass +128 -0
  92. package/src/styles/theme.sass +11 -0
  93. package/src/styles/typings.td.ts +9 -0
  94. package/src/translations/en.json +69 -0
  95. package/src/translations/es.json +69 -0
  96. package/src/translations/fr.json +69 -0
  97. package/src/translations/ja.json +69 -0
  98. package/src/translations/ko.json +69 -0
  99. package/src/translations/zh.json +69 -0
  100. package/src/types/studio.ts +197 -0
  101. package/tsconfig.json +34 -0
  102. package/webpack.config.js +117 -0
  103. package/index.js +0 -9
  104. package/index.js.map +0 -1
  105. package/src/js/components/prism/PrismPlayerPreview.d.ts.map +0 -1
  106. /package/{__tests__ → build/__tests__}/EmbedCode.test.d.ts +0 -0
  107. /package/{__tests__ → build/__tests__}/EmbedCode.test.d.ts.map +0 -0
  108. /package/{__tests__ → build/__tests__}/InteractivityProject.test.d.ts +0 -0
  109. /package/{__tests__ → build/__tests__}/InteractivityProject.test.d.ts.map +0 -0
  110. /package/{__tests__ → build/__tests__}/PlayerPreview.test.d.ts +0 -0
  111. /package/{__tests__ → build/__tests__}/PlayerPreview.test.d.ts.map +0 -0
  112. /package/{__tests__ → build/__tests__}/PrismPlayerControls.test.d.ts +0 -0
  113. /package/{__tests__ → build/__tests__}/PrismPlayerControls.test.d.ts.map +0 -0
  114. /package/{__tests__ → build/__tests__}/PrismPlayerPreview.test.d.ts +0 -0
  115. /package/{__tests__ → build/__tests__}/PrismPlayerPreview.test.d.ts.map +0 -0
  116. /package/{__tests__ → build/__tests__}/TabsHeader.test.d.ts +0 -0
  117. /package/{__tests__ → build/__tests__}/TabsHeader.test.d.ts.map +0 -0
  118. /package/{__tests__ → build/__tests__}/TabsSection.test.d.ts +0 -0
  119. /package/{__tests__ → build/__tests__}/TabsSection.test.d.ts.map +0 -0
  120. /package/{__tests__ → build/__tests__}/TitleWithTooltip.test.d.ts +0 -0
  121. /package/{__tests__ → build/__tests__}/TitleWithTooltip.test.d.ts.map +0 -0
  122. /package/{__tests__ → build/__tests__}/VideoPlayer.test.d.ts +0 -0
  123. /package/{__tests__ → build/__tests__}/VideoPlayer.test.d.ts.map +0 -0
  124. /package/{__tests__ → build/__tests__}/embedCodeGenerator.test.d.ts +0 -0
  125. /package/{__tests__ → build/__tests__}/embedCodeGenerator.test.d.ts.map +0 -0
  126. /package/{__tests__ → build/__tests__}/util.test.d.ts +0 -0
  127. /package/{__tests__ → build/__tests__}/util.test.d.ts.map +0 -0
  128. /package/{src → build/src}/index.d.ts +0 -0
  129. /package/{src → build/src}/index.d.ts.map +0 -0
  130. /package/{src → build/src}/js/EmbedCodeModal.d.ts +0 -0
  131. /package/{src → build/src}/js/EmbedCodeModal.d.ts.map +0 -0
  132. /package/{src → build/src}/js/components/CustomSettingsDropdown.d.ts +0 -0
  133. /package/{src → build/src}/js/components/CustomSettingsDropdown.d.ts.map +0 -0
  134. /package/{src → build/src}/js/components/EmbedCopySection.d.ts +0 -0
  135. /package/{src → build/src}/js/components/EmbedCopySection.d.ts.map +0 -0
  136. /package/{src → build/src}/js/components/ModalContainer.d.ts +0 -0
  137. /package/{src → build/src}/js/components/ModalContainer.d.ts.map +0 -0
  138. /package/{src → build/src}/js/components/NoPlayerModal.d.ts +0 -0
  139. /package/{src → build/src}/js/components/NoPlayerModal.d.ts.map +0 -0
  140. /package/{src → build/src}/js/components/PlayerPreview.d.ts +0 -0
  141. /package/{src → build/src}/js/components/PlayerPreview.d.ts.map +0 -0
  142. /package/{src → build/src}/js/components/TabsHeader.d.ts +0 -0
  143. /package/{src → build/src}/js/components/TabsHeader.d.ts.map +0 -0
  144. /package/{src → build/src}/js/components/TabsSection.d.ts +0 -0
  145. /package/{src → build/src}/js/components/TabsSection.d.ts.map +0 -0
  146. /package/{src → build/src}/js/components/TitleWithTooltip.d.ts +0 -0
  147. /package/{src → build/src}/js/components/TitleWithTooltip.d.ts.map +0 -0
  148. /package/{src → build/src}/js/components/VideoLinks.d.ts +0 -0
  149. /package/{src → build/src}/js/components/VideoLinks.d.ts.map +0 -0
  150. /package/{src → build/src}/js/components/VideoPlayer.d.ts +0 -0
  151. /package/{src → build/src}/js/components/VideoPlayer.d.ts.map +0 -0
  152. /package/{src → build/src}/js/components/index.d.ts +0 -0
  153. /package/{src → build/src}/js/components/index.d.ts.map +0 -0
  154. /package/{src → build/src}/js/components/prism/PrismLiveModal.d.ts +0 -0
  155. /package/{src → build/src}/js/components/prism/index.d.ts +0 -0
  156. /package/{src → build/src}/js/components/prism/index.d.ts.map +0 -0
  157. /package/{src → build/src}/js/constants/baseUrls.d.ts +0 -0
  158. /package/{src → build/src}/js/constants/baseUrls.d.ts.map +0 -0
  159. /package/{src → build/src}/js/constants/embed.d.ts +0 -0
  160. /package/{src → build/src}/js/constants/embed.d.ts.map +0 -0
  161. /package/{src → build/src}/js/constants/player.d.ts +0 -0
  162. /package/{src → build/src}/js/constants/player.d.ts.map +0 -0
  163. /package/{src → build/src}/js/constants/pluginsRegistry.d.ts +0 -0
  164. /package/{src → build/src}/js/constants/pluginsRegistry.d.ts.map +0 -0
  165. /package/{src → build/src}/js/hooks/useGetPlayers.d.ts +0 -0
  166. /package/{src → build/src}/js/hooks/useGetPlayers.d.ts.map +0 -0
  167. /package/{src → build/src}/js/services/players-api.d.ts +0 -0
  168. /package/{src → build/src}/js/services/players-api.d.ts.map +0 -0
  169. /package/{src → build/src}/js/services/url-shortener.d.ts +0 -0
  170. /package/{src → build/src}/js/services/url-shortener.d.ts.map +0 -0
  171. /package/{src → build/src}/js/types/EmbedCodeModalProps.d.ts +0 -0
  172. /package/{src → build/src}/js/types/EmbedCodeModalProps.d.ts.map +0 -0
  173. /package/{src → build/src}/js/types/EmbedCodeProps.d.ts +0 -0
  174. /package/{src → build/src}/js/types/EmbedCodeProps.d.ts.map +0 -0
  175. /package/{src → build/src}/js/types/players.d.ts +0 -0
  176. /package/{src → build/src}/js/types/players.d.ts.map +0 -0
  177. /package/{src → build/src}/js/types/studioModule.d.ts +0 -0
  178. /package/{src → build/src}/js/types/studioModule.d.ts.map +0 -0
  179. /package/{src → build/src}/js/utils/addPluginsDataToPlayers.d.ts +0 -0
  180. /package/{src → build/src}/js/utils/addPluginsDataToPlayers.d.ts.map +0 -0
  181. /package/{src → build/src}/js/utils/call.d.ts +0 -0
  182. /package/{src → build/src}/js/utils/call.d.ts.map +0 -0
  183. /package/{src → build/src}/js/utils/dimensions.d.ts +0 -0
  184. /package/{src → build/src}/js/utils/dimensions.d.ts.map +0 -0
  185. /package/{src → build/src}/js/utils/embedCodeGenerator.d.ts +0 -0
  186. /package/{src → build/src}/js/utils/embedCodeGenerator.d.ts.map +0 -0
  187. /package/{src → build/src}/js/utils/featureSwitches.d.ts +0 -0
  188. /package/{src → build/src}/js/utils/featureSwitches.d.ts.map +0 -0
  189. /package/{src → build/src}/js/utils/util.d.ts +0 -0
  190. /package/{src → build/src}/js/utils/util.d.ts.map +0 -0
  191. /package/{src → build/src}/module.d.ts +0 -0
  192. /package/{src → build/src}/module.d.ts.map +0 -0
  193. /package/{src → build/src}/styles/typings.td.d.ts +0 -0
  194. /package/{src → build/src}/styles/typings.td.d.ts.map +0 -0
  195. /package/{src → build/src}/types/studio.d.ts +0 -0
  196. /package/{src → build/src}/types/studio.d.ts.map +0 -0
@@ -0,0 +1,83 @@
1
+ import React, { useCallback, useState } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { Button, Loader } from '@brightcove/studio-components';
4
+ import css from 'styles/EmbedCodeModal.sass';
5
+ import { shortenUrl } from '../services/url-shortener';
6
+ import { getBrightcove } from 'js/utils/util';
7
+
8
+ interface VideoLinksProps {
9
+ previewUrl: string;
10
+ playerUrl: string;
11
+ }
12
+
13
+ export const VideoLinks = ({ previewUrl, playerUrl }: VideoLinksProps) => {
14
+ const { t } = useTranslation();
15
+ const [isGetUrlLoading, setIsGetUrlLoading] = useState<boolean>(false);
16
+
17
+ const copyPreviewURL = useCallback(
18
+ async (link: string) => {
19
+ setIsGetUrlLoading(true);
20
+ getUrlAndAddItToClipboard(link)
21
+ .then(() => {
22
+ getBrightcove().Toaster.success({
23
+ title: t('toasterTitle'),
24
+ message: t('toasterGetURLSuccess'),
25
+ });
26
+ })
27
+ .catch(() => {
28
+ getBrightcove().Toaster.error({
29
+ message: t('toasterGetURLError'),
30
+ });
31
+ })
32
+ .finally(() => setIsGetUrlLoading(false));
33
+ },
34
+ [t],
35
+ );
36
+ return (
37
+ <div>
38
+ <div className={css.getURLBtnContainer}>
39
+ <Button
40
+ look='tertiary'
41
+ text={t('getURL')}
42
+ testName='get-url-btn'
43
+ icon={isGetUrlLoading ? '' : 'link'}
44
+ iconRight
45
+ onClick={() => copyPreviewURL(playerUrl)}
46
+ />
47
+ {isGetUrlLoading && (
48
+ <Loader className={css.getURLBtnSpinner} size='inline' color='navy' />
49
+ )}
50
+ </div>
51
+ <Button
52
+ look='tertiary'
53
+ text={t('previewButton')}
54
+ testName='preview-player-btn'
55
+ icon='external_link'
56
+ iconRight
57
+ onClick={() => {
58
+ window.open(previewUrl, '_blank');
59
+ }}
60
+ />
61
+ </div>
62
+ );
63
+ };
64
+
65
+ export const getUrlAndAddItToClipboard = async (url: string) => {
66
+ const escapedLongUrl = encodeURI(url);
67
+ // safari
68
+ try {
69
+ return navigator.clipboard.write([
70
+ new ClipboardItem({
71
+ // eslint-disable-next-line no-async-promise-executor
72
+ 'text/plain': new Promise(async (resolve) => {
73
+ const shortUrl = await shortenUrl({ longUrl: escapedLongUrl });
74
+ resolve(new Blob([shortUrl], { type: 'text/plain' }));
75
+ }),
76
+ }),
77
+ ]);
78
+ } catch {
79
+ // chrome and firefox
80
+ const shortUrl = await shortenUrl({ longUrl: escapedLongUrl });
81
+ return navigator.clipboard.writeText(shortUrl);
82
+ }
83
+ };
@@ -0,0 +1,324 @@
1
+ import React, { useEffect, useState, useMemo, useCallback } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import ReactPlayerLoader from '@brightcove/react-player-loader';
4
+ import {
5
+ Checkbox,
6
+ Hideable,
7
+ Input,
8
+ PlayerSelect,
9
+ Select,
10
+ } from '@brightcove/studio-components';
11
+ import {
12
+ VideoPayload,
13
+ ProjectPayload,
14
+ EmbedCodeModalTypes,
15
+ EmbedCodeProperties,
16
+ LivePayload,
17
+ } from 'js/types/EmbedCodeModalProps';
18
+ import {
19
+ PlayerSelectItem,
20
+ PlayersObject,
21
+ PlayersObjectItem,
22
+ } from 'js/types/players';
23
+ import css from 'styles/EmbedCodeModal.sass';
24
+ import {
25
+ getUrlBase,
26
+ getVideoPreviewLink,
27
+ PREVIEW_URL_BASE,
28
+ VideoPreviewLinkOptions,
29
+ } from 'js/utils/util';
30
+ import {
31
+ VideoLinks,
32
+ TitleWithTooltip,
33
+ TabsSection,
34
+ CustomSettingsDropdown,
35
+ } from 'js/components';
36
+ import { DEFAULT_PLAYER_ID, PLAYERS } from 'js/constants/player';
37
+ import { EMBED_DEFAULT } from 'js/constants/embed';
38
+ import { getFavoritePlayer } from 'js/utils/featureSwitches';
39
+ import cx from 'classnames';
40
+
41
+ export const VideoPlayer = ({
42
+ video,
43
+ embedCodeModalType,
44
+ playerListForSelection,
45
+ players,
46
+ hasCreatedPlayer,
47
+ embedCodeProperties,
48
+ showDVR,
49
+ onDVRClick,
50
+ dvrTime,
51
+ showLowLatency,
52
+ onLowLatencyClick,
53
+ adConfigValue,
54
+ onAdConfigChange,
55
+ ssai = false,
56
+ adConfigOptions = [],
57
+ }: {
58
+ video: VideoPayload | ProjectPayload | LivePayload;
59
+ embedCodeModalType: EmbedCodeModalTypes;
60
+ playerListForSelection: PlayerSelectItem[];
61
+ players: PlayersObject;
62
+ hasCreatedPlayer?: boolean;
63
+ embedCodeProperties?: EmbedCodeProperties;
64
+ showDVR?: boolean;
65
+ onDVRClick?: (newValue: boolean) => void;
66
+ dvrTime?: number;
67
+ showLowLatency?: boolean;
68
+ onLowLatencyClick?: (newValue: boolean) => void;
69
+ adConfigValue?: string;
70
+ onAdConfigChange?: (value: string) => void;
71
+ ssai?: boolean;
72
+ adConfigOptions?: Array<{ label: string; value: string }>;
73
+ }) => {
74
+ const { t } = useTranslation();
75
+ const [player, setPlayer] = useState<PlayersObjectItem>(PLAYERS.default);
76
+ const [previewUrl, setPreviewUrl] = useState<string>('');
77
+ const [embedProperties, setEmbedProperties] = useState<EmbedCodeProperties>(
78
+ embedCodeProperties ? embedCodeProperties : EMBED_DEFAULT,
79
+ );
80
+ const [allowLowLatency, setAllowLowLatency] = useState(true);
81
+ const [allowDVR, setAllowDVR] = useState(!showLowLatency);
82
+ const [time, setTime] = useState<number>(dvrTime || 0);
83
+
84
+ useEffect(() => {
85
+ setTime(dvrTime || 0);
86
+ }, [dvrTime]);
87
+
88
+ useEffect(() => {
89
+ if (playerListForSelection.length === 0) return;
90
+ const favoritePlayer = getFavoritePlayer();
91
+ const player = players[favoritePlayer];
92
+
93
+ if (
94
+ playerListForSelection.find((selection) => selection.value === player?.id)
95
+ ) {
96
+ setPlayer(player);
97
+ } else {
98
+ setPlayer(players[playerListForSelection[0].value]);
99
+ }
100
+ // eslint-disable-next-line react-hooks/exhaustive-deps
101
+ }, [playerListForSelection]);
102
+
103
+ const onPlayerSelectChange = (id: string) => {
104
+ const player = players[id];
105
+ setPlayer(player);
106
+ };
107
+
108
+ const interactivityProjectId = useMemo(() => {
109
+ return embedCodeModalType === EmbedCodeModalTypes.PROJECT
110
+ ? (video as ProjectPayload).interactivityProjectId
111
+ : null;
112
+ }, [video, embedCodeModalType]);
113
+
114
+ const onEmbedCreated = useCallback(
115
+ (el: HTMLElement) => {
116
+ if (interactivityProjectId) {
117
+ el.setAttribute(
118
+ 'data-interactivity-project-id',
119
+ interactivityProjectId,
120
+ );
121
+ }
122
+ },
123
+ [interactivityProjectId],
124
+ );
125
+
126
+ const sharedProps: {
127
+ accountId: string;
128
+ playerId: string;
129
+ isCampaign: boolean;
130
+ isViewerIdSource: boolean;
131
+ isBcMapTracking: boolean;
132
+ videoId?: string;
133
+ interactivityProjectId?: string;
134
+ livePlaybackToken?: string;
135
+ adConfigId?: string;
136
+ } = {
137
+ accountId: video.accountId,
138
+ playerId:
139
+ player?.id ||
140
+ (playerListForSelection.length > 0
141
+ ? playerListForSelection[0].value
142
+ : DEFAULT_PLAYER_ID),
143
+ isCampaign: player?.hasCampaignPlugin || false,
144
+ isViewerIdSource: player?.hasViewerIDSource || false,
145
+ isBcMapTracking: player?.hasBcMapTracking || false,
146
+ videoId: video.videoId,
147
+ };
148
+
149
+ const name = interactivityProjectId
150
+ ? (video as ProjectPayload).interactivityProjectName
151
+ : (video as VideoPayload).videoName;
152
+ const id = interactivityProjectId || video.videoId;
153
+
154
+ if (interactivityProjectId) {
155
+ sharedProps.interactivityProjectId = interactivityProjectId;
156
+ }
157
+
158
+ const { livePlaybackToken, adConfigId } = video as LivePayload;
159
+
160
+ if (livePlaybackToken) {
161
+ sharedProps.livePlaybackToken = livePlaybackToken;
162
+ }
163
+
164
+ if (adConfigId) {
165
+ sharedProps.adConfigId = adConfigId;
166
+ }
167
+
168
+ const {
169
+ adConfigId: sharedAdConfigId,
170
+ livePlaybackToken: sharedLivePlaybackToken,
171
+ ...restSharedProps
172
+ } = sharedProps;
173
+
174
+ const playerProps = {
175
+ ...restSharedProps,
176
+ ...(sharedAdConfigId ? {} : { adConfigId: sharedAdConfigId }),
177
+ ...(sharedLivePlaybackToken
178
+ ? {}
179
+ : { livePlaybackToken: sharedLivePlaybackToken }),
180
+ };
181
+
182
+ useEffect(() => {
183
+ if (player?.id) {
184
+ const options: VideoPreviewLinkOptions = {
185
+ accountId: video.accountId,
186
+ playerId: player.id,
187
+ videoId: video.videoId,
188
+ };
189
+ if (interactivityProjectId) {
190
+ options.interactivityProjectId = interactivityProjectId;
191
+ }
192
+
193
+ if (adConfigId) {
194
+ options.adConfigId = adConfigId;
195
+ }
196
+
197
+ if (livePlaybackToken) {
198
+ options.livePlaybackToken = livePlaybackToken;
199
+ }
200
+
201
+ setPreviewUrl(getVideoPreviewLink(options));
202
+ }
203
+ }, [player, video, interactivityProjectId, adConfigId, livePlaybackToken]);
204
+
205
+ return (
206
+ <>
207
+ <div className={css.leftContent}>
208
+ <PlayerSelect
209
+ label={t('player')}
210
+ tooltip={
211
+ <>
212
+ <div>{t('tooltipTextNew')}</div>
213
+ <a href={t('learnmore')} target='_blank' rel='noreferrer'>
214
+ {t('tooltipLink')}
215
+ </a>
216
+ </>
217
+ }
218
+ selectedPlayer={player?.id || DEFAULT_PLAYER_ID}
219
+ players={playerListForSelection}
220
+ onChange={onPlayerSelectChange}
221
+ inputClassName={css.playerSelect}
222
+ testName='embed-player-select'
223
+ />
224
+ {embedCodeModalType === EmbedCodeModalTypes.LIVE &&
225
+ ssai &&
226
+ adConfigOptions.length > 0 &&
227
+ onAdConfigChange && (
228
+ <Select
229
+ label={t('adConfiguration')}
230
+ value={adConfigValue || adConfigOptions[0]?.value || ''}
231
+ options={adConfigOptions}
232
+ onChange={onAdConfigChange}
233
+ testName='ad-config-dropdown'
234
+ />
235
+ )}
236
+ {hasCreatedPlayer ? (
237
+ <iframe
238
+ src={`${PREVIEW_URL_BASE()}/${sharedProps.accountId}/players/${
239
+ sharedProps.playerId
240
+ }/preview/embeds/default/master/index.html?interactivityProjectId=${
241
+ sharedProps.interactivityProjectId
242
+ }&videoId=${sharedProps.videoId}`}
243
+ allowFullScreen
244
+ allow='autoplay;encrypted-media;fullscreen'
245
+ width='539'
246
+ height='303.2'
247
+ data-interactivity-project-id={interactivityProjectId}
248
+ ></iframe>
249
+ ) : (
250
+ <ReactPlayerLoader
251
+ {...playerProps}
252
+ baseUrl={getUrlBase()}
253
+ attrs={{ className: css.playerLoader }}
254
+ embedType='iframe'
255
+ refNode='player-container'
256
+ onEmbedCreated={onEmbedCreated}
257
+ />
258
+ )}
259
+ <Hideable show={player?.hasCustomPlugins}>
260
+ <span className={css.customPluginNotice}>
261
+ {t('customPluginNotice')}
262
+ </span>
263
+ </Hideable>
264
+ <VideoLinks previewUrl={previewUrl} playerUrl={previewUrl} />
265
+ </div>
266
+ <div
267
+ className={cx(css.rightContent, {
268
+ [css.expandRightContent]: showDVR,
269
+ })}
270
+ >
271
+ <TitleWithTooltip name={name} id={id} />
272
+ {(embedCodeModalType === EmbedCodeModalTypes.VIDEO ||
273
+ embedCodeModalType === EmbedCodeModalTypes.PROJECT ||
274
+ embedCodeModalType === EmbedCodeModalTypes.LIVE) && (
275
+ <CustomSettingsDropdown
276
+ setEmbedProperties={setEmbedProperties}
277
+ embedProperties={embedProperties}
278
+ />
279
+ )}
280
+ <TabsSection {...sharedProps} embedCodeProperties={embedProperties} />
281
+ {showLowLatency ? (
282
+ <Checkbox
283
+ disabled={false}
284
+ checked={allowLowLatency}
285
+ onChange={() => {
286
+ onLowLatencyClick?.(!allowLowLatency);
287
+ setAllowLowLatency(!allowLowLatency);
288
+ }}
289
+ className={css.dvrCheckbox}
290
+ testName='low-latency-checkbox'
291
+ >
292
+ {t('lowLatency')}
293
+ </Checkbox>
294
+ ) : null}
295
+ {showDVR && (
296
+ <div>
297
+ <Checkbox
298
+ disabled={false}
299
+ checked={allowDVR}
300
+ onChange={() => {
301
+ onDVRClick?.(!allowDVR);
302
+ setAllowDVR(!allowDVR);
303
+ }}
304
+ className={css.dvrCheckbox}
305
+ testName='dvr-checkbox'
306
+ >
307
+ {t('dvr.allowDVR')}
308
+ </Checkbox>
309
+ {allowDVR && (
310
+ <Input
311
+ className={css.timeInput}
312
+ label={t('dvr.inputLabel')}
313
+ value={time}
314
+ description={t('dvr.inputDescription')}
315
+ disabled
316
+ testName='dvr-time-input'
317
+ />
318
+ )}
319
+ </div>
320
+ )}
321
+ </div>
322
+ </>
323
+ );
324
+ };
@@ -0,0 +1,9 @@
1
+ export * from './EmbedCopySection';
2
+ export * from './ModalContainer';
3
+ export * from './TitleWithTooltip';
4
+ export * from './TabsHeader';
5
+ export * from './TabsSection';
6
+ export * from './VideoLinks';
7
+ export * from './VideoPlayer';
8
+ export * from './PlayerPreview';
9
+ export * from './CustomSettingsDropdown';
@@ -0,0 +1,196 @@
1
+ import React, { useState, useEffect, useCallback } from 'react';
2
+ import { PrismModal } from '@brightcove/prism-core';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { PlayerSelectItem, PlayersObject } from 'js/types/players';
5
+ import {
6
+ LivePayload,
7
+ EmbedCodeProperties,
8
+ EmbedCodeAspectRatio,
9
+ EmbedCodeUnits,
10
+ } from 'js/types/EmbedCodeModalProps';
11
+ import {
12
+ getBrightcove,
13
+ getVideoPreviewLink,
14
+ VideoPreviewLinkOptions,
15
+ } from 'js/utils/util';
16
+ import { getFavoritePlayer } from 'js/utils/featureSwitches';
17
+ import useIsMobile from 'js/hooks/useIsMobile';
18
+ import { PrismPlayerControls } from './PrismPlayerControls';
19
+ import { PrismPlayerPreview } from './PrismPlayerPreview';
20
+
21
+ import css from 'styles/prism/PrismLiveModal.sass';
22
+
23
+ interface PrismLiveModalProps {
24
+ isOpen: boolean;
25
+ onClose: () => void;
26
+ video: LivePayload;
27
+ players: PlayersObject;
28
+ playerListForSelection: PlayerSelectItem[];
29
+ showDVR?: boolean;
30
+ onDVRClick?: (newValue: boolean) => void;
31
+ dvrTime?: number;
32
+ showLowLatency?: boolean;
33
+ onLowLatencyClick?: (newValue: boolean) => void;
34
+ adConfigValue?: string;
35
+ onAdConfigChange?: (value: string) => void;
36
+ ssai?: boolean;
37
+ adConfigOptions?: Array<{ label: string; value: string }>;
38
+ }
39
+
40
+ const DEFAULT_PLAYER_ID = 'default';
41
+ const DEFAULT_EMBED_PROPERTIES: EmbedCodeProperties = {
42
+ width: 960,
43
+ height: 540,
44
+ units: EmbedCodeUnits.PX,
45
+ responsive: false,
46
+ aspectRatio: EmbedCodeAspectRatio.SIXTEEN_NINE,
47
+ };
48
+
49
+ export const PrismLiveModal = ({
50
+ isOpen,
51
+ onClose,
52
+ video,
53
+ players,
54
+ playerListForSelection,
55
+ showDVR,
56
+ onDVRClick,
57
+ dvrTime,
58
+ showLowLatency,
59
+ onLowLatencyClick,
60
+ adConfigValue,
61
+ onAdConfigChange,
62
+ ssai,
63
+ adConfigOptions,
64
+ }: PrismLiveModalProps) => {
65
+ const { t } = useTranslation();
66
+ const isMobile = useIsMobile();
67
+ const [selectedPlayer, setSelectedPlayer] =
68
+ useState<string>(DEFAULT_PLAYER_ID);
69
+ const [embedProperties, setEmbedProperties] = useState<EmbedCodeProperties>(
70
+ DEFAULT_EMBED_PROPERTIES,
71
+ );
72
+ const [previewUrl, setPreviewUrl] = useState<string>('');
73
+ const [embedCode, setEmbedCode] = useState<string>('');
74
+
75
+ // Initialize selected player
76
+ useEffect(() => {
77
+ if (playerListForSelection.length === 0) {
78
+ return;
79
+ }
80
+
81
+ const favoritePlayer = getFavoritePlayer();
82
+ const player = players[favoritePlayer];
83
+
84
+ if (
85
+ playerListForSelection.find((selection) => selection.value === player?.id)
86
+ ) {
87
+ setSelectedPlayer(player.id);
88
+ } else {
89
+ setSelectedPlayer(playerListForSelection[0].value);
90
+ }
91
+ }, [playerListForSelection, players]);
92
+
93
+ // Update preview URL when player or video changes
94
+ useEffect(() => {
95
+ if (!selectedPlayer) {
96
+ setPreviewUrl('');
97
+ return;
98
+ }
99
+
100
+ const options: VideoPreviewLinkOptions = {
101
+ accountId: video.accountId,
102
+ playerId: selectedPlayer,
103
+ videoId: video.videoId,
104
+ };
105
+
106
+ // Use the selected adConfigValue from dropdown, fallback to video.adConfigId
107
+ if (adConfigValue) {
108
+ options.adConfigId = adConfigValue;
109
+ } else if (video.adConfigId) {
110
+ options.adConfigId = video.adConfigId;
111
+ }
112
+
113
+ if (video.livePlaybackToken) {
114
+ options.livePlaybackToken = video.livePlaybackToken;
115
+ }
116
+
117
+ setPreviewUrl(getVideoPreviewLink(options));
118
+ }, [selectedPlayer, video, adConfigValue]);
119
+
120
+ const handlePlayerChange = (playerId: string) => {
121
+ setSelectedPlayer(playerId);
122
+ };
123
+
124
+ const handleCopyEmbedCode = useCallback(async () => {
125
+ if (!embedCode) {
126
+ return;
127
+ }
128
+ try {
129
+ await navigator.clipboard.writeText(embedCode);
130
+ getBrightcove().Toaster.success({
131
+ message: t('codeCopied') as string,
132
+ });
133
+ } catch {
134
+ getBrightcove().Toaster.error({
135
+ message: t('toasterErrorMessage') as string,
136
+ });
137
+ }
138
+ }, [embedCode, t]);
139
+
140
+ return (
141
+ <PrismModal
142
+ isOpen={isOpen}
143
+ onClose={onClose}
144
+ title={(t('modalHeader') as string) || 'Publish Video'}
145
+ bottomSheet={isMobile}
146
+ size='extraLarge'
147
+ forceMaxHeight
148
+ contentScroll={isMobile ? 'auto' : 'none'}
149
+ showHeaderBottomBorder
150
+ onPrimaryAction={isMobile ? handleCopyEmbedCode : onClose}
151
+ primaryButtonText={
152
+ isMobile ? (t('copyCode') as string) : (t('done') as string)
153
+ }
154
+ onSecondaryAction={onClose}
155
+ cancelButtonText={
156
+ isMobile ? (t('done') as string) : (t('cancel') as string)
157
+ }
158
+ >
159
+ <div className={css.prismLiveModalContent}>
160
+ {/* Left Column: Player Controls */}
161
+ <PrismPlayerControls
162
+ video={video}
163
+ players={players}
164
+ playerListForSelection={playerListForSelection}
165
+ selectedPlayer={selectedPlayer}
166
+ onPlayerChange={handlePlayerChange}
167
+ embedProperties={embedProperties}
168
+ setEmbedProperties={setEmbedProperties}
169
+ showDVR={showDVR}
170
+ onDVRClick={onDVRClick}
171
+ dvrTime={dvrTime}
172
+ showLowLatency={showLowLatency}
173
+ onLowLatencyClick={onLowLatencyClick}
174
+ adConfigValue={adConfigValue}
175
+ onAdConfigChange={onAdConfigChange}
176
+ ssai={ssai}
177
+ adConfigOptions={adConfigOptions}
178
+ onEmbedCodeChange={setEmbedCode}
179
+ />
180
+
181
+ {/* Right Column: Player Preview */}
182
+ <div className={css.prismRightColumn}>
183
+ <PrismPlayerPreview
184
+ accountId={video.accountId}
185
+ playerId={selectedPlayer}
186
+ videoId={video.videoId}
187
+ previewUrl={previewUrl}
188
+ livePlaybackToken={video.livePlaybackToken}
189
+ adConfigId={adConfigValue || video.adConfigId}
190
+ isMobile={isMobile}
191
+ />
192
+ </div>
193
+ </div>
194
+ </PrismModal>
195
+ );
196
+ };