UNPKG

18.5 kBJavaScriptView Raw
1"use strict";
2var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3 var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4 if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5 else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6 return c > 3 && r && Object.defineProperty(target, key, r), r;
7};
8var __importStar = (this && this.__importStar) || function (mod) {
9 if (mod && mod.__esModule) return mod;
10 var result = {};
11 if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
12 result["default"] = mod;
13 return result;
14};
15var __importDefault = (this && this.__importDefault) || function (mod) {
16 return (mod && mod.__esModule) ? mod : { "default": mod };
17};
18Object.defineProperty(exports, "__esModule", { value: true });
19const React = __importStar(require("react"));
20const react_draggable_1 = __importDefault(require("react-draggable"));
21const with_html_1 = __importDefault(require("react-markdown/with-html"));
22const plyr_1 = __importDefault(require("plyr"));
23const kbc_intl_1 = require("@kano/kbc-intl");
24const kbc_icons_1 = require("@kano/kbc-icons");
25const kbc_telemetry_1 = require("@kano/kbc-telemetry");
26const kbc_utils_1 = require("@kano/kbc-utils");
27require("./styles/main.scss");
28const messages_1 = __importDefault(require("./messages"));
29const icons_1 = require("./icons");
30class VideoPlyrComp extends React.Component {
31 componentDidMount() {
32 this.initPlayer();
33 }
34 initPlayer() {
35 const options = {
36 controls: ['play-large', 'play', 'progress', 'current-time', 'mute', 'volume', 'captions', 'fullscreen'],
37 autoplay: true,
38 };
39 this.player = new plyr_1.default('#plyr', options);
40 const { action } = this.props;
41 this.player.on('play', event => action('video-on-play'));
42 this.player.on('pause', event => action('video-on-pause'));
43 this.player.on('ended', event => action('video-on-ended'));
44 const debounceSeek = kbc_utils_1.debounce(() => action('video-on-seeked'), 250);
45 this.player.on('seeked', event => debounceSeek());
46 this._checkProviderProps();
47 }
48 componentDidUpdate(prevProps) {
49 const { url, provider } = this.props;
50 if (prevProps.url !== url) {
51 this._checkProviderProps();
52 if (provider === 'youtube') {
53 this;
54 this.player.source = {
55 type: 'video',
56 sources: [
57 {
58 src: url,
59 provider: 'youtube',
60 },
61 ],
62 };
63 }
64 else {
65 this.player.source = {
66 type: 'video',
67 sources: [
68 {
69 src: url,
70 type: 'video/mp4',
71 },
72 ]
73 };
74 }
75 }
76 }
77 componentWillUnmount() {
78 if (this.player) {
79 this.player.destroy();
80 }
81 }
82 _checkProviderProps() {
83 const { provider, url } = this.props;
84 if (provider === 'youtube' && url.startsWith('http'))
85 console.error('To use youtube as a video provider you must only pass the video id and not the full url i.e: hDOV6w3J3eU');
86 }
87 render() {
88 const { url, action, showCountdown, counter, height, provider, intl } = this.props;
89 return (React.createElement("div", { className: "plyr-wrapper", style: { height: `${height}px` } },
90 React.createElement("button", { className: `plyr-countdown kbc-button--remove-browser-defaults${showCountdown ? ' animate--fade-in' : ''}`, onClick: () => action('next-step') }, intl.formatMessage(Object.assign({}, messages_1.default.nextStepIn), { counter })),
91 provider === 'default' ? (React.createElement("video", { autoPlay: true, id: "plyr", src: url, style: { width: '100%' } }, intl.formatMessage(Object.assign({}, messages_1.default.browserNotSupported)))) : provider === 'youtube' ? (React.createElement("div", { id: "plyr", className: "plyr__video-embed", "data-plyr-provider": "youtube", "data-plyr-embed-id": url, style: { width: '100%' } })) : ''));
92 }
93}
94exports.VideoPlyr = kbc_intl_1.withIntl(VideoPlyrComp);
95let KbcVideo = class KbcVideo extends React.Component {
96 constructor(props) {
97 super(props);
98 this.setInactivity = () => {
99 clearInterval(this.inactiveInterval);
100 this.startInactiveInterval();
101 this.setState({ inactive: false });
102 };
103 this.state = {
104 videoSize: props.sizingConfig.defaultSize,
105 ratio: props.sizingConfig.defaultRatio,
106 endedAnimate: false,
107 stage: 0,
108 finalStage: props.steps ? props.steps.length - 1 : null,
109 unlockedSteps: [],
110 minimised: false,
111 currentTranslation: null,
112 showCountdown: false,
113 counter: null,
114 inactive: false,
115 isPlaying: false,
116 isSizeDefault: true,
117 };
118 this.videoRef = React.createRef();
119 }
120 componentDidMount() {
121 const { steps } = this.props;
122 if (steps) {
123 this.setState({ unlockedSteps: [0] });
124 }
125 this.videoRef.current.addEventListener('click', this.setInactivity);
126 this.startInactiveInterval();
127 }
128 componentDidUpdate(prevProps) {
129 const { steps } = this.props;
130 if (prevProps.steps !== steps) {
131 if (steps) {
132 this.setState({ stage: 0, unlockedSteps: [0], finalStage: steps.length - 1 });
133 }
134 }
135 }
136 componentWillUnmount() {
137 if (this.videoRef && this.videoRef.current) {
138 this.videoRef.current.removeEventListener('click', this.setInactivity);
139 }
140 if (this.inactiveInterval) {
141 clearInterval(this.inactiveInterval);
142 this.inactiveInterval = null;
143 }
144 if (this.counterInterval) {
145 clearInterval(this.counterInterval);
146 this.counterInterval = null;
147 }
148 if (this.isPlayingInterval) {
149 clearInterval(this.isPlayingInterval);
150 this.isPlayingInterval = null;
151 }
152 }
153 startInactiveInterval() {
154 this.inactiveInterval = setInterval(() => {
155 if (!this.state.inactive) {
156 this.setState({ inactive: true });
157 clearInterval(this.inactiveInterval);
158 }
159 }, 45000);
160 }
161 startIsPlaying() {
162 if (this.state.isPlaying)
163 return;
164 this.setState({ isPlaying: true });
165 this.isPlayingInterval = setInterval(() => {
166 if (this.state.isPlaying) {
167 this.props.tracking.trackEvent({ event: 'video_playing' });
168 }
169 else {
170 this.clearIsPlaying();
171 }
172 }, 30000);
173 }
174 clearIsPlaying() {
175 if (this.isPlayingInterval === null && !this.state.isPlaying)
176 return;
177 clearInterval(this.isPlayingInterval);
178 this.isPlayingInterval = null;
179 this.setState({ isPlaying: false });
180 }
181 setVideoSize() {
182 const { sizingConfig: { defaultSize, mediumSize, defaultRatio, mediumRatio } } = this.props;
183 this.setState(prevState => ({
184 videoSize: prevState.videoSize === defaultSize ? mediumSize : defaultSize,
185 ratio: prevState.ratio === defaultRatio ? mediumRatio : defaultRatio,
186 }), () => this.props.tracking.trackEvent({ event: 'alter_video_size', action: 'click', data: { size: this.state.videoSize } }));
187 }
188 videoAction(event, stage) {
189 this.props.onVideoAction(event, stage);
190 switch (event) {
191 case 'video-on-play':
192 this.startIsPlaying();
193 break;
194 case 'video-on-pause':
195 this.clearIsPlaying();
196 break;
197 case 'video-on-seeked':
198 if (this.state.showCountdown) {
199 clearInterval(this.counterInterval);
200 this.setState({ showCountdown: false });
201 }
202 break;
203 case 'video-on-ended':
204 this.clearIsPlaying();
205 this.endAnimation();
206 if (!this.props.vidSrc && this.state.stage !== this.state.finalStage) {
207 this.videoEnded(stage);
208 }
209 break;
210 case 'next-step':
211 this.setState({ showCountdown: false });
212 this.setStage(stage + 1);
213 break;
214 default:
215 break;
216 }
217 }
218 setStage(newStage, action) {
219 this.setState({ stage: newStage });
220 if (action === 'forward')
221 this.setUnlockedSteps(newStage);
222 }
223 setMinimised() {
224 const { sizingConfig: { defaultSize, mediumSize } } = this.props;
225 const { videoSize } = this.state;
226 if (!this.state.minimised) {
227 const video = document.getElementById('kbc-video');
228 this.setState({ currentTranslation: video.style.transform, isSizeDefault: videoSize === defaultSize });
229 video.style.transform = 'translate(0,0)';
230 }
231 else {
232 this.setState({ isSizeDefault: videoSize === defaultSize });
233 const video = document.getElementById('kbc-video');
234 video.style.transform = this.state.currentTranslation;
235 }
236 this.setState(prevState => ({
237 minimised: !prevState.minimised,
238 videoSize: this.state.isSizeDefault ? defaultSize : mediumSize,
239 }));
240 }
241 handleOnClose() {
242 this.videoAction('close-video');
243 }
244 setUnlockedSteps(newStage) {
245 this.setState(prevState => {
246 const unlocked = prevState.unlockedSteps;
247 if (!unlocked.includes(newStage))
248 unlocked.push(newStage);
249 return { unlockedSteps: unlocked };
250 });
251 }
252 videoEnded(stage) {
253 this.setState({ showCountdown: true, counter: 10 });
254 this.setUnlockedSteps(stage + 1);
255 this.counterInterval = setInterval(() => {
256 if (this.state.counter === 1) {
257 clearInterval(this.counterInterval);
258 this.setState({ showCountdown: false });
259 this.setStage(stage + 1);
260 return;
261 }
262 this.setState(prevState => ({ counter: prevState.counter - 1 }));
263 }, 1000);
264 }
265 endAnimation() {
266 this.setState({ endedAnimate: true });
267 setTimeout(() => {
268 this.setState({ endedAnimate: false });
269 }, 1500);
270 }
271 canGoBack(stage) {
272 return stage > 0;
273 }
274 canGoForward(stage, finalStage) {
275 return stage < finalStage;
276 }
277 canMove(i) {
278 return this.state.unlockedSteps.length >= i + 1;
279 }
280 render() {
281 const { title, bounds, sizingConfig: { bottom, top, left, defaultSize }, vidSrc, steps, type, icons, actions, draggable, provider } = this.props;
282 const { videoSize, ratio, stage, finalStage, endedAnimate, minimised, showCountdown, counter, inactive } = this.state;
283 const mediaType = steps && steps[stage].image ? 'image' : 'video';
284 const step = steps ? steps[stage] : null;
285 let source;
286 if (mediaType === 'video' && steps) {
287 source = steps[stage].video;
288 }
289 else {
290 source = vidSrc;
291 }
292 const bored = inactive || minimised;
293 return (React.createElement(react_draggable_1.default, { bounds: bounds, disabled: minimised || !draggable, cancel: '.plyr__controls' },
294 React.createElement("div", { className: "video", id: "kbc-video", style: { bottom, top, left: left ? left : `calc(${defaultSize / 2.5}px)`, width: videoSize }, ref: this.videoRef },
295 React.createElement("div", { className: `video-header${minimised ? ' header-disable' : ''}` },
296 React.createElement("div", { className: "video-header-title" },
297 React.createElement("div", { className: "video-header-icon" }, icons.headerStatic !== false ? icons.headerStatic : (React.createElement(React.Fragment, null,
298 icons.headerHappy === 'default' ? !endedAnimate && !bored && React.createElement(icons_1.VideoHappyIcon, null) : icons.headerHappy,
299 icons.headerExcited === 'default' ? endedAnimate && !bored && React.createElement(icons_1.VideoExcitedIcon, null) : icons.headerExcited,
300 icons.headerBored === 'default' ? bored && React.createElement(icons_1.VideoBoredIcon, null) : icons.headerBored))),
301 title ?
302 React.createElement("h5", null, title) :
303 type === 'video' ?
304 React.createElement("h5", null, "Guide") :
305 type === 'stepper' ?
306 React.createElement("h5", null,
307 "Step ",
308 stage + 1) :
309 ''),
310 React.createElement("div", { className: "video-header-actions" },
311 actions.expand ? (!minimised && (React.createElement("button", { className: "kbc-button--remove-browser-defaults", onClick: () => this.setVideoSize() }, videoSize === defaultSize ? React.createElement(kbc_icons_1.KbcIcon, { name: "GuideEnlarge", size: "base" }) : React.createElement(kbc_icons_1.KbcIcon, { name: "GuideShrink", size: "base" })))) : '',
312 actions.minimise ? (React.createElement("button", { className: "kbc-button--remove-browser-defaults", onClick: () => this.setMinimised() }, minimised ? React.createElement(kbc_icons_1.KbcIcon, { name: "Maximise", size: "base" }) : React.createElement(kbc_icons_1.KbcIcon, { name: "Minus", size: "base" }))) : '',
313 actions.close ? (React.createElement("button", { className: "kbc-button--remove-browser-defaults", onClick: () => this.handleOnClose() },
314 React.createElement(kbc_icons_1.KbcIcon, { name: "Delete", size: "base" }))) : '')),
315 React.createElement("div", { className: `video-main${minimised ? ' video-hide' : ''}` },
316 mediaType === 'video' ? React.createElement(exports.VideoPlyr, { url: source, provider: provider, action: (e) => this.videoAction(e, stage), showCountdown: showCountdown, counter: counter, height: Math.round(videoSize * ratio) }) : '',
317 mediaType === 'image' ? React.createElement("img", { src: step.image }) : ''),
318 type === 'stepper' && (React.createElement("div", { className: `stepper-wrapper${minimised ? ' video-hide' : ''}` },
319 React.createElement("div", { className: "stepper-footer" },
320 React.createElement("div", { className: "stepper-footer-text" },
321 React.createElement(with_html_1.default, { source: step.instruction, escapeHtml: false })),
322 React.createElement("div", { className: "stepper-footer-nav" },
323 React.createElement("div", { className: "stepper-buttons" },
324 React.createElement("div", { className: `stepper-button stepper-button-back${this.canGoBack(stage) ? '' : ' stepper-button-disabled'}`, onClick: () => this.canGoBack(stage) ? this.setStage(stage - 1, 'back') : null },
325 React.createElement(kbc_icons_1.KbcIcon, { name: "ArrowCircleLeft", size: "large" })),
326 React.createElement("div", { className: `stepper-button${this.canGoForward(stage, finalStage) ? '' : ' stepper-button-disabled'}`, onClick: () => this.canGoForward(stage, finalStage) ? this.setStage(stage + 1, 'forward') : null },
327 React.createElement(kbc_icons_1.KbcIcon, { name: "ArrowCircleRight", size: "large" }))),
328 React.createElement("div", { className: "stepper-path" }, steps.map((_step, i) => (React.createElement("div", { className: `stepper-step${this.canMove(i) ? '' : ' stepper-step-disabled'}`, onClick: () => this.canMove(i) ? this.setStage(i) : null, key: 'step-' + i },
329 React.createElement("svg", { viewBox: "0 0 100 100", xmlns: "http://www.w3.org/2000/svg", className: 'stepper-step-icon' + (i === stage ? ' stepper-step-selected' : '') },
330 React.createElement("circle", { cx: "50", cy: "50", r: "50" })))))))))))));
331 }
332};
333KbcVideo.defaultProps = {
334 type: 'video',
335 title: false,
336 vidSrc: 'videos/example_club_video.mp4',
337 bounds: 'parent',
338 sizingConfig: {
339 bottom: 30,
340 defaultSize: 400,
341 mediumSize: 600,
342 defaultRatio: 0.5325,
343 mediumRatio: 0.541,
344 },
345 icons: {
346 headerStatic: false,
347 headerHappy: 'default',
348 headerExcited: 'default',
349 headerBored: 'default',
350 },
351 actions: {
352 minimise: true,
353 expand: true,
354 close: false,
355 },
356 draggable: true,
357 provider: 'default',
358};
359__decorate([
360 kbc_telemetry_1.track((_p, _s, args) => ({ event: 'video_action', data: { type: args[0] } }))
361], KbcVideo.prototype, "videoAction", null);
362__decorate([
363 kbc_telemetry_1.track((_p, _s, args) => ({ event: 'video_step_changed', action: 'click', data: { type: args[1], stepIndex: args[0] } }))
364], KbcVideo.prototype, "setStage", null);
365__decorate([
366 kbc_telemetry_1.track((_p, s) => ({ event: 'video_minimise', action: 'click', data: { type: s.minimised ? 'maximise' : 'minimise' } }))
367], KbcVideo.prototype, "setMinimised", null);
368__decorate([
369 kbc_telemetry_1.track((_p, s) => ({ event: 'video_closed', action: 'click' }))
370], KbcVideo.prototype, "handleOnClose", null);
371KbcVideo = __decorate([
372 kbc_telemetry_1.track({ module: 'kbc-video' })
373], KbcVideo);
374exports.default = KbcVideo;
375//# sourceMappingURL=index.js.map
\No newline at end of file