@applicaster/zapp-react-native-ui-components 13.0.0-alpha.3528549047 → 13.0.0-alpha.3592913150
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 +1 -0
- package/Components/Cell/TvOSCellComponent.tsx +20 -22
- package/Components/Cell/index.js +3 -1
- package/Components/Focusable/FocusableTvOS.tsx +5 -1
- package/Components/FocusableList/index.tsx +25 -9
- package/Components/FocusableScrollView/index.tsx +39 -12
- package/Components/Layout/TV/NavBarContainer.tsx +8 -7
- package/Components/River/TV/index.tsx +6 -1
- package/Components/River/__tests__/componentsMap.test.js +1 -14
- package/Components/VideoModal/PlayerWrapper.tsx +35 -16
- package/Components/VideoModal/VideoModal.tsx +23 -14
- package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -1
- package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +90 -0
- package/Components/VideoModal/hooks/utils/index.ts +0 -33
- package/Components/VideoModal/utils.ts +1 -1
- package/Contexts/ComponentsMapOffsetContext/index.tsx +46 -0
- package/Decorators/ZappPipesDataConnector/index.tsx +1 -31
- package/package.json +5 -5
- package/Components/VideoModal/OpaqueLayer.tsx +0 -33
- package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +0 -89
- package/Components/VideoModal/hooks/index.ts +0 -7
- package/Components/VideoModal/hooks/useDelayedPlayerDetails.ts +0 -49
- package/Components/VideoModal/hooks/utils/__tests__/showDetails.test.ts +0 -91
package/Components/Cell/Cell.tsx
CHANGED
|
@@ -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/FocusableTvOS";
|
|
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,6 +65,7 @@ type Props = {
|
|
|
65
65
|
isFocusable: boolean;
|
|
66
66
|
shouldUpdate: boolean;
|
|
67
67
|
behavior: Behavior;
|
|
68
|
+
componentsMapOffset: number;
|
|
68
69
|
};
|
|
69
70
|
|
|
70
71
|
type State = {
|
|
@@ -179,6 +180,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
179
180
|
groupId,
|
|
180
181
|
component,
|
|
181
182
|
index,
|
|
183
|
+
componentsMapOffset,
|
|
182
184
|
} = this.props;
|
|
183
185
|
|
|
184
186
|
this.setScreenLayout(componentAnchorPointY, screenLayout);
|
|
@@ -193,13 +195,11 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
193
195
|
const extraAnchorPointYOffset =
|
|
194
196
|
screenLayout?.extraAnchorPointYOffset || 0;
|
|
195
197
|
|
|
196
|
-
const marginTop = screenLayout?.screenMarginTop || 0;
|
|
197
|
-
|
|
198
198
|
const totalOffset =
|
|
199
199
|
headerOffset +
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
200
|
+
(componentAnchorPointY || 0) +
|
|
201
|
+
extraAnchorPointYOffset -
|
|
202
|
+
componentsMapOffset || 0;
|
|
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={groupId || component?.id}
|
|
283
|
+
groupId={String(groupId || component?.id)}
|
|
284
284
|
onFocus={handleFocus}
|
|
285
285
|
onBlur={onBlur || this.onBlur}
|
|
286
286
|
isParallaxDisabled={this.layout?.width > 1740}
|
|
@@ -292,20 +292,18 @@ 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
|
-
|
|
307
|
-
);
|
|
308
|
-
}}
|
|
295
|
+
{(focused) => (
|
|
296
|
+
<FocusableCell
|
|
297
|
+
{...{
|
|
298
|
+
index,
|
|
299
|
+
CellRenderer,
|
|
300
|
+
item,
|
|
301
|
+
focused: !!(this.props.focused || selectedCell || focused),
|
|
302
|
+
scrollTo: this.scrollTo,
|
|
303
|
+
behavior,
|
|
304
|
+
}}
|
|
305
|
+
/>
|
|
306
|
+
)}
|
|
309
307
|
</Focusable>
|
|
310
308
|
</View>
|
|
311
309
|
);
|
package/Components/Cell/index.js
CHANGED
|
@@ -11,6 +11,7 @@ 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";
|
|
14
15
|
|
|
15
16
|
// TODO | Gallery QB | Extract this
|
|
16
17
|
export const ScrollInterceptorContext = createContext(
|
|
@@ -30,5 +31,6 @@ export const Cell = R.compose(
|
|
|
30
31
|
RiverOffsetContext.withConsumer,
|
|
31
32
|
HorizontalScrollContext.withConsumer,
|
|
32
33
|
withConsumer,
|
|
33
|
-
ScreenLayoutContextConsumer
|
|
34
|
+
ScreenLayoutContextConsumer,
|
|
35
|
+
withComponentsMapOffsetContextConsumer
|
|
34
36
|
)(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 } from "react-native";
|
|
12
|
+
import { findNodeHandle, ViewStyle } from "react-native";
|
|
13
13
|
|
|
14
14
|
function noop() {}
|
|
15
15
|
|
|
@@ -35,6 +35,10 @@ 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;
|
|
38
42
|
};
|
|
39
43
|
|
|
40
44
|
export class Focusable extends BaseFocusable<Props> {
|
|
@@ -15,6 +15,7 @@ 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";
|
|
18
19
|
|
|
19
20
|
const mapIndexed = R.addIndex(R.map);
|
|
20
21
|
|
|
@@ -54,6 +55,7 @@ export type Props<ItemT> = FlatListProps<ItemT> & {
|
|
|
54
55
|
disableNextFocusUp?: boolean;
|
|
55
56
|
disableNextFocusLeft?: boolean;
|
|
56
57
|
disableNextFocusRight?: boolean;
|
|
58
|
+
useScrollView?: boolean;
|
|
57
59
|
} & ParentFocus;
|
|
58
60
|
|
|
59
61
|
function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
|
|
@@ -88,6 +90,7 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
|
|
|
88
90
|
data = [],
|
|
89
91
|
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
90
92
|
omitPropsPropagation = [],
|
|
93
|
+
useScrollView = false,
|
|
91
94
|
} = props;
|
|
92
95
|
|
|
93
96
|
useCheckItemIdsForUnique({ componentId: props.id, items: data });
|
|
@@ -273,6 +276,7 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
|
|
|
273
276
|
"initialFocusDirection",
|
|
274
277
|
"withStateMemory",
|
|
275
278
|
"useSequentialLoading",
|
|
279
|
+
"useScrollView",
|
|
276
280
|
...omitPropsPropagation,
|
|
277
281
|
],
|
|
278
282
|
R.__
|
|
@@ -295,15 +299,27 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
|
|
|
295
299
|
return (
|
|
296
300
|
// @ts-ignore
|
|
297
301
|
<ChildrenFocusDeactivatorView flex={1}>
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
302
|
+
{useScrollView ? (
|
|
303
|
+
<FocusableScrollView
|
|
304
|
+
ref={ref}
|
|
305
|
+
{...getFlatListProps(props)}
|
|
306
|
+
onEndReached={onEndReached}
|
|
307
|
+
initialNumToRender={initialNumToRender}
|
|
308
|
+
renderItem={renderItem}
|
|
309
|
+
focused={focused}
|
|
310
|
+
data={data}
|
|
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
|
+
)}
|
|
307
323
|
</ChildrenFocusDeactivatorView>
|
|
308
324
|
);
|
|
309
325
|
}
|
|
@@ -11,6 +11,13 @@ 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
|
+
|
|
14
21
|
export type Props = ScrollViewProps & {
|
|
15
22
|
id?: number | string;
|
|
16
23
|
horizontal?: boolean;
|
|
@@ -32,17 +39,27 @@ export type Props = ScrollViewProps & {
|
|
|
32
39
|
initialFocusDirection?: FocusManager.Android.FocusNavigationDirections;
|
|
33
40
|
onAllComponentsLoaded?: () => void;
|
|
34
41
|
omitPropsPropagation?: Array<keyof FlatListProps<any>>;
|
|
35
|
-
renderItem:
|
|
36
|
-
item: ZappUIComponent;
|
|
37
|
-
index: number;
|
|
38
|
-
focused: boolean;
|
|
39
|
-
parentFocus: ParentFocus;
|
|
40
|
-
}) => React.ReactElement;
|
|
42
|
+
renderItem: FocusableScrollViewRenderItem;
|
|
41
43
|
} & ParentFocus;
|
|
42
44
|
|
|
43
45
|
type State = boolean[];
|
|
44
46
|
type Action = { index?: number; isFocusable: boolean; type?: string };
|
|
45
47
|
|
|
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
|
+
|
|
46
63
|
const focusableStateReducer = (
|
|
47
64
|
state: State,
|
|
48
65
|
{ index = null, isFocusable, type = null }: Action
|
|
@@ -73,7 +90,7 @@ const extendScrollViewRef = (_ref, childCompsMeasurementsMap) => {
|
|
|
73
90
|
};
|
|
74
91
|
|
|
75
92
|
_ref.scrollToIndex = (params) => {
|
|
76
|
-
const { index, viewOffset } = params;
|
|
93
|
+
const { index, viewOffset = 0 } = params;
|
|
77
94
|
|
|
78
95
|
const itemByIndex = R.find(R.propEq("index", index))(
|
|
79
96
|
R.values(childCompsMeasurementsMap)
|
|
@@ -129,8 +146,11 @@ function FocusableScrollViewComponent(props: Props, ref) {
|
|
|
129
146
|
);
|
|
130
147
|
|
|
131
148
|
const getFocusableEntryId = React.useCallback(
|
|
132
|
-
(_entry: ZappEntry, index?: number) => {
|
|
133
|
-
const entry
|
|
149
|
+
(_entry: ZappEntry | null, index?: number) => {
|
|
150
|
+
const entry: { id?: string } = getEntry(
|
|
151
|
+
R.isNil(index) ? _entry : data,
|
|
152
|
+
index
|
|
153
|
+
);
|
|
134
154
|
|
|
135
155
|
return entry ? `${props.id}--${entry?.id}` : undefined;
|
|
136
156
|
},
|
|
@@ -273,11 +293,18 @@ function FocusableScrollViewComponent(props: Props, ref) {
|
|
|
273
293
|
const renderItem = React.useCallback(
|
|
274
294
|
(item, index) => {
|
|
275
295
|
const renderArgs = { item, index };
|
|
276
|
-
|
|
277
|
-
const
|
|
296
|
+
|
|
297
|
+
const id = getFocusableEntryId(
|
|
298
|
+
Array.isArray(item) ? item[0] : item,
|
|
299
|
+
index
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
const nextFocus = getNextFocus(index);
|
|
278
303
|
|
|
279
304
|
const onItemLayout = (event) => {
|
|
280
|
-
childCompsMeasurementsMap.current[
|
|
305
|
+
childCompsMeasurementsMap.current[
|
|
306
|
+
Array.isArray(item) ? index : item.id
|
|
307
|
+
] = {
|
|
281
308
|
index,
|
|
282
309
|
layout: event.nativeEvent.layout,
|
|
283
310
|
};
|
|
@@ -1,7 +1,5 @@
|
|
|
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";
|
|
5
3
|
import { useCurrentScreenData } from "@applicaster/zapp-react-native-utils/reactHooks/screen";
|
|
6
4
|
import { useScreenConfiguration } from "../../River/useScreenConfiguration";
|
|
7
5
|
|
|
@@ -12,12 +10,15 @@ type Props = {
|
|
|
12
10
|
};
|
|
13
11
|
|
|
14
12
|
const styles = StyleSheet.create({
|
|
15
|
-
container: {
|
|
13
|
+
container: {
|
|
14
|
+
position: "absolute",
|
|
15
|
+
top: 0,
|
|
16
|
+
zIndex: 10,
|
|
17
|
+
width: 1920,
|
|
18
|
+
},
|
|
16
19
|
});
|
|
17
20
|
|
|
18
21
|
export const NavBarContainer = ({ children, isVisible, onReady }: Props) => {
|
|
19
|
-
const screenLayout = React.useContext(ScreenLayoutContext);
|
|
20
|
-
const screenMarginTop = screenLayout?.screenMarginTop || 0;
|
|
21
22
|
const screen = useCurrentScreenData();
|
|
22
23
|
const screenConfig = useScreenConfiguration(screen?.id);
|
|
23
24
|
|
|
@@ -26,9 +27,9 @@ export const NavBarContainer = ({ children, isVisible, onReady }: Props) => {
|
|
|
26
27
|
// limits the height of the focusable container of the TopMenuBarTV component
|
|
27
28
|
// to prevent it from being overlapped by the screen content,
|
|
28
29
|
// as it makes TopMenuBarTV unfocusable on tvOS
|
|
29
|
-
maxHeight:
|
|
30
|
+
maxHeight: 1,
|
|
30
31
|
}),
|
|
31
|
-
[
|
|
32
|
+
[screenConfig]
|
|
32
33
|
);
|
|
33
34
|
|
|
34
35
|
React.useEffect(() => {
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
import { compose } from "ramda";
|
|
1
2
|
import { River as RiverComponent } from "./River";
|
|
2
3
|
import { withTvEventHandler } from "./withTVEventHandler";
|
|
4
|
+
import { withComponentsMapOffsetContext } from "../../../Contexts/ComponentsMapOffsetContext";
|
|
3
5
|
|
|
4
|
-
export const River =
|
|
6
|
+
export const River = compose(
|
|
7
|
+
withTvEventHandler,
|
|
8
|
+
withComponentsMapOffsetContext
|
|
9
|
+
)(RiverComponent);
|
|
@@ -81,13 +81,6 @@ 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
|
-
|
|
91
84
|
jest.mock("@applicaster/zapp-react-native-utils/localizationUtils", () => ({
|
|
92
85
|
useIsRTL: jest.fn(() => mock_rtl_flag),
|
|
93
86
|
}));
|
|
@@ -160,13 +153,7 @@ const plugins = [];
|
|
|
160
153
|
const navigation = {};
|
|
161
154
|
|
|
162
155
|
const props = { components, cellStyles, riverComponents, navigation };
|
|
163
|
-
|
|
164
|
-
const store = mockStore({
|
|
165
|
-
components,
|
|
166
|
-
cellStyles,
|
|
167
|
-
plugins,
|
|
168
|
-
getState: jest.fn(),
|
|
169
|
-
});
|
|
156
|
+
const store = mockStore({ components, cellStyles, plugins });
|
|
170
157
|
|
|
171
158
|
jest.useFakeTimers();
|
|
172
159
|
|
|
@@ -10,8 +10,7 @@ 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 {
|
|
14
|
-
import { useDelayedPlayerDetails } from "./hooks";
|
|
13
|
+
import { playerDimesionsHack } from "./utils";
|
|
15
14
|
|
|
16
15
|
import {
|
|
17
16
|
AnimatedScrollModal,
|
|
@@ -122,6 +121,34 @@ const getTabletWidth = (
|
|
|
122
121
|
return Number(width) - sidebarWidth;
|
|
123
122
|
};
|
|
124
123
|
|
|
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
|
+
|
|
125
152
|
const PlayerWrapperComponent = (props: Props) => {
|
|
126
153
|
const {
|
|
127
154
|
entry,
|
|
@@ -140,15 +167,7 @@ const PlayerWrapperComponent = (props: Props) => {
|
|
|
140
167
|
|
|
141
168
|
const isInlineModal = inline && isModal;
|
|
142
169
|
|
|
143
|
-
//
|
|
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
|
-
});
|
|
170
|
+
// const style = getStyles(baseStyle, !isTablet, isInlineModal, pip);
|
|
152
171
|
|
|
153
172
|
const isTabletLandscape = !isTV() && isTablet && !isTabletPortrait;
|
|
154
173
|
|
|
@@ -184,8 +203,8 @@ const PlayerWrapperComponent = (props: Props) => {
|
|
|
184
203
|
const WrapperView = React.useMemo(() => (isTV() ? View : SafeAreaView), []);
|
|
185
204
|
|
|
186
205
|
const childrenStyles = React.useMemo(
|
|
187
|
-
() => ({ ...playerDimensions, ...
|
|
188
|
-
[containerDimensions,
|
|
206
|
+
() => ({ ...playerDimensions, ...playerDimesionsHack }),
|
|
207
|
+
[containerDimensions, playerDimesionsHack]
|
|
189
208
|
);
|
|
190
209
|
|
|
191
210
|
return (
|
|
@@ -194,7 +213,7 @@ const PlayerWrapperComponent = (props: Props) => {
|
|
|
194
213
|
style={[
|
|
195
214
|
safeAreaStyles(configuration, isTablet),
|
|
196
215
|
style,
|
|
197
|
-
|
|
216
|
+
playerDimesionsHack,
|
|
198
217
|
]}
|
|
199
218
|
>
|
|
200
219
|
<AnimationComponent
|
|
@@ -208,7 +227,7 @@ const PlayerWrapperComponent = (props: Props) => {
|
|
|
208
227
|
defaultStyles.playerContainer,
|
|
209
228
|
containerDimensions,
|
|
210
229
|
containerStyle,
|
|
211
|
-
|
|
230
|
+
playerDimesionsHack,
|
|
212
231
|
]}
|
|
213
232
|
>
|
|
214
233
|
<AnimationComponent
|
|
@@ -227,7 +246,7 @@ const PlayerWrapperComponent = (props: Props) => {
|
|
|
227
246
|
</View>
|
|
228
247
|
|
|
229
248
|
<AnimatedScrollModal>
|
|
230
|
-
{
|
|
249
|
+
{showDetails(!isTablet, docked, isInlineModal, pip) && (
|
|
231
250
|
<AnimationComponent
|
|
232
251
|
animationType={ComponentAnimationType.componentFade}
|
|
233
252
|
style={defaultStyles.flex}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import
|
|
3
|
-
import { StyleSheet, StatusBar } from "react-native";
|
|
2
|
+
import * as R from "ramda";
|
|
3
|
+
import { StyleSheet, View, StatusBar } from "react-native";
|
|
4
4
|
|
|
5
5
|
import { HandlePlayable } from "@applicaster/zapp-react-native-ui-components/Components/HandlePlayable";
|
|
6
6
|
import {
|
|
@@ -17,20 +17,17 @@ 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
|
-
|
|
23
|
-
useInitialPlayerState,
|
|
24
|
-
} from "./hooks";
|
|
25
|
-
|
|
20
|
+
import { useModalSize } from "./hooks/useModalSize";
|
|
21
|
+
import { useBackgroundColor } from "./hooks/useBackgroundColor";
|
|
22
|
+
import { useInitialPlayerState } from "./hooks/useInitialPlayerState";
|
|
26
23
|
import { APP_EVENTS } from "@applicaster/zapp-react-native-utils/appUtils/events";
|
|
24
|
+
import { Spinner } from "../Spinner";
|
|
27
25
|
import { PathnameContext } from "@applicaster/zapp-react-native-ui-components/Contexts/PathnameContext";
|
|
28
26
|
import { ROUTE_TYPES } from "@applicaster/zapp-react-native-utils/navigationUtils/routeTypes";
|
|
29
27
|
import { useSubscriberFor } from "@applicaster/zapp-react-native-utils/reactHooks/useSubscriberFor";
|
|
30
28
|
import { requiresAuthentication } from "@applicaster/zapp-react-native-utils/configurationUtils";
|
|
31
29
|
import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
|
|
32
30
|
import { ScreenContextProvider } from "../../Contexts/ScreenContext";
|
|
33
|
-
import { OpaqueLayer } from "./OpaqueLayer";
|
|
34
31
|
|
|
35
32
|
import { AnimatedPlayerModalWrapper } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
36
33
|
|
|
@@ -42,6 +39,17 @@ const styles = StyleSheet.create({
|
|
|
42
39
|
bottom: 0,
|
|
43
40
|
position: "absolute",
|
|
44
41
|
},
|
|
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
|
+
},
|
|
45
53
|
});
|
|
46
54
|
|
|
47
55
|
const VideoModalComponent = () => {
|
|
@@ -92,7 +100,7 @@ const VideoModalComponent = () => {
|
|
|
92
100
|
|
|
93
101
|
const onHooksSuccess = ({ payload }) => {
|
|
94
102
|
// set new modified entry
|
|
95
|
-
if (!equals(payload, item)) {
|
|
103
|
+
if (!R.equals(payload, item)) {
|
|
96
104
|
onVideoModalHookSuccessAnalyticsEvent();
|
|
97
105
|
setVideoModalItem(payload);
|
|
98
106
|
}
|
|
@@ -128,10 +136,7 @@ const VideoModalComponent = () => {
|
|
|
128
136
|
|
|
129
137
|
<PathnameContext.Provider value={pathname}>
|
|
130
138
|
<ScreenContextProvider pathname={pathname}>
|
|
131
|
-
{
|
|
132
|
-
{mode === "FULLSCREEN" && <OpaqueLayer />}
|
|
133
|
-
|
|
134
|
-
{itemIdHooksFinished === item?.id && (
|
|
139
|
+
{itemIdHooksFinished === item?.id ? (
|
|
135
140
|
<AnimatedPlayerModalWrapper
|
|
136
141
|
style={[
|
|
137
142
|
styles.container,
|
|
@@ -148,6 +153,10 @@ const VideoModalComponent = () => {
|
|
|
148
153
|
mode={mode}
|
|
149
154
|
/>
|
|
150
155
|
</AnimatedPlayerModalWrapper>
|
|
156
|
+
) : (
|
|
157
|
+
<View style={styles.loaderContainer}>
|
|
158
|
+
<Spinner />
|
|
159
|
+
</View>
|
|
151
160
|
)}
|
|
152
161
|
</ScreenContextProvider>
|
|
153
162
|
</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", () => ({ playerDimesionsHack: {} }));
|
|
66
66
|
|
|
67
67
|
const { PlayerWrapper } = require("../PlayerWrapper");
|
|
68
68
|
|
|
@@ -71,6 +71,36 @@ 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>
|
|
74
104
|
</View>
|
|
75
105
|
</RNCSafeAreaView>
|
|
76
106
|
</RNCSafeAreaProvider>
|
|
@@ -147,6 +177,36 @@ exports[`PlayerWrapper renders inline and docked 1`] = `
|
|
|
147
177
|
}
|
|
148
178
|
testID="test-player-container"
|
|
149
179
|
/>
|
|
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>
|
|
150
210
|
</View>
|
|
151
211
|
</RNCSafeAreaView>
|
|
152
212
|
</RNCSafeAreaProvider>
|
|
@@ -239,6 +299,36 @@ exports[`PlayerWrapper renders inline on tablet in landscape orientation 1`] = `
|
|
|
239
299
|
}
|
|
240
300
|
/>
|
|
241
301
|
</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>
|
|
242
332
|
</View>
|
|
243
333
|
</RNCSafeAreaView>
|
|
244
334
|
</RNCSafeAreaProvider>
|
|
@@ -35,36 +35,3 @@ 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
|
-
};
|
|
@@ -0,0 +1,46 @@
|
|
|
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
|
+
};
|
|
@@ -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
|
-
|
|
6
4
|
import * as R from "ramda";
|
|
7
5
|
import { Platform } from "react-native";
|
|
8
6
|
import Url from "url";
|
|
9
|
-
|
|
7
|
+
|
|
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,6 @@ 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";
|
|
30
22
|
|
|
31
23
|
type Props = {
|
|
32
24
|
component: ZappUIComponent;
|
|
@@ -299,28 +291,6 @@ export function zappPipesDataConnector(
|
|
|
299
291
|
if (addListener) {
|
|
300
292
|
return addListener(reloadData);
|
|
301
293
|
}
|
|
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
|
-
}
|
|
324
294
|
}
|
|
325
295
|
}, [dataSourceUrl, reloadData]);
|
|
326
296
|
|
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.3592913150",
|
|
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.3592913150",
|
|
38
|
+
"@applicaster/zapp-react-native-bridge": "13.0.0-alpha.3592913150",
|
|
39
|
+
"@applicaster/zapp-react-native-redux": "13.0.0-alpha.3592913150",
|
|
40
|
+
"@applicaster/zapp-react-native-utils": "13.0.0-alpha.3592913150",
|
|
41
41
|
"promise": "^8.3.0",
|
|
42
42
|
"react-router-native": "^5.1.2",
|
|
43
43
|
"url": "^0.11.0",
|
|
@@ -1,33 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,89 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,49 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,91 +0,0 @@
|
|
|
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
|
-
});
|