@applicaster/zapp-react-native-ui-components 13.0.0-rc.99 → 14.0.0-alpha.1216545755

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.
Files changed (67) hide show
  1. package/Components/AudioPlayer/index.tsx +15 -0
  2. package/Components/AudioPlayer/mobile/Layout.tsx +66 -0
  3. package/Components/AudioPlayer/{__tests__/__snapshots__/audioPlayer.test.js.snap → mobile/__tests__/__snapshots__/audioPlayerMobileLayout.test.js.snap} +2 -2
  4. package/Components/AudioPlayer/mobile/__tests__/audioPlayerMobileLayout.test.js +18 -0
  5. package/Components/AudioPlayer/mobile/index.tsx +18 -0
  6. package/Components/AudioPlayer/tv/Artwork.tsx +40 -0
  7. package/Components/AudioPlayer/{AudioPlayerLayout.tsx → tv/Layout.tsx} +37 -79
  8. package/Components/AudioPlayer/{Runtime.tsx → tv/Runtime.tsx} +2 -1
  9. package/Components/AudioPlayer/{Summary.tsx → tv/Summary.tsx} +12 -9
  10. package/Components/AudioPlayer/{Title.tsx → tv/Title.tsx} +12 -9
  11. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/artWork.test.js.snap +9 -4
  12. package/Components/AudioPlayer/tv/__tests__/__snapshots__/audioPlayer.test.js.snap +170 -0
  13. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/summary.test.js.snap +1 -1
  14. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/title.test.js.snap +1 -1
  15. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/audioPlayer.test.js +7 -3
  16. package/Components/AudioPlayer/{helpers.tsx → tv/helpers.tsx} +3 -6
  17. package/Components/AudioPlayer/{AudioPlayer.tsx → tv/index.tsx} +14 -70
  18. package/Components/AudioPlayer/types.ts +40 -0
  19. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +3 -2
  20. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +4 -2
  21. package/Components/GeneralContentScreen/utils/useEventAlerts.ts +30 -0
  22. package/Components/MasterCell/DefaultComponents/Text/index.tsx +1 -0
  23. package/Components/MasterCell/utils/behaviorProvider.ts +82 -14
  24. package/Components/MasterCell/utils/index.ts +23 -3
  25. package/Components/ModalComponent/BottomSheetModalContent.tsx +32 -46
  26. package/Components/ModalComponent/Button/index.tsx +25 -29
  27. package/Components/ModalComponent/Header/index.tsx +9 -8
  28. package/Components/PlayerContainer/PlayerContainer.tsx +25 -42
  29. package/Components/PlayerImageBackground/index.tsx +1 -1
  30. package/Components/River/ComponentsMap/ComponentsMap.tsx +7 -3
  31. package/Components/River/RiverItem.tsx +8 -4
  32. package/Components/River/TV/River.tsx +0 -3
  33. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -1
  34. package/Components/RouteManager/TestId.tsx +1 -5
  35. package/Components/RouteManager/__tests__/__snapshots__/routeManager.test.js.snap +0 -1
  36. package/Components/RouteManager/__tests__/testId.test.js +0 -4
  37. package/Components/Screen/TV/__tests__/index.web.test.tsx +26 -0
  38. package/Components/Screen/__tests__/Screen.test.tsx +22 -14
  39. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -2
  40. package/Components/TopMarginApplicator/TopMarginApplicator.tsx +16 -15
  41. package/Components/Transitioner/Scene.tsx +1 -0
  42. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +1 -2
  43. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +1 -0
  44. package/Components/VideoModal/ModalAnimation/utils.ts +3 -9
  45. package/Components/VideoModal/PlayerWrapper.tsx +9 -19
  46. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +60 -0
  47. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +17 -55
  48. package/Components/VideoModal/hooks/useDelayedPlayerDetails.ts +15 -26
  49. package/Contexts/ScreenDataContext/index.tsx +2 -0
  50. package/Decorators/RiverFeedLoader/index.tsx +8 -2
  51. package/Decorators/RiverFeedLoader/utils/index.ts +7 -2
  52. package/Decorators/ZappPipesDataConnector/index.tsx +20 -2
  53. package/index.d.ts +0 -1
  54. package/package.json +5 -6
  55. package/Components/AudioPlayer/Artwork.tsx +0 -35
  56. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayerLayout.test.js.snap +0 -66
  57. package/Components/AudioPlayer/__tests__/audioPlayerLayout.test.js +0 -26
  58. package/Components/AudioPlayer/index.ts +0 -1
  59. package/Decorators/Navigator/__tests__/react-router-native-mock.js +0 -11
  60. package/Components/AudioPlayer/{Channel.tsx → tv/Channel.tsx} +0 -0
  61. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/Runtime.test.js +0 -0
  62. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/Runtime.test.js.snap +2 -2
  63. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/channel.test.js.snap +0 -0
  64. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/artWork.test.js +0 -0
  65. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/channel.test.js +0 -0
  66. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/summary.test.js +0 -0
  67. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/title.test.js +0 -0
@@ -1,7 +1,11 @@
1
1
  import React from "react";
2
2
  import { render } from "@testing-library/react-native";
3
3
 
4
- import { AudioPlayer } from "../AudioPlayer";
4
+ import { AudioPlayerTV } from "..";
5
+
6
+ jest.mock("@applicaster/zapp-react-native-utils/audioPlayerUtils", () => ({
7
+ useArtworkImage: jest.fn(() => "artwork_url"),
8
+ }));
5
9
 
6
10
  const audioPlayerProps = {
7
11
  audio_item: {
@@ -45,9 +49,9 @@ const audioPlayerProps = {
45
49
  styles: {},
46
50
  };
47
51
 
48
- describe("<AudioPlayer />", () => {
52
+ describe("<AudioPlayerTV />", () => {
49
53
  it("renders correctly", () => {
50
- const { toJSON } = render(<AudioPlayer {...audioPlayerProps} />);
54
+ const { toJSON } = render(<AudioPlayerTV {...audioPlayerProps} />);
51
55
  expect(toJSON()).toMatchSnapshot();
52
56
  });
53
57
  });
@@ -2,11 +2,8 @@ const defaults = {
2
2
  audio_player_title_color: "white",
3
3
  audio_player_summary_color: "white",
4
4
  audio_player_background_color: "black",
5
- audio_player_artwork_aspect_ratio: "1:1",
5
+ audio_player_background_image: undefined,
6
6
  audio_player_rtl: false,
7
- magic_background: false,
8
- audio_player_background_image_query: "",
9
- audio_player_background_image_default_color: "",
10
7
  };
11
8
 
12
9
  export function getPropertyFromEntryOrConfig({ entry, plugin_configuration }) {
@@ -28,14 +25,14 @@ export function getPropertyFromEntryOrConfig({ entry, plugin_configuration }) {
28
25
  const LTR = {
29
26
  flexDirection: "row",
30
27
  justifyContent: "flex-start",
31
- textAlign: "right",
28
+ textAlign: "left",
32
29
  alignItems: "flex-end",
33
30
  };
34
31
 
35
32
  const RTL = {
36
33
  flexDirection: "row-reverse",
37
34
  justifyContent: "flex-end",
38
- textAlign: "left",
35
+ textAlign: "right",
39
36
  alignItems: "flex-start",
40
37
  };
41
38
 
@@ -1,64 +1,24 @@
1
1
  import React, { useCallback, useMemo } from "react";
2
2
 
3
3
  import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
4
+ import { useArtworkImage } from "@applicaster/zapp-react-native-utils/audioPlayerUtils";
4
5
 
5
- import { imageSrcFromMediaItem } from "@applicaster/zapp-react-native-utils/configurationUtils";
6
+ import { AudioPlayerTVLayout } from "./Layout";
6
7
 
7
- import { AudioPlayerLayout } from "./AudioPlayerLayout";
8
8
  import { Channel } from "./Channel";
9
9
  import { Title } from "./Title";
10
10
  import { Summary } from "./Summary";
11
11
  import { Runtime } from "./Runtime";
12
12
  import { getPropertyFromEntryOrConfig } from "./helpers";
13
- import { ViewStyle } from "react-native";
14
-
15
- type Props = {
16
- audio_item: ZappEntry & {
17
- extensions?: {
18
- audio_player_artwork_aspect_ratio?: string;
19
- audio_player_background_image?: string;
20
- audio_player_background_color?: string;
21
- audio_player_channel_icon?: string;
22
- audio_player_title_color?: string;
23
- audio_player_summary_color?: string;
24
- audio_player_rtl?: boolean;
25
- magic_background?: boolean;
26
- audio_player_background_image_query?: string;
27
- audio_player_background_image_default_color?: string;
28
- start_time?: string;
29
- end_time?: string;
30
- };
31
- };
32
- plugin_configuration: {
33
- audio_player_background_color?: string;
34
- audio_player_title_color?: string;
35
- audio_player_summary_color?: string;
36
- audio_player_rtl?: string;
37
- magic_background?: string;
38
- audio_player_background_image_query?: string;
39
- audio_player_background_image_default_color?: string;
40
- audio_player_background_image?: string;
41
- audio_player_artwork_aspect_ratio?: string;
42
- lg_tv_audio_player_title_font_family?: string;
43
- lg_tv_audio_player_title_font_size?: number;
44
- lg_tv_audio_player_summary_font_family?: string;
45
- lg_tv_audio_player_summary_font_size?: number;
46
- samsung_tv_audio_player_title_font_family?: string;
47
- samsung_tv_audio_player_title_font_size?: number;
48
- samsung_tv_audio_player_summary_font_family?: string;
49
- samsung_tv_audio_player_summary_font_size?: number;
50
- tv_os_audio_player_title_font_family?: string;
51
- tv_os_audio_player_title_font_size?: number;
52
- tv_os_audio_player_summary_font_family?: string;
53
- tv_os_audio_player_summary_font_size?: number;
54
- };
55
- style?: ViewStyle;
56
- };
57
-
58
- export function AudioPlayer(props: Props) {
59
- const { audio_item, plugin_configuration, style } = props;
13
+
14
+ import { Props } from "../types";
15
+
16
+ export function AudioPlayerTV(props: Props) {
17
+ const { audio_item, plugin_configuration, style = {} } = props;
60
18
  const { extensions, title, summary } = audio_item;
61
19
 
20
+ const artwork = useArtworkImage(audio_item);
21
+
62
22
  const getProp = useCallback(
63
23
  getPropertyFromEntryOrConfig({
64
24
  entry: audio_item,
@@ -68,23 +28,14 @@ export function AudioPlayer(props: Props) {
68
28
  );
69
29
 
70
30
  const config = useMemo(() => {
71
- // Checking if we are recieving items from the DSP
31
+ // Checking if we are receiving items from the DSP
72
32
  const titleColor = getProp("audio_player_title_color");
73
33
  const summaryColor = getProp("audio_player_summary_color");
74
34
  const backgroundColor = getProp("audio_player_background_color");
75
35
  const backgroundImage = getProp("audio_player_background_image");
76
- const artworkAspectRatio = getProp("audio_player_artwork_aspect_ratio");
77
36
  const channelIcon = getProp("audio_player_channel_icon");
78
37
  const rtlFlag = getProp("audio_player_rtl");
79
- const magicBackground = getProp("magic_background");
80
-
81
- const audioPlayerBackgroundImageQuery = getProp(
82
- "audio_player_background_image_query"
83
- );
84
-
85
- const audioPlayerBackgroundImageDefaultColor = getProp(
86
- "audio_player_background_image_default_color"
87
- );
38
+ const artworkBorderRadius = getProp("audio_player_artwork_border_radius");
88
39
 
89
40
  const isRTL = rtlFlag === "1" || rtlFlag === "true" || rtlFlag === true;
90
41
 
@@ -160,24 +111,17 @@ export function AudioPlayer(props: Props) {
160
111
  summaryFontSize,
161
112
  runTimeFontFamily,
162
113
  runTimeFontSize,
163
- artworkAspectRatio,
164
114
  channelIcon,
165
- magicBackground,
166
- audioPlayerBackgroundImageQuery,
167
- audioPlayerBackgroundImageDefaultColor,
115
+ artworkBorderRadius,
168
116
  };
169
117
  }, [getProp]);
170
118
 
171
- const artwork = imageSrcFromMediaItem(audio_item, [
172
- config?.artworkAspectRatio,
173
- ]);
174
-
175
119
  return (
176
- <AudioPlayerLayout artwork={artwork} config={config} style={style || {}}>
120
+ <AudioPlayerTVLayout artwork={artwork} config={config} style={style}>
177
121
  <Channel srcImage={config?.channelIcon} config={config} />
178
122
  <Title title={title} config={config} />
179
123
  <Summary summary={summary} config={config} />
180
124
  <Runtime {...extensions} config={config} />
181
- </AudioPlayerLayout>
125
+ </AudioPlayerTVLayout>
182
126
  );
183
127
  }
@@ -0,0 +1,40 @@
1
+ import { ViewStyle } from "react-native";
2
+
3
+ export type Props = {
4
+ audio_item: ZappEntry & {
5
+ extensions?: {
6
+ audio_player_artwork_aspect_ratio?: string;
7
+ audio_player_background_image?: string;
8
+ audio_player_background_color?: string;
9
+ audio_player_channel_icon?: string;
10
+ audio_player_title_color?: string;
11
+ audio_player_summary_color?: string;
12
+ audio_player_rtl?: boolean;
13
+ audio_player_background_image_default_color?: string;
14
+ start_time?: string;
15
+ end_time?: string;
16
+ };
17
+ };
18
+ plugin_configuration: {
19
+ audio_player_background_color?: string;
20
+ audio_player_title_color?: string;
21
+ audio_player_summary_color?: string;
22
+ audio_player_rtl?: string;
23
+ audio_player_background_image_default_color?: string;
24
+ audio_player_background_image?: string;
25
+ audio_player_artwork_aspect_ratio?: string;
26
+ lg_tv_audio_player_title_font_family?: string;
27
+ lg_tv_audio_player_title_font_size?: number;
28
+ lg_tv_audio_player_summary_font_family?: string;
29
+ lg_tv_audio_player_summary_font_size?: number;
30
+ samsung_tv_audio_player_title_font_family?: string;
31
+ samsung_tv_audio_player_title_font_size?: number;
32
+ samsung_tv_audio_player_summary_font_family?: string;
33
+ samsung_tv_audio_player_summary_font_size?: number;
34
+ tv_os_audio_player_title_font_family?: string;
35
+ tv_os_audio_player_title_font_size?: number;
36
+ tv_os_audio_player_summary_font_family?: string;
37
+ tv_os_audio_player_summary_font_size?: number;
38
+ };
39
+ style?: ViewStyle;
40
+ };
@@ -11,6 +11,7 @@ import { allSettled } from "promise";
11
11
  import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
12
12
  import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
13
13
  import { ScreenTrackedViewPositionsContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenTrackedViewPositionsContext";
14
+ import { useEventAlerts } from "./utils/useEventAlerts";
14
15
 
15
16
  const { log_info } = createLogger({
16
17
  category: "ScreenContainer",
@@ -29,7 +30,6 @@ export const GeneralContentScreen = ({
29
30
  isScreenWrappedInContainer,
30
31
  componentsMapExtraProps = {},
31
32
  focused,
32
- extraOffset,
33
33
  parentFocus,
34
34
  containerHeight,
35
35
  preferredFocus = false,
@@ -103,6 +103,8 @@ export const GeneralContentScreen = ({
103
103
  [typeof cellTapAction === "function" ? cellTapAction : onCellTapAction]
104
104
  );
105
105
 
106
+ useEventAlerts(screenData);
107
+
106
108
  if (!isReady || isNilOrEmpty(components || uiComponents)) return null;
107
109
 
108
110
  return (
@@ -119,7 +121,6 @@ export const GeneralContentScreen = ({
119
121
  isScreenWrappedInContainer={isScreenWrappedInContainer}
120
122
  parentFocus={parentFocus}
121
123
  focused={focused}
122
- extraOffset={extraOffset}
123
124
  containerHeight={containerHeight}
124
125
  preferredFocus={preferredFocus}
125
126
  {...componentsMapExtraProps}
@@ -3,8 +3,10 @@ import { all, equals, path, prop, isEmpty, pluck, values } from "ramda";
3
3
  import { useEffect, useMemo } from "react";
4
4
  import { useDispatch } from "react-redux";
5
5
 
6
- import { useLayoutPresets } from "@applicaster/zapp-react-native-redux/hooks/useLayoutPresets";
7
- import { useZappPipesFeeds } from "@applicaster/zapp-react-native-redux/hooks/useZappPipesFeeds";
6
+ import {
7
+ useLayoutPresets,
8
+ useZappPipesFeeds,
9
+ } from "@applicaster/zapp-react-native-redux/hooks";
8
10
  import { loadPipesData } from "@applicaster/zapp-react-native-redux/ZappPipes";
9
11
  import { isEmptyOrNil } from "@applicaster/zapp-react-native-utils/cellUtils";
10
12
  import { Categories } from "./logger";
@@ -0,0 +1,30 @@
1
+ import React from "react";
2
+ import { showAlertDialog } from "@applicaster/zapp-react-native-utils/alertUtils";
3
+ import { TOGGLE_FLAG_MAX_ITEMS_REACHED_EVENT } from "@applicaster/zapp-react-native-utils/actionsExecutor/consts";
4
+ import { useLocalizedStrings } from "@applicaster/zapp-react-native-utils/localizationUtils";
5
+ import { useIsScreenActive } from "@applicaster/zapp-react-native-utils/reactHooks";
6
+ import { useSubscriberFor } from "@applicaster/zapp-react-native-utils/reactHooks/useSubscriberFor";
7
+
8
+ export const useEventAlerts = (screenData: ZappRiver) => {
9
+ const localizations = useLocalizedStrings({
10
+ localizations: screenData?.localizations || {},
11
+ });
12
+
13
+ const isActive = useIsScreenActive();
14
+
15
+ const onMaxTagsReached = React.useCallback(() => {
16
+ // We can't skip subscribe hook call, so we have to check.
17
+ if (!isActive || !localizations?.msg_maximum_selection_reached_message) {
18
+ return;
19
+ }
20
+
21
+ showAlertDialog({
22
+ title: "",
23
+ message: localizations.msg_maximum_selection_reached_message,
24
+ okButtonText:
25
+ localizations.msg_maximum_selection_reached_message_ok_button || "OK",
26
+ });
27
+ }, [localizations, isActive]);
28
+
29
+ useSubscriberFor(TOGGLE_FLAG_MAX_ITEMS_REACHED_EVENT, onMaxTagsReached);
30
+ };
@@ -52,6 +52,7 @@ const _Text = ({
52
52
  withScaledLineHeight(withFocusedStyles({ style, otherProps })),
53
53
  { height },
54
54
  ]}
55
+ allowFontScaling={false}
55
56
  {...withoutLabel(otherProps)}
56
57
  >
57
58
  {dateTransformEnabled
@@ -1,28 +1,58 @@
1
1
  import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
2
- import { StorageSingleValueProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/StorageSingleSelectProvider";
2
+ import { StorageSingleValueProvider } from "@applicaster/zapp-react-native-utils/storage/StorageSingleSelectProvider";
3
3
  import { PushTopicManager } from "@applicaster/zapp-react-native-bridge/PushNotifications/PushTopicManager";
4
- import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/StorageMultiSelectProvider";
4
+ import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-utils/storage/StorageMultiSelectProvider";
5
5
  import React, { useEffect } from "react";
6
6
  import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
7
7
  import { BehaviorSubject } from "rxjs";
8
8
  import { masterCellLogger } from "../logger";
9
9
  import get from "lodash/get";
10
-
11
- const parseContextKey = (key: string): string | null => {
12
- if (!key?.startsWith("@{ctx/")) return null;
13
-
14
- return key.substring("@{ctx/".length, key.length - 1);
10
+ import { ScreenMultiSelectProvider } from "@applicaster/zapp-react-native-utils/storage/ScreenStateMultiSelectProvider";
11
+ import { ScreenSingleValueProvider } from "@applicaster/zapp-react-native-utils/storage/ScreenSingleValueProvider";
12
+ import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks";
13
+ import { useScreenStateStore } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useScreenStateStore";
14
+
15
+ const parseContextKey = (
16
+ key: string,
17
+ context: string = "ctx"
18
+ ): string | null => {
19
+ if (!key?.startsWith(`@{${context}/`)) return null;
20
+
21
+ return key.substring(`@{${context}/`.length, key.length - 1);
15
22
  };
16
23
 
17
24
  const getDataSourceProvider = (
18
- behavior: Behavior
25
+ behavior: Behavior,
26
+ screenRoute: string,
27
+ screenStateStore: ScreenStateStore
19
28
  ): BehaviorSubject<string[] | string> | null => {
20
29
  if (!behavior) return null;
21
30
 
22
31
  const selection = String(behavior.current_selection);
32
+ const screenKey = parseContextKey(selection, "screen");
33
+
34
+ if (screenKey) {
35
+ if (behavior.select_mode === "multi") {
36
+ return ScreenMultiSelectProvider.getProvider(
37
+ screenKey,
38
+ screenRoute,
39
+ screenStateStore
40
+ ).getObservable();
41
+ }
42
+
43
+ if (behavior.select_mode === "single") {
44
+ return ScreenSingleValueProvider.getProvider(
45
+ screenKey,
46
+ screenRoute,
47
+ screenStateStore
48
+ ).getObservable();
49
+ }
50
+ }
51
+
23
52
  const contextKey = parseContextKey(selection);
24
53
 
25
54
  if (contextKey) {
55
+ // TODO: Add storage scope to behavior
26
56
  if (behavior.select_mode === "multi") {
27
57
  return StorageMultiSelectProvider.getProvider(contextKey).getObservable();
28
58
  }
@@ -41,6 +71,8 @@ const getDataSourceProvider = (
41
71
 
42
72
  export const useBehaviorUpdate = (behavior: Behavior) => {
43
73
  const [lastUpdate, setLastUpdate] = React.useState<number | null>(null);
74
+ const screenRoute = useRoute()?.pathname || "";
75
+ const screenStateStore = useScreenStateStore();
44
76
  const player = usePlayer();
45
77
 
46
78
  const triggerUpdate = () => setLastUpdate(Date.now());
@@ -48,7 +80,11 @@ export const useBehaviorUpdate = (behavior: Behavior) => {
48
80
  useEffect(() => {
49
81
  if (!behavior) return;
50
82
 
51
- const dataSource = getDataSourceProvider(behavior);
83
+ const dataSource = getDataSourceProvider(
84
+ behavior,
85
+ screenRoute,
86
+ screenStateStore
87
+ );
52
88
 
53
89
  if (dataSource) {
54
90
  const subscription = dataSource.subscribe(triggerUpdate);
@@ -72,10 +108,17 @@ export const useBehaviorUpdate = (behavior: Behavior) => {
72
108
 
73
109
  // We cant use async in this function (its inside render),
74
110
  // so we rely on useBehaviorUpdate to update current value and trigger re-render
75
- export const isCellSelected = (
76
- item: ZappEntry,
77
- behavior?: Behavior
78
- ): boolean => {
111
+ export const isCellSelected = ({
112
+ item,
113
+ screenRoute,
114
+ screenStateStore,
115
+ behavior,
116
+ }: {
117
+ item: ZappEntry;
118
+ screenRoute: string;
119
+ screenStateStore: ScreenStateStore;
120
+ behavior?: Behavior;
121
+ }): boolean => {
79
122
  if (!behavior) return false;
80
123
 
81
124
  const id = behavior.selector ? get(item, behavior.selector) : item.id;
@@ -99,7 +142,32 @@ export const isCellSelected = (
99
142
  }
100
143
 
101
144
  const selection = String(behavior.current_selection);
102
- const contextKey = parseContextKey(selection);
145
+
146
+ const screenKey = parseContextKey(selection, "screen");
147
+
148
+ if (screenKey) {
149
+ if (behavior.select_mode === "single") {
150
+ const selectedItem = ScreenSingleValueProvider.getProvider(
151
+ screenKey,
152
+ screenRoute,
153
+ screenStateStore
154
+ ).getValue();
155
+
156
+ return selectedItem === String(id);
157
+ }
158
+
159
+ if (behavior.select_mode === "multi") {
160
+ const selectedItems = ScreenMultiSelectProvider.getProvider(
161
+ screenKey,
162
+ screenRoute,
163
+ screenStateStore
164
+ ).getSelectedItems();
165
+
166
+ return selectedItems?.includes(String(id));
167
+ }
168
+ }
169
+
170
+ const contextKey = parseContextKey(selection, "ctx");
103
171
 
104
172
  if (contextKey) {
105
173
  if (behavior.select_mode === "single") {
@@ -8,6 +8,8 @@ import { masterCellLogger } from "../logger";
8
8
  import { getCellState } from "../../Cell/utils";
9
9
  import { getColorFromData } from "@applicaster/zapp-react-native-utils/cellUtils";
10
10
  import { isCellSelected, useBehaviorUpdate } from "./behaviorProvider";
11
+ import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks";
12
+ import { useScreenStateStore } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useScreenStateStore";
11
13
 
12
14
  const hasElementSpecificViewType = (viewType) => (element) => {
13
15
  if (R.isNil(element)) {
@@ -190,8 +192,18 @@ export const getFocusedButtonId = (focusable) => {
190
192
  });
191
193
  };
192
194
 
193
- export const isSelected = (item: ZappEntry, behavior?: Behavior) => {
194
- return isCellSelected(item, behavior);
195
+ export const isSelected = ({
196
+ item,
197
+ screenRoute,
198
+ screenStateStore,
199
+ behavior,
200
+ }: {
201
+ item: ZappEntry;
202
+ screenRoute: string;
203
+ screenStateStore: ScreenStateStore;
204
+ behavior?: Behavior;
205
+ }) => {
206
+ return isCellSelected({ item, screenRoute, screenStateStore, behavior });
195
207
  };
196
208
 
197
209
  export const useCellState = ({
@@ -204,9 +216,17 @@ export const useCellState = ({
204
216
  focused: boolean;
205
217
  }): CellState => {
206
218
  const lastUpdate = useBehaviorUpdate(behavior);
219
+ const router = useRoute();
220
+ const screenStateStore = useScreenStateStore();
207
221
 
208
222
  const _isSelected = useMemo(
209
- () => isSelected(item, behavior),
223
+ () =>
224
+ isSelected({
225
+ item,
226
+ screenRoute: router?.pathname,
227
+ screenStateStore,
228
+ behavior,
229
+ }),
210
230
  [behavior, item, lastUpdate]
211
231
  );
212
232
 
@@ -1,11 +1,5 @@
1
- import React, {
2
- useCallback,
3
- useEffect,
4
- useMemo,
5
- useRef,
6
- useState,
7
- } from "react";
8
- import { View, LayoutChangeEvent, ScrollView } from "react-native";
1
+ import React, { useCallback, useEffect, useMemo, useRef } from "react";
2
+ import { View, ScrollView } from "react-native";
9
3
  import { Button } from "./Button";
10
4
  import { ItemIconProps } from "./Button/ItemIcon";
11
5
  import { ItemProps } from "./Button/Item";
@@ -15,7 +9,8 @@ import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
15
9
 
16
10
  import type { PluginConfiguration } from "./";
17
11
 
18
- import { ModalHeader } from "./Header";
12
+ import { ModalHeader as DefaultModalHeader } from "./Header";
13
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
19
14
 
20
15
  type ModalComponentProps = {
21
16
  items: any[];
@@ -27,6 +22,7 @@ type ModalComponentProps = {
27
22
  title?: string;
28
23
  maxHeight?: number;
29
24
  dismiss: () => void;
25
+ headerComponent?: React.ComponentType;
30
26
  buttonComponent?: React.ComponentType;
31
27
  iconProps?: ItemIconProps | ((theme: PluginConfiguration) => ItemIconProps);
32
28
  itemProps?:
@@ -48,33 +44,25 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
48
44
  const {
49
45
  items,
50
46
  currentRoute,
51
- maxHeight,
52
47
  current_selection = null,
53
48
  onPress,
54
49
  dismiss,
55
50
  summary,
56
51
  title,
52
+ headerComponent: ModalHeader = DefaultModalHeader,
57
53
  buttonComponent: ButtonComponent = Button,
58
- getSelectedItemIcon = (theme) =>
59
- theme.modal_bottom_sheet_item_selected_icon,
54
+ getSelectedItemIcon = ({
55
+ modal_bottom_sheet_item_selected_icon,
56
+ }: BaseThemePropertiesMobile) => modal_bottom_sheet_item_selected_icon,
60
57
  getDefaultItemIcon = () => null,
61
58
  iconPlacement,
62
59
  } = props;
63
60
 
64
- const [headerHeight, setHeaderHeight] = useState(0);
65
61
  const route = useRef(currentRoute);
66
62
  const theme = useTheme<BaseThemePropertiesMobile>();
67
63
  const paddingTop = Number(theme.modal_bottom_sheet_padding_top);
68
64
  const paddingBottom = Number(theme.modal_bottom_sheet_padding_bottom);
69
65
 
70
- const maxContentHeight = maxHeight
71
- ? maxHeight - headerHeight - paddingTop
72
- : undefined;
73
-
74
- const onHeaderLayout = useCallback((event: LayoutChangeEvent) => {
75
- setHeaderHeight(event.nativeEvent.layout.height);
76
- }, []);
77
-
78
66
  useEffect(() => {
79
67
  if (currentRoute !== route.current) {
80
68
  props.dismiss();
@@ -101,46 +89,44 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
101
89
  [onPress, dismiss]
102
90
  );
103
91
 
92
+ const bottomInset = useSafeAreaInsets().bottom;
93
+
104
94
  return (
105
95
  <View
106
96
  style={{
107
- maxWidth: props.width,
108
- paddingTop,
97
+ maxHeight: props.maxHeight,
98
+ paddingTop: paddingTop,
109
99
  }}
110
100
  >
111
101
  <ModalHeader
112
- width={props.width}
113
102
  dismiss={dismiss}
114
103
  configuration={theme}
115
- onLayout={onHeaderLayout}
116
104
  summary={summary}
117
105
  title={title}
118
106
  />
119
107
  <ScrollView
120
- bounces={false}
121
- style={{ maxHeight: maxContentHeight }}
122
108
  contentContainerStyle={{
123
- paddingBottom,
124
- paddingTop,
109
+ paddingBottom: paddingBottom + bottomInset,
125
110
  }}
126
111
  >
127
- {items.map((item, index) => (
128
- <ButtonComponent
129
- key={index}
130
- width={props.width}
131
- configuration={theme}
132
- selectedItem={current_selection}
133
- item={item}
134
- onPress={handlePress}
135
- label={theme[item?.label] ?? item?.label}
136
- iconBaseProps={iconBaseProps}
137
- itemBaseProps={itemBaseProps}
138
- labelBaseProps={labelBaseProps}
139
- selectedItemIcon={getSelectedItemIcon(theme)}
140
- defaultItemIcon={getDefaultItemIcon(theme)}
141
- iconPlacement={iconPlacement}
142
- />
143
- ))}
112
+ {items.map((item, index) =>
113
+ item ? (
114
+ <ButtonComponent
115
+ key={index}
116
+ configuration={theme}
117
+ selectedItem={current_selection}
118
+ item={item}
119
+ onPress={handlePress}
120
+ label={theme[item?.label] ?? item?.label}
121
+ iconBaseProps={iconBaseProps}
122
+ itemBaseProps={itemBaseProps}
123
+ labelBaseProps={labelBaseProps}
124
+ selectedItemIcon={getSelectedItemIcon(theme)}
125
+ defaultItemIcon={getDefaultItemIcon(theme)}
126
+ iconPlacement={iconPlacement}
127
+ />
128
+ ) : null
129
+ )}
144
130
  </ScrollView>
145
131
  </View>
146
132
  );