@applicaster/zapp-react-native-ui-components 13.0.0-alpha.6944415045 → 13.0.0-alpha.7192404241
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.
- package/Components/Cell/Cell.tsx +7 -4
- package/Components/Cell/TvOSCellComponent.tsx +22 -20
- package/Components/Cell/index.js +1 -3
- package/Components/Focusable/FocusableTvOS.tsx +1 -5
- package/Components/FocusableList/index.tsx +9 -25
- package/Components/FocusableScrollView/index.tsx +12 -39
- package/Components/Layout/TV/NavBarContainer.tsx +7 -8
- package/Components/River/TV/index.tsx +1 -6
- package/Components/River/__tests__/componentsMap.test.js +14 -1
- package/Components/VideoModal/OpaqueLayer.tsx +33 -0
- package/Components/VideoModal/PlayerWrapper.tsx +16 -35
- package/Components/VideoModal/VideoModal.tsx +14 -23
- package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -1
- package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +0 -90
- package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +89 -0
- package/Components/VideoModal/hooks/index.ts +7 -0
- package/Components/VideoModal/hooks/useDelayedPlayerDetails.ts +49 -0
- package/Components/VideoModal/hooks/utils/__tests__/showDetails.test.ts +91 -0
- package/Components/VideoModal/hooks/utils/index.ts +33 -0
- package/Components/VideoModal/utils.ts +1 -1
- package/Decorators/ZappPipesDataConnector/index.tsx +31 -1
- package/package.json +5 -5
- package/Contexts/ComponentsMapOffsetContext/index.tsx +0 -46
package/Components/Cell/Cell.tsx
CHANGED
|
@@ -61,7 +61,6 @@ type Props = {
|
|
|
61
61
|
skipFocusManagerRegistration?: boolean;
|
|
62
62
|
shouldUpdate: boolean;
|
|
63
63
|
behavior: Behavior;
|
|
64
|
-
componentsMapOffset: number;
|
|
65
64
|
};
|
|
66
65
|
|
|
67
66
|
type State = {
|
|
@@ -274,10 +273,14 @@ export class CellComponent extends React.Component<Props, State> {
|
|
|
274
273
|
if (isFocused) {
|
|
275
274
|
const accessibilityManager = AccessibilityManager.getInstance();
|
|
276
275
|
|
|
276
|
+
const accessibilityTitle =
|
|
277
|
+
item?.extensions?.accessibility?.label || item?.title || "";
|
|
278
|
+
|
|
279
|
+
const accessibilityHint =
|
|
280
|
+
item?.extensions?.accessibility?.hint || "";
|
|
281
|
+
|
|
277
282
|
accessibilityManager.readText({
|
|
278
|
-
text:
|
|
279
|
-
item.extensions?.accessibility?.label || item.title
|
|
280
|
-
),
|
|
283
|
+
text: `${accessibilityTitle} ${accessibilityHint}`,
|
|
281
284
|
});
|
|
282
285
|
}
|
|
283
286
|
|
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import * as R from "ramda";
|
|
3
3
|
import { View, StyleSheet } from "react-native";
|
|
4
4
|
|
|
5
|
-
import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable
|
|
5
|
+
import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable";
|
|
6
6
|
import { FocusableCell } from "@applicaster/zapp-react-native-ui-components/Components/FocusableCell";
|
|
7
7
|
import { getItemType } from "@applicaster/zapp-react-native-utils/navigationUtils";
|
|
8
8
|
import { SCREEN_TYPES } from "@applicaster/zapp-react-native-utils/navigationUtils/itemTypes";
|
|
@@ -65,7 +65,6 @@ type Props = {
|
|
|
65
65
|
isFocusable: boolean;
|
|
66
66
|
shouldUpdate: boolean;
|
|
67
67
|
behavior: Behavior;
|
|
68
|
-
componentsMapOffset: number;
|
|
69
68
|
};
|
|
70
69
|
|
|
71
70
|
type State = {
|
|
@@ -180,7 +179,6 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
180
179
|
groupId,
|
|
181
180
|
component,
|
|
182
181
|
index,
|
|
183
|
-
componentsMapOffset,
|
|
184
182
|
} = this.props;
|
|
185
183
|
|
|
186
184
|
this.setScreenLayout(componentAnchorPointY, screenLayout);
|
|
@@ -195,11 +193,13 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
195
193
|
const extraAnchorPointYOffset =
|
|
196
194
|
screenLayout?.extraAnchorPointYOffset || 0;
|
|
197
195
|
|
|
196
|
+
const marginTop = screenLayout?.screenMarginTop || 0;
|
|
197
|
+
|
|
198
198
|
const totalOffset =
|
|
199
199
|
headerOffset +
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
200
|
+
(componentAnchorPointY || 0) +
|
|
201
|
+
extraAnchorPointYOffset -
|
|
202
|
+
marginTop;
|
|
203
203
|
|
|
204
204
|
mainOffsetUpdater?.(
|
|
205
205
|
{ tag: this.target },
|
|
@@ -247,7 +247,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
247
247
|
|
|
248
248
|
const focusableId = R.join("-", [component?.id, id, index]);
|
|
249
249
|
|
|
250
|
-
const handleFocus = (arg1: any, index
|
|
250
|
+
const handleFocus = (arg1: any, index: number) => {
|
|
251
251
|
const focusFn = onFocus || noop;
|
|
252
252
|
focusFn(arg1, index);
|
|
253
253
|
|
|
@@ -280,7 +280,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
280
280
|
<View onLayout={this.onLayout} style={styles.container}>
|
|
281
281
|
<Focusable
|
|
282
282
|
id={focusableId}
|
|
283
|
-
groupId={
|
|
283
|
+
groupId={groupId || component?.id}
|
|
284
284
|
onFocus={handleFocus}
|
|
285
285
|
onBlur={onBlur || this.onBlur}
|
|
286
286
|
isParallaxDisabled={this.layout?.width > 1740}
|
|
@@ -292,18 +292,20 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
292
292
|
style={baseCellStyles}
|
|
293
293
|
isFocusable={isFocusable}
|
|
294
294
|
>
|
|
295
|
-
{(focused) =>
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
295
|
+
{(focused) => {
|
|
296
|
+
return (
|
|
297
|
+
<FocusableCell
|
|
298
|
+
{...{
|
|
299
|
+
index,
|
|
300
|
+
CellRenderer,
|
|
301
|
+
item,
|
|
302
|
+
focused: this.props.focused || selectedCell || focused,
|
|
303
|
+
scrollTo: this.scrollTo,
|
|
304
|
+
behavior,
|
|
305
|
+
}}
|
|
306
|
+
/>
|
|
307
|
+
);
|
|
308
|
+
}}
|
|
307
309
|
</Focusable>
|
|
308
310
|
</View>
|
|
309
311
|
);
|
package/Components/Cell/index.js
CHANGED
|
@@ -11,7 +11,6 @@ import { ScreenScrollingContext } from "../../Contexts/ScreenScrollingContext";
|
|
|
11
11
|
|
|
12
12
|
import { ScreenLayoutContextConsumer } from "../../Contexts/ScreenLayoutContext";
|
|
13
13
|
import { createContext } from "@applicaster/zapp-react-native-utils/reactUtils/createContext";
|
|
14
|
-
import { withComponentsMapOffsetContextConsumer } from "../../Contexts/ComponentsMapOffsetContext";
|
|
15
14
|
|
|
16
15
|
// TODO | Gallery QB | Extract this
|
|
17
16
|
export const ScrollInterceptorContext = createContext(
|
|
@@ -31,6 +30,5 @@ export const Cell = R.compose(
|
|
|
31
30
|
RiverOffsetContext.withConsumer,
|
|
32
31
|
HorizontalScrollContext.withConsumer,
|
|
33
32
|
withConsumer,
|
|
34
|
-
ScreenLayoutContextConsumer
|
|
35
|
-
withComponentsMapOffsetContextConsumer
|
|
33
|
+
ScreenLayoutContextConsumer
|
|
36
34
|
)(Component);
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
focusManager,
|
|
10
10
|
forceFocusableFocus,
|
|
11
11
|
} from "@applicaster/zapp-react-native-utils/appUtils/focusManager/index.ios";
|
|
12
|
-
import { findNodeHandle
|
|
12
|
+
import { findNodeHandle } from "react-native";
|
|
13
13
|
|
|
14
14
|
function noop() {}
|
|
15
15
|
|
|
@@ -35,10 +35,6 @@ type Props = {
|
|
|
35
35
|
forceFocus?: boolean;
|
|
36
36
|
initialFocus?: boolean;
|
|
37
37
|
onLayout?: (e: any) => void;
|
|
38
|
-
willReceiveFocus: () => void;
|
|
39
|
-
hasReceivedFocus: () => void;
|
|
40
|
-
offsetUpdater: (arg1: string, arg2: number) => number;
|
|
41
|
-
style: ViewStyle;
|
|
42
38
|
};
|
|
43
39
|
|
|
44
40
|
export class Focusable extends BaseFocusable<Props> {
|
|
@@ -15,7 +15,6 @@ import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
|
15
15
|
|
|
16
16
|
import { useParentFocus, useCheckItemIdsForUnique } from "./hooks";
|
|
17
17
|
import { FocusableListItemWrapper } from "./FocusableListItemWrapper";
|
|
18
|
-
import { FocusableScrollView } from "../FocusableScrollView";
|
|
19
18
|
|
|
20
19
|
const mapIndexed = R.addIndex(R.map);
|
|
21
20
|
|
|
@@ -55,7 +54,6 @@ export type Props<ItemT> = FlatListProps<ItemT> & {
|
|
|
55
54
|
disableNextFocusUp?: boolean;
|
|
56
55
|
disableNextFocusLeft?: boolean;
|
|
57
56
|
disableNextFocusRight?: boolean;
|
|
58
|
-
useScrollView?: boolean;
|
|
59
57
|
} & ParentFocus;
|
|
60
58
|
|
|
61
59
|
function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
|
|
@@ -90,7 +88,6 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
|
|
|
90
88
|
data = [],
|
|
91
89
|
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
92
90
|
omitPropsPropagation = [],
|
|
93
|
-
useScrollView = false,
|
|
94
91
|
} = props;
|
|
95
92
|
|
|
96
93
|
useCheckItemIdsForUnique({ componentId: props.id, items: data });
|
|
@@ -276,7 +273,6 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
|
|
|
276
273
|
"initialFocusDirection",
|
|
277
274
|
"withStateMemory",
|
|
278
275
|
"useSequentialLoading",
|
|
279
|
-
"useScrollView",
|
|
280
276
|
...omitPropsPropagation,
|
|
281
277
|
],
|
|
282
278
|
R.__
|
|
@@ -299,27 +295,15 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
|
|
|
299
295
|
return (
|
|
300
296
|
// @ts-ignore
|
|
301
297
|
<ChildrenFocusDeactivatorView flex={1}>
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
/>
|
|
312
|
-
) : (
|
|
313
|
-
<FlatList
|
|
314
|
-
focusable={false}
|
|
315
|
-
scrollEnabled={false}
|
|
316
|
-
ref={ref}
|
|
317
|
-
{...getFlatListProps(props)}
|
|
318
|
-
renderItem={renderItem}
|
|
319
|
-
onEndReached={onEndReached}
|
|
320
|
-
initialNumToRender={initialNumToRender}
|
|
321
|
-
/>
|
|
322
|
-
)}
|
|
298
|
+
<FlatList
|
|
299
|
+
focusable={false}
|
|
300
|
+
scrollEnabled={false}
|
|
301
|
+
ref={ref}
|
|
302
|
+
{...getFlatListProps(props)}
|
|
303
|
+
renderItem={renderItem}
|
|
304
|
+
onEndReached={onEndReached}
|
|
305
|
+
initialNumToRender={initialNumToRender}
|
|
306
|
+
/>
|
|
323
307
|
</ChildrenFocusDeactivatorView>
|
|
324
308
|
);
|
|
325
309
|
}
|
|
@@ -11,13 +11,6 @@ import {
|
|
|
11
11
|
useInitialFocus,
|
|
12
12
|
} from "@applicaster/zapp-react-native-utils/focusManager";
|
|
13
13
|
|
|
14
|
-
export type FocusableScrollViewRenderItem = (info: {
|
|
15
|
-
item: ZappUIComponent;
|
|
16
|
-
index: number;
|
|
17
|
-
focused: boolean;
|
|
18
|
-
parentFocus: ParentFocus;
|
|
19
|
-
}) => React.ReactElement;
|
|
20
|
-
|
|
21
14
|
export type Props = ScrollViewProps & {
|
|
22
15
|
id?: number | string;
|
|
23
16
|
horizontal?: boolean;
|
|
@@ -39,27 +32,17 @@ export type Props = ScrollViewProps & {
|
|
|
39
32
|
initialFocusDirection?: FocusManager.Android.FocusNavigationDirections;
|
|
40
33
|
onAllComponentsLoaded?: () => void;
|
|
41
34
|
omitPropsPropagation?: Array<keyof FlatListProps<any>>;
|
|
42
|
-
renderItem:
|
|
35
|
+
renderItem: (info: {
|
|
36
|
+
item: ZappUIComponent;
|
|
37
|
+
index: number;
|
|
38
|
+
focused: boolean;
|
|
39
|
+
parentFocus: ParentFocus;
|
|
40
|
+
}) => React.ReactElement;
|
|
43
41
|
} & ParentFocus;
|
|
44
42
|
|
|
45
43
|
type State = boolean[];
|
|
46
44
|
type Action = { index?: number; isFocusable: boolean; type?: string };
|
|
47
45
|
|
|
48
|
-
const unwrapArray = (maybeArray) =>
|
|
49
|
-
Array.isArray(maybeArray) ? maybeArray[0] : maybeArray;
|
|
50
|
-
|
|
51
|
-
function getEntry<T>(data: T | T[], index: unknown): T {
|
|
52
|
-
let entry;
|
|
53
|
-
|
|
54
|
-
if (R.isNil(index)) {
|
|
55
|
-
entry = unwrapArray(data);
|
|
56
|
-
} else if (typeof index === "number") {
|
|
57
|
-
entry = unwrapArray(data[index]);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return entry;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
46
|
const focusableStateReducer = (
|
|
64
47
|
state: State,
|
|
65
48
|
{ index = null, isFocusable, type = null }: Action
|
|
@@ -90,7 +73,7 @@ const extendScrollViewRef = (_ref, childCompsMeasurementsMap) => {
|
|
|
90
73
|
};
|
|
91
74
|
|
|
92
75
|
_ref.scrollToIndex = (params) => {
|
|
93
|
-
const { index, viewOffset
|
|
76
|
+
const { index, viewOffset } = params;
|
|
94
77
|
|
|
95
78
|
const itemByIndex = R.find(R.propEq("index", index))(
|
|
96
79
|
R.values(childCompsMeasurementsMap)
|
|
@@ -146,11 +129,8 @@ function FocusableScrollViewComponent(props: Props, ref) {
|
|
|
146
129
|
);
|
|
147
130
|
|
|
148
131
|
const getFocusableEntryId = React.useCallback(
|
|
149
|
-
(_entry: ZappEntry
|
|
150
|
-
const entry
|
|
151
|
-
R.isNil(index) ? _entry : data,
|
|
152
|
-
index
|
|
153
|
-
);
|
|
132
|
+
(_entry: ZappEntry, index?: number) => {
|
|
133
|
+
const entry = R.isNil(index) ? _entry : data[index];
|
|
154
134
|
|
|
155
135
|
return entry ? `${props.id}--${entry?.id}` : undefined;
|
|
156
136
|
},
|
|
@@ -293,18 +273,11 @@ function FocusableScrollViewComponent(props: Props, ref) {
|
|
|
293
273
|
const renderItem = React.useCallback(
|
|
294
274
|
(item, index) => {
|
|
295
275
|
const renderArgs = { item, index };
|
|
296
|
-
|
|
297
|
-
const
|
|
298
|
-
Array.isArray(item) ? item[0] : item,
|
|
299
|
-
index
|
|
300
|
-
);
|
|
301
|
-
|
|
302
|
-
const nextFocus = getNextFocus(index);
|
|
276
|
+
const id = `${props.id}--${renderArgs?.item?.id}`;
|
|
277
|
+
const nextFocus = getNextFocus(renderArgs?.index);
|
|
303
278
|
|
|
304
279
|
const onItemLayout = (event) => {
|
|
305
|
-
childCompsMeasurementsMap.current[
|
|
306
|
-
Array.isArray(item) ? index : item.id
|
|
307
|
-
] = {
|
|
280
|
+
childCompsMeasurementsMap.current[item.id] = {
|
|
308
281
|
index,
|
|
309
282
|
layout: event.nativeEvent.layout,
|
|
310
283
|
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { View, StyleSheet } from "react-native";
|
|
3
|
+
import { ScreenLayoutContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenLayoutContext";
|
|
4
|
+
import { ifEmptyUseFallback } from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
3
5
|
import { useCurrentScreenData } from "@applicaster/zapp-react-native-utils/reactHooks/screen";
|
|
4
6
|
import { useScreenConfiguration } from "../../River/useScreenConfiguration";
|
|
5
7
|
|
|
@@ -10,15 +12,12 @@ type Props = {
|
|
|
10
12
|
};
|
|
11
13
|
|
|
12
14
|
const styles = StyleSheet.create({
|
|
13
|
-
container: {
|
|
14
|
-
position: "absolute",
|
|
15
|
-
top: 0,
|
|
16
|
-
zIndex: 10,
|
|
17
|
-
width: 1920,
|
|
18
|
-
},
|
|
15
|
+
container: { position: "absolute", top: 0, zIndex: 10, width: 1920 },
|
|
19
16
|
});
|
|
20
17
|
|
|
21
18
|
export const NavBarContainer = ({ children, isVisible, onReady }: Props) => {
|
|
19
|
+
const screenLayout = React.useContext(ScreenLayoutContext);
|
|
20
|
+
const screenMarginTop = screenLayout?.screenMarginTop || 0;
|
|
22
21
|
const screen = useCurrentScreenData();
|
|
23
22
|
const screenConfig = useScreenConfiguration(screen?.id);
|
|
24
23
|
|
|
@@ -27,9 +26,9 @@ export const NavBarContainer = ({ children, isVisible, onReady }: Props) => {
|
|
|
27
26
|
// limits the height of the focusable container of the TopMenuBarTV component
|
|
28
27
|
// to prevent it from being overlapped by the screen content,
|
|
29
28
|
// as it makes TopMenuBarTV unfocusable on tvOS
|
|
30
|
-
maxHeight:
|
|
29
|
+
maxHeight: ifEmptyUseFallback(screenConfig?.marginTop, screenMarginTop),
|
|
31
30
|
}),
|
|
32
|
-
[screenConfig]
|
|
31
|
+
[screenLayout, screenConfig]
|
|
33
32
|
);
|
|
34
33
|
|
|
35
34
|
React.useEffect(() => {
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import { compose } from "ramda";
|
|
2
1
|
import { River as RiverComponent } from "./River";
|
|
3
2
|
import { withTvEventHandler } from "./withTVEventHandler";
|
|
4
|
-
import { withComponentsMapOffsetContext } from "../../../Contexts/ComponentsMapOffsetContext";
|
|
5
3
|
|
|
6
|
-
export const River =
|
|
7
|
-
withTvEventHandler,
|
|
8
|
-
withComponentsMapOffsetContext
|
|
9
|
-
)(RiverComponent);
|
|
4
|
+
export const River = withTvEventHandler(RiverComponent);
|
|
@@ -81,6 +81,13 @@ const mockScreenData = {
|
|
|
81
81
|
id: "A1234",
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
+
jest.mock("@applicaster/zapp-react-native-redux/AppStore", () => ({
|
|
85
|
+
appStore: {
|
|
86
|
+
get: jest.fn((prop) => mockStore[prop]),
|
|
87
|
+
getState: jest.fn(),
|
|
88
|
+
},
|
|
89
|
+
}));
|
|
90
|
+
|
|
84
91
|
jest.mock("@applicaster/zapp-react-native-utils/localizationUtils", () => ({
|
|
85
92
|
useIsRTL: jest.fn(() => mock_rtl_flag),
|
|
86
93
|
}));
|
|
@@ -153,7 +160,13 @@ const plugins = [];
|
|
|
153
160
|
const navigation = {};
|
|
154
161
|
|
|
155
162
|
const props = { components, cellStyles, riverComponents, navigation };
|
|
156
|
-
|
|
163
|
+
|
|
164
|
+
const store = mockStore({
|
|
165
|
+
components,
|
|
166
|
+
cellStyles,
|
|
167
|
+
plugins,
|
|
168
|
+
getState: jest.fn(),
|
|
169
|
+
});
|
|
157
170
|
|
|
158
171
|
jest.useFakeTimers();
|
|
159
172
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { StyleSheet, View } from "react-native";
|
|
3
|
+
|
|
4
|
+
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
5
|
+
|
|
6
|
+
import { Spinner } from "../Spinner";
|
|
7
|
+
|
|
8
|
+
const styles = StyleSheet.create({
|
|
9
|
+
container: {
|
|
10
|
+
...StyleSheet.absoluteFillObject,
|
|
11
|
+
alignItems: "center",
|
|
12
|
+
justifyContent: "center",
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const OpaqueLayer = () => {
|
|
17
|
+
const theme = useTheme();
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<View
|
|
21
|
+
style={[
|
|
22
|
+
styles.container,
|
|
23
|
+
{
|
|
24
|
+
// we assume that it's non-transparent and we could show spinner always until it will be covered by player
|
|
25
|
+
// this spinner hides all content under modal
|
|
26
|
+
backgroundColor: theme?.app_background_color,
|
|
27
|
+
},
|
|
28
|
+
]}
|
|
29
|
+
>
|
|
30
|
+
<Spinner />
|
|
31
|
+
</View>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
@@ -10,7 +10,8 @@ import { Edge, SafeAreaView } from "react-native-safe-area-context";
|
|
|
10
10
|
import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
11
11
|
import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
12
12
|
import { PlayerDetails } from "./PlayerDetails";
|
|
13
|
-
import {
|
|
13
|
+
import { playerDimensionsHack } from "./utils";
|
|
14
|
+
import { useDelayedPlayerDetails } from "./hooks";
|
|
14
15
|
|
|
15
16
|
import {
|
|
16
17
|
AnimatedScrollModal,
|
|
@@ -121,34 +122,6 @@ const getTabletWidth = (
|
|
|
121
122
|
return Number(width) - sidebarWidth;
|
|
122
123
|
};
|
|
123
124
|
|
|
124
|
-
const showDetails = (
|
|
125
|
-
isMobile: boolean,
|
|
126
|
-
docked: boolean,
|
|
127
|
-
isInlineModal,
|
|
128
|
-
pip
|
|
129
|
-
): boolean => {
|
|
130
|
-
if (pip) {
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (!isInlineModal) {
|
|
135
|
-
return false;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// for mobile we always show details. Mounting of it very heavy operation.
|
|
139
|
-
if (isMobile) {
|
|
140
|
-
return true;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// workaround for tablets we need to mount/unmount in order to avoid PlayerDetails content dissappearing
|
|
144
|
-
// FIXME - find out the reason of dissapearing of content
|
|
145
|
-
if (docked) {
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return true;
|
|
150
|
-
};
|
|
151
|
-
|
|
152
125
|
const PlayerWrapperComponent = (props: Props) => {
|
|
153
126
|
const {
|
|
154
127
|
entry,
|
|
@@ -167,7 +140,15 @@ const PlayerWrapperComponent = (props: Props) => {
|
|
|
167
140
|
|
|
168
141
|
const isInlineModal = inline && isModal;
|
|
169
142
|
|
|
170
|
-
//
|
|
143
|
+
// Mounting the PlayerDetails component is a resource-intensive process.
|
|
144
|
+
// Therefore, for performance reasons, we mount it with a delay to make the rotation process as smooth as possible.
|
|
145
|
+
// The flow is as follows: the rotation occurs first, and then, after a short delay, we mount the PlayerDetails component.
|
|
146
|
+
// This helps to avoid blocking the rotation and any animations related to the rotation.
|
|
147
|
+
const isShowPlayerDetails = useDelayedPlayerDetails({
|
|
148
|
+
isInline: isInlineModal,
|
|
149
|
+
isDocked: docked,
|
|
150
|
+
isPip: pip,
|
|
151
|
+
});
|
|
171
152
|
|
|
172
153
|
const isTabletLandscape = !isTV() && isTablet && !isTabletPortrait;
|
|
173
154
|
|
|
@@ -203,8 +184,8 @@ const PlayerWrapperComponent = (props: Props) => {
|
|
|
203
184
|
const WrapperView = React.useMemo(() => (isTV() ? View : SafeAreaView), []);
|
|
204
185
|
|
|
205
186
|
const childrenStyles = React.useMemo(
|
|
206
|
-
() => ({ ...playerDimensions, ...
|
|
207
|
-
[containerDimensions,
|
|
187
|
+
() => ({ ...playerDimensions, ...playerDimensionsHack }),
|
|
188
|
+
[containerDimensions, playerDimensionsHack]
|
|
208
189
|
);
|
|
209
190
|
|
|
210
191
|
return (
|
|
@@ -213,7 +194,7 @@ const PlayerWrapperComponent = (props: Props) => {
|
|
|
213
194
|
style={[
|
|
214
195
|
safeAreaStyles(configuration, isTablet),
|
|
215
196
|
style,
|
|
216
|
-
|
|
197
|
+
playerDimensionsHack,
|
|
217
198
|
]}
|
|
218
199
|
>
|
|
219
200
|
<AnimationComponent
|
|
@@ -227,7 +208,7 @@ const PlayerWrapperComponent = (props: Props) => {
|
|
|
227
208
|
defaultStyles.playerContainer,
|
|
228
209
|
containerDimensions,
|
|
229
210
|
containerStyle,
|
|
230
|
-
|
|
211
|
+
playerDimensionsHack,
|
|
231
212
|
]}
|
|
232
213
|
>
|
|
233
214
|
<AnimationComponent
|
|
@@ -246,7 +227,7 @@ const PlayerWrapperComponent = (props: Props) => {
|
|
|
246
227
|
</View>
|
|
247
228
|
|
|
248
229
|
<AnimatedScrollModal>
|
|
249
|
-
{
|
|
230
|
+
{isShowPlayerDetails && (
|
|
250
231
|
<AnimationComponent
|
|
251
232
|
animationType={ComponentAnimationType.componentFade}
|
|
252
233
|
style={defaultStyles.flex}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import
|
|
3
|
-
import { StyleSheet,
|
|
2
|
+
import { equals } from "ramda";
|
|
3
|
+
import { StyleSheet, StatusBar } from "react-native";
|
|
4
4
|
|
|
5
5
|
import { HandlePlayable } from "@applicaster/zapp-react-native-ui-components/Components/HandlePlayable";
|
|
6
6
|
import {
|
|
@@ -17,17 +17,20 @@ import {
|
|
|
17
17
|
import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks/device/useIsTablet";
|
|
18
18
|
|
|
19
19
|
import { withModalNavigationContextProvider } from "../../Contexts/ModalNavigationContext";
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
import {
|
|
21
|
+
useModalSize,
|
|
22
|
+
useBackgroundColor,
|
|
23
|
+
useInitialPlayerState,
|
|
24
|
+
} from "./hooks";
|
|
25
|
+
|
|
23
26
|
import { APP_EVENTS } from "@applicaster/zapp-react-native-utils/appUtils/events";
|
|
24
|
-
import { Spinner } from "../Spinner";
|
|
25
27
|
import { PathnameContext } from "@applicaster/zapp-react-native-ui-components/Contexts/PathnameContext";
|
|
26
28
|
import { ROUTE_TYPES } from "@applicaster/zapp-react-native-utils/navigationUtils/routeTypes";
|
|
27
29
|
import { useSubscriberFor } from "@applicaster/zapp-react-native-utils/reactHooks/useSubscriberFor";
|
|
28
30
|
import { requiresAuthentication } from "@applicaster/zapp-react-native-utils/configurationUtils";
|
|
29
31
|
import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
|
|
30
32
|
import { ScreenContextProvider } from "../../Contexts/ScreenContext";
|
|
33
|
+
import { OpaqueLayer } from "./OpaqueLayer";
|
|
31
34
|
|
|
32
35
|
import { AnimatedPlayerModalWrapper } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
33
36
|
|
|
@@ -39,17 +42,6 @@ const styles = StyleSheet.create({
|
|
|
39
42
|
bottom: 0,
|
|
40
43
|
position: "absolute",
|
|
41
44
|
},
|
|
42
|
-
// eslint-disable-next-line react-native/no-color-literals
|
|
43
|
-
loaderContainer: {
|
|
44
|
-
top: 0,
|
|
45
|
-
left: 0,
|
|
46
|
-
right: 0,
|
|
47
|
-
bottom: 0,
|
|
48
|
-
position: "absolute",
|
|
49
|
-
alignItems: "center",
|
|
50
|
-
justifyContent: "center",
|
|
51
|
-
backgroundColor: "rgba(64,64,64,.5)",
|
|
52
|
-
},
|
|
53
45
|
});
|
|
54
46
|
|
|
55
47
|
const VideoModalComponent = () => {
|
|
@@ -100,7 +92,7 @@ const VideoModalComponent = () => {
|
|
|
100
92
|
|
|
101
93
|
const onHooksSuccess = ({ payload }) => {
|
|
102
94
|
// set new modified entry
|
|
103
|
-
if (!
|
|
95
|
+
if (!equals(payload, item)) {
|
|
104
96
|
onVideoModalHookSuccessAnalyticsEvent();
|
|
105
97
|
setVideoModalItem(payload);
|
|
106
98
|
}
|
|
@@ -136,7 +128,10 @@ const VideoModalComponent = () => {
|
|
|
136
128
|
|
|
137
129
|
<PathnameContext.Provider value={pathname}>
|
|
138
130
|
<ScreenContextProvider pathname={pathname}>
|
|
139
|
-
{
|
|
131
|
+
{/* Hide content underneath when we switch to next video in fullscreen mode */}
|
|
132
|
+
{mode === "FULLSCREEN" && <OpaqueLayer />}
|
|
133
|
+
|
|
134
|
+
{itemIdHooksFinished === item?.id && (
|
|
140
135
|
<AnimatedPlayerModalWrapper
|
|
141
136
|
style={[
|
|
142
137
|
styles.container,
|
|
@@ -153,10 +148,6 @@ const VideoModalComponent = () => {
|
|
|
153
148
|
mode={mode}
|
|
154
149
|
/>
|
|
155
150
|
</AnimatedPlayerModalWrapper>
|
|
156
|
-
) : (
|
|
157
|
-
<View style={styles.loaderContainer}>
|
|
158
|
-
<Spinner />
|
|
159
|
-
</View>
|
|
160
151
|
)}
|
|
161
152
|
</ScreenContextProvider>
|
|
162
153
|
</PathnameContext.Provider>
|
|
@@ -62,7 +62,7 @@ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
|
|
|
62
62
|
})),
|
|
63
63
|
}));
|
|
64
64
|
|
|
65
|
-
jest.mock("../utils", () => ({
|
|
65
|
+
jest.mock("../utils", () => ({ playerDimensionsHack: {} }));
|
|
66
66
|
|
|
67
67
|
const { PlayerWrapper } = require("../PlayerWrapper");
|
|
68
68
|
|
|
@@ -71,36 +71,6 @@ exports[`PlayerWrapper renders inline 1`] = `
|
|
|
71
71
|
}
|
|
72
72
|
testID="test-player-container"
|
|
73
73
|
/>
|
|
74
|
-
<View
|
|
75
|
-
animationType="componentFade"
|
|
76
|
-
style={
|
|
77
|
-
{
|
|
78
|
-
"flex": 1,
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
>
|
|
82
|
-
<View
|
|
83
|
-
configuration={
|
|
84
|
-
{
|
|
85
|
-
"tablet_landscape_player_container_background_color": "red",
|
|
86
|
-
"tablet_landscape_sidebar_width": "35%",
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
entry={
|
|
90
|
-
{
|
|
91
|
-
"id": "test",
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
isTablet={false}
|
|
95
|
-
isTabletLandscape={false}
|
|
96
|
-
style={
|
|
97
|
-
{
|
|
98
|
-
"flex": 1,
|
|
99
|
-
"paddingTop": 20,
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
/>
|
|
103
|
-
</View>
|
|
104
74
|
</View>
|
|
105
75
|
</RNCSafeAreaView>
|
|
106
76
|
</RNCSafeAreaProvider>
|
|
@@ -177,36 +147,6 @@ exports[`PlayerWrapper renders inline and docked 1`] = `
|
|
|
177
147
|
}
|
|
178
148
|
testID="test-player-container"
|
|
179
149
|
/>
|
|
180
|
-
<View
|
|
181
|
-
animationType="componentFade"
|
|
182
|
-
style={
|
|
183
|
-
{
|
|
184
|
-
"flex": 1,
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
>
|
|
188
|
-
<View
|
|
189
|
-
configuration={
|
|
190
|
-
{
|
|
191
|
-
"tablet_landscape_player_container_background_color": "red",
|
|
192
|
-
"tablet_landscape_sidebar_width": "35%",
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
entry={
|
|
196
|
-
{
|
|
197
|
-
"id": "test",
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
isTablet={false}
|
|
201
|
-
isTabletLandscape={false}
|
|
202
|
-
style={
|
|
203
|
-
{
|
|
204
|
-
"flex": 1,
|
|
205
|
-
"paddingTop": 20,
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
/>
|
|
209
|
-
</View>
|
|
210
150
|
</View>
|
|
211
151
|
</RNCSafeAreaView>
|
|
212
152
|
</RNCSafeAreaProvider>
|
|
@@ -299,36 +239,6 @@ exports[`PlayerWrapper renders inline on tablet in landscape orientation 1`] = `
|
|
|
299
239
|
}
|
|
300
240
|
/>
|
|
301
241
|
</View>
|
|
302
|
-
<View
|
|
303
|
-
animationType="componentFade"
|
|
304
|
-
style={
|
|
305
|
-
{
|
|
306
|
-
"flex": 1,
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
>
|
|
310
|
-
<View
|
|
311
|
-
configuration={
|
|
312
|
-
{
|
|
313
|
-
"tablet_landscape_player_container_background_color": "red",
|
|
314
|
-
"tablet_landscape_sidebar_width": "35%",
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
entry={
|
|
318
|
-
{
|
|
319
|
-
"id": "test",
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
isTablet={true}
|
|
323
|
-
isTabletLandscape={true}
|
|
324
|
-
style={
|
|
325
|
-
{
|
|
326
|
-
"flex": 1,
|
|
327
|
-
"paddingTop": 20,
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
/>
|
|
331
|
-
</View>
|
|
332
242
|
</View>
|
|
333
243
|
</RNCSafeAreaView>
|
|
334
244
|
</RNCSafeAreaProvider>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react-hooks";
|
|
2
|
+
import { useDelayedPlayerDetails } from "../useDelayedPlayerDetails";
|
|
3
|
+
import { withTimeout$ } from "@applicaster/zapp-react-native-utils/idleUtils";
|
|
4
|
+
import { showDetails } from "../utils";
|
|
5
|
+
import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
6
|
+
|
|
7
|
+
jest.mock("@applicaster/zapp-react-native-utils/idleUtils", () => ({
|
|
8
|
+
withTimeout$: jest.fn(),
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
jest.mock("../utils", () => ({
|
|
12
|
+
showDetails: jest.fn(),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
jest.mock("@applicaster/zapp-react-native-utils/reactHooks", () => ({
|
|
16
|
+
useIsTablet: jest.fn(),
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
describe("useDelayedPlayerDetails", () => {
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
jest.clearAllMocks();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should return false initially", () => {
|
|
25
|
+
(withTimeout$ as jest.Mock).mockReturnValue({
|
|
26
|
+
subscribe: jest.fn().mockReturnValue({ unsubscribe: jest.fn() }),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const { result } = renderHook(() =>
|
|
30
|
+
useDelayedPlayerDetails({ isInline: true, isDocked: false, isPip: false })
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
expect(result.current).toBe(false);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("should return true if showDetails returns true", () => {
|
|
37
|
+
(withTimeout$ as jest.Mock).mockReturnValue({
|
|
38
|
+
subscribe: (callback) => {
|
|
39
|
+
callback.next();
|
|
40
|
+
|
|
41
|
+
return { unsubscribe: jest.fn() };
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
(showDetails as jest.Mock).mockReturnValue(true);
|
|
46
|
+
(useIsTablet as jest.Mock).mockReturnValue(false);
|
|
47
|
+
|
|
48
|
+
const { result } = renderHook(() =>
|
|
49
|
+
useDelayedPlayerDetails({ isInline: true, isDocked: false, isPip: false })
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
expect(result.current).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("should return false if showDetails returns false", () => {
|
|
56
|
+
(withTimeout$ as jest.Mock).mockReturnValue({
|
|
57
|
+
subscribe: (callback) => {
|
|
58
|
+
callback.next();
|
|
59
|
+
|
|
60
|
+
return { unsubscribe: jest.fn() };
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
(showDetails as jest.Mock).mockReturnValue(false);
|
|
65
|
+
(useIsTablet as jest.Mock).mockReturnValue(false);
|
|
66
|
+
|
|
67
|
+
const { result } = renderHook(() =>
|
|
68
|
+
useDelayedPlayerDetails({ isInline: true, isDocked: false, isPip: false })
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
expect(result.current).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("should unsubscribe on unmount", () => {
|
|
75
|
+
const unsubscribeMock = jest.fn();
|
|
76
|
+
|
|
77
|
+
(withTimeout$ as jest.Mock).mockReturnValue({
|
|
78
|
+
subscribe: jest.fn().mockReturnValue({ unsubscribe: unsubscribeMock }),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const { unmount } = renderHook(() =>
|
|
82
|
+
useDelayedPlayerDetails({ isInline: true, isDocked: false, isPip: false })
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
unmount();
|
|
86
|
+
|
|
87
|
+
expect(unsubscribeMock).toHaveBeenCalled();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
3
|
+
import { withTimeout$ } from "@applicaster/zapp-react-native-utils/idleUtils";
|
|
4
|
+
|
|
5
|
+
import { showDetails } from "./utils";
|
|
6
|
+
|
|
7
|
+
const TIMEOUT = 100; // ms
|
|
8
|
+
|
|
9
|
+
type Props = { isInline: boolean; isDocked: boolean; isPip: boolean };
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Custom hook to determine whether to show player details with a delay.
|
|
13
|
+
*
|
|
14
|
+
* @param {Object} params - The parameters object.
|
|
15
|
+
* @param {boolean} params.isInline - Indicates if the player is inline.
|
|
16
|
+
* @param {boolean} params.isDocked - Indicates if the player is docked.
|
|
17
|
+
* @param {boolean} params.isPip - Indicates if the player is in PIP mode.
|
|
18
|
+
* @returns {boolean} - Returns true if player details should be shown, otherwise false.
|
|
19
|
+
*/
|
|
20
|
+
export const useDelayedPlayerDetails = ({
|
|
21
|
+
isInline,
|
|
22
|
+
isDocked,
|
|
23
|
+
isPip,
|
|
24
|
+
}: Props): boolean => {
|
|
25
|
+
const [shouldShowDetails, setShouldShowDetails] = React.useState(false);
|
|
26
|
+
|
|
27
|
+
const isTablet = useIsTablet();
|
|
28
|
+
|
|
29
|
+
React.useEffect(() => {
|
|
30
|
+
const subscription = withTimeout$(TIMEOUT).subscribe({
|
|
31
|
+
next: () => {
|
|
32
|
+
setShouldShowDetails(() => {
|
|
33
|
+
return showDetails({
|
|
34
|
+
isMobile: !isTablet,
|
|
35
|
+
isInline,
|
|
36
|
+
isDocked,
|
|
37
|
+
isPip,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return () => {
|
|
44
|
+
subscription.unsubscribe();
|
|
45
|
+
};
|
|
46
|
+
}, [isDocked, isTablet, isInline, isPip]);
|
|
47
|
+
|
|
48
|
+
return shouldShowDetails;
|
|
49
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { showDetails } from "..";
|
|
2
|
+
|
|
3
|
+
describe("showDetails", () => {
|
|
4
|
+
it("should return false if isPip is true", () => {
|
|
5
|
+
const result = showDetails({
|
|
6
|
+
isMobile: true,
|
|
7
|
+
isInline: true,
|
|
8
|
+
isDocked: false,
|
|
9
|
+
isPip: true,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
expect(result).toBe(false);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("should return false if isDocked is true", () => {
|
|
16
|
+
const result = showDetails({
|
|
17
|
+
isMobile: true,
|
|
18
|
+
isInline: true,
|
|
19
|
+
isDocked: true,
|
|
20
|
+
isPip: false,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
expect(result).toBe(false);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should return true if isMobile is true and isInline is true", () => {
|
|
27
|
+
const result = showDetails({
|
|
28
|
+
isMobile: true,
|
|
29
|
+
isInline: true,
|
|
30
|
+
isDocked: false,
|
|
31
|
+
isPip: false,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
expect(result).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should return false if isMobile is true and isInline is false", () => {
|
|
38
|
+
const result = showDetails({
|
|
39
|
+
isMobile: true,
|
|
40
|
+
isInline: false,
|
|
41
|
+
isDocked: false,
|
|
42
|
+
isPip: false,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
expect(result).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should return true if isMobile is false", () => {
|
|
49
|
+
const result = showDetails({
|
|
50
|
+
isMobile: false,
|
|
51
|
+
isInline: true,
|
|
52
|
+
isDocked: false,
|
|
53
|
+
isPip: false,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
expect(result).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should return true if isMobile is false and isInline is false", () => {
|
|
60
|
+
const result = showDetails({
|
|
61
|
+
isMobile: false,
|
|
62
|
+
isInline: false,
|
|
63
|
+
isDocked: false,
|
|
64
|
+
isPip: false,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
expect(result).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should return false if all properties are false except isMobile", () => {
|
|
71
|
+
const result = showDetails({
|
|
72
|
+
isMobile: true,
|
|
73
|
+
isInline: false,
|
|
74
|
+
isDocked: false,
|
|
75
|
+
isPip: false,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
expect(result).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("should return true if all properties are false except isInline", () => {
|
|
82
|
+
const result = showDetails({
|
|
83
|
+
isMobile: false,
|
|
84
|
+
isInline: true,
|
|
85
|
+
isDocked: false,
|
|
86
|
+
isPip: false,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
expect(result).toBe(true);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -35,3 +35,36 @@ export const orientationWasChangedFromPortraitToLandscape = ({
|
|
|
35
35
|
isOrientationLandscape(toOrientation)
|
|
36
36
|
);
|
|
37
37
|
};
|
|
38
|
+
|
|
39
|
+
export const showDetails = ({
|
|
40
|
+
isMobile,
|
|
41
|
+
isInline,
|
|
42
|
+
isDocked,
|
|
43
|
+
isPip,
|
|
44
|
+
}: {
|
|
45
|
+
isMobile: boolean;
|
|
46
|
+
isInline: boolean;
|
|
47
|
+
isDocked: boolean;
|
|
48
|
+
isPip: boolean;
|
|
49
|
+
}): boolean => {
|
|
50
|
+
if (isPip) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (isDocked) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// for mobile with inline mode(rotation is portrait) we always show details.
|
|
59
|
+
if (isMobile && isInline) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// for mobile with landscape rotation(player is fullscreen) we always hide details. Mounting of it is very heavy operation.
|
|
64
|
+
if (isMobile && !isInline) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// for tablets we always show details
|
|
69
|
+
return true;
|
|
70
|
+
};
|
|
@@ -1,10 +1,12 @@
|
|
|
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
|
import * as R from "ramda";
|
|
5
7
|
import { Platform } from "react-native";
|
|
6
8
|
import Url from "url";
|
|
7
|
-
|
|
9
|
+
import { ENDPOINT_TAGS } from "@applicaster/zapp-react-native-utils/types";
|
|
8
10
|
import { favoritesListener } from "@applicaster/zapp-react-native-bridge/Favorites";
|
|
9
11
|
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
10
12
|
import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
|
|
@@ -19,6 +21,12 @@ import { ZappPipesSearchContext } from "@applicaster/zapp-react-native-ui-compon
|
|
|
19
21
|
import { useScreenContext } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
|
|
20
22
|
|
|
21
23
|
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
30
|
|
|
23
31
|
type Props = {
|
|
24
32
|
component: ZappUIComponent;
|
|
@@ -291,6 +299,28 @@ export function zappPipesDataConnector(
|
|
|
291
299
|
if (addListener) {
|
|
292
300
|
return addListener(reloadData);
|
|
293
301
|
}
|
|
302
|
+
} 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
|
+
}
|
|
294
324
|
}
|
|
295
325
|
}, [dataSourceUrl, reloadData]);
|
|
296
326
|
|
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.
|
|
3
|
+
"version": "13.0.0-alpha.7192404241",
|
|
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",
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"redux-mock-store": "^1.5.3"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@applicaster/applicaster-types": "13.0.0-alpha.
|
|
38
|
-
"@applicaster/zapp-react-native-bridge": "13.0.0-alpha.
|
|
39
|
-
"@applicaster/zapp-react-native-redux": "13.0.0-alpha.
|
|
40
|
-
"@applicaster/zapp-react-native-utils": "13.0.0-alpha.
|
|
37
|
+
"@applicaster/applicaster-types": "13.0.0-alpha.7192404241",
|
|
38
|
+
"@applicaster/zapp-react-native-bridge": "13.0.0-alpha.7192404241",
|
|
39
|
+
"@applicaster/zapp-react-native-redux": "13.0.0-alpha.7192404241",
|
|
40
|
+
"@applicaster/zapp-react-native-utils": "13.0.0-alpha.7192404241",
|
|
41
41
|
"promise": "^8.3.0",
|
|
42
42
|
"react-router-native": "^5.1.2",
|
|
43
43
|
"url": "^0.11.0",
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
2
|
-
import React, { PropsWithChildren } from "react";
|
|
3
|
-
|
|
4
|
-
export const ComponentsMapOffsetContext = React.createContext({
|
|
5
|
-
setComponentsMapOffset: (_offset: number) => void 0,
|
|
6
|
-
componentsMapOffset: 0,
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
export const ComponentsMapOffsetContextProvider = ({
|
|
10
|
-
children,
|
|
11
|
-
}: PropsWithChildren<{}>) => {
|
|
12
|
-
const theme = useTheme<BaseThemePropertiesTV>();
|
|
13
|
-
|
|
14
|
-
const [componentsMapOffset, setComponentsMapOffset] = React.useState(
|
|
15
|
-
theme?.screen_margin_top
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
const contextValue = {
|
|
19
|
-
componentsMapOffset,
|
|
20
|
-
setComponentsMapOffset,
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
return (
|
|
24
|
-
<ComponentsMapOffsetContext.Provider value={contextValue}>
|
|
25
|
-
{children}
|
|
26
|
-
</ComponentsMapOffsetContext.Provider>
|
|
27
|
-
);
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export const withComponentsMapOffsetContext = (Component: any) =>
|
|
31
|
-
function ComponentsMapOffsetContextProviderWrapper(props: {}) {
|
|
32
|
-
return (
|
|
33
|
-
<ComponentsMapOffsetContextProvider>
|
|
34
|
-
<Component {...props} />
|
|
35
|
-
</ComponentsMapOffsetContextProvider>
|
|
36
|
-
);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export const withComponentsMapOffsetContextConsumer = (Component: any) =>
|
|
40
|
-
function ComponentsMapOffsetContextConsumerWrapper(props: {}) {
|
|
41
|
-
const { componentsMapOffset } = React.useContext(
|
|
42
|
-
ComponentsMapOffsetContext
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
return <Component {...props} componentsMapOffset={componentsMapOffset} />;
|
|
46
|
-
};
|