@applicaster/zapp-react-native-ui-components 13.0.0-alpha.3099523645 → 13.0.0-alpha.3144359118

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 (30) hide show
  1. package/Components/AudioPlayer/AudioPlayer.tsx +7 -0
  2. package/Components/AudioPlayer/helpers.tsx +1 -0
  3. package/Components/FocusableGroup/FocusableTvOS.tsx +3 -6
  4. package/Components/FocusableGroup/index.tsx +0 -1
  5. package/Components/Layout/TV/ScreenContainer.tsx +8 -1
  6. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +5 -0
  7. package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +2 -1
  8. package/Components/MasterCell/utils/behaviorProvider.ts +133 -0
  9. package/Components/MasterCell/utils/index.ts +3 -129
  10. package/Components/PlayerContainer/PlayerContainer.tsx +9 -5
  11. package/Components/PlayerImageBackground/index.tsx +4 -24
  12. package/Components/Screen/TV/index.web.tsx +7 -6
  13. package/Components/Screen/hooks.ts +3 -6
  14. package/Components/TrackedView/index.tsx +1 -0
  15. package/Components/Transitioner/AnimationManager.js +15 -15
  16. package/Components/VideoLive/PlayerLiveImageComponent.tsx +4 -0
  17. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +1 -0
  18. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +1 -1
  19. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +5 -5
  20. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +3 -3
  21. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +2 -2
  22. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +2 -2
  23. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +4 -10
  24. package/Contexts/HeaderOffsetContext/index.tsx +4 -6
  25. package/Contexts/ScreenContext/index.tsx +3 -10
  26. package/Contexts/ScreenLayoutContext/index.tsx +5 -3
  27. package/Decorators/RiverResolver/index.tsx +5 -7
  28. package/Decorators/ZappPipesDataConnector/index.tsx +2 -29
  29. package/package.json +5 -8
  30. package/tsconfig.json +2 -3
@@ -24,6 +24,7 @@ type Props = {
24
24
  audio_player_rtl?: boolean;
25
25
  magic_background?: boolean;
26
26
  audio_player_background_image_query?: string;
27
+ audio_player_background_image_default_color?: string;
27
28
  start_time?: string;
28
29
  end_time?: string;
29
30
  };
@@ -35,6 +36,7 @@ type Props = {
35
36
  audio_player_rtl?: string;
36
37
  magic_background?: string;
37
38
  audio_player_background_image_query?: string;
39
+ audio_player_background_image_default_color?: string;
38
40
  audio_player_background_image?: string;
39
41
  audio_player_artwork_aspect_ratio?: string;
40
42
  lg_tv_audio_player_title_font_family?: string;
@@ -80,6 +82,10 @@ export function AudioPlayer(props: Props) {
80
82
  "audio_player_background_image_query"
81
83
  );
82
84
 
85
+ const audioPlayerBackgroundImageDefaultColor = getProp(
86
+ "audio_player_background_image_default_color"
87
+ );
88
+
83
89
  const isRTL = rtlFlag === "1" || rtlFlag === "true" || rtlFlag === true;
84
90
 
85
91
  const titleFontFamily = getProp(
@@ -158,6 +164,7 @@ export function AudioPlayer(props: Props) {
158
164
  channelIcon,
159
165
  magicBackground,
160
166
  audioPlayerBackgroundImageQuery,
167
+ audioPlayerBackgroundImageDefaultColor,
161
168
  };
162
169
  }, [getProp]);
163
170
 
@@ -6,6 +6,7 @@ const defaults = {
6
6
  audio_player_rtl: false,
7
7
  magic_background: false,
8
8
  audio_player_background_image_query: "",
9
+ audio_player_background_image_default_color: "",
9
10
  };
10
11
 
11
12
  export function getPropertyFromEntryOrConfig({ entry, plugin_configuration }) {
@@ -24,8 +24,7 @@ type Props = {
24
24
  id: string;
25
25
  children: (arg1: boolean) => React.ComponentType<any>;
26
26
  isFocusDisabled: boolean;
27
- initialItemId: string;
28
- isPreferredFocusDisabled: boolean;
27
+ isWithMemory: boolean;
29
28
  focusGroupRef: React.Component;
30
29
  shouldUsePreferredFocus: boolean;
31
30
  groupId: string;
@@ -40,8 +39,7 @@ export class FocusableGroup extends BaseFocusable<Props> {
40
39
  children,
41
40
  id,
42
41
  isFocusDisabled,
43
- initialItemId,
44
- isPreferredFocusDisabled,
42
+ isWithMemory,
45
43
  style = {},
46
44
  groupId,
47
45
  ...otherProps
@@ -63,8 +61,7 @@ export class FocusableGroup extends BaseFocusable<Props> {
63
61
  itemId={id}
64
62
  ref={this.ref}
65
63
  isFocusDisabled={isFocusDisabled}
66
- initialItemId={initialItemId}
67
- isPreferredFocusDisabled={isPreferredFocusDisabled}
64
+ isWithMemory={isWithMemory}
68
65
  onGroupFocus={onGroupFocus}
69
66
  onGroupBlur={onGroupBlur}
70
67
  style={style}
@@ -16,7 +16,6 @@ type Props = {
16
16
  prioritiseFocusOn?: number;
17
17
  groupId?: string;
18
18
  hasTVPreferredFocus?: boolean;
19
- isPreferredFocusDisabled?: boolean;
20
19
  onFocus?: () => void;
21
20
  onBlur?: () => void;
22
21
  willReceiveFocus?: (event?: any) => void;
@@ -55,8 +55,11 @@ export const ScreenContainer = React.memo(function ScreenContainer({
55
55
  ComponentsExtraProps,
56
56
  NavBar,
57
57
  }: Props) {
58
- const { screenMarginBottom } = React.useContext(ScreenLayoutContext);
58
+ const { screenMarginBottom, resetScreenLayout } =
59
+ React.useContext(ScreenLayoutContext);
60
+
59
61
  const { currentRoute, screenData } = useNavigation();
62
+
60
63
  const screen = useCurrentScreenData();
61
64
 
62
65
  const { marginBottom, backgroundColor, paddingTop, paddingBottom } =
@@ -65,6 +68,10 @@ export const ScreenContainer = React.memo(function ScreenContainer({
65
68
  const theme = useTheme();
66
69
  const getThemeValue = React.useCallback(R.propOr(0, R.__, theme), [theme]);
67
70
 
71
+ React.useEffect(() => {
72
+ resetScreenLayout();
73
+ }, [currentRoute]);
74
+
68
75
  const fullscreen = React.useCallback(
69
76
  displayFullScreen({ currentRoute, screenData }),
70
77
  [currentRoute, screenData]
@@ -60,10 +60,15 @@ export function ScreenLayoutContextProvider(props: {
60
60
  initialState
61
61
  );
62
62
 
63
+ const resetScreenLayout = React.useCallback(() => {
64
+ setScreenLayout(initialState);
65
+ }, [initialState]);
66
+
63
67
  const screenLayoutValue = React.useMemo(
64
68
  () => ({
65
69
  ...screenLayout,
66
70
  setScreenLayout,
71
+ resetScreenLayout,
67
72
  }),
68
73
  [screenLayout]
69
74
  );
@@ -56,11 +56,12 @@ describe("ScreenContainer", () => {
56
56
  };
57
57
 
58
58
  const setScreenLayout = () => {};
59
+ const resetScreenLayout = () => {};
59
60
 
60
61
  const wrapper = ({ children }) => (
61
62
  <WrappedWithProviders>
62
63
  <ScreenLayoutContext.Provider
63
- value={{ ...screenLayout, setScreenLayout }}
64
+ value={{ ...screenLayout, setScreenLayout, resetScreenLayout }}
64
65
  >
65
66
  <ScreenContainer NavBar={View}>{children}</ScreenContainer>
66
67
  </ScreenLayoutContext.Provider>
@@ -0,0 +1,133 @@
1
+ import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
2
+ import { StorageSingleValueProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/StorageSingleSelectProvider";
3
+ import { PushTopicManager } from "@applicaster/zapp-react-native-bridge/PushNotifications/PushTopicManager";
4
+ import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/StorageMultiSelectProvider";
5
+ import React, { useEffect } from "react";
6
+ import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
7
+ import { BehaviorSubject } from "rxjs";
8
+ import { masterCellLogger } from "../logger";
9
+
10
+ const parseContextKey = (key: string): string | null => {
11
+ if (!key?.startsWith("@{ctx/")) return null;
12
+
13
+ return key.substring("@{ctx/".length, key.length - 1);
14
+ };
15
+
16
+ const getDataSourceProvider = (
17
+ behavior: Behavior
18
+ ): BehaviorSubject<string[] | string> | null => {
19
+ if (!behavior) return null;
20
+
21
+ const selection = String(behavior.current_selection);
22
+ const contextKey = parseContextKey(selection);
23
+
24
+ if (contextKey) {
25
+ if (behavior.select_mode === "multi") {
26
+ return StorageMultiSelectProvider.getProvider(contextKey).getObservable();
27
+ }
28
+
29
+ if (behavior.select_mode === "single") {
30
+ return StorageSingleValueProvider.getProvider(contextKey).getObservable();
31
+ }
32
+ }
33
+
34
+ if (behavior.selection_source === "@{push/topics}") {
35
+ return PushTopicManager.getInstance().getEntryObservable();
36
+ }
37
+
38
+ return null;
39
+ };
40
+
41
+ export const useBehaviorUpdate = (behavior: Behavior) => {
42
+ const [lastUpdate, setLastUpdate] = React.useState<number | null>(null);
43
+ const player = usePlayer();
44
+
45
+ const triggerUpdate = () => setLastUpdate(Date.now());
46
+
47
+ useEffect(() => {
48
+ if (!behavior) return;
49
+
50
+ const dataSource = getDataSourceProvider(behavior);
51
+
52
+ if (dataSource) {
53
+ const subscription = dataSource.subscribe(triggerUpdate);
54
+
55
+ return () => subscription.unsubscribe();
56
+ }
57
+ }, [behavior]);
58
+
59
+ useEffect(() => {
60
+ if (!behavior || !player || behavior.selection_source !== "now_playing") {
61
+ return;
62
+ }
63
+
64
+ const subscription = player.getEntryObservable().subscribe(triggerUpdate);
65
+
66
+ return () => subscription.unsubscribe();
67
+ }, [behavior, player]);
68
+
69
+ return lastUpdate;
70
+ };
71
+
72
+ // We cant use async in this function (its inside render),
73
+ // so we rely on useBehaviorUpdate to update current value and trigger re-render
74
+ export const isCellSelected = (
75
+ id: string | number,
76
+ behavior?: Behavior
77
+ ): boolean => {
78
+ if (!behavior) return false;
79
+
80
+ if (behavior.selection_source === "now_playing") {
81
+ const player = playerManager.getActivePlayer();
82
+
83
+ return player?.entry?.id === id;
84
+ }
85
+
86
+ if (behavior.selection_source === "@{push/topics}") {
87
+ if (behavior.select_mode === "single") {
88
+ masterCellLogger.warning(
89
+ "Unexpected single selection mode for push topics"
90
+ );
91
+ }
92
+
93
+ const tags = PushTopicManager.getInstance().getRegisteredTags();
94
+
95
+ return tags.includes(String(id));
96
+ }
97
+
98
+ const selection = String(behavior.current_selection);
99
+ const contextKey = parseContextKey(selection);
100
+
101
+ if (contextKey) {
102
+ if (behavior.select_mode === "single") {
103
+ const selectedItem =
104
+ StorageSingleValueProvider.getProvider(contextKey)?.getValue();
105
+
106
+ return selectedItem === String(id);
107
+ }
108
+
109
+ if (behavior.select_mode === "multi") {
110
+ const selectedItems =
111
+ StorageMultiSelectProvider.getProvider(contextKey)?.getSelectedItems();
112
+
113
+ return selectedItems?.includes(String(id));
114
+ }
115
+ }
116
+
117
+ if (behavior.select_mode === "single") {
118
+ return behavior.current_selection === id;
119
+ }
120
+
121
+ if (
122
+ behavior.select_mode === "multi" &&
123
+ Array.isArray(behavior.current_selection)
124
+ ) {
125
+ const currentSelection: string[] = behavior.current_selection.map(
126
+ (item): string => String(item)
127
+ );
128
+
129
+ return currentSelection.includes(String(id));
130
+ }
131
+
132
+ return false;
133
+ };
@@ -1,16 +1,13 @@
1
- import React, { useEffect, useMemo } from "react";
1
+ import React, { useMemo } from "react";
2
2
  import * as R from "ramda";
3
3
  import validateColor from "validate-color";
4
4
  import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
5
5
  import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
6
6
 
7
7
  import { masterCellLogger } from "../logger";
8
- import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
9
- import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
10
- import { PushTopicManager } from "@applicaster/zapp-react-native-bridge/PushNotifications/PushTopicManager";
11
- import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/StorageMultiSelectProvider";
12
8
  import { getCellState } from "../../Cell/utils";
13
9
  import { getColorFromData } from "@applicaster/zapp-react-native-utils/cellUtils";
10
+ import { isCellSelected, useBehaviorUpdate } from "./behaviorProvider";
14
11
 
15
12
  const hasElementSpecificViewType = (viewType) => (element) => {
16
13
  if (R.isNil(element)) {
@@ -194,130 +191,7 @@ export const getFocusedButtonId = (focusable) => {
194
191
  };
195
192
 
196
193
  export const isSelected = (id: string | number, behavior?: Behavior) => {
197
- if (!behavior) {
198
- return false;
199
- }
200
-
201
- if (behavior?.selection_source === "now_playing") {
202
- const player = playerManager.getActivePlayer();
203
-
204
- if (player?.entry?.id === id) {
205
- return true;
206
- }
207
- }
208
-
209
- if (behavior?.select_mode === "single") {
210
- return behavior.current_selection === id;
211
- }
212
-
213
- if (behavior?.select_mode === "multi") {
214
- // TODO: Use generic resolver source
215
-
216
- if (behavior.selection_source === "@{push/topics}") {
217
- const tags = PushTopicManager.getInstance().getRegisteredTags();
218
-
219
- return tags.includes(String(id));
220
- }
221
-
222
- if (Array.isArray(behavior.current_selection)) {
223
- return behavior.current_selection.includes(id);
224
- }
225
-
226
- const currentSelection = String(behavior.current_selection);
227
-
228
- if (currentSelection?.startsWith("@{ctx/")) {
229
- const keyWithoutCtx = currentSelection.substring(
230
- "@{ctx/".length,
231
- currentSelection.length - 1
232
- );
233
-
234
- const selectedItems =
235
- StorageMultiSelectProvider.getProvider(
236
- keyWithoutCtx
237
- )?.getSelectedItems();
238
-
239
- return selectedItems?.includes(String(id));
240
- }
241
- }
242
-
243
- return false;
244
- };
245
-
246
- export const useBehaviorUpdate = (behavior: Behavior) => {
247
- const [lastUpdate, setLastUpdate] = React.useState(null);
248
-
249
- const player = usePlayer();
250
-
251
- const triggerUpdate = () => {
252
- setLastUpdate(Date.now());
253
- };
254
-
255
- // TODO: Create generic RX to state update
256
- useEffect(() => {
257
- // TODO: Use generic resolver source
258
- if (!behavior) {
259
- return;
260
- }
261
-
262
- const currentSelection = String(behavior.current_selection);
263
-
264
- if (currentSelection?.startsWith("@{ctx/")) {
265
- const keyWithoutCtx = currentSelection.substring(
266
- "@{ctx/".length,
267
- currentSelection.length - 1
268
- );
269
-
270
- if (keyWithoutCtx) {
271
- const subscription = StorageMultiSelectProvider.getProvider(
272
- keyWithoutCtx
273
- )
274
- .getObservable()
275
- .subscribe(() => {
276
- triggerUpdate();
277
- });
278
-
279
- return () => {
280
- subscription.unsubscribe();
281
- };
282
- }
283
- }
284
- }, [behavior]);
285
-
286
- useEffect(() => {
287
- if (!behavior) {
288
- return;
289
- }
290
-
291
- if (behavior?.selection_source === "@{push/topics}") {
292
- const subscription = PushTopicManager.getInstance()
293
- .getEntryObservable()
294
- .subscribe(() => {
295
- triggerUpdate();
296
- });
297
-
298
- return () => {
299
- subscription.unsubscribe();
300
- };
301
- }
302
- }, [behavior]);
303
-
304
- useEffect(() => {
305
- if (!behavior) {
306
- return;
307
- }
308
-
309
- if (behavior?.selection_source === "now_playing" && player) {
310
- const subscription = player.getEntryObservable().subscribe(() => {
311
- triggerUpdate();
312
- });
313
-
314
- return () => {
315
- subscription.unsubscribe();
316
- };
317
- }
318
- }, [behavior, player]);
319
-
320
- return lastUpdate;
194
+ return isCellSelected(id, behavior);
321
195
  };
322
196
 
323
197
  export const useCellState = ({
@@ -335,9 +335,9 @@ const PlayerContainerComponent = (props: Props) => {
335
335
 
336
336
  const resumeTime = Number(item?.extensions?.resumeTime);
337
337
 
338
- // TODO: This is temp hack, will be removed on next pr with player refactoring
338
+ // Сhecking that the player itself knows where to start playing, and there is no need to call seekTo after returning from the Сhromecast
339
339
  if (
340
- !!playerManager.getActivePlayer()?.getContinueWatchingOffset === false &&
340
+ !playerManager.getActivePlayer()?.hasResumePosition() &&
341
341
  !isNaN(resumeTime)
342
342
  ) {
343
343
  player?.seekTo(resumeTime);
@@ -514,6 +514,12 @@ const PlayerContainerComponent = (props: Props) => {
514
514
  useEffect(() => {
515
515
  playerEvent("source_changed", { item });
516
516
 
517
+ return () => {
518
+ playerEvent("player_did_close", { item });
519
+ };
520
+ }, [item?.id, Player]);
521
+
522
+ useEffect(() => {
517
523
  if (!isModal) {
518
524
  showNavBar(false);
519
525
  }
@@ -523,13 +529,11 @@ const PlayerContainerComponent = (props: Props) => {
523
529
  }
524
530
 
525
531
  return () => {
526
- playerEvent("player_did_close", { item });
527
-
528
532
  if (!isModal) {
529
533
  showNavBar(true);
530
534
  }
531
535
  };
532
- }, [showNavBar, /* added as suggested: */ item?.id, isModal, Player]);
536
+ }, [showNavBar, isModal, Player]);
533
537
 
534
538
  useEffect(() => {
535
539
  if (prevItemId && !R.equals(prevItemId, item?.id)) {
@@ -1,4 +1,4 @@
1
- import React, { PropsWithChildren, useEffect, useState } from "react";
1
+ import React, { PropsWithChildren } from "react";
2
2
  import { ImageBackground, View } from "react-native";
3
3
 
4
4
  import { imageSrcFromMediaItem } from "@applicaster/zapp-react-native-utils/configurationUtils";
@@ -12,7 +12,6 @@ import {
12
12
  type Props = PropsWithChildren<{
13
13
  entry: ZappEntry;
14
14
  style?: { [K: string]: any };
15
- docked?: boolean;
16
15
  imageStyle?: { [K: string]: any };
17
16
  imageKey?: string;
18
17
  defaultImageDimensions?: { [K: string]: any };
@@ -24,7 +23,6 @@ const PlayerImageBackgroundComponent = ({
24
23
  entry,
25
24
  children,
26
25
  style,
27
- docked,
28
26
  imageStyle,
29
27
  imageKey,
30
28
  defaultImageDimensions,
@@ -36,42 +34,24 @@ const PlayerImageBackgroundComponent = ({
36
34
 
37
35
  const { playerAnimationState } = useModalAnimationContext();
38
36
 
39
- const [lastNonNullAnimationState, setLastNonNullAnimationState] =
40
- useState(playerAnimationState);
41
-
42
- useEffect(() => {
43
- if (playerAnimationState !== null) {
44
- setLastNonNullAnimationState(playerAnimationState);
45
- }
46
- }, [playerAnimationState]);
47
-
48
37
  if (!source) return <>{children}</>;
49
38
 
50
- const imageBackgroundStyle =
51
- lastNonNullAnimationState !== PlayerAnimationStateEnum.minimize && !docked
52
- ? defaultImageDimensions
53
- : imageSize;
54
-
55
39
  return (
56
40
  <View
57
41
  style={
58
- playerAnimationState === PlayerAnimationStateEnum.maximaze
42
+ playerAnimationState === PlayerAnimationStateEnum.maximize
59
43
  ? defaultImageDimensions
60
44
  : style
61
45
  }
62
46
  >
63
47
  <AnimationComponent
64
- style={
65
- playerAnimationState === PlayerAnimationStateEnum.maximaze
66
- ? defaultImageDimensions
67
- : style
68
- }
48
+ style={style}
69
49
  animationType={ComponentAnimationType.player}
70
50
  additionalData={defaultImageDimensions}
71
51
  >
72
52
  <ImageBackground
73
53
  resizeMode="cover"
74
- style={imageBackgroundStyle}
54
+ style={imageSize}
75
55
  imageStyle={imageStyle}
76
56
  source={source}
77
57
  >
@@ -92,6 +92,12 @@ const getNavigations = ({
92
92
  return fallbackNavigations;
93
93
  };
94
94
 
95
+ const onRelease = () => {
96
+ setTimeout(() => {
97
+ focusManager.setInitialFocus();
98
+ }, 50);
99
+ };
100
+
95
101
  export const Screen = ({ route, Components }: Props) => {
96
102
  if (isNilOrEmpty(route)) {
97
103
  throw Error("Required props: route is missing");
@@ -152,12 +158,7 @@ export const Screen = ({ route, Components }: Props) => {
152
158
  const isScreenActive = useIsScreenActive();
153
159
 
154
160
  return (
155
- <FreezeWithCallback
156
- freeze={!isScreenActive}
157
- onRelease={() => {
158
- focusManager.setInitialFocus();
159
- }}
160
- >
161
+ <FreezeWithCallback freeze={!isScreenActive} onRelease={onRelease}>
161
162
  <View style={[styles.container, { backgroundColor }]}>
162
163
  <NavBarContainer isVisible={isNavBarVisible} onReady={noop}>
163
164
  <NavBar
@@ -7,15 +7,12 @@ import {
7
7
  useCurrentScreenData,
8
8
  useDimensions,
9
9
  useRoute,
10
+ useIsTablet,
10
11
  } from "@applicaster/zapp-react-native-utils/reactHooks";
11
12
  import { useMemo, useEffect, useState } from "react";
12
13
 
13
14
  export const useWaitForValidOrientation = () => {
14
- const {
15
- width: screenWidth,
16
- height,
17
- deviceInfo,
18
- } = useDimensions("screen", {
15
+ const { width: screenWidth, height } = useDimensions("screen", {
19
16
  fullDimensions: true,
20
17
  updateForInactiveScreens: false,
21
18
  });
@@ -26,7 +23,7 @@ export const useWaitForValidOrientation = () => {
26
23
 
27
24
  const [readyState, setReadyState] = useState(false);
28
25
 
29
- const isTablet = deviceInfo?.isTablet;
26
+ const isTablet = useIsTablet();
30
27
 
31
28
  const { appData } = usePickFromState(["appData"]);
32
29
  const isTabletPortrait = appData?.isTabletPortrait;
@@ -12,6 +12,7 @@ type TrackedViewProps = {
12
12
  onPositionUpdated: (props: { rect?: Record<string, number> }) => void;
13
13
  testId?: string | undefined;
14
14
  groupId?: string;
15
+ clipThreshold?: number;
15
16
  };
16
17
 
17
18
  export const TrackedView = memo(function TrackedView(props: TrackedViewProps) {
@@ -2,21 +2,21 @@ import { Animated } from "react-native";
2
2
 
3
3
  import { NAV_ACTION_PUSH, NAV_ACTION_BACK } from "./Transitioner";
4
4
 
5
- type TransitionConfig = {
6
- duration: number;
7
- easing: any;
8
- from: {
9
- style: any;
10
- };
11
- to: {
12
- style: any;
13
- };
14
- };
5
+ // type TransitionConfig = {
6
+ // duration: number;
7
+ // easing: any;
8
+ // from: {
9
+ // style: any;
10
+ // };
11
+ // to: {
12
+ // style: any;
13
+ // };
14
+ // };
15
15
 
16
- type Props = {
17
- transitionConfig: TransitionConfig;
18
- contentStyle: { [string]: any };
19
- };
16
+ // type Props = {
17
+ // transitionConfig: TransitionConfig;
18
+ // contentStyle: { [string]: any };
19
+ // };
20
20
 
21
21
  /**
22
22
  * Manages animation for the Transitioner,
@@ -25,7 +25,7 @@ type Props = {
25
25
  * which must have a proper structure, see types above ^.
26
26
  */
27
27
  export class AnimationManager {
28
- constructor(props: Props) {
28
+ constructor(props) {
29
29
  this.animatedValue = new Animated.Value(0.0);
30
30
 
31
31
  this.config = props.transitionConfig(
@@ -32,6 +32,9 @@ const { log_error, log_debug } = loggerLiveImageComponent;
32
32
  const isMeasurement = (item: ZappEntry) =>
33
33
  isString(item.id) && item.id.startsWith("pre-measurement-");
34
34
 
35
+ // Pixels by which the view can slightly extend outside the viewport and still be considered fully visible.
36
+ const CLIP_THRESHOLD = 10;
37
+
35
38
  type Props = {
36
39
  item: ZappEntry;
37
40
  style: Record<string, any>;
@@ -328,6 +331,7 @@ const PlayerLiveImageComponent = (props: Props) => {
328
331
  <TrackedView
329
332
  testId={`tracked-view-${playerId}-${item.title}`}
330
333
  onPositionUpdated={onPositionUpdated}
334
+ clipThreshold={CLIP_THRESHOLD}
331
335
  >
332
336
  <View ref={trackViewRef}>
333
337
  {isVideoMode ? (
@@ -2,6 +2,7 @@
2
2
 
3
3
  exports[`PlayerLiveImageComponent should render correctly with default props 1`] = `
4
4
  <TrackedView
5
+ clipThreshold={10}
5
6
  onPositionUpdated={[Function]}
6
7
  testId="tracked-view-player1-Test"
7
8
  >
@@ -46,7 +46,7 @@ export const AnimatedPlayerModalWrapper = (props: Props) => {
46
46
 
47
47
  React.useEffect(() => {
48
48
  (playerAnimationState === PlayerAnimationStateEnum.minimize ||
49
- playerAnimationState === PlayerAnimationStateEnum.maximaze) &&
49
+ playerAnimationState === PlayerAnimationStateEnum.maximize) &&
50
50
  setStartComponentsAnimation(true);
51
51
  }, [playerAnimationState]);
52
52
 
@@ -77,7 +77,7 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
77
77
 
78
78
  const isNotMinimizeMaximazeAnimation =
79
79
  playerAnimationState !== PlayerAnimationStateEnum.minimize &&
80
- playerAnimationState !== PlayerAnimationStateEnum.maximaze;
80
+ playerAnimationState !== PlayerAnimationStateEnum.maximize;
81
81
 
82
82
  const isEnablePanGesture =
83
83
  enableGesture &&
@@ -143,7 +143,7 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
143
143
  translateYOffset.flattenOffset();
144
144
  dragScrollY.setValue(0);
145
145
 
146
- setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
146
+ setPlayerAnimationState(PlayerAnimationStateEnum.maximize);
147
147
  } else {
148
148
  setPlayerAnimationState(PlayerAnimationStateEnum.minimize);
149
149
  }
@@ -158,7 +158,7 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
158
158
 
159
159
  dragScrollY.setValue(0);
160
160
 
161
- setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
161
+ setPlayerAnimationState(PlayerAnimationStateEnum.maximize);
162
162
  } else {
163
163
  resetPlayerAnimationState();
164
164
  }
@@ -252,7 +252,7 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
252
252
  (previousMode === "MINIMIZED" || previousMode === "MAXIMIZED")) ||
253
253
  (previousItemId !== item?.id && mode !== "FULLSCREEN"))
254
254
  ) {
255
- setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
255
+ setPlayerAnimationState(PlayerAnimationStateEnum.maximize);
256
256
  }
257
257
  }, [videoModalState]);
258
258
 
@@ -296,7 +296,7 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
296
296
 
297
297
  resetPlayerAnimationState();
298
298
  });
299
- } else if (playerAnimationState === PlayerAnimationStateEnum.maximaze) {
299
+ } else if (playerAnimationState === PlayerAnimationStateEnum.maximize) {
300
300
  Animated.timing(translateYOffset, getAnimatedConfig(0)).start(() => {
301
301
  maximiseVideoModal();
302
302
  setScrollModalAnimatedValue(translateYOffset, 0, setLastSnap);
@@ -50,7 +50,7 @@ export const AnimatedVideoPlayer = ({ children }: Props) => {
50
50
 
51
51
  const isNotMinimizeMaximazeAnimation =
52
52
  playerAnimationState !== PlayerAnimationStateEnum.minimize &&
53
- playerAnimationState !== PlayerAnimationStateEnum.maximaze;
53
+ playerAnimationState !== PlayerAnimationStateEnum.maximize;
54
54
 
55
55
  const isEnablePanGesture =
56
56
  !isLanguageOverlayVisible &&
@@ -89,7 +89,7 @@ export const AnimatedVideoPlayer = ({ children }: Props) => {
89
89
  setLastSnap(destSnapPoint);
90
90
 
91
91
  if (destSnapPoint === modalSnapPoints[0]) {
92
- setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
92
+ setPlayerAnimationState(PlayerAnimationStateEnum.maximize);
93
93
  } else {
94
94
  lastScrollY.setValue(0);
95
95
  lastScrollYValue.current = 0;
@@ -105,7 +105,7 @@ export const AnimatedVideoPlayer = ({ children }: Props) => {
105
105
  modalSnapPoints[1] - preparedTranslationY
106
106
  );
107
107
 
108
- setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
108
+ setPlayerAnimationState(PlayerAnimationStateEnum.maximize);
109
109
  } else {
110
110
  resetPlayerAnimationState();
111
111
  }
@@ -246,7 +246,7 @@ export const AnimationView = ({
246
246
  default:
247
247
  return getAnimationValue(
248
248
  animationType,
249
- PlayerAnimationStateEnum.maximaze
249
+ PlayerAnimationStateEnum.maximize
250
250
  ).toValue;
251
251
  }
252
252
  }, [defaultValue, videoModalState]);
@@ -344,7 +344,7 @@ export const AnimationView = ({
344
344
  animatedValue,
345
345
  getAnimationValue(animationType, playerAnimationState)
346
346
  ).start();
347
- } else if (playerAnimationState === PlayerAnimationStateEnum.maximaze) {
347
+ } else if (playerAnimationState === PlayerAnimationStateEnum.maximize) {
348
348
  animatedValue.setValue(defaultValue);
349
349
  }
350
350
  }, [playerAnimationState, startComponentsAnimation, defaultValue]);
@@ -14,12 +14,12 @@ import { useConfiguration } from "../utils";
14
14
 
15
15
  export enum PlayerAnimationStateEnum {
16
16
  minimize = "minimize",
17
- maximaze = "maximaze",
17
+ maximize = "maximize",
18
18
  drag_player = "drag_player",
19
19
  drag_scroll = "drag_scroll",
20
20
  }
21
21
 
22
- type PlayerAnimationStateT = number | PlayerAnimationStateEnum | null;
22
+ export type PlayerAnimationStateT = number | PlayerAnimationStateEnum | null;
23
23
 
24
24
  export type ModalAnimationContextT = {
25
25
  isActiveGesture: boolean;
@@ -7,17 +7,11 @@ export const withFocusableContext = (Component) => {
7
7
  { groupId, ...props }: Record<string, any>,
8
8
  ref
9
9
  ) => {
10
- return (
11
- <FocusableGroupContext.Consumer>
12
- {(groupIdContext: string) => {
13
- // eslint-disable-next-line react/display-name
14
- const propsGroupId = groupId || null;
15
- const providedGroupId = propsGroupId || groupIdContext;
10
+ const groupIdContext = React.useContext(FocusableGroupContext);
11
+ const propsGroupId = groupId || null;
12
+ const providedGroupId = propsGroupId || groupIdContext;
16
13
 
17
- return <Component {...props} groupId={providedGroupId} ref={ref} />;
18
- }}
19
- </FocusableGroupContext.Consumer>
20
- );
14
+ return <Component {...props} groupId={providedGroupId} ref={ref} />;
21
15
  };
22
16
 
23
17
  return React.forwardRef(WithFocusableContext);
@@ -13,7 +13,7 @@ type ProviderProps = {
13
13
  };
14
14
  };
15
15
 
16
- const initialHeaderOffsetContext = () => ({
16
+ const initialHeaderOffsetContext: HeaderOffsetContextType = () => ({
17
17
  headerOffset: 0,
18
18
  headersAbove: null,
19
19
  });
@@ -24,11 +24,9 @@ export const HeaderOffsetContext = React.createContext<HeaderOffsetContextType>(
24
24
 
25
25
  export function withConsumer(Component) {
26
26
  return function WithConsumer(props) {
27
- return (
28
- <HeaderOffsetContext.Consumer>
29
- {(context) => <Component getHeaderOffset={context} {...props} />}
30
- </HeaderOffsetContext.Consumer>
31
- );
27
+ const getHeaderOffset = React.useContext(HeaderOffsetContext);
28
+
29
+ return <Component getHeaderOffset={getHeaderOffset} {...props} />;
32
30
  };
33
31
  }
34
32
 
@@ -176,15 +176,8 @@ export function ScreenContextProvider({
176
176
 
177
177
  export function withScreenContext(Component: React.ComponentType<any>) {
178
178
  return function WithScreenContextWrapper(props) {
179
- return (
180
- <ScreenContext.Consumer>
181
- {useCallback(
182
- (value) => (
183
- <Component {...props} screenContext={value} />
184
- ),
185
- [Component, props]
186
- )}
187
- </ScreenContext.Consumer>
188
- );
179
+ const screenContext = React.useContext(ScreenContext);
180
+
181
+ return <Component {...props} screenContext={screenContext} />;
189
182
  };
190
183
  }
@@ -8,10 +8,11 @@ type LayoutContext = {
8
8
  componentAnchorPointY: number | null;
9
9
  componentAvailableWidth: number | null;
10
10
  setScreenLayout?: (properties: {}) => void;
11
+ resetScreenLayout?: () => void;
11
12
  extraAnchorPointYOffset: number;
12
13
  };
13
14
 
14
- const screenLayoutContext: LayoutContext = {
15
+ const initialScreenLayoutContext: LayoutContext = {
15
16
  screenMarginTop: null,
16
17
  screenMarginBottom: null,
17
18
  screenHeight: null,
@@ -21,8 +22,9 @@ const screenLayoutContext: LayoutContext = {
21
22
  extraAnchorPointYOffset: 0,
22
23
  };
23
24
 
24
- export const ScreenLayoutContext =
25
- React.createContext<LayoutContext>(screenLayoutContext);
25
+ export const ScreenLayoutContext = React.createContext<LayoutContext>(
26
+ initialScreenLayoutContext
27
+ );
26
28
 
27
29
  export function ScreenLayoutContextConsumer(Component) {
28
30
  return function WithConsumer(props) {
@@ -1,8 +1,8 @@
1
1
  import * as React from "react";
2
2
 
3
- import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
4
3
  import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
5
4
  import { usePathname } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/usePathname";
5
+ import { useRivers } from "@applicaster/zapp-react-native-utils/reactHooks";
6
6
 
7
7
  type Props = ZappUIComponentProps & {
8
8
  rivers: ZappRivers;
@@ -12,27 +12,25 @@ type Props = ZappUIComponentProps & {
12
12
 
13
13
  export function withRiver(Component: ZappComponent): ZappComponent {
14
14
  const RiverWithData = ({ screenId, ...otherProps }: Props) => {
15
- const { rivers } = usePickFromState(["rivers"]);
15
+ const river = useRivers()?.[screenId];
16
16
  const pathname = usePathname();
17
- const river = rivers[screenId];
18
17
 
19
18
  if (!river) {
20
19
  return null;
21
20
  }
22
21
 
23
- if (!otherProps.screenData) {
24
- otherProps.screenData = river;
25
- }
26
-
27
22
  return (
28
23
  <Component
29
24
  screenId={screenId}
30
25
  river={river}
31
26
  key={isTV() ? pathname : undefined}
32
27
  {...otherProps}
28
+ screenData={otherProps.screenData || river}
33
29
  />
34
30
  );
35
31
  };
36
32
 
33
+ RiverWithData.displayName = `RiverWithData(${Component.displayName || Component.name})`;
34
+
37
35
  return RiverWithData;
38
36
  }
@@ -1,12 +1,10 @@
1
1
  /// <reference types="@applicaster/applicaster-types" />
2
2
  /// <reference types="@applicaster/zapp-react-native-ui-components" />
3
3
  import React, { useEffect, useMemo } from "react";
4
- import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
5
4
 
6
5
  import * as R from "ramda";
7
6
  import { Platform } from "react-native";
8
7
  import Url from "url";
9
- import { ENDPOINT_TAGS } from "@applicaster/zapp-react-native-utils/types";
10
8
  import { favoritesListener } from "@applicaster/zapp-react-native-bridge/Favorites";
11
9
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
12
10
  import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
@@ -21,12 +19,7 @@ import { ZappPipesSearchContext } from "@applicaster/zapp-react-native-ui-compon
21
19
  import { useScreenContext } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
22
20
 
23
21
  import { isVerticalListOrGrid } from "./utils";
24
- import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
25
- import {
26
- findEndpointForURL,
27
- HTTP_METHODS,
28
- } from "@applicaster/zapp-pipes-v2-client";
29
- import { getNamespaceAndKey } from "@applicaster/zapp-react-native-utils/appUtils/contextKeysManager/utils";
22
+ import { subscribeForUrlContextKeyChanges } from "@applicaster/zapp-pipes-v2-client";
30
23
 
31
24
  type Props = {
32
25
  component: ZappUIComponent;
@@ -300,27 +293,7 @@ export function zappPipesDataConnector(
300
293
  return addListener(reloadData);
301
294
  }
302
295
  } else {
303
- const pipesEndpoints = appStore.get("pipesEndpoints");
304
-
305
- const endpointURL = findEndpointForURL(
306
- dataSourceUrl,
307
- pipesEndpoints,
308
- HTTP_METHODS.GET
309
- );
310
-
311
- const endpoint = pipesEndpoints?.[endpointURL];
312
-
313
- if (endpoint?.tags?.includes(ENDPOINT_TAGS.observe_storage)) {
314
- const subscriptions: (() => void)[] = endpoint.context_obj.map(
315
- (data: Record<string, any>) => {
316
- const { namespace, key } = getNamespaceAndKey(data.key);
317
-
318
- return localStorage.addListener({ key, namespace }, reloadData);
319
- }
320
- );
321
-
322
- return () => subscriptions.forEach((listener) => listener());
323
- }
296
+ return subscribeForUrlContextKeyChanges(dataSourceUrl, {}, reloadData);
324
297
  }
325
298
  }, [dataSourceUrl, reloadData]);
326
299
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-ui-components",
3
- "version": "13.0.0-alpha.3099523645",
3
+ "version": "13.0.0-alpha.3144359118",
4
4
  "description": "Applicaster Zapp React Native ui components for the Quick Brick App",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -29,15 +29,13 @@
29
29
  },
30
30
  "homepage": "https://github.com/applicaster/quickbrick#readme",
31
31
  "devDependencies": {
32
- "@types/react": "17.0.2",
33
- "@types/react-native": "0.69.6",
34
32
  "redux-mock-store": "^1.5.3"
35
33
  },
36
34
  "dependencies": {
37
- "@applicaster/applicaster-types": "13.0.0-alpha.3099523645",
38
- "@applicaster/zapp-react-native-bridge": "13.0.0-alpha.3099523645",
39
- "@applicaster/zapp-react-native-redux": "13.0.0-alpha.3099523645",
40
- "@applicaster/zapp-react-native-utils": "13.0.0-alpha.3099523645",
35
+ "@applicaster/applicaster-types": "13.0.0-alpha.3144359118",
36
+ "@applicaster/zapp-react-native-bridge": "13.0.0-alpha.3144359118",
37
+ "@applicaster/zapp-react-native-redux": "13.0.0-alpha.3144359118",
38
+ "@applicaster/zapp-react-native-utils": "13.0.0-alpha.3144359118",
41
39
  "promise": "^8.3.0",
42
40
  "react-router-native": "^5.1.2",
43
41
  "url": "^0.11.0",
@@ -46,7 +44,6 @@
46
44
  "peerDependencies": {
47
45
  "@applicaster/zapp-pipes-v2-client": "*",
48
46
  "@react-native-community/netinfo": "*",
49
- "@types/node": "*",
50
47
  "immer": "*",
51
48
  "react": "*",
52
49
  "react-native": "*",
package/tsconfig.json CHANGED
@@ -2,8 +2,7 @@
2
2
  "extends": "../../tsconfig.base.json",
3
3
  "compilerOptions": {
4
4
  "outDir": "./lib",
5
- "rootDir": "./src"
5
+ "rootDir": "./"
6
6
  },
7
- "exclude": ["src/**/*.test.tsx"],
8
- "include": ["src/**/*"]
7
+ "include": ["**/*"],
9
8
  }