@applicaster/zapp-react-dom-app 13.0.0-rc.4 → 13.0.0-rc.41
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.
- package/App/InteractionManager/index.tsx +203 -115
- package/App/Loader/__tests__/__snapshots__/loadDomPlugins.test.js.snap +6 -6
- package/App/Loader/loadLocale.js +2 -2
- package/App/Loader/utils/platform.js +46 -26
- package/Polyfills/Storage/__tests__/__snapshots__/storage.test.js.snap +4 -4
- package/index.js +0 -7
- package/package.json +17 -8
- package/App/Router/index.js +0 -17
- package/App/Splash/Spinner.js +0 -30
- package/App/Splash/spinner-styles.css +0 -81
|
@@ -23,8 +23,6 @@ import {
|
|
|
23
23
|
|
|
24
24
|
import { showConfirmationDialog } from "@applicaster/zapp-react-native-utils/alertUtils";
|
|
25
25
|
|
|
26
|
-
import { withUseSeek } from "@applicaster/zapp-react-native-utils/reactHooks/player";
|
|
27
|
-
|
|
28
26
|
import {
|
|
29
27
|
DISPLAY_STATES,
|
|
30
28
|
DisplayStateContext,
|
|
@@ -33,7 +31,10 @@ import {
|
|
|
33
31
|
import { PlayerContentContext } from "@applicaster/zapp-react-dom-ui-components/Contexts";
|
|
34
32
|
|
|
35
33
|
import { withNavigator } from "@applicaster/zapp-react-native-ui-components/Decorators/Navigator";
|
|
36
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
getXray,
|
|
36
|
+
createLogger,
|
|
37
|
+
} from "@applicaster/zapp-react-native-utils/logger";
|
|
37
38
|
|
|
38
39
|
import {
|
|
39
40
|
confirmationDialogStore,
|
|
@@ -48,10 +49,16 @@ import {
|
|
|
48
49
|
|
|
49
50
|
import { OnScreenKeyboard } from "@applicaster/zapp-react-dom-ui-components/Components/OnScreenKeyboard";
|
|
50
51
|
import { KeyboardLongPressManager } from "@applicaster/zapp-react-dom-ui-components/Utils/KeyboardLongPressManager";
|
|
52
|
+
import { KeyInputHandler } from "@applicaster/zapp-react-native-utils/appUtils/keyInputHandler/KeyInputHandler";
|
|
51
53
|
|
|
52
54
|
const { withXray } = getXray();
|
|
53
55
|
const globalAny: any = global;
|
|
54
56
|
|
|
57
|
+
const { log_debug, log_warning } = createLogger({
|
|
58
|
+
category: "InteractionManager",
|
|
59
|
+
subsystem: "zapp-react-dom-app",
|
|
60
|
+
});
|
|
61
|
+
|
|
55
62
|
const shouldUseOnScreenKeyboard = () =>
|
|
56
63
|
isVizioPlatform() || window.applicaster.useOnScreenKeyboard;
|
|
57
64
|
|
|
@@ -62,14 +69,6 @@ type Props = {
|
|
|
62
69
|
navigator: QuickBrickAppNavigator;
|
|
63
70
|
rivers: {};
|
|
64
71
|
xray: XRayContext;
|
|
65
|
-
seekMethods: {
|
|
66
|
-
onRewindPress: () => void;
|
|
67
|
-
onRewindLongPress: () => void;
|
|
68
|
-
onForwardPress: () => void;
|
|
69
|
-
onForwardLongPress: () => void;
|
|
70
|
-
onRewindPressOut: () => void;
|
|
71
|
-
onForwardPressOut: () => void;
|
|
72
|
-
};
|
|
73
72
|
confirmDialog: typeof confirmationDialogStore;
|
|
74
73
|
};
|
|
75
74
|
|
|
@@ -149,13 +148,16 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
149
148
|
this.onConfirmDialogOpen = this.onConfirmDialogOpen.bind(this);
|
|
150
149
|
this.onConfirmDialogClose = this.onConfirmDialogClose.bind(this);
|
|
151
150
|
|
|
151
|
+
this.handlePhysicalKeyboardDismiss =
|
|
152
|
+
this.handlePhysicalKeyboardDismiss.bind(this);
|
|
153
|
+
|
|
152
154
|
this.confirmDialog.setConfirmAction(this.performExit);
|
|
153
155
|
this.confirmDialog.setCancelAction(this.onConfirmDialogClose);
|
|
154
156
|
this.longPressDetector = new KeyboardLongPressManager();
|
|
155
157
|
}
|
|
156
158
|
|
|
157
159
|
componentDidMount() {
|
|
158
|
-
window.addEventListener("keydown", this.onKeyDownListener);
|
|
160
|
+
window.addEventListener("keydown", this.onKeyDownListener, true);
|
|
159
161
|
window.addEventListener("keyup", this.onKeyUp);
|
|
160
162
|
window.addEventListener("mousemove", this.onMouseMoveListener);
|
|
161
163
|
|
|
@@ -181,10 +183,20 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
181
183
|
|
|
182
184
|
window.addEventListener("onLongPress", this.handleLongPress.bind(this));
|
|
183
185
|
|
|
186
|
+
window.addEventListener(
|
|
187
|
+
"onPressRelease",
|
|
188
|
+
this.handleLongPressRelease.bind(this)
|
|
189
|
+
);
|
|
190
|
+
|
|
184
191
|
window.addEventListener(
|
|
185
192
|
"onLongPressRelease",
|
|
186
193
|
this.handleLongPressRelease.bind(this)
|
|
187
194
|
);
|
|
195
|
+
|
|
196
|
+
document.addEventListener(
|
|
197
|
+
"keyboardDismissedByPhysicalKeypress",
|
|
198
|
+
this.handlePhysicalKeyboardDismiss
|
|
199
|
+
);
|
|
188
200
|
}
|
|
189
201
|
|
|
190
202
|
componentWillUnmount() {
|
|
@@ -213,6 +225,11 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
213
225
|
"onLongPressRelease",
|
|
214
226
|
this.handleLongPressRelease
|
|
215
227
|
);
|
|
228
|
+
|
|
229
|
+
document.removeEventListener(
|
|
230
|
+
"keyboardDismissedByPhysicalKeypress",
|
|
231
|
+
this.handlePhysicalKeyboardDismiss
|
|
232
|
+
);
|
|
216
233
|
}
|
|
217
234
|
|
|
218
235
|
performExit() {
|
|
@@ -274,8 +291,7 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
274
291
|
this.handleWebOsBack();
|
|
275
292
|
}
|
|
276
293
|
} catch (e) {
|
|
277
|
-
|
|
278
|
-
console.warn("Failed to execute platformBack", e);
|
|
294
|
+
log_warning("Failed to execute platformBack", e);
|
|
279
295
|
}
|
|
280
296
|
}
|
|
281
297
|
|
|
@@ -339,39 +355,145 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
339
355
|
}
|
|
340
356
|
|
|
341
357
|
onKeyUp(event: KeyboardEvent) {
|
|
358
|
+
const { setDisplayState } = this.props;
|
|
359
|
+
|
|
342
360
|
if (this.keySupportsLongPress(event)) {
|
|
343
361
|
this.longPressDetector.endKeyPressMonitoring(event.code);
|
|
344
362
|
}
|
|
363
|
+
|
|
364
|
+
if (
|
|
365
|
+
keyCode(event).matches(KEYS.Enter) ||
|
|
366
|
+
(__DEV__ && keyCode(event).matches(KEYS.X))
|
|
367
|
+
) {
|
|
368
|
+
focusManager.pressOut();
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (
|
|
372
|
+
keyCode(event).matches(KEYS.Forward) ||
|
|
373
|
+
(__DEV__ && keyCode(event).matches(KEYS.C))
|
|
374
|
+
) {
|
|
375
|
+
setDisplayState(DISPLAY_STATES.HUD);
|
|
376
|
+
KeyInputHandler.getInstance().onForwardPressOut();
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (
|
|
380
|
+
keyCode(event).matches(KEYS.Rewind) ||
|
|
381
|
+
(__DEV__ && keyCode(event).matches(KEYS.Z))
|
|
382
|
+
) {
|
|
383
|
+
setDisplayState(DISPLAY_STATES.HUD);
|
|
384
|
+
KeyInputHandler.getInstance().onRewindPressOut();
|
|
385
|
+
}
|
|
345
386
|
}
|
|
346
387
|
|
|
347
|
-
|
|
388
|
+
/**
|
|
389
|
+
When pressing escape or the Native "Back" button:
|
|
390
|
+
if on the transport controls: we show the player
|
|
391
|
+
if on the player : we show the UI
|
|
392
|
+
if on the UI : we exit the app
|
|
393
|
+
note: backspace affects the keyboard input, so it behaves differently
|
|
394
|
+
*/
|
|
395
|
+
onBackPress(event) {
|
|
348
396
|
const { displayState, setDisplayState, navigator } = this.props;
|
|
397
|
+
const { isDialogVisible } = confirmationDialogStore.getState();
|
|
349
398
|
const { isKeyboardVisible } = this.state;
|
|
350
399
|
|
|
351
|
-
|
|
400
|
+
log_debug("onBackPress", {
|
|
401
|
+
eventCode: event.code,
|
|
402
|
+
displayState,
|
|
403
|
+
isDialogVisible,
|
|
404
|
+
isKeyboardVisible,
|
|
405
|
+
});
|
|
352
406
|
|
|
353
407
|
if (isKeyboardVisible && shouldUseOnScreenKeyboard()) {
|
|
354
|
-
|
|
355
|
-
this.handleKeyboardDismiss();
|
|
356
|
-
}
|
|
357
|
-
|
|
408
|
+
this.handleKeyboardDismiss();
|
|
358
409
|
event.preventDefault();
|
|
359
410
|
event.stopPropagation();
|
|
360
411
|
|
|
361
412
|
return;
|
|
362
413
|
}
|
|
363
414
|
|
|
415
|
+
switch (displayState) {
|
|
416
|
+
case DISPLAY_STATES.DEFAULT:
|
|
417
|
+
case DISPLAY_STATES.PLAYER:
|
|
418
|
+
if (isDialogVisible) {
|
|
419
|
+
this.onConfirmDialogClose();
|
|
420
|
+
} else if (navigator.canGoBack()) {
|
|
421
|
+
navigator.goBack();
|
|
422
|
+
} else if (this.isHomeScreen()) {
|
|
423
|
+
this.platformBack();
|
|
424
|
+
} else if (navigator.currentRoute.includes("hook")) {
|
|
425
|
+
// If code reaches this block navigator cant go back, it is not home,
|
|
426
|
+
// and this is a login plugin that was pushed on top of the home
|
|
427
|
+
// Treat it like a home screen and run platformBack()
|
|
428
|
+
const entryData = navigator?.data?.entry;
|
|
429
|
+
const isHookInHomescreen = entryData?.payload?.home;
|
|
430
|
+
|
|
431
|
+
const isHookFlowBlocker =
|
|
432
|
+
typeof entryData?.hookPlugin?.module?.isFlowBlocker === "function"
|
|
433
|
+
? entryData?.hookPlugin?.module?.isFlowBlocker()
|
|
434
|
+
: entryData?.hookPlugin?.module?.isFlowBlocker;
|
|
435
|
+
|
|
436
|
+
if (isHookInHomescreen && isHookFlowBlocker) {
|
|
437
|
+
this.platformBack();
|
|
438
|
+
} else {
|
|
439
|
+
navigator.goBack(false, true);
|
|
440
|
+
}
|
|
441
|
+
} else {
|
|
442
|
+
this.goToHome();
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
break;
|
|
446
|
+
case DISPLAY_STATES.HUD:
|
|
447
|
+
// IF transport controls are open, close them instead of closing player
|
|
448
|
+
setDisplayState(DISPLAY_STATES.PLAYER);
|
|
449
|
+
break;
|
|
450
|
+
default:
|
|
451
|
+
setDisplayState(DISPLAY_STATES.PLAYER);
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
onBackspacePress(event) {
|
|
457
|
+
const isInputFocused =
|
|
458
|
+
document.activeElement instanceof HTMLInputElement ||
|
|
459
|
+
document.activeElement instanceof HTMLTextAreaElement;
|
|
460
|
+
|
|
461
|
+
// Don't handle backspace if input is focused
|
|
462
|
+
if (isInputFocused) {
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
this.onBackPress(event);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
onKeyDown(event) {
|
|
470
|
+
const { displayState, setDisplayState, navigator } = this.props;
|
|
471
|
+
const { isDialogVisible } = confirmationDialogStore.getState();
|
|
472
|
+
|
|
473
|
+
if (keyCode(event).matches("Backspace")) {
|
|
474
|
+
this.onBackspacePress(event);
|
|
475
|
+
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (keyCode(event).matchesAny(KEYS.Back, KEYS.Escape, KEYS.EscapeDevice)) {
|
|
480
|
+
this.onBackPress(event);
|
|
481
|
+
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
|
|
364
485
|
if (this.keySupportsLongPress(event)) {
|
|
365
486
|
this.longPressDetector.monitorKeyPress(event.code, event);
|
|
366
487
|
}
|
|
367
488
|
|
|
368
|
-
//
|
|
369
|
-
if (event.repeat) return;
|
|
489
|
+
// Only handle repeat events for arrow keys
|
|
490
|
+
if (event.repeat && !keyCode(event).matchesAny(...ARROW_KEYS)) return;
|
|
370
491
|
|
|
371
492
|
/**
|
|
372
493
|
When the player is displayed, we listen to Native Play / Pause / Stop
|
|
373
494
|
buttons. Any other key press will display the transport controls
|
|
374
495
|
*/
|
|
496
|
+
|
|
375
497
|
if (
|
|
376
498
|
displayState === DISPLAY_STATES.PLAYER ||
|
|
377
499
|
displayState === DISPLAY_STATES.HUD
|
|
@@ -404,79 +526,35 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
404
526
|
return;
|
|
405
527
|
}
|
|
406
528
|
|
|
407
|
-
if (
|
|
529
|
+
if (
|
|
530
|
+
keyCode(event).matches(KEYS.Forward) ||
|
|
531
|
+
(__DEV__ && keyCode(event).matches(KEYS.C))
|
|
532
|
+
) {
|
|
408
533
|
setDisplayState(DISPLAY_STATES.HUD);
|
|
409
|
-
|
|
534
|
+
KeyInputHandler.getInstance().onForwardPress();
|
|
410
535
|
|
|
411
536
|
return;
|
|
412
537
|
}
|
|
413
538
|
|
|
414
|
-
if (
|
|
539
|
+
if (
|
|
540
|
+
keyCode(event).matches(KEYS.Rewind) ||
|
|
541
|
+
(__DEV__ && keyCode(event).matches(KEYS.Z))
|
|
542
|
+
) {
|
|
415
543
|
setDisplayState(DISPLAY_STATES.HUD);
|
|
416
|
-
|
|
544
|
+
KeyInputHandler.getInstance().onRewindPress();
|
|
417
545
|
|
|
418
546
|
return;
|
|
419
547
|
}
|
|
420
548
|
}
|
|
421
549
|
|
|
422
|
-
/**
|
|
423
|
-
When pressing backspace or the Native "Back" button:
|
|
424
|
-
if on the transport controls: we show the player
|
|
425
|
-
if on the player : we show the UI
|
|
426
|
-
if on the UI : we exit the app
|
|
427
|
-
*/
|
|
428
|
-
|
|
429
550
|
if (
|
|
430
|
-
keyCode(event).
|
|
431
|
-
|
|
432
|
-
KEYS.Escape,
|
|
433
|
-
KEYS.EscapeDevice,
|
|
434
|
-
KEYS.Backspace
|
|
435
|
-
)
|
|
551
|
+
keyCode(event).matches(KEYS.Enter) ||
|
|
552
|
+
(__DEV__ && keyCode(event).matches(KEYS.X))
|
|
436
553
|
) {
|
|
437
|
-
switch (displayState) {
|
|
438
|
-
case DISPLAY_STATES.DEFAULT:
|
|
439
|
-
case DISPLAY_STATES.PLAYER:
|
|
440
|
-
if (isDialogVisible) {
|
|
441
|
-
this.onConfirmDialogClose();
|
|
442
|
-
} else if (navigator.canGoBack()) {
|
|
443
|
-
navigator.goBack();
|
|
444
|
-
} else if (this.isHomeScreen()) {
|
|
445
|
-
this.platformBack();
|
|
446
|
-
} else if (navigator.currentRoute.includes("hook")) {
|
|
447
|
-
// If code reaches this block navigator cant go back, it is not home,
|
|
448
|
-
// and this is a login plugin that was pushed on top of the home
|
|
449
|
-
// Treat it like a home screen and run platformBack()
|
|
450
|
-
const entryData = navigator?.data?.entry;
|
|
451
|
-
const isHookInHomescreen = entryData?.payload?.home;
|
|
452
|
-
|
|
453
|
-
const isHookFlowBlocker =
|
|
454
|
-
entryData?.hookPlugin?.module?.isFlowBlocker();
|
|
455
|
-
|
|
456
|
-
if (isHookInHomescreen && isHookFlowBlocker) {
|
|
457
|
-
this.platformBack();
|
|
458
|
-
} else {
|
|
459
|
-
navigator.goBack(false, true);
|
|
460
|
-
}
|
|
461
|
-
} else {
|
|
462
|
-
this.goToHome();
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
break;
|
|
466
|
-
case DISPLAY_STATES.HUD:
|
|
467
|
-
// IF transport controls are open, close them instead of closing player
|
|
468
|
-
setDisplayState(DISPLAY_STATES.PLAYER);
|
|
469
|
-
break;
|
|
470
|
-
default:
|
|
471
|
-
setDisplayState(DISPLAY_STATES.PLAYER);
|
|
472
|
-
break;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
if (keyCode(event).matches(KEYS.Enter)) {
|
|
477
554
|
if (displayState === DISPLAY_STATES.PLAYER) {
|
|
478
555
|
setDisplayState(DISPLAY_STATES.HUD);
|
|
479
556
|
} else {
|
|
557
|
+
focusManager.pressIn();
|
|
480
558
|
focusManager.press();
|
|
481
559
|
}
|
|
482
560
|
|
|
@@ -493,10 +571,7 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
493
571
|
this.platformBack();
|
|
494
572
|
}
|
|
495
573
|
|
|
496
|
-
if (
|
|
497
|
-
keyCode(event).matchesAny(...ARROW_KEYS) &&
|
|
498
|
-
!this.state.isKeyboardVisible
|
|
499
|
-
) {
|
|
574
|
+
if (keyCode(event).matchesAny(...ARROW_KEYS)) {
|
|
500
575
|
if (displayState === DISPLAY_STATES.PLAYER) {
|
|
501
576
|
setDisplayState(DISPLAY_STATES.HUD);
|
|
502
577
|
}
|
|
@@ -509,12 +584,6 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
509
584
|
focusManager.moveFocus(direction);
|
|
510
585
|
}
|
|
511
586
|
|
|
512
|
-
if (!isDialogVisible) {
|
|
513
|
-
// run focus recovery on user interaction attempt
|
|
514
|
-
// probably we failed to set initial focus for some reason
|
|
515
|
-
focusManager.recoverFocus();
|
|
516
|
-
}
|
|
517
|
-
|
|
518
587
|
if (
|
|
519
588
|
displayState === DISPLAY_STATES.PLAYER ||
|
|
520
589
|
displayState === DISPLAY_STATES.HUD
|
|
@@ -590,6 +659,7 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
590
659
|
|
|
591
660
|
if (
|
|
592
661
|
(target.tagName === "INPUT" && target.type === "text") ||
|
|
662
|
+
(target.tagName === "INPUT" && target.type === "password") ||
|
|
593
663
|
target.tagName === "TEXTAREA"
|
|
594
664
|
) {
|
|
595
665
|
this.setState({
|
|
@@ -618,7 +688,7 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
618
688
|
}
|
|
619
689
|
}
|
|
620
690
|
|
|
621
|
-
handleKeyboardDismiss
|
|
691
|
+
handleKeyboardDismiss() {
|
|
622
692
|
this.isDismissing = true;
|
|
623
693
|
|
|
624
694
|
this.setState(
|
|
@@ -629,9 +699,10 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
629
699
|
},
|
|
630
700
|
() => {
|
|
631
701
|
this.isDismissing = false;
|
|
702
|
+
focusManager.recoverFocus();
|
|
632
703
|
}
|
|
633
704
|
);
|
|
634
|
-
}
|
|
705
|
+
}
|
|
635
706
|
|
|
636
707
|
handleKeyboardInput = (value: string) => {
|
|
637
708
|
const { activeInputRef } = this.state;
|
|
@@ -688,53 +759,71 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
688
759
|
}, SEQUENCE_ENTRY_TIME);
|
|
689
760
|
}
|
|
690
761
|
|
|
762
|
+
// TODO: Delete it on next PR if not needed
|
|
691
763
|
private handleLongPress = (event: CustomEvent) => {
|
|
692
764
|
const { keyCode } = event.detail;
|
|
693
|
-
const { displayState } = this.props;
|
|
694
|
-
const playerState = displayState === DISPLAY_STATES.PLAYER;
|
|
695
|
-
const hudState = displayState === DISPLAY_STATES.HUD;
|
|
696
|
-
const playbackState = playerState || hudState;
|
|
697
765
|
this.longPressActive = true;
|
|
698
766
|
this.cancelHudTimeout();
|
|
699
767
|
|
|
700
768
|
switch (keyCode) {
|
|
701
|
-
case KEYS.Forward.code:
|
|
702
|
-
playbackState && this.props.seekMethods.onForwardLongPress();
|
|
703
|
-
break;
|
|
704
|
-
|
|
705
|
-
case KEYS.Rewind.code:
|
|
706
|
-
playbackState && this.props.seekMethods.onRewindLongPress();
|
|
707
|
-
break;
|
|
708
|
-
|
|
709
769
|
case KEYS.Enter.code:
|
|
770
|
+
case KEYS.X.code:
|
|
710
771
|
focusManager.longPress();
|
|
711
772
|
break;
|
|
712
773
|
}
|
|
713
774
|
};
|
|
714
775
|
|
|
776
|
+
// TODO: Delete it on next PR if not needed
|
|
715
777
|
private handleLongPressRelease = (event: CustomEvent) => {
|
|
716
778
|
const { keyCode } = event.detail;
|
|
717
779
|
this.longPressActive = false;
|
|
718
780
|
this.resetHudTimeout();
|
|
719
781
|
|
|
720
782
|
switch (keyCode) {
|
|
721
|
-
case KEYS.Forward.code:
|
|
722
|
-
this.props.seekMethods.onForwardPressOut();
|
|
723
|
-
break;
|
|
724
|
-
|
|
725
|
-
case KEYS.Rewind.code:
|
|
726
|
-
this.props.seekMethods.onRewindPressOut();
|
|
727
|
-
break;
|
|
728
|
-
|
|
729
783
|
case KEYS.Enter.code:
|
|
730
|
-
|
|
784
|
+
case KEYS.X.code:
|
|
785
|
+
// focusManager.pressOut();
|
|
731
786
|
break;
|
|
732
787
|
}
|
|
733
788
|
};
|
|
734
789
|
|
|
735
790
|
private keySupportsLongPress(event: KeyboardEvent): boolean {
|
|
736
|
-
return [
|
|
737
|
-
|
|
791
|
+
return [
|
|
792
|
+
KEYS.Forward.code,
|
|
793
|
+
KEYS.Rewind.code,
|
|
794
|
+
KEYS.Enter.code,
|
|
795
|
+
KEYS.Z.code,
|
|
796
|
+
KEYS.X.code,
|
|
797
|
+
KEYS.C.code,
|
|
798
|
+
].includes(event.code);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
handlePhysicalKeyboardDismiss(event: CustomEvent) {
|
|
802
|
+
const { value, position, inputElement } = event.detail;
|
|
803
|
+
|
|
804
|
+
this.isDismissing = true;
|
|
805
|
+
|
|
806
|
+
this.setState(
|
|
807
|
+
{
|
|
808
|
+
isKeyboardVisible: false,
|
|
809
|
+
keyboardInput: "",
|
|
810
|
+
activeInputRef: null,
|
|
811
|
+
},
|
|
812
|
+
() => {
|
|
813
|
+
if (inputElement) {
|
|
814
|
+
inputElement.value = value;
|
|
815
|
+
inputElement.focus();
|
|
816
|
+
inputElement.setSelectionRange(position, position);
|
|
817
|
+
|
|
818
|
+
const keyboardEvent = new CustomEvent("keyboardUpdate", {
|
|
819
|
+
detail: { value },
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
document.dispatchEvent(keyboardEvent);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
this.isDismissing = false;
|
|
826
|
+
}
|
|
738
827
|
);
|
|
739
828
|
}
|
|
740
829
|
|
|
@@ -754,7 +843,6 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
754
843
|
|
|
755
844
|
export const InteractionManager = R.compose(
|
|
756
845
|
withNavigator,
|
|
757
|
-
withUseSeek,
|
|
758
846
|
withXray,
|
|
759
847
|
connectToStore(R.pick(["rivers"])),
|
|
760
848
|
PlayerContentContext.withConsumer,
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
3
|
exports[`loadDomPlugins when there are plugins requiring to write to the dom skips plugins without init method 1`] = `
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
[
|
|
5
|
+
[
|
|
6
6
|
"could not initialize plugin Plugin with Dom node & no init method - it doesn't have an init method",
|
|
7
|
-
|
|
8
|
-
"plugin":
|
|
9
|
-
"configuration":
|
|
7
|
+
{
|
|
8
|
+
"plugin": {
|
|
9
|
+
"configuration": {
|
|
10
10
|
"foo": "bar",
|
|
11
11
|
},
|
|
12
|
-
"module":
|
|
12
|
+
"module": {
|
|
13
13
|
"requiresAppendingDomNode": true,
|
|
14
14
|
},
|
|
15
15
|
"name": "Plugin with Dom node & no init method",
|
package/App/Loader/loadLocale.js
CHANGED
|
@@ -4,8 +4,8 @@ import { QuickBrickCommunicationModule } from "../../Polyfills/QuickBrickCommuni
|
|
|
4
4
|
import { loadLocaleImpl, getLanguageCode } from "./utils";
|
|
5
5
|
|
|
6
6
|
type Locale = {
|
|
7
|
-
language: string
|
|
8
|
-
country: string
|
|
7
|
+
language: string;
|
|
8
|
+
country: string;
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
async function getBrowserLocale() {
|
|
@@ -11,11 +11,28 @@ import {
|
|
|
11
11
|
PLATFORMS,
|
|
12
12
|
} from "./const";
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {"tv" | "web" | "other"} DeviceType
|
|
16
|
+
* @typedef {"samsung_tv" | "lg_tv" | unknown} CustomPlatformTypes
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Retrieves the user agent string from the browser or global navigator.
|
|
21
|
+
* @returns {string} The user agent string.
|
|
22
|
+
*/
|
|
14
23
|
export const getUserAgent = () =>
|
|
15
24
|
window?.navigator?.userAgent || global?.navigator?.userAgent;
|
|
16
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Checks if the device is HD (1280 width).
|
|
28
|
+
* @returns {boolean} True if the device is HD, false otherwise.
|
|
29
|
+
*/
|
|
17
30
|
export const isHD = () => window.innerWidth === 1280;
|
|
18
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Checks if the platform is LG.
|
|
34
|
+
* @returns {boolean} True if the platform is LG, false otherwise.
|
|
35
|
+
*/
|
|
19
36
|
export const isLgPlatform = () => {
|
|
20
37
|
const userAgent = getUserAgent();
|
|
21
38
|
|
|
@@ -23,10 +40,13 @@ export const isLgPlatform = () => {
|
|
|
23
40
|
return false;
|
|
24
41
|
}
|
|
25
42
|
|
|
26
|
-
// Note: LG uses the number 0 in their userAgent, possibly because of Palm WebOS
|
|
27
43
|
return hasWebOS || userAgent.includes(PLATFORMS.webos);
|
|
28
44
|
};
|
|
29
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Checks if the platform is Samsung.
|
|
48
|
+
* @returns {boolean} True if the platform is Samsung, false otherwise.
|
|
49
|
+
*/
|
|
30
50
|
export const isSamsungPlatform = () => {
|
|
31
51
|
const userAgent = getUserAgent();
|
|
32
52
|
|
|
@@ -42,42 +62,39 @@ export const isSamsungPlatform = () => {
|
|
|
42
62
|
};
|
|
43
63
|
|
|
44
64
|
/**
|
|
45
|
-
* Checks if
|
|
46
|
-
*
|
|
47
|
-
* Use this method when you need to identify the platform on runtime
|
|
48
|
-
* before the VIZIO Companion Library has loaded
|
|
65
|
+
* Checks if the platform is Vizio by using the user agent string.
|
|
66
|
+
* @returns {boolean} True if the platform is Vizio, false otherwise.
|
|
49
67
|
*/
|
|
50
68
|
export const isVizioPlatform = () => {
|
|
51
69
|
const userAgent = getUserAgent();
|
|
52
70
|
|
|
53
|
-
|
|
71
|
+
return (
|
|
54
72
|
userAgent.includes(PLATFORMS.vizio) ||
|
|
55
73
|
userAgent.includes(PLATFORMS.smartcast) ||
|
|
56
|
-
userAgent.includes(PLATFORMS.conjure)
|
|
57
|
-
|
|
58
|
-
return isVizioAgent;
|
|
74
|
+
userAgent.includes(PLATFORMS.conjure)
|
|
75
|
+
);
|
|
59
76
|
};
|
|
60
77
|
|
|
61
78
|
/**
|
|
62
|
-
* Checks if
|
|
63
|
-
*
|
|
64
|
-
* because it mainly checks whether we have access to apis, which may not be available yet
|
|
79
|
+
* Checks if the Vizio APIs are available.
|
|
80
|
+
* @returns {boolean} True if the Vizio APIs are available, false otherwise.
|
|
65
81
|
*/
|
|
66
82
|
export const hasVizioAPIs = () => {
|
|
67
|
-
|
|
83
|
+
return (
|
|
68
84
|
typeof window.VIZIO !== "undefined" &&
|
|
69
|
-
window?.applicaster?.vizioLibraryDidLoad
|
|
70
|
-
|
|
71
|
-
return hasAPIs;
|
|
85
|
+
window?.applicaster?.vizioLibraryDidLoad
|
|
86
|
+
);
|
|
72
87
|
};
|
|
73
88
|
|
|
74
89
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* @returns {"tv" | "web" | "other"} value
|
|
90
|
+
* Determines the device type based on the platform.
|
|
91
|
+
* @returns {DeviceType} The device type ("tv", "web", or "other").
|
|
78
92
|
*/
|
|
79
93
|
export const getDeviceType = () => {
|
|
80
|
-
|
|
94
|
+
/** @type {CustomPlatformTypes} */
|
|
95
|
+
const platformOS = Platform.OS;
|
|
96
|
+
|
|
97
|
+
switch (platformOS) {
|
|
81
98
|
case "samsung_tv":
|
|
82
99
|
return isSamsungPlatform() ? "tv" : "web";
|
|
83
100
|
case "lg_tv":
|
|
@@ -88,14 +105,15 @@ export const getDeviceType = () => {
|
|
|
88
105
|
};
|
|
89
106
|
|
|
90
107
|
/**
|
|
91
|
-
*
|
|
92
|
-
* @param {string} key - key
|
|
108
|
+
* Renames keys based on a predefined map.
|
|
109
|
+
* @param {string} key - The key to rename.
|
|
110
|
+
* @returns {string} The renamed key.
|
|
93
111
|
*/
|
|
94
112
|
export const renameKeys = (key) => desiredKeysMap?.[key] || key;
|
|
95
113
|
|
|
96
114
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
115
|
+
* Retrieves the connection info from WebOS.
|
|
116
|
+
* @returns {Promise<{networkType: string} | null>} A promise that resolves with the connection info or null.
|
|
99
117
|
*/
|
|
100
118
|
export const getWebOSConnectionInfo = () => {
|
|
101
119
|
return new Promise((resolve, reject) => {
|
|
@@ -108,7 +126,6 @@ export const getWebOSConnectionInfo = () => {
|
|
|
108
126
|
webOS.service.request("luna://com.palm.connectionmanager", {
|
|
109
127
|
method: "getStatus",
|
|
110
128
|
parameters: { subscribe: false },
|
|
111
|
-
|
|
112
129
|
onSuccess: (args) => {
|
|
113
130
|
const types = Object.keys(args)
|
|
114
131
|
.filter((key) => args[key].state === "connected")
|
|
@@ -116,7 +133,6 @@ export const getWebOSConnectionInfo = () => {
|
|
|
116
133
|
|
|
117
134
|
resolve({ networkType: types });
|
|
118
135
|
},
|
|
119
|
-
|
|
120
136
|
onFailure: (error) => {
|
|
121
137
|
console.error(
|
|
122
138
|
"getWebOSConnectionInfo: There was an error getting connection info",
|
|
@@ -129,6 +145,10 @@ export const getWebOSConnectionInfo = () => {
|
|
|
129
145
|
});
|
|
130
146
|
};
|
|
131
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Retrieves the connection info from Tizen.
|
|
150
|
+
* @returns {Promise<{networkType: string} | null>} A promise that resolves with the connection info or null.
|
|
151
|
+
*/
|
|
132
152
|
export const getTizenConnectionInfo = () => {
|
|
133
153
|
return new Promise((resolve, reject) => {
|
|
134
154
|
if (!isSamsungPlatform) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
3
|
exports[`storageObject when the requested storage exists getAllItems returns all keys 1`] = `
|
|
4
|
-
|
|
4
|
+
{
|
|
5
5
|
"baz_::_foo": "baz",
|
|
6
6
|
"baz_::_otherKey": "otherValue",
|
|
7
7
|
"foo": "bar",
|
|
@@ -10,7 +10,7 @@ Object {
|
|
|
10
10
|
`;
|
|
11
11
|
|
|
12
12
|
exports[`storageObject when the requested storage exists getAllItems returns all keys 2`] = `
|
|
13
|
-
|
|
13
|
+
{
|
|
14
14
|
"baz_::_foo": "baz",
|
|
15
15
|
"baz_::_otherKey": "otherValue",
|
|
16
16
|
"foo": "bar",
|
|
@@ -19,14 +19,14 @@ Object {
|
|
|
19
19
|
`;
|
|
20
20
|
|
|
21
21
|
exports[`storageObject when the requested storage exists getAllItems returns all keys in the namespace if provided 1`] = `
|
|
22
|
-
|
|
22
|
+
{
|
|
23
23
|
"foo": "baz",
|
|
24
24
|
"otherKey": "otherValue",
|
|
25
25
|
}
|
|
26
26
|
`;
|
|
27
27
|
|
|
28
28
|
exports[`storageObject when the requested storage exists getAllItems returns all keys in the namespace if provided 2`] = `
|
|
29
|
-
|
|
29
|
+
{
|
|
30
30
|
"foo": "baz",
|
|
31
31
|
"otherKey": "otherValue",
|
|
32
32
|
}
|
package/index.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
/// <reference types="@applicaster/applicaster-types" />
|
|
2
2
|
import * as R from "ramda";
|
|
3
|
-
import { useHistory, useLocation } from "react-router-dom";
|
|
4
3
|
|
|
5
4
|
import { createQuickBrickApp } from "@applicaster/quick-brick-core";
|
|
6
5
|
import * as QuickBrickManager from "@applicaster/zapp-react-native-bridge/QuickBrick";
|
|
7
6
|
import * as Contexts from "@applicaster/zapp-react-native-ui-components/Contexts";
|
|
8
7
|
import { OfflineHandler } from "@applicaster/zapp-react-native-ui-components/Components/OfflineHandler";
|
|
9
8
|
import { getXray } from "@applicaster/zapp-react-native-utils/logger";
|
|
10
|
-
import { ZappDomRouter } from "./App/Router";
|
|
11
9
|
import { Layout } from "./App/Layout";
|
|
12
10
|
import { SplashLoader } from "./App/Loader/SplashLoader";
|
|
13
11
|
import { appLoader } from "./App/Loader";
|
|
@@ -24,11 +22,6 @@ const contextProviders = R.map(R.prop("withProvider"), R.values(Contexts));
|
|
|
24
22
|
if (withXray) contextProviders.push(withXray);
|
|
25
23
|
|
|
26
24
|
export const ZappApp = createQuickBrickApp({
|
|
27
|
-
navigations: {
|
|
28
|
-
Router: ZappDomRouter,
|
|
29
|
-
useLocation,
|
|
30
|
-
useHistory,
|
|
31
|
-
},
|
|
32
25
|
QuickBrickManager,
|
|
33
26
|
Layout,
|
|
34
27
|
OfflineHandler,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-dom-app",
|
|
3
|
-
"version": "13.0.0-rc.
|
|
3
|
+
"version": "13.0.0-rc.41",
|
|
4
4
|
"description": "Zapp App Component for Applicaster's Quick Brick React Native App",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -22,13 +22,12 @@
|
|
|
22
22
|
},
|
|
23
23
|
"homepage": "https://github.com/applicaster/zapp-react-dom-app#readme",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@applicaster/zapp-react-dom-ui-components": "13.0.0-rc.
|
|
26
|
-
"@applicaster/zapp-react-native-bridge": "13.0.0-rc.
|
|
27
|
-
"@applicaster/zapp-react-native-redux": "13.0.0-rc.
|
|
28
|
-
"@applicaster/zapp-react-native-ui-components": "13.0.0-rc.
|
|
29
|
-
"@applicaster/zapp-react-native-utils": "13.0.0-rc.
|
|
25
|
+
"@applicaster/zapp-react-dom-ui-components": "13.0.0-rc.41",
|
|
26
|
+
"@applicaster/zapp-react-native-bridge": "13.0.0-rc.41",
|
|
27
|
+
"@applicaster/zapp-react-native-redux": "13.0.0-rc.41",
|
|
28
|
+
"@applicaster/zapp-react-native-ui-components": "13.0.0-rc.41",
|
|
29
|
+
"@applicaster/zapp-react-native-utils": "13.0.0-rc.41",
|
|
30
30
|
"abortcontroller-polyfill": "^1.7.5",
|
|
31
|
-
"history": "^4.7.2",
|
|
32
31
|
"react-router-dom": "^5.1.2",
|
|
33
32
|
"typeface-montserrat": "^0.0.54",
|
|
34
33
|
"video.js": "7.14.3",
|
|
@@ -36,9 +35,19 @@
|
|
|
36
35
|
"videojs-contrib-quality-levels": "3.0.0"
|
|
37
36
|
},
|
|
38
37
|
"peerDependencies": {
|
|
38
|
+
"@applicaster/zapp-pipes-v2-client": "*",
|
|
39
|
+
"@react-native-community/netinfo": "*",
|
|
40
|
+
"@types/node": "*",
|
|
39
41
|
"core-js": "3.29.1",
|
|
42
|
+
"immer": "*",
|
|
40
43
|
"react": "17.0.2",
|
|
41
44
|
"react-dom": "17.0.2",
|
|
42
|
-
"react-native
|
|
45
|
+
"react-native": "*",
|
|
46
|
+
"react-native-safe-area-context": "*",
|
|
47
|
+
"react-native-svg": "*",
|
|
48
|
+
"react-native-web": "0.18.11",
|
|
49
|
+
"uglify-js": "*",
|
|
50
|
+
"validate-color": "*",
|
|
51
|
+
"zustand": "*"
|
|
43
52
|
}
|
|
44
53
|
}
|
package/App/Router/index.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import PropTypes from "prop-types";
|
|
3
|
-
|
|
4
|
-
import { Router } from "react-router";
|
|
5
|
-
import { createMemoryHistory } from "history";
|
|
6
|
-
|
|
7
|
-
const history = createMemoryHistory();
|
|
8
|
-
|
|
9
|
-
export const ZappDomRouter = ({ children, ...otherProps }) => (
|
|
10
|
-
<Router history={history} {...otherProps}>
|
|
11
|
-
{children}
|
|
12
|
-
</Router>
|
|
13
|
-
);
|
|
14
|
-
|
|
15
|
-
ZappDomRouter.propTypes = {
|
|
16
|
-
children: PropTypes.node.isRequired,
|
|
17
|
-
};
|
package/App/Splash/Spinner.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
|
|
3
|
-
import "./spinner-styles.css";
|
|
4
|
-
|
|
5
|
-
export const Spinner = (color) => {
|
|
6
|
-
const spinnerColor = {
|
|
7
|
-
background: color.color,
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
return (
|
|
11
|
-
<div>
|
|
12
|
-
<div id="progress">
|
|
13
|
-
<div className="spinner">
|
|
14
|
-
<div style={spinnerColor} className="bar1" />
|
|
15
|
-
<div style={spinnerColor} className="bar2" />
|
|
16
|
-
<div style={spinnerColor} className="bar3" />
|
|
17
|
-
<div style={spinnerColor} className="bar4" />
|
|
18
|
-
<div style={spinnerColor} className="bar5" />
|
|
19
|
-
<div style={spinnerColor} className="bar6" />
|
|
20
|
-
<div style={spinnerColor} className="bar7" />
|
|
21
|
-
<div style={spinnerColor} className="bar8" />
|
|
22
|
-
<div style={spinnerColor} className="bar9" />
|
|
23
|
-
<div style={spinnerColor} className="bar10" />
|
|
24
|
-
<div style={spinnerColor} className="bar11" />
|
|
25
|
-
<div style={spinnerColor} className="bar12" />
|
|
26
|
-
</div>
|
|
27
|
-
</div>
|
|
28
|
-
</div>
|
|
29
|
-
);
|
|
30
|
-
};
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
div.spinner {
|
|
2
|
-
position: absolute;
|
|
3
|
-
width: 100px;
|
|
4
|
-
height: 100px;
|
|
5
|
-
display: inline-block;
|
|
6
|
-
top: 50%;
|
|
7
|
-
left: 50%;
|
|
8
|
-
margin: -60px 0 0 -70px;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
div.spinner div {
|
|
12
|
-
width: 6%;
|
|
13
|
-
height: 16%;
|
|
14
|
-
position: absolute;
|
|
15
|
-
left: 49%;
|
|
16
|
-
top: 43%;
|
|
17
|
-
opacity: 0;
|
|
18
|
-
-webkit-border-radius: 50px;
|
|
19
|
-
-webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.2);
|
|
20
|
-
-webkit-animation: fade 1s linear infinite;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
@-webkit-keyframes fade {
|
|
24
|
-
from {
|
|
25
|
-
opacity: 1;
|
|
26
|
-
}
|
|
27
|
-
to {
|
|
28
|
-
opacity: 0.25;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
div.spinner div.bar1 {
|
|
33
|
-
-webkit-transform: rotate(0deg) translate(0, -130%);
|
|
34
|
-
-webkit-animation-delay: 0s;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
div.spinner div.bar2 {
|
|
38
|
-
-webkit-transform: rotate(30deg) translate(0, -130%);
|
|
39
|
-
-webkit-animation-delay: -0.9167s;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
div.spinner div.bar3 {
|
|
43
|
-
-webkit-transform: rotate(60deg) translate(0, -130%);
|
|
44
|
-
-webkit-animation-delay: -0.833s;
|
|
45
|
-
}
|
|
46
|
-
div.spinner div.bar4 {
|
|
47
|
-
-webkit-transform: rotate(90deg) translate(0, -130%);
|
|
48
|
-
-webkit-animation-delay: -0.7497s;
|
|
49
|
-
}
|
|
50
|
-
div.spinner div.bar5 {
|
|
51
|
-
-webkit-transform: rotate(120deg) translate(0, -130%);
|
|
52
|
-
-webkit-animation-delay: -0.667s;
|
|
53
|
-
}
|
|
54
|
-
div.spinner div.bar6 {
|
|
55
|
-
-webkit-transform: rotate(150deg) translate(0, -130%);
|
|
56
|
-
-webkit-animation-delay: -0.5837s;
|
|
57
|
-
}
|
|
58
|
-
div.spinner div.bar7 {
|
|
59
|
-
-webkit-transform: rotate(180deg) translate(0, -130%);
|
|
60
|
-
-webkit-animation-delay: -0.5s;
|
|
61
|
-
}
|
|
62
|
-
div.spinner div.bar8 {
|
|
63
|
-
-webkit-transform: rotate(210deg) translate(0, -130%);
|
|
64
|
-
-webkit-animation-delay: -0.4167s;
|
|
65
|
-
}
|
|
66
|
-
div.spinner div.bar9 {
|
|
67
|
-
-webkit-transform: rotate(240deg) translate(0, -130%);
|
|
68
|
-
-webkit-animation-delay: -0.333s;
|
|
69
|
-
}
|
|
70
|
-
div.spinner div.bar10 {
|
|
71
|
-
-webkit-transform: rotate(270deg) translate(0, -130%);
|
|
72
|
-
-webkit-animation-delay: -0.2497s;
|
|
73
|
-
}
|
|
74
|
-
div.spinner div.bar11 {
|
|
75
|
-
-webkit-transform: rotate(300deg) translate(0, -130%);
|
|
76
|
-
-webkit-animation-delay: -0.167s;
|
|
77
|
-
}
|
|
78
|
-
div.spinner div.bar12 {
|
|
79
|
-
-webkit-transform: rotate(330deg) translate(0, -130%);
|
|
80
|
-
-webkit-animation-delay: -0.0833s;
|
|
81
|
-
}
|