@applicaster/zapp-react-native-utils 13.0.0-alpha.3322736490 → 13.0.0-alpha.3332871333

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.
@@ -65,6 +65,36 @@ export function getUILanguage() {
65
65
  return getLanguageCode();
66
66
  }
67
67
 
68
+ /**
69
+ * fallback to en for missing dayjslocalization
70
+ * converts the locale code to a dayjs compatible locale code
71
+ * lowercase input code is expected, e.g. "en-GB" => "en-gb"
72
+ * @param {string} code - locale code, e.g. "en", "fr", "es-LA"
73
+ * */
74
+ export const toDayJSLocaleMap = (code) => {
75
+ const map = {
76
+ hy: "hy-am",
77
+ fj: null,
78
+ la: null,
79
+ no: null,
80
+ pa: "pa-in",
81
+ qu: null,
82
+ sm: null,
83
+ to: null,
84
+ xh: null,
85
+ "es-LA": "es",
86
+ "en-UK": "en-gb",
87
+ };
88
+
89
+ if (map[code] === null) {
90
+ return "en";
91
+ } else if (map[code]) {
92
+ return map[code];
93
+ } else {
94
+ return code;
95
+ }
96
+ };
97
+
68
98
  /**
69
99
  * returns the localization for the app based on the locale. If the locale is null
70
100
  * or undefined, or if the given locale doesn't exist in the localizations,
@@ -3,12 +3,18 @@ import { OverlaysObserver } from "./OverlayObserver/OverlaysObserver";
3
3
  import { useFeedLoader } from "../../reactHooks";
4
4
  import { usePlayer } from "./usePlayer";
5
5
 
6
+ const getLiveUrl = (url: string) => {
7
+ // Use the replace method with a regular expression to replace all instances of subClass with livePage
8
+ return url.replace(new RegExp("subClass", "g"), "livePage");
9
+ };
10
+
6
11
  export const useTitleSummaryOverlay = (
7
- feedUrl,
8
12
  entry,
9
13
  isLive = false,
10
14
  interval: number
11
15
  ) => {
16
+ // TODO: Have generic place for live url from configuration. Currently its hardcoded for client.
17
+ const feedUrl = getLiveUrl(entry?.extensions?.live?.channel_src ?? "");
12
18
  const player = usePlayer();
13
19
  const [title, setTitle] = useState<string | number>(entry?.title || "");
14
20
  const [summary, setSummary] = useState<string | number>(entry?.summary || "");
@@ -0,0 +1,33 @@
1
+ import { isFirstComponentGallery } from "../index";
2
+
3
+ describe("isFirstComponentGallery", () => {
4
+ it("should return true if the first component is a gallery", () => {
5
+ const components = [
6
+ { component_type: "gallery-qb" },
7
+ { component_type: "other-component" },
8
+ ];
9
+
10
+ expect(isFirstComponentGallery(components)).toBe(true);
11
+ });
12
+
13
+ it("should return false if the first component is not a gallery", () => {
14
+ const components = [
15
+ { component_type: "other-component" },
16
+ { component_type: "gallery-qb" },
17
+ ];
18
+
19
+ expect(isFirstComponentGallery(components)).toBe(false);
20
+ });
21
+
22
+ it("should return false if the components array is empty", () => {
23
+ const components = [];
24
+
25
+ expect(isFirstComponentGallery(components)).toBe(false);
26
+ });
27
+
28
+ it("should return false if the components array is undefined", () => {
29
+ const components = undefined;
30
+
31
+ expect(isFirstComponentGallery(components)).toBe(false);
32
+ });
33
+ });
@@ -0,0 +1,33 @@
1
+ import { isFirstComponentScreenPicker } from "..";
2
+
3
+ describe("isFirstComponentScreenPicker", () => {
4
+ it("should return true if the first component screen-picker", () => {
5
+ const items = [{ component_type: "screen-picker-qb-tv" }];
6
+ expect(isFirstComponentScreenPicker(items)).toBe(true);
7
+ });
8
+
9
+ it("should return false if the first component type is not screen-picker", () => {
10
+ const items = [{ component_type: "other-component" }];
11
+ expect(isFirstComponentScreenPicker(items)).toBe(false);
12
+ });
13
+
14
+ it("should return false if the component type is undefined", () => {
15
+ const items = [{ component_type: undefined }];
16
+ expect(isFirstComponentScreenPicker(items)).toBe(false);
17
+ });
18
+
19
+ it("should return false if the item is null", () => {
20
+ const items = null;
21
+ expect(isFirstComponentScreenPicker(items)).toBe(false);
22
+ });
23
+
24
+ it("should return false if the item is undefined", () => {
25
+ const items = undefined;
26
+ expect(isFirstComponentScreenPicker(items)).toBe(false);
27
+ });
28
+
29
+ it("should return false if the item is an empty object", () => {
30
+ const items = {};
31
+ expect(isFirstComponentScreenPicker(items)).toBe(false);
32
+ });
33
+ });
@@ -0,0 +1,33 @@
1
+ import { isGallery } from "..";
2
+
3
+ describe("isGallery", () => {
4
+ it("should return true if the component type is 'gallery-qb'", () => {
5
+ const item = { component_type: "gallery-qb" };
6
+ expect(isGallery(item)).toBe(true);
7
+ });
8
+
9
+ it("should return false if the component type is not 'gallery-qb'", () => {
10
+ const item = { component_type: "other-component" };
11
+ expect(isGallery(item)).toBe(false);
12
+ });
13
+
14
+ it("should return false if the component type is undefined", () => {
15
+ const item = { component_type: undefined };
16
+ expect(isGallery(item)).toBe(false);
17
+ });
18
+
19
+ it("should return false if the item is null", () => {
20
+ const item = null;
21
+ expect(isGallery(item)).toBe(false);
22
+ });
23
+
24
+ it("should return false if the item is undefined", () => {
25
+ const item = undefined;
26
+ expect(isGallery(item)).toBe(false);
27
+ });
28
+
29
+ it("should return false if the item is an empty object", () => {
30
+ const item = {};
31
+ expect(isGallery(item)).toBe(false);
32
+ });
33
+ });
@@ -0,0 +1,33 @@
1
+ import { isScreenPicker } from "..";
2
+
3
+ describe("isScreenPicker", () => {
4
+ it("should return true if the component type is 'screen-picker-qb-tv'", () => {
5
+ const item = { component_type: "screen-picker-qb-tv" };
6
+ expect(isScreenPicker(item)).toBe(true);
7
+ });
8
+
9
+ it("should return false if the component type is not 'screen-picker-qb-tv'", () => {
10
+ const item = { component_type: "other-component" };
11
+ expect(isScreenPicker(item)).toBe(false);
12
+ });
13
+
14
+ it("should return false if the component type is undefined", () => {
15
+ const item = { component_type: undefined };
16
+ expect(isScreenPicker(item)).toBe(false);
17
+ });
18
+
19
+ it("should return false if the item is null", () => {
20
+ const item = null;
21
+ expect(isScreenPicker(item)).toBe(false);
22
+ });
23
+
24
+ it("should return false if the item is undefined", () => {
25
+ const item = undefined;
26
+ expect(isScreenPicker(item)).toBe(false);
27
+ });
28
+
29
+ it("should return false if the item is an empty object", () => {
30
+ const item = {};
31
+ expect(isScreenPicker(item)).toBe(false);
32
+ });
33
+ });
@@ -0,0 +1,32 @@
1
+ const GROUP = "group-qb";
2
+ const GROUP_INFO = "group-info-qb";
3
+ const EMPTY_GROUP_COMPONENT = "empty_group_component";
4
+
5
+ const GALLERY = "gallery-qb";
6
+
7
+ const SCREEN_PICKER = "screen-picker-qb-tv";
8
+
9
+ export const isGallery = (item): boolean => item?.component_type === GALLERY;
10
+
11
+ export const isScreenPicker = (item): boolean =>
12
+ item?.component_type === SCREEN_PICKER;
13
+
14
+ export const isFirstComponentScreenPicker = (
15
+ components: ZappUIComponent[]
16
+ ): boolean => {
17
+ return isScreenPicker(components?.[0]);
18
+ };
19
+
20
+ export const isFirstComponentGallery = (
21
+ components: ZappUIComponent[]
22
+ ): boolean => {
23
+ return isGallery(components?.[0]);
24
+ };
25
+
26
+ export const isGroup = (item): boolean => item?.component_type === GROUP;
27
+
28
+ export const isEmptyGroup = (item): boolean =>
29
+ item?.component_type === EMPTY_GROUP_COMPONENT;
30
+
31
+ export const isGroupInfo = (item): boolean =>
32
+ item?.component_type === GROUP_INFO;
@@ -3,6 +3,7 @@ import utc from "dayjs/plugin/utc";
3
3
  import timezone from "dayjs/plugin/timezone";
4
4
  import isoWeek from "dayjs/plugin/isoWeek";
5
5
  import duration from "dayjs/plugin/duration";
6
+ import relativeTime from "dayjs/plugin/relativeTime";
6
7
 
7
8
  // Extend dayjs with plugins
8
9
  dayjs.extend(utc);
@@ -10,4 +11,20 @@ dayjs.extend(timezone);
10
11
  dayjs.extend(isoWeek);
11
12
  dayjs.extend(duration);
12
13
 
14
+ dayjs.extend(relativeTime, {
15
+ thresholds: [
16
+ { l: "s", r: 1 },
17
+ { l: "m", r: 1 },
18
+ { l: "mm", r: 59, d: "minute" },
19
+ { l: "h", r: 1 },
20
+ { l: "hh", r: 23, d: "hour" },
21
+ { l: "d", r: 1 },
22
+ { l: "dd", r: 29, d: "day" },
23
+ { l: "M", r: 1 },
24
+ { l: "MM", r: 11, d: "month" },
25
+ { l: "y", r: 1 },
26
+ { l: "yy", d: "year" },
27
+ ],
28
+ });
29
+
13
30
  export { dayjs };
@@ -1,4 +1,3 @@
1
- import * as R from "ramda";
2
1
  import { ViewStyle } from "react-native";
3
2
 
4
3
  import { useMemo } from "react";
@@ -29,14 +28,16 @@ const RTL_LOCALES = [
29
28
  "yi", // Yiddish
30
29
  ];
31
30
 
32
- export function useLocalizedStrings({ localizations }) {
31
+ export function useLocalizedStrings({ localizations = {} }) {
33
32
  const languageCode = getUILanguage();
34
33
 
35
34
  // TODO: probably we can call getLocalizations here from localizationsHelper.js
36
35
  const result = useMemo(
37
36
  () =>
38
37
  localizations?.[languageCode] ||
39
- R.compose(R.head, R.filter(R.is(Object)), R.values)(localizations) ||
38
+ Object.values(localizations).find(
39
+ (value) => typeof value === "object" && value !== null
40
+ ) ||
40
41
  {},
41
42
  [languageCode, localizations]
42
43
  );
@@ -53,13 +54,7 @@ export function getIsRTL(): boolean {
53
54
  return false;
54
55
  }
55
56
 
56
- const [languageCode] = uiLanguage.split("-");
57
-
58
- const lowerCaseLanguageCode = languageCode.toLocaleLowerCase();
59
-
60
- const isRTL = R.contains(lowerCaseLanguageCode, RTL_LOCALES);
61
-
62
- return isRTL;
57
+ return RTL_LOCALES.includes(uiLanguage.split("-")[0].toLocaleLowerCase());
63
58
  }
64
59
 
65
60
  export function useIsRTL(): boolean {
@@ -2555,6 +2555,12 @@ function getPlayerConfiguration({ platform }) {
2555
2555
  multiple: false,
2556
2556
  label_tooltip: "Choose the default aspect ratio for the artwork",
2557
2557
  },
2558
+ {
2559
+ key: "audio_player_artwork_border_radius",
2560
+ label: "Audio Player Artwork Corner Radius",
2561
+ type: "number_input",
2562
+ initial_value: 16,
2563
+ },
2558
2564
  ]
2559
2565
  )
2560
2566
  );
@@ -23,6 +23,13 @@ const ZAPPIFEST_FIELDS = {
23
23
 
24
24
  const TV_PLATFORMS = ["tvOS", "android", "LG", "samsung", "roku", "vizio"];
25
25
 
26
+ const MOBILE_PLATFORMS = [
27
+ "ios",
28
+ "ios_for_quickbrick",
29
+ "android",
30
+ "android_for_quickbrick",
31
+ ];
32
+
26
33
  const FontPropsForTVPlatform = (platform) => ({
27
34
  [`${R.toLower(platform)}FontFamily`]: {
28
35
  name: `${platform} font family`,
@@ -2083,4 +2090,6 @@ module.exports = {
2083
2090
  TV_CELL_BADGE_FIELDS,
2084
2091
  progressBarFields,
2085
2092
  secondaryImageFields,
2093
+ TV_PLATFORMS,
2094
+ MOBILE_PLATFORMS,
2086
2095
  };
@@ -5,6 +5,7 @@ exports[`getNavigationProps returns the props otherwise 1`] = `
5
5
  "assets": {
6
6
  "appLogo": "appLogo.png",
7
7
  },
8
+ "data": undefined,
8
9
  "home": false,
9
10
  "nav_items": [
10
11
  {
@@ -111,6 +111,7 @@ export function getNavigationProps({
111
111
  assets: ZappNavBarAssets;
112
112
  rules: ZappNavBarRules;
113
113
  nav_items: NavBarItem[];
114
+ data: MenuItemDataProperties;
114
115
  } | null {
115
116
  const { activeRiver } = navigator;
116
117
  const { navigations, id: selected, home } = activeRiver; // use useRiver hook instead
@@ -124,7 +125,7 @@ export function getNavigationProps({
124
125
  return null;
125
126
  }
126
127
 
127
- const { styles, assets, nav_items, rules } = navigation;
128
+ const { styles, assets, nav_items, rules, data } = navigation;
128
129
 
129
130
  return {
130
131
  selected,
@@ -134,6 +135,7 @@ export function getNavigationProps({
134
135
  assets: assets as ZappNavBarAssets,
135
136
  nav_items: nav_items as any as NavBarItem[],
136
137
  rules: rules as any as ZappNavBarRules,
138
+ data: data as MenuItemDataProperties,
137
139
  };
138
140
  }
139
141
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-utils",
3
- "version": "13.0.0-alpha.3322736490",
3
+ "version": "13.0.0-alpha.3332871333",
4
4
  "description": "Applicaster Zapp React Native utilities package",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "homepage": "https://github.com/applicaster/quickbrick#readme",
29
29
  "dependencies": {
30
- "@applicaster/applicaster-types": "13.0.0-alpha.3322736490",
30
+ "@applicaster/applicaster-types": "13.0.0-alpha.3332871333",
31
31
  "buffer": "^5.2.1",
32
32
  "camelize": "^1.0.0",
33
33
  "dayjs": "^1.11.10",
@@ -2557,6 +2557,12 @@ function getPlayerConfiguration({ platform }) {
2557
2557
  multiple: false,
2558
2558
  label_tooltip: "Choose the default aspect ratio for the artwork",
2559
2559
  },
2560
+ {
2561
+ key: "audio_player_artwork_border_radius",
2562
+ label: "Audio Player Artwork Corner Radius",
2563
+ type: "number_input",
2564
+ initial_value: 16,
2565
+ },
2560
2566
  ]
2561
2567
  )
2562
2568
  );
@@ -20,6 +20,7 @@ type Props = {
20
20
  silentRefresh?: boolean;
21
21
  method?: "get" | "post";
22
22
  bodyParams?: Record<string, unknown>;
23
+ skipLoading?: boolean;
23
24
  };
24
25
  };
25
26
 
@@ -95,7 +96,7 @@ export const useFeedLoader = ({
95
96
  if (
96
97
  shouldDispatchData(callableFeedUrl, currentFeed, pipesOptions.clearCache)
97
98
  ) {
98
- if (callableFeedUrl) {
99
+ if (callableFeedUrl && !pipesOptions.skipLoading) {
99
100
  dispatch(
100
101
  loadPipesData(callableFeedUrl, {
101
102
  ...pipesOptions,
@@ -103,26 +104,33 @@ export const useFeedLoader = ({
103
104
  riverId,
104
105
  })
105
106
  );
106
- } else {
107
+ } else if (!callableFeedUrl) {
107
108
  logger.info({
108
109
  message:
109
110
  "Feed is not defined or not inflated, skipping loading. \nMake sure you have correct feedUrl and mapping.",
110
111
  jsOnly: true,
111
112
  });
113
+ } else {
114
+ logger.info({
115
+ message:
116
+ "Loading skipped as per pipesOptions.skipLoading configuration.",
117
+ data: { url: callableFeedUrl },
118
+ jsOnly: true,
119
+ });
112
120
  }
113
121
  } else {
114
122
  logger.info({
115
123
  message:
116
- "Feed already exist, skipping loading. \nCall reloadData() if you want to refresh data.",
124
+ "Feed already exists, skipping loading. \nCall reloadData() if you want to refresh data.",
117
125
  data: { url: callableFeedUrl },
118
126
  jsOnly: true,
119
127
  });
120
128
  }
121
129
  }, []);
122
130
 
123
- // Reload feed when feedUrl changes
131
+ // Reload feed when feedUrl changes, unless skipLoading is true
124
132
  useEffect(() => {
125
- if (!isInitialRender && callableFeedUrl) {
133
+ if (!isInitialRender && callableFeedUrl && !pipesOptions.skipLoading) {
126
134
  dispatch(loadPipesData(callableFeedUrl, { ...pipesOptions, riverId }));
127
135
  }
128
136
  }, [callableFeedUrl]);
@@ -158,7 +166,6 @@ export const useFeedLoader = ({
158
166
  currentFeed?.data,
159
167
  currentFeed?.error,
160
168
  currentFeed?.loading,
161
-
162
169
  reloadData,
163
170
  loadNext,
164
171
  ]);