@applicaster/zapp-react-dom-app 14.0.0-rc.6 → 14.0.0-rc.61
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/hoc/__tests__/withBacktoTopActionHOC.test.tsx +149 -0
- package/App/InteractionManager/hoc/hooks/index.ts +34 -0
- package/App/InteractionManager/hoc/index.ts +4 -0
- package/App/InteractionManager/hoc/withBackToTopAction.tsx +56 -0
- package/App/InteractionManager/index.tsx +66 -22
- package/App/Layout/index.tsx +2 -1
- package/App/Loader/SplashLoader.js +1 -1
- package/App/Loader/loadSessionStorageData.js +22 -17
- package/App/Loader/utils/const.js +1 -0
- package/App/Loader/utils/platform.js +1 -21
- package/App/Splash/index.js +1 -1
- package/Polyfills/Storage/__tests__/storage.test.js +8 -17
- package/Polyfills/index.js +5 -6
- package/package.json +6 -10
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@testing-library/react-native";
|
|
3
|
+
import { withBackToTopActionHOC } from "../withBackToTopAction";
|
|
4
|
+
|
|
5
|
+
// Mock focusManager and hook
|
|
6
|
+
jest.mock("@applicaster/zapp-react-native-utils/appUtils", () => ({
|
|
7
|
+
focusManager: {
|
|
8
|
+
isFocusOnMenu: jest.fn(),
|
|
9
|
+
isFocusOnContent: jest.fn(),
|
|
10
|
+
},
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
jest.mock("../hooks", () => ({
|
|
14
|
+
useCurrentScreenIsHome: jest.fn(),
|
|
15
|
+
useCurrentScreenIsRoot: jest.fn(),
|
|
16
|
+
useCurrentScreenIsTabs: jest.fn(),
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils";
|
|
20
|
+
import {
|
|
21
|
+
useCurrentScreenIsHome,
|
|
22
|
+
useCurrentScreenIsRoot,
|
|
23
|
+
useCurrentScreenIsTabs,
|
|
24
|
+
} from "../hooks";
|
|
25
|
+
|
|
26
|
+
jest.mock("@applicaster/zapp-react-native-utils/reactHooks", () => ({
|
|
27
|
+
useNavigation: jest.fn(),
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
31
|
+
|
|
32
|
+
describe("withBackToTopActionHOC", () => {
|
|
33
|
+
let receivedProps: any;
|
|
34
|
+
|
|
35
|
+
const BaseComponent = (props) => {
|
|
36
|
+
receivedProps = props; // capture injected props
|
|
37
|
+
|
|
38
|
+
return <></>;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const Wrapped = withBackToTopActionHOC(BaseComponent);
|
|
42
|
+
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
jest.clearAllMocks();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("returns PLATFORM_BACK when on root screen, focus on menu, and is home", () => {
|
|
48
|
+
expect.assertions(1);
|
|
49
|
+
|
|
50
|
+
(useCurrentScreenIsRoot as jest.Mock).mockReturnValue(true);
|
|
51
|
+
(useCurrentScreenIsHome as jest.Mock).mockReturnValue(true);
|
|
52
|
+
(useCurrentScreenIsTabs as jest.Mock).mockReturnValue(false);
|
|
53
|
+
|
|
54
|
+
(useNavigation as jest.Mock).mockReturnValue({
|
|
55
|
+
getNestedEntry: () => undefined,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
(focusManager.isFocusOnMenu as jest.Mock).mockReturnValue(true);
|
|
59
|
+
(focusManager.isFocusOnContent as jest.Mock).mockReturnValue(false);
|
|
60
|
+
|
|
61
|
+
render(<Wrapped />);
|
|
62
|
+
|
|
63
|
+
expect(receivedProps.backToTopAction()).toEqual({
|
|
64
|
+
action: "PLATFORM_BACK",
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("returns GO_HOME when on root screen, focus on menu, and not home", () => {
|
|
69
|
+
expect.assertions(1);
|
|
70
|
+
|
|
71
|
+
(useCurrentScreenIsRoot as jest.Mock).mockReturnValue(true);
|
|
72
|
+
(useCurrentScreenIsHome as jest.Mock).mockReturnValue(false);
|
|
73
|
+
(useCurrentScreenIsTabs as jest.Mock).mockReturnValue(false);
|
|
74
|
+
|
|
75
|
+
(useNavigation as jest.Mock).mockReturnValue({
|
|
76
|
+
getNestedEntry: () => undefined,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
(focusManager.isFocusOnMenu as jest.Mock).mockReturnValue(true);
|
|
80
|
+
(focusManager.isFocusOnContent as jest.Mock).mockReturnValue(false);
|
|
81
|
+
|
|
82
|
+
render(<Wrapped />);
|
|
83
|
+
expect(receivedProps.backToTopAction()).toEqual({ action: "GO_HOME" });
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("returns FOCUS_TOP_NAVIGATION when on root screen and focus on content without tabs_screen", () => {
|
|
87
|
+
(useCurrentScreenIsRoot as jest.Mock).mockReturnValue(true);
|
|
88
|
+
(useCurrentScreenIsHome as jest.Mock).mockReturnValue(false);
|
|
89
|
+
(useCurrentScreenIsTabs as jest.Mock).mockReturnValue(false);
|
|
90
|
+
|
|
91
|
+
(useNavigation as jest.Mock).mockReturnValue({
|
|
92
|
+
getNestedEntry: () => "selectedEntry",
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
(focusManager.isFocusOnMenu as jest.Mock).mockReturnValue(false);
|
|
96
|
+
(focusManager.isFocusOnContent as jest.Mock).mockReturnValue(true);
|
|
97
|
+
|
|
98
|
+
render(<Wrapped />);
|
|
99
|
+
|
|
100
|
+
expect(receivedProps.backToTopAction()).toEqual({
|
|
101
|
+
action: "FOCUS_TOP_NAVIGATION",
|
|
102
|
+
payload: {
|
|
103
|
+
isTabsScreen: false,
|
|
104
|
+
selectedEntry: "selectedEntry",
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("returns FOCUS_TOP_NAVIGATION when on root screen and focus on content with tabs_screen", () => {
|
|
110
|
+
(useCurrentScreenIsRoot as jest.Mock).mockReturnValue(true);
|
|
111
|
+
(useCurrentScreenIsHome as jest.Mock).mockReturnValue(false);
|
|
112
|
+
(useCurrentScreenIsTabs as jest.Mock).mockReturnValue(true);
|
|
113
|
+
|
|
114
|
+
(useNavigation as jest.Mock).mockReturnValue({
|
|
115
|
+
getNestedEntry: () => "selectedEntry",
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
(focusManager.isFocusOnMenu as jest.Mock).mockReturnValue(false);
|
|
119
|
+
(focusManager.isFocusOnContent as jest.Mock).mockReturnValue(true);
|
|
120
|
+
|
|
121
|
+
render(<Wrapped />);
|
|
122
|
+
|
|
123
|
+
expect(receivedProps.backToTopAction()).toEqual({
|
|
124
|
+
action: "FOCUS_TOP_NAVIGATION",
|
|
125
|
+
payload: {
|
|
126
|
+
isTabsScreen: true,
|
|
127
|
+
selectedEntry: "selectedEntry",
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("returns GO_BACK in default case", () => {
|
|
133
|
+
expect.assertions(1);
|
|
134
|
+
|
|
135
|
+
(useCurrentScreenIsRoot as jest.Mock).mockReturnValue(false);
|
|
136
|
+
(useCurrentScreenIsHome as jest.Mock).mockReturnValue(false);
|
|
137
|
+
(useCurrentScreenIsTabs as jest.Mock).mockReturnValue(false);
|
|
138
|
+
|
|
139
|
+
(useNavigation as jest.Mock).mockReturnValue({
|
|
140
|
+
getNestedEntry: () => undefined,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
(focusManager.isFocusOnMenu as jest.Mock).mockReturnValue(false);
|
|
144
|
+
(focusManager.isFocusOnContent as jest.Mock).mockReturnValue(false);
|
|
145
|
+
|
|
146
|
+
render(<Wrapped />);
|
|
147
|
+
expect(receivedProps.backToTopAction()).toEqual({ action: "GO_BACK" });
|
|
148
|
+
});
|
|
149
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
2
|
+
import {
|
|
3
|
+
useHomeRiver,
|
|
4
|
+
useRivers,
|
|
5
|
+
} from "@applicaster/zapp-react-native-utils/reactHooks/state";
|
|
6
|
+
import { last } from "@applicaster/zapp-react-native-utils/utils";
|
|
7
|
+
|
|
8
|
+
export const useCurrentScreenIsHome = (): boolean => {
|
|
9
|
+
const navigator = useNavigation();
|
|
10
|
+
const homeRiver = useHomeRiver();
|
|
11
|
+
|
|
12
|
+
const homePath = `/river/${homeRiver.id}`;
|
|
13
|
+
|
|
14
|
+
return homePath === navigator.currentRoute;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const useCurrentScreenIsRoot = (): boolean => {
|
|
18
|
+
const { mainStack = [] } = useNavigation();
|
|
19
|
+
|
|
20
|
+
// root screen is the bottom(first pushed, deepest) element of the stack
|
|
21
|
+
return mainStack.length <= 1;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const useCurrentScreenIsTabs = (): boolean => {
|
|
25
|
+
const navigator = useNavigation();
|
|
26
|
+
|
|
27
|
+
const riverId = last(navigator.currentRoute.split("/"));
|
|
28
|
+
|
|
29
|
+
const rivers = useRivers();
|
|
30
|
+
|
|
31
|
+
const river = rivers[riverId];
|
|
32
|
+
|
|
33
|
+
return river?.type === "tabs_screen";
|
|
34
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils";
|
|
3
|
+
import {
|
|
4
|
+
useCurrentScreenIsHome,
|
|
5
|
+
useCurrentScreenIsRoot,
|
|
6
|
+
useCurrentScreenIsTabs,
|
|
7
|
+
} from "./hooks";
|
|
8
|
+
import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
9
|
+
|
|
10
|
+
export type BACK_TO_TOP_ACTION = {
|
|
11
|
+
action: "PLATFORM_BACK" | "GO_HOME" | "FOCUS_TOP_NAVIGATION" | "GO_BACK";
|
|
12
|
+
payload?: { isTabsScreen: boolean; selectedEntry: ZappEntry };
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type BackToTopAction = () => BACK_TO_TOP_ACTION;
|
|
16
|
+
|
|
17
|
+
export function withBackToTopActionHOC(Component) {
|
|
18
|
+
return function WrappedComponent(props) {
|
|
19
|
+
const isHome = useCurrentScreenIsHome();
|
|
20
|
+
const isRoot = useCurrentScreenIsRoot();
|
|
21
|
+
const isTabsScreen = useCurrentScreenIsTabs();
|
|
22
|
+
|
|
23
|
+
const navigator = useNavigation();
|
|
24
|
+
const selectedEntry = navigator.getNestedEntry();
|
|
25
|
+
|
|
26
|
+
const isStartUpHook =
|
|
27
|
+
Array.isArray(navigator.startUpHooks) &&
|
|
28
|
+
navigator.startUpHooks.length > 0;
|
|
29
|
+
|
|
30
|
+
const backToTopAction = React.useCallback((): BACK_TO_TOP_ACTION => {
|
|
31
|
+
if (isRoot && isStartUpHook) {
|
|
32
|
+
return { action: "PLATFORM_BACK" };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (isRoot && focusManager.isFocusOnMenu() && isHome) {
|
|
36
|
+
return { action: "PLATFORM_BACK" };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (isRoot && focusManager.isFocusOnMenu() && !isHome) {
|
|
40
|
+
return { action: "GO_HOME" };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (isRoot && focusManager.isFocusOnContent()) {
|
|
44
|
+
return {
|
|
45
|
+
action: "FOCUS_TOP_NAVIGATION",
|
|
46
|
+
payload: { isTabsScreen, selectedEntry },
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// default
|
|
51
|
+
return { action: "GO_BACK" };
|
|
52
|
+
}, [isHome, isRoot, isTabsScreen, selectedEntry]);
|
|
53
|
+
|
|
54
|
+
return <Component {...props} backToTopAction={backToTopAction} />;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import * as R from "ramda";
|
|
3
3
|
|
|
4
|
-
import { connectToStore } from "@applicaster/zapp-react-native-redux";
|
|
4
|
+
import { connectToStore } from "@applicaster/zapp-react-native-redux/utils/connectToStore";
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
QUICK_BRICK_EVENTS,
|
|
8
8
|
sendQuickBrickEvent,
|
|
9
9
|
} from "@applicaster/zapp-react-native-bridge/QuickBrick";
|
|
10
10
|
|
|
11
|
+
import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
|
|
12
|
+
|
|
11
13
|
import {
|
|
12
14
|
debounce,
|
|
13
15
|
noop,
|
|
14
16
|
} from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
15
17
|
|
|
16
18
|
import {
|
|
19
|
+
AccessibilityManager,
|
|
17
20
|
ARROW_KEYS,
|
|
18
21
|
focusManager,
|
|
19
22
|
keyCode,
|
|
@@ -50,6 +53,8 @@ import {
|
|
|
50
53
|
import { OnScreenKeyboard } from "@applicaster/zapp-react-dom-ui-components/Components/OnScreenKeyboard";
|
|
51
54
|
import { KeyboardLongPressManager } from "@applicaster/zapp-react-dom-ui-components/Utils/KeyboardLongPressManager";
|
|
52
55
|
import { KeyInputHandler } from "@applicaster/zapp-react-native-utils/appUtils/keyInputHandler/KeyInputHandler";
|
|
56
|
+
import { getHomeRiver } from "@applicaster/zapp-react-native-utils/reactHooks/state";
|
|
57
|
+
import { withBackToTopActionHOC, BackToTopAction } from "./hoc";
|
|
53
58
|
|
|
54
59
|
const { withXray } = getXray();
|
|
55
60
|
const globalAny: any = global;
|
|
@@ -60,7 +65,8 @@ const { log_debug, log_warning } = createLogger({
|
|
|
60
65
|
});
|
|
61
66
|
|
|
62
67
|
const shouldUseOnScreenKeyboard = () =>
|
|
63
|
-
isVizioPlatform() ||
|
|
68
|
+
isVizioPlatform() ||
|
|
69
|
+
toBooleanWithDefaultFalse(window?.applicaster?.useOnScreenKeyboard);
|
|
64
70
|
|
|
65
71
|
type Props = {
|
|
66
72
|
displayState: string;
|
|
@@ -70,6 +76,7 @@ type Props = {
|
|
|
70
76
|
rivers: {};
|
|
71
77
|
xray: XRayContext;
|
|
72
78
|
confirmDialog: typeof confirmationDialogStore;
|
|
79
|
+
backToTopAction: BackToTopAction;
|
|
73
80
|
};
|
|
74
81
|
|
|
75
82
|
interface State {
|
|
@@ -80,8 +87,8 @@ interface State {
|
|
|
80
87
|
|
|
81
88
|
type KeyCode = { code: string; keyCode: number | string };
|
|
82
89
|
|
|
83
|
-
|
|
84
|
-
"
|
|
90
|
+
const VIDEO_PLAYER_CONTROLS_NAVIGATION_MESSAGE =
|
|
91
|
+
"Video player controls - use left/right to navigate";
|
|
85
92
|
|
|
86
93
|
class InteractionManagerClass extends React.Component<Props, State> {
|
|
87
94
|
onKeyDownListener: (keyCode: KeyCode) => void;
|
|
@@ -98,6 +105,8 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
98
105
|
private confirmDialog: ConfirmationDialogState =
|
|
99
106
|
confirmationDialogStore.getState();
|
|
100
107
|
|
|
108
|
+
accessibilityManager: AccessibilityManager;
|
|
109
|
+
|
|
101
110
|
state: State = {
|
|
102
111
|
isKeyboardVisible: false,
|
|
103
112
|
keyboardInput: "",
|
|
@@ -139,14 +148,9 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
139
148
|
this.handleInputBlur = this.handleInputBlur.bind(this);
|
|
140
149
|
this.handleKeyboardInput = this.handleKeyboardInput.bind(this);
|
|
141
150
|
this.handleKeyboardDismiss = this.handleKeyboardDismiss.bind(this);
|
|
142
|
-
this.onKeyDownListener = this.onKeyDown.bind(this);
|
|
143
|
-
this.onKeyUp = this.onKeyUp.bind(this);
|
|
144
151
|
this.onMouseMoveListener = this.onMouseMove.bind(this);
|
|
145
152
|
this.onScrollListener = this.onScroll.bind(this);
|
|
146
153
|
this.onMouseDownListener = this.onMouseDown.bind(this);
|
|
147
|
-
this.onKeyboardStateChange = this.onKeyboardStateChange.bind(this);
|
|
148
|
-
this.onConfirmDialogOpen = this.onConfirmDialogOpen.bind(this);
|
|
149
|
-
this.onConfirmDialogClose = this.onConfirmDialogClose.bind(this);
|
|
150
154
|
|
|
151
155
|
this.handlePhysicalKeyboardDismiss =
|
|
152
156
|
this.handlePhysicalKeyboardDismiss.bind(this);
|
|
@@ -154,6 +158,7 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
154
158
|
this.confirmDialog.setConfirmAction(this.performExit);
|
|
155
159
|
this.confirmDialog.setCancelAction(this.onConfirmDialogClose);
|
|
156
160
|
this.longPressDetector = new KeyboardLongPressManager();
|
|
161
|
+
this.accessibilityManager = AccessibilityManager.getInstance();
|
|
157
162
|
}
|
|
158
163
|
|
|
159
164
|
componentDidMount() {
|
|
@@ -259,7 +264,6 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
259
264
|
*/
|
|
260
265
|
showConfirmationDialog({
|
|
261
266
|
title: "",
|
|
262
|
-
message: alertConfirmationMesage,
|
|
263
267
|
confirmCompletion: this.performExit,
|
|
264
268
|
});
|
|
265
269
|
}
|
|
@@ -282,7 +286,6 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
282
286
|
} else {
|
|
283
287
|
showConfirmationDialog({
|
|
284
288
|
title: "",
|
|
285
|
-
message: alertConfirmationMesage,
|
|
286
289
|
confirmCompletion: this.performExit,
|
|
287
290
|
cancelCompletion: this.onConfirmDialogClose,
|
|
288
291
|
});
|
|
@@ -318,13 +321,9 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
318
321
|
const { navigator } = this.props;
|
|
319
322
|
const { rivers } = this.props;
|
|
320
323
|
|
|
321
|
-
const
|
|
322
|
-
R.prop("id"),
|
|
323
|
-
R.find(R.prop("home")),
|
|
324
|
-
R.values
|
|
325
|
-
)(rivers);
|
|
324
|
+
const homeRiver = getHomeRiver(rivers);
|
|
326
325
|
|
|
327
|
-
const homePath = `/river/${
|
|
326
|
+
const homePath = `/river/${homeRiver?.id}`;
|
|
328
327
|
|
|
329
328
|
return homePath === navigator.currentRoute;
|
|
330
329
|
}
|
|
@@ -332,7 +331,7 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
332
331
|
goToHome() {
|
|
333
332
|
const { navigator } = this.props;
|
|
334
333
|
const { rivers } = this.props;
|
|
335
|
-
const homeRiver =
|
|
334
|
+
const homeRiver = getHomeRiver(rivers);
|
|
336
335
|
|
|
337
336
|
navigator.replace(homeRiver);
|
|
338
337
|
}
|
|
@@ -393,7 +392,9 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
393
392
|
note: backspace affects the keyboard input, so it behaves differently
|
|
394
393
|
*/
|
|
395
394
|
onBackPress(event) {
|
|
396
|
-
const { displayState, setDisplayState, navigator } =
|
|
395
|
+
const { displayState, setDisplayState, navigator, backToTopAction } =
|
|
396
|
+
this.props;
|
|
397
|
+
|
|
397
398
|
const { isDialogVisible } = confirmationDialogStore.getState();
|
|
398
399
|
const { isKeyboardVisible } = this.state;
|
|
399
400
|
|
|
@@ -412,8 +413,32 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
412
413
|
return;
|
|
413
414
|
}
|
|
414
415
|
|
|
416
|
+
const { action, payload } = backToTopAction();
|
|
417
|
+
|
|
415
418
|
switch (displayState) {
|
|
416
419
|
case DISPLAY_STATES.DEFAULT:
|
|
420
|
+
if (action === "PLATFORM_BACK") {
|
|
421
|
+
this.platformBack();
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (action === "GO_HOME") {
|
|
425
|
+
this.goToHome();
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (action === "FOCUS_TOP_NAVIGATION") {
|
|
429
|
+
focusManager.focusTopNavigation(
|
|
430
|
+
payload.isTabsScreen,
|
|
431
|
+
payload.selectedEntry
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (action === "GO_BACK") {
|
|
436
|
+
if (navigator.canGoBack()) {
|
|
437
|
+
navigator.goBack();
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
break;
|
|
417
442
|
case DISPLAY_STATES.PLAYER:
|
|
418
443
|
if (isDialogVisible) {
|
|
419
444
|
this.onConfirmDialogClose();
|
|
@@ -553,6 +578,13 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
553
578
|
) {
|
|
554
579
|
if (displayState === DISPLAY_STATES.PLAYER) {
|
|
555
580
|
setDisplayState(DISPLAY_STATES.HUD);
|
|
581
|
+
|
|
582
|
+
this.accessibilityManager.addHeading(
|
|
583
|
+
VIDEO_PLAYER_CONTROLS_NAVIGATION_MESSAGE
|
|
584
|
+
);
|
|
585
|
+
|
|
586
|
+
focusManager.recoverFocus();
|
|
587
|
+
this.resetHudTimeout();
|
|
556
588
|
} else {
|
|
557
589
|
focusManager.pressIn();
|
|
558
590
|
focusManager.press();
|
|
@@ -597,6 +629,12 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
597
629
|
this.resetHudTimeout();
|
|
598
630
|
}
|
|
599
631
|
|
|
632
|
+
if (displayState === DISPLAY_STATES.PLAYER) {
|
|
633
|
+
this.accessibilityManager.addHeading(
|
|
634
|
+
VIDEO_PLAYER_CONTROLS_NAVIGATION_MESSAGE
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
|
|
600
638
|
return true;
|
|
601
639
|
}
|
|
602
640
|
}
|
|
@@ -653,8 +691,13 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
653
691
|
onConfirmDialogClose() {
|
|
654
692
|
this.confirmDialog.hideDialog();
|
|
655
693
|
|
|
656
|
-
|
|
657
|
-
|
|
694
|
+
const context: FocusManager.FocusContext = {
|
|
695
|
+
source: "cancel",
|
|
696
|
+
preserveScroll: true,
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
// restore initial focus after closing dialog(with preserve scrolling)
|
|
700
|
+
focusManager.setInitialFocus(undefined, context);
|
|
658
701
|
}
|
|
659
702
|
|
|
660
703
|
handleInputFocus = (event: FocusEvent) => {
|
|
@@ -852,5 +895,6 @@ export const InteractionManager = R.compose(
|
|
|
852
895
|
withXray,
|
|
853
896
|
connectToStore(R.pick(["rivers"])),
|
|
854
897
|
PlayerContentContext.withConsumer,
|
|
855
|
-
DisplayStateContext.withConsumer
|
|
898
|
+
DisplayStateContext.withConsumer,
|
|
899
|
+
withBackToTopActionHOC
|
|
856
900
|
)(InteractionManagerClass);
|
package/App/Layout/index.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import { createPortal } from "react-dom";
|
|
2
3
|
|
|
3
4
|
import { Background } from "@applicaster/zapp-react-dom-ui-components/Components/Background";
|
|
4
5
|
import { ConfirmDialog } from "@applicaster/zapp-react-dom-ui-components/Components/ConfirmDialog";
|
|
@@ -29,7 +30,7 @@ export function Layout({ children }: Props) {
|
|
|
29
30
|
>
|
|
30
31
|
{children}
|
|
31
32
|
</LayoutComponent>
|
|
32
|
-
<ConfirmDialog
|
|
33
|
+
{createPortal(<ConfirmDialog />, document.body)}
|
|
33
34
|
</>
|
|
34
35
|
);
|
|
35
36
|
}
|
|
@@ -3,7 +3,7 @@ import * as React from "react";
|
|
|
3
3
|
import * as R from "ramda";
|
|
4
4
|
import PropTypes from "prop-types";
|
|
5
5
|
|
|
6
|
-
import { connectToStore } from "@applicaster/zapp-react-native-redux";
|
|
6
|
+
import { connectToStore } from "@applicaster/zapp-react-native-redux/utils/connectToStore";
|
|
7
7
|
import { QUICK_BRICK_EVENTS } from "@applicaster/zapp-react-native-bridge/QuickBrick";
|
|
8
8
|
|
|
9
9
|
import { QuickBrickEvents } from "../../Polyfills/QuickBrickCommunicationModule";
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import * as R from "ramda";
|
|
2
1
|
import { NativeModules } from "react-native";
|
|
3
2
|
import uuidv4 from "uuid/v4";
|
|
4
3
|
|
|
5
4
|
import { isEmptyOrNil } from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
6
|
-
import { mapKeys } from "@applicaster/zapp-react-native-utils/objectUtils";
|
|
7
5
|
import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
|
|
8
6
|
import { sessionStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/SessionStorage";
|
|
9
7
|
import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
|
|
10
8
|
|
|
11
|
-
import { renameKeys, getDeviceData } from "./utils/platform";
|
|
9
|
+
import { renameKeys, getDeviceData, getUserAgent } from "./utils/platform";
|
|
12
10
|
import { desiredKeysMap } from "./utils/const";
|
|
13
11
|
|
|
14
12
|
/**
|
|
@@ -55,6 +53,9 @@ export const loadPluginDataIntoSession = async (plugins = null) => {
|
|
|
55
53
|
async function gatherData(uuid) {
|
|
56
54
|
const deviceData = await getDeviceData();
|
|
57
55
|
|
|
56
|
+
// Prevent the device sdkVersion from overriding the build time value
|
|
57
|
+
delete deviceData.sdkVersion;
|
|
58
|
+
|
|
58
59
|
const { languageLocale, countryLocale } =
|
|
59
60
|
NativeModules?.QuickBrickCommunicationModule || {};
|
|
60
61
|
|
|
@@ -63,7 +64,9 @@ async function gatherData(uuid) {
|
|
|
63
64
|
...NativeModules?.QuickBrickCommunicationModule,
|
|
64
65
|
// Device data retrieved from native device apis
|
|
65
66
|
...deviceData,
|
|
66
|
-
|
|
67
|
+
userAgent: deviceData.userAgent || getUserAgent(),
|
|
68
|
+
uuid: uuid || uuidv4(), // Created on first session, persisted in local thereafter
|
|
69
|
+
sessionId: uuidv4(), // Created on every session
|
|
67
70
|
locale: `{language=${languageLocale}, country=${countryLocale}}`,
|
|
68
71
|
};
|
|
69
72
|
|
|
@@ -78,21 +81,23 @@ async function gatherData(uuid) {
|
|
|
78
81
|
* @param {Object} desiredKeys - The map of original key names to desired key names.
|
|
79
82
|
*/
|
|
80
83
|
function storeData(data, desiredKeys) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
84
|
+
const desiredKeysList = Object.keys(desiredKeys);
|
|
85
|
+
|
|
86
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
87
|
+
if (!desiredKeysList.includes(key)) return;
|
|
88
|
+
|
|
89
|
+
if (isEmptyOrNil(value)) return;
|
|
90
|
+
|
|
91
|
+
if (typeof value === "object" || typeof value === "function") return;
|
|
92
|
+
|
|
93
|
+
const renamedKey = renameKeys(key);
|
|
94
|
+
|
|
95
|
+
if (renamedKey === "uuid") {
|
|
96
|
+
localStorage.setItem(renamedKey, value);
|
|
92
97
|
}
|
|
93
98
|
|
|
94
|
-
sessionStorage.setItem(
|
|
95
|
-
}
|
|
99
|
+
sessionStorage.setItem(renamedKey, value);
|
|
100
|
+
});
|
|
96
101
|
}
|
|
97
102
|
|
|
98
103
|
/**
|
|
@@ -257,26 +257,7 @@ export const getTizenInfo = () => {
|
|
|
257
257
|
*/
|
|
258
258
|
export const getDeviceData = async () => {
|
|
259
259
|
try {
|
|
260
|
-
let deviceData = {
|
|
261
|
-
modelName: null,
|
|
262
|
-
version: null,
|
|
263
|
-
versionMajor: null,
|
|
264
|
-
versionMinor: null,
|
|
265
|
-
versionDot: null,
|
|
266
|
-
sdkVersion: null,
|
|
267
|
-
screenWidth: null,
|
|
268
|
-
screenHeight: null,
|
|
269
|
-
uhd: null,
|
|
270
|
-
oled: null,
|
|
271
|
-
ddrSize: null,
|
|
272
|
-
uhd8K: null,
|
|
273
|
-
hdr10: null,
|
|
274
|
-
dolbyVision: null,
|
|
275
|
-
dolbyAtmos: null,
|
|
276
|
-
name: null,
|
|
277
|
-
osVersion: null,
|
|
278
|
-
networkType: null,
|
|
279
|
-
};
|
|
260
|
+
let deviceData = {};
|
|
280
261
|
|
|
281
262
|
if (isLgPlatform()) {
|
|
282
263
|
const [webOSInfo, webOSConnectionInfo] = await Promise.all([
|
|
@@ -308,7 +289,6 @@ export const getDeviceData = async () => {
|
|
|
308
289
|
platform: "vizio",
|
|
309
290
|
deviceMake: "Vizio",
|
|
310
291
|
deviceType: "tv",
|
|
311
|
-
userAgent: getUserAgent(),
|
|
312
292
|
deviceWidth: window.innerWidth,
|
|
313
293
|
deviceHeight: window.innerHeight,
|
|
314
294
|
};
|
package/App/Splash/index.js
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import * as R from "ramda";
|
|
2
|
-
import {
|
|
3
|
-
getStorageModule,
|
|
4
|
-
applyNamespaceToKeyName,
|
|
5
|
-
STORAGE_TYPES,
|
|
6
|
-
DEFAULT_NAMESPACE,
|
|
7
|
-
} from "../";
|
|
2
|
+
import { getStorageModule, STORAGE_TYPES } from "../";
|
|
8
3
|
|
|
9
4
|
import { StorageMock } from "./StorageMocks";
|
|
10
5
|
|
|
@@ -37,7 +32,6 @@ describe("storageObject", () => {
|
|
|
37
32
|
const value = "bar";
|
|
38
33
|
const namespaceValue = "baz";
|
|
39
34
|
const namespace = "baz";
|
|
40
|
-
const keyName = applyNamespaceToKeyName(key, namespace);
|
|
41
35
|
const failKey = "fail_key";
|
|
42
36
|
|
|
43
37
|
beforeEach(clearStorage);
|
|
@@ -54,16 +48,11 @@ describe("storageObject", () => {
|
|
|
54
48
|
it.each(storages)("sets a property in the storage", async (storage) => {
|
|
55
49
|
const result = await storage.setItem(key, value, null);
|
|
56
50
|
|
|
57
|
-
const keyWithDefaultNameSpace = applyNamespaceToKeyName(
|
|
58
|
-
key,
|
|
59
|
-
DEFAULT_NAMESPACE
|
|
60
|
-
);
|
|
61
|
-
|
|
62
51
|
expect(result).toBe(true);
|
|
63
52
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
);
|
|
53
|
+
const res = await storage.getItem(key, null);
|
|
54
|
+
|
|
55
|
+
expect(res).toEqual(value);
|
|
67
56
|
});
|
|
68
57
|
|
|
69
58
|
it.each(storages)(
|
|
@@ -73,7 +62,9 @@ describe("storageObject", () => {
|
|
|
73
62
|
|
|
74
63
|
expect(result).toBe(true);
|
|
75
64
|
|
|
76
|
-
|
|
65
|
+
const res = await storage.getItem(key, namespace);
|
|
66
|
+
|
|
67
|
+
expect(res).toEqual(namespaceValue);
|
|
77
68
|
}
|
|
78
69
|
);
|
|
79
70
|
|
|
@@ -108,7 +99,7 @@ describe("storageObject", () => {
|
|
|
108
99
|
"gets properties from the storage with a namespace",
|
|
109
100
|
(storage) => {
|
|
110
101
|
expect(storage.getItem(key, namespace)).resolves.toEqual(
|
|
111
|
-
|
|
102
|
+
namespaceValue
|
|
112
103
|
);
|
|
113
104
|
}
|
|
114
105
|
);
|
package/Polyfills/index.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import { QuickBrickCommunicationModule } from "./QuickBrickCommunicationModule";
|
|
2
|
-
import { AnalyticsBridge } from "./AnalyticsBridge";
|
|
3
1
|
import { getStorageModule } from "./Storage";
|
|
4
2
|
import { DeviceEventEmitter } from "./DeviceEventEmitter";
|
|
5
|
-
import { AppLoaderBridge } from "./AppLoaderBridge";
|
|
6
3
|
import { isSamsungPlatform, isLgPlatform } from "../App/Loader/utils/platform";
|
|
7
4
|
|
|
8
5
|
// Polyfill for abort controller required by shaka-player 4.x.x
|
|
@@ -24,11 +21,13 @@ const PLATFORM_KEYS = {
|
|
|
24
21
|
// const DESKTOP_BROWSER = [PLATFORMS.linux, PLATFORMS.mac, PLATFORMS.win];
|
|
25
22
|
|
|
26
23
|
export function registerNativeModulesPolyfills(NativeModules) {
|
|
27
|
-
NativeModules.QuickBrickCommunicationModule = QuickBrickCommunicationModule;
|
|
28
24
|
NativeModules.LocalStorage = getStorageModule("localStorage");
|
|
29
25
|
NativeModules.SessionStorage = getStorageModule("sessionStorage");
|
|
30
|
-
NativeModules.AnalyticsBridge = AnalyticsBridge;
|
|
31
|
-
NativeModules.AppLoaderBridge = AppLoaderBridge;
|
|
26
|
+
NativeModules.AnalyticsBridge = require("./AnalyticsBridge").AnalyticsBridge;
|
|
27
|
+
NativeModules.AppLoaderBridge = require("./AppLoaderBridge").AppLoaderBridge;
|
|
28
|
+
|
|
29
|
+
NativeModules.QuickBrickCommunicationModule =
|
|
30
|
+
require("./QuickBrickCommunicationModule").QuickBrickCommunicationModule;
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
function getWebPlatform(fallbackPlatform) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-dom-app",
|
|
3
|
-
"version": "14.0.0-rc.
|
|
3
|
+
"version": "14.0.0-rc.61",
|
|
4
4
|
"description": "Zapp App Component for Applicaster's Quick Brick React Native App",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
},
|
|
23
23
|
"homepage": "https://github.com/applicaster/zapp-react-dom-app#readme",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@applicaster/zapp-react-dom-ui-components": "14.0.0-rc.
|
|
26
|
-
"@applicaster/zapp-react-native-bridge": "14.0.0-rc.
|
|
27
|
-
"@applicaster/zapp-react-native-redux": "14.0.0-rc.
|
|
28
|
-
"@applicaster/zapp-react-native-ui-components": "14.0.0-rc.
|
|
29
|
-
"@applicaster/zapp-react-native-utils": "14.0.0-rc.
|
|
25
|
+
"@applicaster/zapp-react-dom-ui-components": "14.0.0-rc.61",
|
|
26
|
+
"@applicaster/zapp-react-native-bridge": "14.0.0-rc.61",
|
|
27
|
+
"@applicaster/zapp-react-native-redux": "14.0.0-rc.61",
|
|
28
|
+
"@applicaster/zapp-react-native-ui-components": "14.0.0-rc.61",
|
|
29
|
+
"@applicaster/zapp-react-native-utils": "14.0.0-rc.61",
|
|
30
30
|
"abortcontroller-polyfill": "^1.7.5",
|
|
31
31
|
"typeface-montserrat": "^0.0.54",
|
|
32
32
|
"video.js": "7.14.3",
|
|
@@ -37,13 +37,9 @@
|
|
|
37
37
|
"@applicaster/zapp-pipes-v2-client": "*",
|
|
38
38
|
"@react-native-community/netinfo": "*",
|
|
39
39
|
"core-js": "3.29.1",
|
|
40
|
-
"immer": "*",
|
|
41
40
|
"react": "*",
|
|
42
|
-
"react-dom": "*",
|
|
43
41
|
"react-native": "*",
|
|
44
|
-
"react-native-safe-area-context": "*",
|
|
45
42
|
"react-native-svg": "*",
|
|
46
|
-
"react-native-web": "*",
|
|
47
43
|
"uglify-js": "*",
|
|
48
44
|
"validate-color": "*",
|
|
49
45
|
"zustand": "*"
|