@applicaster/zapp-react-dom-app 16.0.0-rc.1 → 16.0.0-rc.10

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.
@@ -8,12 +8,12 @@ import {
8
8
  useCurrentScreenIsHome,
9
9
  useCurrentScreenIsRoot,
10
10
  useCurrentScreenIsTabs,
11
- useIsStandaloneFullscreen,
12
11
  } from "../hooks";
13
12
 
14
13
  import {
15
14
  useCurrentScreenIsHook,
16
15
  useCurrentScreenIsStartupHook,
16
+ useIsStandaloneFullscreen,
17
17
  } from "@applicaster/zapp-react-native-utils/reactHooks/screen";
18
18
 
19
19
  jest.mock("@applicaster/zapp-react-native-utils/appUtils", () => ({
@@ -64,14 +64,14 @@ describe("withBackToTopActionHOC - backToTopAction", () => {
64
64
  });
65
65
  });
66
66
 
67
- it("returns PLATFORM_BACK for startup hook", () => {
67
+ it("returns PLATFORM_TO_BACKGROUND for startup hook", () => {
68
68
  expect.assertions(1);
69
69
 
70
70
  (useCurrentScreenIsStartupHook as jest.Mock).mockReturnValue(true);
71
71
 
72
72
  render(<Wrapped />);
73
73
 
74
- expect(receivedProps.backToTopAction()).toBe("PLATFORM_BACK");
74
+ expect(receivedProps.backToTopAction()).toBe("PLATFORM_TO_BACKGROUND");
75
75
  });
76
76
 
77
77
  it("returns GO_BACK_FROM_HOOK when isHook", () => {
@@ -94,7 +94,7 @@ describe("withBackToTopActionHOC - backToTopAction", () => {
94
94
  expect(receivedProps.backToTopAction()).toBe("GO_HOME");
95
95
  });
96
96
 
97
- it("returns PLATFORM_BACK when root + menu focus + home", () => {
97
+ it("returns PLATFORM_TO_BACKGROUND when root + menu focus + home", () => {
98
98
  expect.assertions(1);
99
99
 
100
100
  (useCurrentScreenIsRoot as jest.Mock).mockReturnValue(true);
@@ -106,7 +106,7 @@ describe("withBackToTopActionHOC - backToTopAction", () => {
106
106
 
107
107
  render(<Wrapped />);
108
108
 
109
- expect(receivedProps.backToTopAction()).toBe("PLATFORM_BACK");
109
+ expect(receivedProps.backToTopAction()).toBe("PLATFORM_TO_BACKGROUND");
110
110
  });
111
111
 
112
112
  it("returns GO_HOME when root + menu focus + NOT home", () => {
@@ -4,7 +4,6 @@ import {
4
4
  useRivers,
5
5
  } from "@applicaster/zapp-react-native-utils/reactHooks/state";
6
6
  import { last } from "@applicaster/zapp-react-native-utils/utils";
7
- import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
8
7
 
9
8
  export const useCurrentScreenIsHome = (): boolean => {
10
9
  const navigator = useNavigation();
@@ -33,12 +32,3 @@ export const useCurrentScreenIsTabs = (): boolean => {
33
32
 
34
33
  return river?.type === "tabs_screen";
35
34
  };
36
-
37
- export const useIsStandaloneFullscreen = (): boolean => {
38
- const navigator = useNavigation();
39
-
40
- return toBooleanWithDefaultFalse(
41
- !navigator?.canGoBack() &&
42
- navigator?.screenData?.general?.allow_screen_plugin_presentation
43
- );
44
- };
@@ -8,12 +8,12 @@ import {
8
8
  useCurrentScreenIsHome,
9
9
  useCurrentScreenIsRoot,
10
10
  useCurrentScreenIsTabs,
11
- useIsStandaloneFullscreen,
12
11
  } from "./hooks";
13
12
 
14
13
  import {
15
14
  useCurrentScreenIsHook,
16
15
  useCurrentScreenIsStartupHook,
16
+ useIsStandaloneFullscreen,
17
17
  } from "@applicaster/zapp-react-native-utils/reactHooks/screen";
18
18
 
19
19
  export function withBackToTopActionHOC(Component) {
@@ -38,20 +38,20 @@ export function withBackToTopActionHOC(Component) {
38
38
 
39
39
  const backToTopAction = React.useCallback((): BackToTopAction => {
40
40
  if (isStartUpHook) {
41
- return "PLATFORM_BACK";
41
+ return "PLATFORM_TO_BACKGROUND";
42
42
  }
43
43
 
44
44
  if (isHook) {
45
45
  return "GO_BACK_FROM_HOOK";
46
46
  }
47
47
 
48
- // Handling case where is not top menu visible
48
+ // Handle the case where the top menu is not visible
49
49
  if (isStandaloneFullscreen) {
50
50
  return "GO_HOME";
51
51
  }
52
52
 
53
53
  if (isRoot && focusManager.isFocusOnMenu() && isHome) {
54
- return "PLATFORM_BACK";
54
+ return "PLATFORM_TO_BACKGROUND";
55
55
  }
56
56
 
57
57
  if (isRoot && focusManager.isFocusOnMenu() && !isHome) {
@@ -3,11 +3,6 @@ import * as R from "ramda";
3
3
 
4
4
  import { connectToStore } from "@applicaster/zapp-react-native-redux/utils/connectToStore";
5
5
 
6
- import {
7
- QUICK_BRICK_EVENTS,
8
- sendQuickBrickEvent,
9
- } from "@applicaster/zapp-react-native-bridge/QuickBrick";
10
-
11
6
  import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
12
7
 
13
8
  import {
@@ -50,6 +45,8 @@ import {
50
45
  isVizioPlatform,
51
46
  } from "@applicaster/zapp-react-native-utils/reactUtils";
52
47
 
48
+ import { platformToBackground as sendAppToBackground } from "@applicaster/zapp-react-native-utils/appUtils/platform";
49
+
53
50
  import { OnScreenKeyboard } from "@applicaster/zapp-react-dom-ui-components/Components/OnScreenKeyboard";
54
51
  import { KeyboardLongPressManager } from "@applicaster/zapp-react-dom-ui-components/Utils/KeyboardLongPressManager";
55
52
  import { KeyInputHandler } from "@applicaster/zapp-react-native-utils/appUtils/keyInputHandler/KeyInputHandler";
@@ -242,7 +239,7 @@ class InteractionManagerClass extends React.Component<Props, State> {
242
239
  performExit() {
243
240
  if (isSamsungPlatform()) {
244
241
  this.onConfirmDialogClose();
245
- sendQuickBrickEvent(QUICK_BRICK_EVENTS.MOVE_APP_TO_BACKGROUND);
242
+ sendAppToBackground();
246
243
  }
247
244
 
248
245
  if (isLgPlatform()) {
@@ -256,7 +253,7 @@ class InteractionManagerClass extends React.Component<Props, State> {
256
253
  }
257
254
 
258
255
  handleWebOsBack() {
259
- const systemBack = globalAny?.webOS?.platformBack;
256
+ const systemBack = globalAny?.webOS?.platformToBackground;
260
257
 
261
258
  if (systemBack) {
262
259
  systemBack();
@@ -277,7 +274,7 @@ class InteractionManagerClass extends React.Component<Props, State> {
277
274
  * WebOS behavior depends on the SDK version, WebOS 4 would pull up the launcher menu
278
275
  * following versions would show a native exit prompt
279
276
  */
280
- platformBack() {
277
+ platformToBackground() {
281
278
  try {
282
279
  // TODO: temporary hack until this code is refactored
283
280
  if (isSamsungPlatform() || isVizioPlatform()) {
@@ -294,9 +291,11 @@ class InteractionManagerClass extends React.Component<Props, State> {
294
291
  }
295
292
  } else if (isLgPlatform()) {
296
293
  this.handleWebOsBack();
294
+ } else {
295
+ sendAppToBackground();
297
296
  }
298
297
  } catch (e) {
299
- log_warning("Failed to execute platformBack", e);
298
+ log_warning("Failed to execute platformToBackground", e);
300
299
  }
301
300
  }
302
301
 
@@ -425,10 +424,10 @@ class InteractionManagerClass extends React.Component<Props, State> {
425
424
 
426
425
  switch (displayState) {
427
426
  case DISPLAY_STATES.DEFAULT:
428
- if (action === "PLATFORM_BACK") {
427
+ if (action === "PLATFORM_TO_BACKGROUND") {
429
428
  log_info(action);
430
429
 
431
- this.platformBack();
430
+ this.platformToBackground();
432
431
  }
433
432
 
434
433
  if (action === "GO_HOME") {
@@ -477,11 +476,11 @@ class InteractionManagerClass extends React.Component<Props, State> {
477
476
  } else if (navigator.canGoBack()) {
478
477
  navigator.goBack();
479
478
  } else if (this.isHomeScreen()) {
480
- this.platformBack();
479
+ this.platformToBackground();
481
480
  } else if (navigator.currentRoute.includes("hook")) {
482
481
  // If code reaches this block navigator cant go back, it is not home,
483
482
  // and this is a login plugin that was pushed on top of the home
484
- // Treat it like a home screen and run platformBack()
483
+ // Treat it like a home screen and run platformToBackground()
485
484
  const entryData = navigator?.data?.entry;
486
485
  const isHookInHomescreen = entryData?.payload?.home;
487
486
 
@@ -491,7 +490,7 @@ class InteractionManagerClass extends React.Component<Props, State> {
491
490
  : entryData?.hookPlugin?.module?.isFlowBlocker;
492
491
 
493
492
  if (isHookInHomescreen && isHookFlowBlocker) {
494
- this.platformBack();
493
+ this.platformToBackground();
495
494
  } else {
496
495
  navigator.goBack(false, true);
497
496
  }
@@ -640,7 +639,7 @@ class InteractionManagerClass extends React.Component<Props, State> {
640
639
  return;
641
640
  }
642
641
 
643
- this.platformBack();
642
+ this.platformToBackground();
644
643
  }
645
644
 
646
645
  if (keyCode(event).matchesAny(...ARROW_KEYS)) {
@@ -1,7 +1,8 @@
1
1
  import * as React from "react";
2
2
  import { createPortal } from "react-dom";
3
3
 
4
- import { Background } from "@applicaster/zapp-react-dom-ui-components/Components/Background";
4
+ import { BackgroundImage } from "@applicaster/zapp-react-native-ui-components/Components/BackgroundImage";
5
+
5
6
  import { ConfirmDialog } from "@applicaster/zapp-react-dom-ui-components/Components/ConfirmDialog";
6
7
  import { NavWrapper } from "@applicaster/zapp-react-dom-ui-components/Components/NavWrapper";
7
8
  import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
@@ -25,7 +26,7 @@ export function Layout({ children }: Props) {
25
26
  return (
26
27
  <>
27
28
  <LayoutComponent
28
- Components={{ Background, NavBar: NavWrapper }}
29
+ Components={{ Background: BackgroundImage, NavBar: NavWrapper }}
29
30
  ComponentsExtraProps={ComponentsExtraProps}
30
31
  >
31
32
  {children}
@@ -0,0 +1,104 @@
1
+ import { getContextResolverBridge } from "../";
2
+
3
+ function createStorageMock(initial = {}) {
4
+ const data = { ...initial };
5
+
6
+ return {
7
+ getItem: (key) => (key in data ? data[key] : null),
8
+ setItem: (key, value) => {
9
+ data[key] = value;
10
+ },
11
+ };
12
+ }
13
+
14
+ describe("ContextResolverBridge polyfill", () => {
15
+ let bridge;
16
+
17
+ beforeEach(() => {
18
+ global.window = {
19
+ sessionStorage: createStorageMock(),
20
+ localStorage: createStorageMock(),
21
+ };
22
+
23
+ bridge = getContextResolverBridge();
24
+ });
25
+
26
+ it("resolves a key from session storage using the web namespace separator", () => {
27
+ // the namespace itself contains dots, so the key must be split on the last dot
28
+ window.sessionStorage.setItem("applicaster.v2_::_appVersion", "1.0.0");
29
+
30
+ const result = bridge.resolveContextKeys({
31
+ "applicaster.v2.appVersion": false,
32
+ });
33
+
34
+ expect(result).toEqual({ "applicaster.v2.appVersion": "1.0.0" });
35
+ });
36
+
37
+ it("falls back to local storage when the key is missing in session storage", () => {
38
+ window.localStorage.setItem("applicaster.v2_::_appVersion", "1.0.0");
39
+
40
+ const result = bridge.resolveContextKeys({
41
+ "applicaster.v2.appVersion": false,
42
+ });
43
+
44
+ expect(result).toEqual({ "applicaster.v2.appVersion": "1.0.0" });
45
+ });
46
+
47
+ it("prefers the session storage value when the key exists in both", () => {
48
+ window.sessionStorage.setItem("applicaster.v2_::_appVersion", "session");
49
+ window.localStorage.setItem("applicaster.v2_::_appVersion", "local");
50
+
51
+ const result = bridge.resolveContextKeys({
52
+ "applicaster.v2.appVersion": false,
53
+ });
54
+
55
+ expect(result).toEqual({ "applicaster.v2.appVersion": "session" });
56
+ });
57
+
58
+ it("returns null for keys that are missing from both storages", () => {
59
+ const result = bridge.resolveContextKeys({
60
+ "applicaster.v2.missing": false,
61
+ });
62
+
63
+ expect(result).toEqual({ "applicaster.v2.missing": null });
64
+ });
65
+
66
+ it("JSON-parses stored values just like the async storage path", () => {
67
+ window.sessionStorage.setItem(
68
+ "applicaster.v2_::_profile",
69
+ JSON.stringify({ name: "Ada" })
70
+ );
71
+
72
+ window.localStorage.setItem("applicaster.v2_::_count", "42");
73
+
74
+ const result = bridge.resolveContextKeys({
75
+ "applicaster.v2.profile": false,
76
+ "applicaster.v2.count": true,
77
+ });
78
+
79
+ expect(result).toEqual({
80
+ "applicaster.v2.profile": { name: "Ada" },
81
+ "applicaster.v2.count": 42,
82
+ });
83
+ });
84
+
85
+ it("resolves multiple keys synchronously in a single call", () => {
86
+ window.sessionStorage.setItem("applicaster.v2_::_a", "valueA");
87
+ window.localStorage.setItem("applicaster.v2_::_b", "valueB");
88
+
89
+ const result = bridge.resolveContextKeys({
90
+ "applicaster.v2.a": false,
91
+ "applicaster.v2.b": false,
92
+ "applicaster.v2.c": false,
93
+ });
94
+
95
+ // returned object is not a promise - reads happen synchronously
96
+ expect(result).not.toBeInstanceOf(Promise);
97
+
98
+ expect(result).toEqual({
99
+ "applicaster.v2.a": "valueA",
100
+ "applicaster.v2.b": "valueB",
101
+ "applicaster.v2.c": null,
102
+ });
103
+ });
104
+ });
@@ -200,3 +200,74 @@ export function getStorageModule(type) {
200
200
  removeItem,
201
201
  };
202
202
  }
203
+
204
+ const STORAGE_READ_ORDER = ["sessionStorage", "localStorage"];
205
+
206
+ /**
207
+ * Splits a normalized context key ("<namespace>.<key>") back into its
208
+ * namespace and key parts. Mirrors getNamespaceAndKey in the context keys
209
+ * manager: the key is everything after the last dot, since the namespace
210
+ * itself may contain dots (e.g. "applicaster.v2").
211
+ * @param {String} namespacedKey
212
+ * @returns {{ namespace: String, key: String }}
213
+ */
214
+ function splitNamespacedKey(namespacedKey) {
215
+ const lastDotIndex = namespacedKey.lastIndexOf(".");
216
+
217
+ if (lastDotIndex === -1) {
218
+ return { namespace: DEFAULT_NAMESPACE, key: namespacedKey };
219
+ }
220
+
221
+ return {
222
+ namespace: namespacedKey.slice(0, lastDotIndex),
223
+ key: namespacedKey.slice(lastDotIndex + 1),
224
+ };
225
+ }
226
+
227
+ /**
228
+ * Synchronously reads a single context key from session storage, then local
229
+ * storage (matching the native ContextResolverBridge resolution order), and
230
+ * returns the parsed value, or null when the key is absent from both.
231
+ * @param {String} namespacedKey
232
+ * @returns {Any|null}
233
+ */
234
+ function resolveContextKey(namespacedKey) {
235
+ const { namespace, key } = splitNamespacedKey(namespacedKey);
236
+ const storageKey = applyNamespaceToKeyName(key, namespace);
237
+
238
+ for (const type of STORAGE_READ_ORDER) {
239
+ const storage = window[type];
240
+ const value = storage ? storage.getItem(storageKey) : null;
241
+
242
+ if (value != null) {
243
+ return parseJsonIfNeeded(value);
244
+ }
245
+ }
246
+
247
+ return null;
248
+ }
249
+
250
+ /**
251
+ * Web polyfill for the native NativeModules.ContextResolverBridge module.
252
+ * Unlike the per-key async storage path, it resolves every requested key in a
253
+ * single synchronous pass over window.sessionStorage / window.localStorage.
254
+ * @returns {Object} bridge
255
+ * @returns {Function} bridge.resolveContextKeys: resolves a batch of keys
256
+ */
257
+ export function getContextResolverBridge() {
258
+ return {
259
+ /**
260
+ * @param {Object} keys map of normalized key -> required flag
261
+ * @returns {Object} map of normalized key -> resolved value (or null)
262
+ */
263
+ resolveContextKeys(keys) {
264
+ const result = {};
265
+
266
+ for (const namespacedKey of Object.keys(keys || {})) {
267
+ result[namespacedKey] = resolveContextKey(namespacedKey);
268
+ }
269
+
270
+ return result;
271
+ },
272
+ };
273
+ }
@@ -1,4 +1,4 @@
1
- import { getStorageModule } from "./Storage";
1
+ import { getStorageModule, getContextResolverBridge } from "./Storage";
2
2
  import { DeviceEventEmitter } from "./DeviceEventEmitter";
3
3
  import { isSamsungPlatform, isLgPlatform } from "../App/Loader/utils/platform";
4
4
 
@@ -23,6 +23,7 @@ const PLATFORM_KEYS = {
23
23
  export function registerNativeModulesPolyfills(NativeModules) {
24
24
  NativeModules.LocalStorage = getStorageModule("localStorage");
25
25
  NativeModules.SessionStorage = getStorageModule("sessionStorage");
26
+ NativeModules.ContextResolverBridge = getContextResolverBridge();
26
27
  NativeModules.AnalyticsBridge = require("./AnalyticsBridge").AnalyticsBridge;
27
28
  NativeModules.AppLoaderBridge = require("./AppLoaderBridge").AppLoaderBridge;
28
29
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-dom-app",
3
- "version": "16.0.0-rc.1",
3
+ "version": "16.0.0-rc.10",
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": "16.0.0-rc.1",
26
- "@applicaster/zapp-react-native-bridge": "16.0.0-rc.1",
27
- "@applicaster/zapp-react-native-redux": "16.0.0-rc.1",
28
- "@applicaster/zapp-react-native-ui-components": "16.0.0-rc.1",
29
- "@applicaster/zapp-react-native-utils": "16.0.0-rc.1",
25
+ "@applicaster/zapp-react-dom-ui-components": "16.0.0-rc.10",
26
+ "@applicaster/zapp-react-native-bridge": "16.0.0-rc.10",
27
+ "@applicaster/zapp-react-native-redux": "16.0.0-rc.10",
28
+ "@applicaster/zapp-react-native-ui-components": "16.0.0-rc.10",
29
+ "@applicaster/zapp-react-native-utils": "16.0.0-rc.10",
30
30
  "abortcontroller-polyfill": "^1.7.5",
31
31
  "typeface-montserrat": "^0.0.54",
32
32
  "video.js": "7.14.3",