@applicaster/zapp-react-dom-app 14.0.0-rc.8 → 14.0.0-rc.80
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 +183 -0
- package/App/InteractionManager/hoc/hooks/index.ts +34 -0
- package/App/InteractionManager/hoc/index.ts +4 -0
- package/App/InteractionManager/hoc/withBackToTopAction.tsx +83 -0
- package/App/InteractionManager/index.tsx +99 -23
- 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,183 @@
|
|
|
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
|
+
isTabsScreenContentFocused: jest.fn(),
|
|
11
|
+
},
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
jest.mock("../hooks", () => ({
|
|
15
|
+
useCurrentScreenIsHome: jest.fn(),
|
|
16
|
+
useCurrentScreenIsRoot: jest.fn(),
|
|
17
|
+
useCurrentScreenIsTabs: jest.fn(),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils";
|
|
21
|
+
import {
|
|
22
|
+
useCurrentScreenIsHome,
|
|
23
|
+
useCurrentScreenIsRoot,
|
|
24
|
+
useCurrentScreenIsTabs,
|
|
25
|
+
} from "../hooks";
|
|
26
|
+
|
|
27
|
+
jest.mock("@applicaster/zapp-react-native-utils/reactHooks", () => ({
|
|
28
|
+
useNavigation: jest.fn(),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
32
|
+
|
|
33
|
+
describe("withBackToTopActionHOC", () => {
|
|
34
|
+
let receivedProps: any;
|
|
35
|
+
|
|
36
|
+
const BaseComponent = (props) => {
|
|
37
|
+
receivedProps = props; // capture injected props
|
|
38
|
+
|
|
39
|
+
return <></>;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const Wrapped = withBackToTopActionHOC(BaseComponent);
|
|
43
|
+
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
jest.clearAllMocks();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("returns PLATFORM_BACK when on root screen, and startup hooks", () => {
|
|
49
|
+
expect.assertions(1);
|
|
50
|
+
|
|
51
|
+
(useCurrentScreenIsRoot as jest.Mock).mockReturnValue(true);
|
|
52
|
+
|
|
53
|
+
(useNavigation as jest.Mock).mockReturnValue({
|
|
54
|
+
startUpHooks: ["some_startup_hook"],
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
render(<Wrapped />);
|
|
58
|
+
|
|
59
|
+
expect(receivedProps.backToTopAction()).toBe("PLATFORM_BACK");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("returns PLATFORM_BACK when on root screen, focus on menu, and is home", () => {
|
|
63
|
+
expect.assertions(1);
|
|
64
|
+
|
|
65
|
+
(useCurrentScreenIsRoot as jest.Mock).mockReturnValue(true);
|
|
66
|
+
(useCurrentScreenIsHome as jest.Mock).mockReturnValue(true);
|
|
67
|
+
(useCurrentScreenIsTabs as jest.Mock).mockReturnValue(false);
|
|
68
|
+
|
|
69
|
+
(useNavigation as jest.Mock).mockReturnValue({
|
|
70
|
+
startUpHooks: [],
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
(focusManager.isFocusOnMenu as jest.Mock).mockReturnValue(true);
|
|
74
|
+
(focusManager.isFocusOnContent as jest.Mock).mockReturnValue(false);
|
|
75
|
+
|
|
76
|
+
render(<Wrapped />);
|
|
77
|
+
|
|
78
|
+
expect(receivedProps.backToTopAction()).toBe("PLATFORM_BACK");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("returns GO_HOME when on root screen, focus on menu, and not home", () => {
|
|
82
|
+
expect.assertions(1);
|
|
83
|
+
|
|
84
|
+
(useCurrentScreenIsRoot as jest.Mock).mockReturnValue(true);
|
|
85
|
+
(useCurrentScreenIsHome as jest.Mock).mockReturnValue(false);
|
|
86
|
+
(useCurrentScreenIsTabs as jest.Mock).mockReturnValue(false);
|
|
87
|
+
|
|
88
|
+
(useNavigation as jest.Mock).mockReturnValue({
|
|
89
|
+
startUpHooks: [],
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
(focusManager.isFocusOnMenu as jest.Mock).mockReturnValue(true);
|
|
93
|
+
(focusManager.isFocusOnContent as jest.Mock).mockReturnValue(false);
|
|
94
|
+
|
|
95
|
+
render(<Wrapped />);
|
|
96
|
+
expect(receivedProps.backToTopAction()).toBe("GO_HOME");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("returns FOCUS_ON_SELECTED_TOP_MENU_ITEM when on root screen and focus on content without tabs_screen", () => {
|
|
100
|
+
(useCurrentScreenIsRoot as jest.Mock).mockReturnValue(true);
|
|
101
|
+
(useCurrentScreenIsHome as jest.Mock).mockReturnValue(false);
|
|
102
|
+
(useCurrentScreenIsTabs as jest.Mock).mockReturnValue(false);
|
|
103
|
+
|
|
104
|
+
(useNavigation as jest.Mock).mockReturnValue({
|
|
105
|
+
startUpHooks: [],
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
(focusManager.isFocusOnMenu as jest.Mock).mockReturnValue(false);
|
|
109
|
+
(focusManager.isFocusOnContent as jest.Mock).mockReturnValue(true);
|
|
110
|
+
|
|
111
|
+
render(<Wrapped />);
|
|
112
|
+
|
|
113
|
+
expect(receivedProps.backToTopAction()).toBe(
|
|
114
|
+
"FOCUS_ON_SELECTED_TOP_MENU_ITEM"
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("returns FOCUS_ON_SELECTED_TAB_ITEM when on root screen and focus on content with tabs_screen", () => {
|
|
119
|
+
(useCurrentScreenIsRoot as jest.Mock).mockReturnValue(true);
|
|
120
|
+
(useCurrentScreenIsHome as jest.Mock).mockReturnValue(false);
|
|
121
|
+
(useCurrentScreenIsTabs as jest.Mock).mockReturnValue(true);
|
|
122
|
+
|
|
123
|
+
(useNavigation as jest.Mock).mockReturnValue({
|
|
124
|
+
startUpHooks: [],
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
(focusManager.isFocusOnMenu as jest.Mock).mockReturnValue(false);
|
|
128
|
+
(focusManager.isFocusOnContent as jest.Mock).mockReturnValue(true);
|
|
129
|
+
|
|
130
|
+
(focusManager.isTabsScreenContentFocused as jest.Mock).mockReturnValue(
|
|
131
|
+
true
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
render(<Wrapped />);
|
|
135
|
+
|
|
136
|
+
expect(receivedProps.backToTopAction()).toBe("FOCUS_ON_SELECTED_TAB_ITEM");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("returns GO_BACK in default case", () => {
|
|
140
|
+
expect.assertions(1);
|
|
141
|
+
|
|
142
|
+
(useCurrentScreenIsRoot as jest.Mock).mockReturnValue(false);
|
|
143
|
+
(useCurrentScreenIsHome as jest.Mock).mockReturnValue(false);
|
|
144
|
+
(useCurrentScreenIsTabs as jest.Mock).mockReturnValue(false);
|
|
145
|
+
|
|
146
|
+
(useNavigation as jest.Mock).mockReturnValue({
|
|
147
|
+
startUpHooks: [],
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
(focusManager.isFocusOnMenu as jest.Mock).mockReturnValue(false);
|
|
151
|
+
(focusManager.isFocusOnContent as jest.Mock).mockReturnValue(false);
|
|
152
|
+
|
|
153
|
+
(focusManager.isTabsScreenContentFocused as jest.Mock).mockReturnValue(
|
|
154
|
+
false
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
render(<Wrapped />);
|
|
158
|
+
expect(receivedProps.backToTopAction()).toBe("GO_BACK");
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("returns FORCE_GO_BACK in hook case", () => {
|
|
162
|
+
expect.assertions(1);
|
|
163
|
+
|
|
164
|
+
(useCurrentScreenIsRoot as jest.Mock).mockReturnValue(false);
|
|
165
|
+
(useCurrentScreenIsHome as jest.Mock).mockReturnValue(false);
|
|
166
|
+
(useCurrentScreenIsTabs as jest.Mock).mockReturnValue(false);
|
|
167
|
+
|
|
168
|
+
(useNavigation as jest.Mock).mockReturnValue({
|
|
169
|
+
startUpHooks: [],
|
|
170
|
+
currentRoute: "/hooks/some_route",
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
(focusManager.isFocusOnMenu as jest.Mock).mockReturnValue(false);
|
|
174
|
+
(focusManager.isFocusOnContent as jest.Mock).mockReturnValue(false);
|
|
175
|
+
|
|
176
|
+
(focusManager.isTabsScreenContentFocused as jest.Mock).mockReturnValue(
|
|
177
|
+
false
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
render(<Wrapped />);
|
|
181
|
+
expect(receivedProps.backToTopAction()).toBe("FORCE_GO_BACK");
|
|
182
|
+
});
|
|
183
|
+
});
|
|
@@ -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,83 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { isFilledArray } from "@applicaster/zapp-react-native-utils/arrayUtils";
|
|
3
|
+
import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils";
|
|
4
|
+
|
|
5
|
+
import { useSubscriberFor } from "@applicaster/zapp-react-native-utils/reactHooks/useSubscriberFor";
|
|
6
|
+
import { QBUIComponentEvents } from "@applicaster/zapp-react-native-ui-components/events";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
useCurrentScreenIsHome,
|
|
10
|
+
useCurrentScreenIsRoot,
|
|
11
|
+
useCurrentScreenIsTabs,
|
|
12
|
+
} from "./hooks";
|
|
13
|
+
import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
14
|
+
|
|
15
|
+
export type BACK_TO_TOP_ACTION =
|
|
16
|
+
| "PLATFORM_BACK"
|
|
17
|
+
| "GO_HOME"
|
|
18
|
+
| "GO_BACK"
|
|
19
|
+
| "FOCUS_ON_SELECTED_TOP_MENU_ITEM"
|
|
20
|
+
| "FORCE_GO_BACK"
|
|
21
|
+
| "FOCUS_ON_SELECTED_TAB_ITEM";
|
|
22
|
+
|
|
23
|
+
export type BackToTopAction = () => BACK_TO_TOP_ACTION;
|
|
24
|
+
|
|
25
|
+
export function withBackToTopActionHOC(Component) {
|
|
26
|
+
return function WrappedComponent(props) {
|
|
27
|
+
const isHome = useCurrentScreenIsHome();
|
|
28
|
+
const isRoot = useCurrentScreenIsRoot();
|
|
29
|
+
const isTabsScreen = useCurrentScreenIsTabs();
|
|
30
|
+
|
|
31
|
+
const navigator = useNavigation();
|
|
32
|
+
|
|
33
|
+
const isStartUpHook = isFilledArray(navigator.startUpHooks);
|
|
34
|
+
|
|
35
|
+
const isHook = navigator.currentRoute?.includes("hook");
|
|
36
|
+
|
|
37
|
+
const emitFocusOnSelectedTab = useSubscriberFor(
|
|
38
|
+
QBUIComponentEvents.focusOnSelectedTab
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const emitFocusOnSelectedTopMenuItem = useSubscriberFor(
|
|
42
|
+
QBUIComponentEvents.focusOnSelectedTopMenuItem
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const backToTopAction = React.useCallback((): BACK_TO_TOP_ACTION => {
|
|
46
|
+
if (isRoot && isStartUpHook) {
|
|
47
|
+
return "PLATFORM_BACK";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (isRoot && focusManager.isFocusOnMenu() && isHome) {
|
|
51
|
+
return "PLATFORM_BACK";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (isRoot && focusManager.isFocusOnMenu() && !isHome) {
|
|
55
|
+
return "GO_HOME";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (isRoot && isTabsScreen && focusManager.isTabsScreenContentFocused()) {
|
|
59
|
+
return "FOCUS_ON_SELECTED_TAB_ITEM";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (isRoot && focusManager.isFocusOnContent()) {
|
|
63
|
+
return "FOCUS_ON_SELECTED_TOP_MENU_ITEM";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (isHook) {
|
|
67
|
+
return "FORCE_GO_BACK";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// default
|
|
71
|
+
return "GO_BACK";
|
|
72
|
+
}, [isHome, isRoot, isTabsScreen, isStartUpHook, isHook]);
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<Component
|
|
76
|
+
{...props}
|
|
77
|
+
backToTopAction={backToTopAction}
|
|
78
|
+
emitFocusOnSelectedTab={emitFocusOnSelectedTab}
|
|
79
|
+
emitFocusOnSelectedTopMenuItem={emitFocusOnSelectedTopMenuItem}
|
|
80
|
+
/>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
}
|
|
@@ -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,17 +53,20 @@ 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;
|
|
56
61
|
|
|
57
|
-
const { log_debug, log_warning } = createLogger({
|
|
62
|
+
const { log_debug, log_warning, log_info } = createLogger({
|
|
58
63
|
category: "InteractionManager",
|
|
59
64
|
subsystem: "zapp-react-dom-app",
|
|
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,9 @@ type Props = {
|
|
|
70
76
|
rivers: {};
|
|
71
77
|
xray: XRayContext;
|
|
72
78
|
confirmDialog: typeof confirmationDialogStore;
|
|
79
|
+
backToTopAction: BackToTopAction;
|
|
80
|
+
emitFocusOnSelectedTopMenuItem: Callback;
|
|
81
|
+
emitFocusOnSelectedTab: Callback;
|
|
73
82
|
};
|
|
74
83
|
|
|
75
84
|
interface State {
|
|
@@ -80,8 +89,8 @@ interface State {
|
|
|
80
89
|
|
|
81
90
|
type KeyCode = { code: string; keyCode: number | string };
|
|
82
91
|
|
|
83
|
-
|
|
84
|
-
"
|
|
92
|
+
const VIDEO_PLAYER_CONTROLS_NAVIGATION_MESSAGE =
|
|
93
|
+
"Video player controls - use left/right to navigate";
|
|
85
94
|
|
|
86
95
|
class InteractionManagerClass extends React.Component<Props, State> {
|
|
87
96
|
onKeyDownListener: (keyCode: KeyCode) => void;
|
|
@@ -98,6 +107,8 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
98
107
|
private confirmDialog: ConfirmationDialogState =
|
|
99
108
|
confirmationDialogStore.getState();
|
|
100
109
|
|
|
110
|
+
accessibilityManager: AccessibilityManager;
|
|
111
|
+
|
|
101
112
|
state: State = {
|
|
102
113
|
isKeyboardVisible: false,
|
|
103
114
|
keyboardInput: "",
|
|
@@ -139,14 +150,9 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
139
150
|
this.handleInputBlur = this.handleInputBlur.bind(this);
|
|
140
151
|
this.handleKeyboardInput = this.handleKeyboardInput.bind(this);
|
|
141
152
|
this.handleKeyboardDismiss = this.handleKeyboardDismiss.bind(this);
|
|
142
|
-
this.onKeyDownListener = this.onKeyDown.bind(this);
|
|
143
|
-
this.onKeyUp = this.onKeyUp.bind(this);
|
|
144
153
|
this.onMouseMoveListener = this.onMouseMove.bind(this);
|
|
145
154
|
this.onScrollListener = this.onScroll.bind(this);
|
|
146
155
|
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
156
|
|
|
151
157
|
this.handlePhysicalKeyboardDismiss =
|
|
152
158
|
this.handlePhysicalKeyboardDismiss.bind(this);
|
|
@@ -154,6 +160,7 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
154
160
|
this.confirmDialog.setConfirmAction(this.performExit);
|
|
155
161
|
this.confirmDialog.setCancelAction(this.onConfirmDialogClose);
|
|
156
162
|
this.longPressDetector = new KeyboardLongPressManager();
|
|
163
|
+
this.accessibilityManager = AccessibilityManager.getInstance();
|
|
157
164
|
}
|
|
158
165
|
|
|
159
166
|
componentDidMount() {
|
|
@@ -259,7 +266,6 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
259
266
|
*/
|
|
260
267
|
showConfirmationDialog({
|
|
261
268
|
title: "",
|
|
262
|
-
message: alertConfirmationMesage,
|
|
263
269
|
confirmCompletion: this.performExit,
|
|
264
270
|
});
|
|
265
271
|
}
|
|
@@ -282,7 +288,6 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
282
288
|
} else {
|
|
283
289
|
showConfirmationDialog({
|
|
284
290
|
title: "",
|
|
285
|
-
message: alertConfirmationMesage,
|
|
286
291
|
confirmCompletion: this.performExit,
|
|
287
292
|
cancelCompletion: this.onConfirmDialogClose,
|
|
288
293
|
});
|
|
@@ -318,13 +323,9 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
318
323
|
const { navigator } = this.props;
|
|
319
324
|
const { rivers } = this.props;
|
|
320
325
|
|
|
321
|
-
const
|
|
322
|
-
R.prop("id"),
|
|
323
|
-
R.find(R.prop("home")),
|
|
324
|
-
R.values
|
|
325
|
-
)(rivers);
|
|
326
|
+
const homeRiver = getHomeRiver(rivers);
|
|
326
327
|
|
|
327
|
-
const homePath = `/river/${
|
|
328
|
+
const homePath = `/river/${homeRiver?.id}`;
|
|
328
329
|
|
|
329
330
|
return homePath === navigator.currentRoute;
|
|
330
331
|
}
|
|
@@ -332,7 +333,7 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
332
333
|
goToHome() {
|
|
333
334
|
const { navigator } = this.props;
|
|
334
335
|
const { rivers } = this.props;
|
|
335
|
-
const homeRiver =
|
|
336
|
+
const homeRiver = getHomeRiver(rivers);
|
|
336
337
|
|
|
337
338
|
navigator.replace(homeRiver);
|
|
338
339
|
}
|
|
@@ -393,7 +394,15 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
393
394
|
note: backspace affects the keyboard input, so it behaves differently
|
|
394
395
|
*/
|
|
395
396
|
onBackPress(event) {
|
|
396
|
-
const {
|
|
397
|
+
const {
|
|
398
|
+
displayState,
|
|
399
|
+
setDisplayState,
|
|
400
|
+
navigator,
|
|
401
|
+
backToTopAction,
|
|
402
|
+
emitFocusOnSelectedTopMenuItem,
|
|
403
|
+
emitFocusOnSelectedTab,
|
|
404
|
+
} = this.props;
|
|
405
|
+
|
|
397
406
|
const { isDialogVisible } = confirmationDialogStore.getState();
|
|
398
407
|
const { isKeyboardVisible } = this.state;
|
|
399
408
|
|
|
@@ -412,8 +421,56 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
412
421
|
return;
|
|
413
422
|
}
|
|
414
423
|
|
|
424
|
+
const action = backToTopAction();
|
|
425
|
+
|
|
415
426
|
switch (displayState) {
|
|
416
427
|
case DISPLAY_STATES.DEFAULT:
|
|
428
|
+
if (action === "PLATFORM_BACK") {
|
|
429
|
+
log_info(action);
|
|
430
|
+
|
|
431
|
+
this.platformBack();
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (action === "GO_HOME") {
|
|
435
|
+
log_info(action);
|
|
436
|
+
|
|
437
|
+
this.goToHome();
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (action === "FOCUS_ON_SELECTED_TOP_MENU_ITEM") {
|
|
441
|
+
log_info(action);
|
|
442
|
+
|
|
443
|
+
// we don't have enough context at this point to set focus properly on selected top-menu-item, just emit event about it.
|
|
444
|
+
// TopMenu component is supposed to handle this event.
|
|
445
|
+
emitFocusOnSelectedTopMenuItem();
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (action === "FOCUS_ON_SELECTED_TAB_ITEM") {
|
|
449
|
+
log_info(action);
|
|
450
|
+
|
|
451
|
+
// we don't have enough context at this point to set focus properly on selected tab-menu-item, just emit event about it
|
|
452
|
+
// Tabs component is supposed to handle this event.
|
|
453
|
+
emitFocusOnSelectedTab();
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (action === "GO_BACK") {
|
|
457
|
+
log_info(action);
|
|
458
|
+
|
|
459
|
+
if (navigator.canGoBack()) {
|
|
460
|
+
navigator.goBack();
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (action === "FORCE_GO_BACK") {
|
|
465
|
+
log_info(action);
|
|
466
|
+
|
|
467
|
+
const fallbackToHome = false;
|
|
468
|
+
const fromHook = true;
|
|
469
|
+
|
|
470
|
+
navigator.goBack(fallbackToHome, fromHook);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
break;
|
|
417
474
|
case DISPLAY_STATES.PLAYER:
|
|
418
475
|
if (isDialogVisible) {
|
|
419
476
|
this.onConfirmDialogClose();
|
|
@@ -553,6 +610,13 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
553
610
|
) {
|
|
554
611
|
if (displayState === DISPLAY_STATES.PLAYER) {
|
|
555
612
|
setDisplayState(DISPLAY_STATES.HUD);
|
|
613
|
+
|
|
614
|
+
this.accessibilityManager.addHeading(
|
|
615
|
+
VIDEO_PLAYER_CONTROLS_NAVIGATION_MESSAGE
|
|
616
|
+
);
|
|
617
|
+
|
|
618
|
+
focusManager.recoverFocus();
|
|
619
|
+
this.resetHudTimeout();
|
|
556
620
|
} else {
|
|
557
621
|
focusManager.pressIn();
|
|
558
622
|
focusManager.press();
|
|
@@ -597,6 +661,12 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
597
661
|
this.resetHudTimeout();
|
|
598
662
|
}
|
|
599
663
|
|
|
664
|
+
if (displayState === DISPLAY_STATES.PLAYER) {
|
|
665
|
+
this.accessibilityManager.addHeading(
|
|
666
|
+
VIDEO_PLAYER_CONTROLS_NAVIGATION_MESSAGE
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
|
|
600
670
|
return true;
|
|
601
671
|
}
|
|
602
672
|
}
|
|
@@ -653,8 +723,13 @@ class InteractionManagerClass extends React.Component<Props, State> {
|
|
|
653
723
|
onConfirmDialogClose() {
|
|
654
724
|
this.confirmDialog.hideDialog();
|
|
655
725
|
|
|
656
|
-
|
|
657
|
-
|
|
726
|
+
const context: FocusManager.FocusContext = {
|
|
727
|
+
source: "cancel",
|
|
728
|
+
preserveScroll: true,
|
|
729
|
+
};
|
|
730
|
+
|
|
731
|
+
// restore initial focus after closing dialog(with preserve scrolling)
|
|
732
|
+
focusManager.setInitialFocus(undefined, context);
|
|
658
733
|
}
|
|
659
734
|
|
|
660
735
|
handleInputFocus = (event: FocusEvent) => {
|
|
@@ -852,5 +927,6 @@ export const InteractionManager = R.compose(
|
|
|
852
927
|
withXray,
|
|
853
928
|
connectToStore(R.pick(["rivers"])),
|
|
854
929
|
PlayerContentContext.withConsumer,
|
|
855
|
-
DisplayStateContext.withConsumer
|
|
930
|
+
DisplayStateContext.withConsumer,
|
|
931
|
+
withBackToTopActionHOC
|
|
856
932
|
)(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.80",
|
|
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.80",
|
|
26
|
+
"@applicaster/zapp-react-native-bridge": "14.0.0-rc.80",
|
|
27
|
+
"@applicaster/zapp-react-native-redux": "14.0.0-rc.80",
|
|
28
|
+
"@applicaster/zapp-react-native-ui-components": "14.0.0-rc.80",
|
|
29
|
+
"@applicaster/zapp-react-native-utils": "14.0.0-rc.80",
|
|
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": "*"
|