@applicaster/zapp-react-dom-app 16.0.0-rc.3 → 16.0.0-rc.30

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", () => ({
@@ -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) {
@@ -45,7 +45,7 @@ export function withBackToTopActionHOC(Component) {
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
  }
@@ -569,7 +569,7 @@ class InteractionManagerClass extends React.Component<Props, State> {
569
569
  const { playing } = playerManager.getState() || {};
570
570
 
571
571
  if (!playing) {
572
- return playerManager.togglePlayPause();
572
+ return playerManager.getInstanceController()?.togglePlayPause();
573
573
  }
574
574
  }
575
575
 
@@ -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}
@@ -64,6 +64,7 @@ export const PLATFORMS = {
64
64
  android: "Android",
65
65
  mobile: "Mobile",
66
66
  vizio: "VIZIO",
67
+ vidaa: "VIDAA",
67
68
  smartcast: "SmartCast",
68
69
  conjure: "Conjure",
69
70
  };
@@ -13,7 +13,7 @@ import {
13
13
 
14
14
  /**
15
15
  * @typedef {"tv" | "web" | "other"} DeviceType
16
- * @typedef {"samsung_tv" | "lg_tv" | unknown} CustomPlatformTypes
16
+ * @typedef {"samsung_tv" | "lg_tv" | "vidaa" | unknown} CustomPlatformTypes
17
17
  */
18
18
 
19
19
  /**
@@ -75,6 +75,16 @@ export const isVizioPlatform = () => {
75
75
  );
76
76
  };
77
77
 
78
+ /**
79
+ * Checks if the platform is Vida.
80
+ * @returns {boolean} True if the platform is Vida, false otherwise.
81
+ */
82
+ export const isVidaaPlatform = () => {
83
+ const userAgent = getUserAgent();
84
+
85
+ return userAgent.includes(PLATFORMS.vidaa);
86
+ };
87
+
78
88
  /**
79
89
  * Checks if the Vizio APIs are available.
80
90
  * @returns {boolean} True if the Vizio APIs are available, false otherwise.
@@ -99,6 +109,10 @@ export const getDeviceType = () => {
99
109
  return isSamsungPlatform() ? "tv" : "web";
100
110
  case "lg_tv":
101
111
  return isLgPlatform() ? "tv" : "web";
112
+ case "vizio":
113
+ return isVizioPlatform() ? "tv" : "web";
114
+ case "vidaa":
115
+ return isVidaaPlatform() ? "tv" : "web";
102
116
  default:
103
117
  return "other";
104
118
  }
@@ -9,4 +9,5 @@ export enum STORE_PLATFORM {
9
9
  lg = "lg_content_store",
10
10
  samsung = "samsung_app_store",
11
11
  vizio = "vizio_app_store",
12
+ vidaa = "vidaa_app_store",
12
13
  }
@@ -26,7 +26,7 @@ export function StorageMock() {
26
26
 
27
27
  delete this[key];
28
28
 
29
- return true;
29
+ // Return undefined like the real API; a truthy return would re-mask the bug.
30
30
  };
31
31
 
32
32
  storage.getItem = (key) => {
@@ -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
+ });
@@ -155,7 +155,11 @@ export function getStorageModule(type) {
155
155
  function removeItem(key, namespace = DEFAULT_NAMESPACE) {
156
156
  const keyName = applyNamespaceToKeyName(key, namespace);
157
157
 
158
- return tryAndResolve(() => Storage.callMethod("removeItem", keyName));
158
+ return tryAndResolve(() => {
159
+ Storage.callMethod("removeItem", keyName);
160
+
161
+ return true;
162
+ });
159
163
  }
160
164
 
161
165
  /**
@@ -200,3 +204,74 @@ export function getStorageModule(type) {
200
204
  removeItem,
201
205
  };
202
206
  }
207
+
208
+ const STORAGE_READ_ORDER = ["sessionStorage", "localStorage"];
209
+
210
+ /**
211
+ * Splits a normalized context key ("<namespace>.<key>") back into its
212
+ * namespace and key parts. Mirrors getNamespaceAndKey in the context keys
213
+ * manager: the key is everything after the last dot, since the namespace
214
+ * itself may contain dots (e.g. "applicaster.v2").
215
+ * @param {String} namespacedKey
216
+ * @returns {{ namespace: String, key: String }}
217
+ */
218
+ function splitNamespacedKey(namespacedKey) {
219
+ const lastDotIndex = namespacedKey.lastIndexOf(".");
220
+
221
+ if (lastDotIndex === -1) {
222
+ return { namespace: DEFAULT_NAMESPACE, key: namespacedKey };
223
+ }
224
+
225
+ return {
226
+ namespace: namespacedKey.slice(0, lastDotIndex),
227
+ key: namespacedKey.slice(lastDotIndex + 1),
228
+ };
229
+ }
230
+
231
+ /**
232
+ * Synchronously reads a single context key from session storage, then local
233
+ * storage (matching the native ContextResolverBridge resolution order), and
234
+ * returns the parsed value, or null when the key is absent from both.
235
+ * @param {String} namespacedKey
236
+ * @returns {Any|null}
237
+ */
238
+ function resolveContextKey(namespacedKey) {
239
+ const { namespace, key } = splitNamespacedKey(namespacedKey);
240
+ const storageKey = applyNamespaceToKeyName(key, namespace);
241
+
242
+ for (const type of STORAGE_READ_ORDER) {
243
+ const storage = window[type];
244
+ const value = storage ? storage.getItem(storageKey) : null;
245
+
246
+ if (value != null) {
247
+ return parseJsonIfNeeded(value);
248
+ }
249
+ }
250
+
251
+ return null;
252
+ }
253
+
254
+ /**
255
+ * Web polyfill for the native NativeModules.ContextResolverBridge module.
256
+ * Unlike the per-key async storage path, it resolves every requested key in a
257
+ * single synchronous pass over window.sessionStorage / window.localStorage.
258
+ * @returns {Object} bridge
259
+ * @returns {Function} bridge.resolveContextKeys: resolves a batch of keys
260
+ */
261
+ export function getContextResolverBridge() {
262
+ return {
263
+ /**
264
+ * @param {Object} keys map of normalized key -> required flag
265
+ * @returns {Object} map of normalized key -> resolved value (or null)
266
+ */
267
+ resolveContextKeys(keys) {
268
+ const result = {};
269
+
270
+ for (const namespacedKey of Object.keys(keys || {})) {
271
+ result[namespacedKey] = resolveContextKey(namespacedKey);
272
+ }
273
+
274
+ return result;
275
+ },
276
+ };
277
+ }
@@ -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
 
@@ -15,6 +15,7 @@ import "@webcomponents/webcomponentsjs";
15
15
  const PLATFORM_KEYS = {
16
16
  samsung: "samsung_tv",
17
17
  lg: "lg_tv",
18
+ vidaa: "vidaa",
18
19
  web: "web",
19
20
  };
20
21
 
@@ -23,6 +24,7 @@ const PLATFORM_KEYS = {
23
24
  export function registerNativeModulesPolyfills(NativeModules) {
24
25
  NativeModules.LocalStorage = getStorageModule("localStorage");
25
26
  NativeModules.SessionStorage = getStorageModule("sessionStorage");
27
+ NativeModules.ContextResolverBridge = getContextResolverBridge();
26
28
  NativeModules.AnalyticsBridge = require("./AnalyticsBridge").AnalyticsBridge;
27
29
  NativeModules.AppLoaderBridge = require("./AppLoaderBridge").AppLoaderBridge;
28
30
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-dom-app",
3
- "version": "16.0.0-rc.3",
3
+ "version": "16.0.0-rc.30",
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.3",
26
- "@applicaster/zapp-react-native-bridge": "16.0.0-rc.3",
27
- "@applicaster/zapp-react-native-redux": "16.0.0-rc.3",
28
- "@applicaster/zapp-react-native-ui-components": "16.0.0-rc.3",
29
- "@applicaster/zapp-react-native-utils": "16.0.0-rc.3",
25
+ "@applicaster/zapp-react-dom-ui-components": "16.0.0-rc.30",
26
+ "@applicaster/zapp-react-native-bridge": "16.0.0-rc.30",
27
+ "@applicaster/zapp-react-native-redux": "16.0.0-rc.30",
28
+ "@applicaster/zapp-react-native-ui-components": "16.0.0-rc.30",
29
+ "@applicaster/zapp-react-native-utils": "16.0.0-rc.30",
30
30
  "abortcontroller-polyfill": "^1.7.5",
31
31
  "typeface-montserrat": "^0.0.54",
32
32
  "video.js": "7.14.3",