@applicaster/zapp-react-dom-app 14.0.0-alpha.1887720441 → 14.0.0-alpha.2012126431

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.
@@ -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,4 @@
1
+ export {
2
+ withBackToTopActionHOC,
3
+ type BackToTopAction,
4
+ } from "./withBackToTopAction";
@@ -0,0 +1,48 @@
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 backToTopAction = React.useCallback((): BACK_TO_TOP_ACTION => {
27
+ if (isRoot && focusManager.isFocusOnMenu() && isHome) {
28
+ return { action: "PLATFORM_BACK" };
29
+ }
30
+
31
+ if (isRoot && focusManager.isFocusOnMenu() && !isHome) {
32
+ return { action: "GO_HOME" };
33
+ }
34
+
35
+ if (isRoot && focusManager.isFocusOnContent()) {
36
+ return {
37
+ action: "FOCUS_TOP_NAVIGATION",
38
+ payload: { isTabsScreen, selectedEntry },
39
+ };
40
+ }
41
+
42
+ // default
43
+ return { action: "GO_BACK" };
44
+ }, [isHome, isRoot, isTabsScreen, selectedEntry]);
45
+
46
+ return <Component {...props} backToTopAction={backToTopAction} />;
47
+ };
48
+ }
@@ -8,6 +8,8 @@ import {
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,
@@ -50,6 +52,8 @@ import {
50
52
  import { OnScreenKeyboard } from "@applicaster/zapp-react-dom-ui-components/Components/OnScreenKeyboard";
51
53
  import { KeyboardLongPressManager } from "@applicaster/zapp-react-dom-ui-components/Utils/KeyboardLongPressManager";
52
54
  import { KeyInputHandler } from "@applicaster/zapp-react-native-utils/appUtils/keyInputHandler/KeyInputHandler";
55
+ import { getHomeRiver } from "@applicaster/zapp-react-native-utils/reactHooks/state";
56
+ import { withBackToTopActionHOC, BackToTopAction } from "./hoc";
53
57
 
54
58
  const { withXray } = getXray();
55
59
  const globalAny: any = global;
@@ -60,7 +64,8 @@ const { log_debug, log_warning } = createLogger({
60
64
  });
61
65
 
62
66
  const shouldUseOnScreenKeyboard = () =>
63
- isVizioPlatform() || window.applicaster.useOnScreenKeyboard;
67
+ isVizioPlatform() ||
68
+ toBooleanWithDefaultFalse(window?.applicaster?.useOnScreenKeyboard);
64
69
 
65
70
  type Props = {
66
71
  displayState: string;
@@ -70,6 +75,7 @@ type Props = {
70
75
  rivers: {};
71
76
  xray: XRayContext;
72
77
  confirmDialog: typeof confirmationDialogStore;
78
+ backToTopAction: BackToTopAction;
73
79
  };
74
80
 
75
81
  interface State {
@@ -313,13 +319,9 @@ class InteractionManagerClass extends React.Component<Props, State> {
313
319
  const { navigator } = this.props;
314
320
  const { rivers } = this.props;
315
321
 
316
- const homeId = R.compose(
317
- R.prop("id"),
318
- R.find(R.prop("home")),
319
- R.values
320
- )(rivers);
322
+ const homeRiver = getHomeRiver(rivers);
321
323
 
322
- const homePath = `/river/${homeId}`;
324
+ const homePath = `/river/${homeRiver?.id}`;
323
325
 
324
326
  return homePath === navigator.currentRoute;
325
327
  }
@@ -327,7 +329,7 @@ class InteractionManagerClass extends React.Component<Props, State> {
327
329
  goToHome() {
328
330
  const { navigator } = this.props;
329
331
  const { rivers } = this.props;
330
- const homeRiver = R.find(R.propEq("home", true), R.values(rivers));
332
+ const homeRiver = getHomeRiver(rivers);
331
333
 
332
334
  navigator.replace(homeRiver);
333
335
  }
@@ -388,7 +390,9 @@ class InteractionManagerClass extends React.Component<Props, State> {
388
390
  note: backspace affects the keyboard input, so it behaves differently
389
391
  */
390
392
  onBackPress(event) {
391
- const { displayState, setDisplayState, navigator } = this.props;
393
+ const { displayState, setDisplayState, navigator, backToTopAction } =
394
+ this.props;
395
+
392
396
  const { isDialogVisible } = confirmationDialogStore.getState();
393
397
  const { isKeyboardVisible } = this.state;
394
398
 
@@ -407,8 +411,32 @@ class InteractionManagerClass extends React.Component<Props, State> {
407
411
  return;
408
412
  }
409
413
 
414
+ const { action, payload } = backToTopAction();
415
+
410
416
  switch (displayState) {
411
417
  case DISPLAY_STATES.DEFAULT:
418
+ if (action === "PLATFORM_BACK") {
419
+ this.platformBack();
420
+ }
421
+
422
+ if (action === "GO_HOME") {
423
+ this.goToHome();
424
+ }
425
+
426
+ if (action === "FOCUS_TOP_NAVIGATION") {
427
+ focusManager.focusTopNavigation(
428
+ payload.isTabsScreen,
429
+ payload.selectedEntry
430
+ );
431
+ }
432
+
433
+ if (action === "GO_BACK") {
434
+ if (navigator.canGoBack()) {
435
+ navigator.goBack();
436
+ }
437
+ }
438
+
439
+ break;
412
440
  case DISPLAY_STATES.PLAYER:
413
441
  if (isDialogVisible) {
414
442
  this.onConfirmDialogClose();
@@ -648,8 +676,13 @@ class InteractionManagerClass extends React.Component<Props, State> {
648
676
  onConfirmDialogClose() {
649
677
  this.confirmDialog.hideDialog();
650
678
 
651
- // restore initial focus after closing dialog
652
- focusManager.setInitialFocus();
679
+ const context: FocusManager.FocusContext = {
680
+ source: "cancel",
681
+ preserveScroll: true,
682
+ };
683
+
684
+ // restore initial focus after closing dialog(with preserve scrolling)
685
+ focusManager.setInitialFocus(undefined, context);
653
686
  }
654
687
 
655
688
  handleInputFocus = (event: FocusEvent) => {
@@ -847,5 +880,6 @@ export const InteractionManager = R.compose(
847
880
  withXray,
848
881
  connectToStore(R.pick(["rivers"])),
849
882
  PlayerContentContext.withConsumer,
850
- DisplayStateContext.withConsumer
883
+ DisplayStateContext.withConsumer,
884
+ withBackToTopActionHOC
851
885
  )(InteractionManagerClass);
@@ -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,6 +64,7 @@ async function gatherData(uuid) {
63
64
  ...NativeModules?.QuickBrickCommunicationModule,
64
65
  // Device data retrieved from native device apis
65
66
  ...deviceData,
67
+ userAgent: deviceData.userAgent || getUserAgent(),
66
68
  uuid: uuid || uuidv4(), // Created on first session, persisted in local thereafter
67
69
  sessionId: uuidv4(), // Created on every session
68
70
  locale: `{language=${languageLocale}, country=${countryLocale}}`,
@@ -79,21 +81,23 @@ async function gatherData(uuid) {
79
81
  * @param {Object} desiredKeys - The map of original key names to desired key names.
80
82
  */
81
83
  function storeData(data, desiredKeys) {
82
- // Pick only the keys we want and need, map them to the correct name, and reject any unusuable data
83
- const filteredData = R.compose(
84
- R.reject(isEmptyOrNil),
85
- R.reject(R.either(R.is(Object), R.is(Function))),
86
- mapKeys(renameKeys),
87
- R.pick(Object.keys(desiredKeys))
88
- )(data);
89
-
90
- R.forEachObjIndexed((value, key) => {
91
- if (key === "uuid") {
92
- localStorage.setItem(key, value);
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);
93
97
  }
94
98
 
95
- sessionStorage.setItem(key, value);
96
- }, filteredData);
99
+ sessionStorage.setItem(renamedKey, value);
100
+ });
97
101
  }
98
102
 
99
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-dom-app",
3
- "version": "14.0.0-alpha.1887720441",
3
+ "version": "14.0.0-alpha.2012126431",
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-alpha.1887720441",
26
- "@applicaster/zapp-react-native-bridge": "14.0.0-alpha.1887720441",
27
- "@applicaster/zapp-react-native-redux": "14.0.0-alpha.1887720441",
28
- "@applicaster/zapp-react-native-ui-components": "14.0.0-alpha.1887720441",
29
- "@applicaster/zapp-react-native-utils": "14.0.0-alpha.1887720441",
25
+ "@applicaster/zapp-react-dom-ui-components": "14.0.0-alpha.2012126431",
26
+ "@applicaster/zapp-react-native-bridge": "14.0.0-alpha.2012126431",
27
+ "@applicaster/zapp-react-native-redux": "14.0.0-alpha.2012126431",
28
+ "@applicaster/zapp-react-native-ui-components": "14.0.0-alpha.2012126431",
29
+ "@applicaster/zapp-react-native-utils": "14.0.0-alpha.2012126431",
30
30
  "abortcontroller-polyfill": "^1.7.5",
31
31
  "typeface-montserrat": "^0.0.54",
32
32
  "video.js": "7.14.3",