@applicaster/zapp-react-native-ui-components 16.0.0-alpha.9235607812 → 16.0.0-alpha.9338540010
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/MasterCell/DefaultComponents/LiveImage/index.tsx +12 -3
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +1 -1
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +4 -2
- package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/factory.test.ts +2 -2
- package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +2 -1
- package/Components/MasterCell/__tests__/masterCell.test.js +60 -0
- package/Components/MasterCell/index.tsx +28 -4
- package/Components/MasterCell/utils/__tests__/withoutFocusedState.test.js +20 -0
- package/Components/MasterCell/utils/index.ts +18 -0
- package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +22 -1
- package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +52 -1
- package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +77 -0
- package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +58 -16
- package/Components/ModalComponent/BottomSheetModalContent.tsx +9 -74
- package/Components/ModalComponent/__tests__/showTextInput.test.ts +25 -0
- package/Components/ModalComponent/showTextInput.ts +2 -0
- package/Components/River/ComponentsMap/ComponentsMap.tsx +4 -1
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -0
- package/Components/Transitioner/AnimationManager.js +1 -1
- package/Components/Transitioner/Scene.tsx +4 -3
- package/Components/Transitioner/Transitioner.tsx +69 -30
- package/Components/Transitioner/__tests__/transitioner.test.js +308 -20
- package/Components/VideoModal/PlayerWrapper.tsx +4 -1
- package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -0
- package/Components/VideoModal/__tests__/playerWrapperStyle.test.ts +58 -0
- package/Components/VideoModal/__tests__/playerWrapperUtils.test.ts +16 -0
- package/Components/VideoModal/playerWrapperStyle.ts +21 -3
- package/Components/VideoModal/playerWrapperUtils.ts +7 -1
- package/Decorators/ZappPipesDataConnector/__tests__/screenToolbox.test.ts +347 -0
- package/Decorators/ZappPipesDataConnector/__tests__/screenToolboxUnavailable.test.ts +53 -0
- package/Decorators/ZappPipesDataConnector/index.tsx +19 -4
- package/Decorators/ZappPipesDataConnector/screenToolbox.ts +384 -0
- package/package.json +5 -5
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { Text, View } from "react-native";
|
|
2
|
+
import { Animated, Text, View } from "react-native";
|
|
3
|
+
import { v4 as uuidV4 } from "uuid";
|
|
3
4
|
|
|
4
5
|
import { AnimationManager } from "../AnimationManager";
|
|
5
6
|
import {
|
|
6
7
|
NAV_ACTION_BACK,
|
|
7
8
|
NAV_ACTION_PUSH,
|
|
9
|
+
NAV_ACTION_REPLACE,
|
|
8
10
|
TransitionerComponent,
|
|
9
11
|
} from "../Transitioner";
|
|
10
12
|
import { act, render } from "@testing-library/react-native";
|
|
@@ -66,6 +68,26 @@ const sceneFor = (route) => (
|
|
|
66
68
|
</View>
|
|
67
69
|
);
|
|
68
70
|
|
|
71
|
+
const root = "/river/root";
|
|
72
|
+
const child = `${root}/river/child`;
|
|
73
|
+
const grandchild = `${child}/river/grandchild`;
|
|
74
|
+
|
|
75
|
+
const propsFor = (route, previousAction, children = sceneFor(route)) => ({
|
|
76
|
+
navigator: {
|
|
77
|
+
previousAction,
|
|
78
|
+
currentRoute: route,
|
|
79
|
+
screenData: {},
|
|
80
|
+
},
|
|
81
|
+
transitionConfig: sceneTransitionConfig,
|
|
82
|
+
layoutData: {
|
|
83
|
+
isTablet: false,
|
|
84
|
+
isTabletPortrait: false,
|
|
85
|
+
},
|
|
86
|
+
children,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const sceneKeys = (ref) => ref.current.state.scenes.map((scene) => scene.key);
|
|
90
|
+
|
|
69
91
|
describe("<TransitionerComponent />", () => {
|
|
70
92
|
it("renders correctly", () => {
|
|
71
93
|
const wrapper = render(
|
|
@@ -83,26 +105,12 @@ describe("<TransitionerComponent />", () => {
|
|
|
83
105
|
.mockImplementation((_, done) => done());
|
|
84
106
|
|
|
85
107
|
const ref = React.createRef();
|
|
86
|
-
const root = "/river/root";
|
|
87
|
-
const child = `${root}/river/child`;
|
|
88
|
-
const grandchild = `${child}/river/grandchild`;
|
|
89
|
-
|
|
90
|
-
const propsFor = (route, previousAction) => ({
|
|
91
|
-
navigator: {
|
|
92
|
-
previousAction,
|
|
93
|
-
currentRoute: route,
|
|
94
|
-
screenData: {},
|
|
95
|
-
},
|
|
96
|
-
transitionConfig: sceneTransitionConfig,
|
|
97
|
-
layoutData: {
|
|
98
|
-
isTablet: false,
|
|
99
|
-
isTabletPortrait: false,
|
|
100
|
-
},
|
|
101
|
-
children: sceneFor(route),
|
|
102
|
-
});
|
|
103
108
|
|
|
104
109
|
const { rerender } = render(
|
|
105
|
-
<TransitionerComponent
|
|
110
|
+
<TransitionerComponent
|
|
111
|
+
ref={ref}
|
|
112
|
+
{...propsFor(root, NAV_ACTION_REPLACE)}
|
|
113
|
+
/>
|
|
106
114
|
);
|
|
107
115
|
|
|
108
116
|
act(() => {
|
|
@@ -129,6 +137,286 @@ describe("<TransitionerComponent />", () => {
|
|
|
129
137
|
);
|
|
130
138
|
});
|
|
131
139
|
|
|
132
|
-
expect(ref
|
|
140
|
+
expect(sceneKeys(ref)).toEqual([root]);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("with interruptible animations", () => {
|
|
144
|
+
// End callback of the animation that is currently "running"
|
|
145
|
+
let runningAnimationEnd = null;
|
|
146
|
+
|
|
147
|
+
const finishAnimation = () => {
|
|
148
|
+
act(() => {
|
|
149
|
+
const done = runningAnimationEnd;
|
|
150
|
+
runningAnimationEnd = null;
|
|
151
|
+
done?.();
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const mountTransitioner = ({
|
|
156
|
+
initialRoute = root,
|
|
157
|
+
renderScene = sceneFor,
|
|
158
|
+
} = {}) => {
|
|
159
|
+
const ref = React.createRef();
|
|
160
|
+
|
|
161
|
+
const transitioner = (route, action) => (
|
|
162
|
+
<TransitionerComponent
|
|
163
|
+
ref={ref}
|
|
164
|
+
{...propsFor(route, action, renderScene(route))}
|
|
165
|
+
/>
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const { rerender } = render(
|
|
169
|
+
transitioner(initialRoute, NAV_ACTION_REPLACE)
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
const navigate = (route, action) => {
|
|
173
|
+
act(() => {
|
|
174
|
+
rerender(transitioner(route, action));
|
|
175
|
+
});
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
return { ref, navigate };
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
beforeEach(() => {
|
|
182
|
+
let sceneCount = 0;
|
|
183
|
+
uuidV4.mockImplementation(() => `scene-${++sceneCount}`);
|
|
184
|
+
|
|
185
|
+
jest
|
|
186
|
+
.spyOn(AnimationManager.prototype, "animate")
|
|
187
|
+
.mockImplementation((_, done) => {
|
|
188
|
+
runningAnimationEnd = done;
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const setValue = Animated.Value.prototype.setValue;
|
|
192
|
+
|
|
193
|
+
// RN 0.77: setValue() stops the running animation, which calls
|
|
194
|
+
// its end callback synchronously with { finished: false }
|
|
195
|
+
jest
|
|
196
|
+
.spyOn(Animated.Value.prototype, "setValue")
|
|
197
|
+
.mockImplementation(function (value) {
|
|
198
|
+
const interrupted = runningAnimationEnd;
|
|
199
|
+
runningAnimationEnd = null;
|
|
200
|
+
interrupted?.();
|
|
201
|
+
|
|
202
|
+
return setValue.call(this, value);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
afterEach(() => {
|
|
207
|
+
runningAnimationEnd = null;
|
|
208
|
+
jest.restoreAllMocks();
|
|
209
|
+
uuidV4.mockReset();
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it("drops interrupted scenes when going back twice during a back animation", () => {
|
|
213
|
+
const { ref, navigate } = mountTransitioner();
|
|
214
|
+
|
|
215
|
+
navigate(child, NAV_ACTION_PUSH);
|
|
216
|
+
finishAnimation();
|
|
217
|
+
navigate(grandchild, NAV_ACTION_PUSH);
|
|
218
|
+
finishAnimation();
|
|
219
|
+
|
|
220
|
+
navigate(child, NAV_ACTION_BACK);
|
|
221
|
+
navigate(root, NAV_ACTION_BACK);
|
|
222
|
+
|
|
223
|
+
expect(ref.current.state.animating).toBe(true);
|
|
224
|
+
|
|
225
|
+
finishAnimation();
|
|
226
|
+
|
|
227
|
+
expect(sceneKeys(ref)).toEqual([root]);
|
|
228
|
+
expect(ref.current.state.animating).toBe(false);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("remounts the screen when revisiting it after a quick double back", () => {
|
|
232
|
+
const mounts = {};
|
|
233
|
+
|
|
234
|
+
const MountProbe = ({ pathname }) => {
|
|
235
|
+
React.useEffect(() => {
|
|
236
|
+
mounts[pathname] = (mounts[pathname] || 0) + 1;
|
|
237
|
+
}, [pathname]);
|
|
238
|
+
|
|
239
|
+
return <Text>{pathname}</Text>;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const { ref, navigate } = mountTransitioner({
|
|
243
|
+
renderScene: (route) => <MountProbe pathname={route} />,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
navigate(child, NAV_ACTION_PUSH);
|
|
247
|
+
finishAnimation();
|
|
248
|
+
|
|
249
|
+
const originalChildId = ref.current.state.scenes[1].screenUniqueId;
|
|
250
|
+
|
|
251
|
+
navigate(grandchild, NAV_ACTION_PUSH);
|
|
252
|
+
finishAnimation();
|
|
253
|
+
navigate(child, NAV_ACTION_BACK);
|
|
254
|
+
navigate(root, NAV_ACTION_BACK);
|
|
255
|
+
finishAnimation();
|
|
256
|
+
|
|
257
|
+
navigate(child, NAV_ACTION_PUSH);
|
|
258
|
+
finishAnimation();
|
|
259
|
+
|
|
260
|
+
const { scenes, to } = ref.current.state;
|
|
261
|
+
|
|
262
|
+
expect(scenes).toHaveLength(2);
|
|
263
|
+
expect(to.index).toBe(1);
|
|
264
|
+
expect(scenes[1].screenUniqueId).not.toBe(originalChildId);
|
|
265
|
+
expect(mounts[child]).toBe(2);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("keeps a single scene when going back from the first scene", () => {
|
|
269
|
+
const { ref, navigate } = mountTransitioner({ initialRoute: child });
|
|
270
|
+
|
|
271
|
+
navigate(root, NAV_ACTION_BACK);
|
|
272
|
+
finishAnimation();
|
|
273
|
+
|
|
274
|
+
expect(sceneKeys(ref)).toEqual([root]);
|
|
275
|
+
expect(ref.current.state.to.index).toBe(0);
|
|
276
|
+
expect(ref.current.state.animating).toBe(false);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("pushes a fresh scene after a completed single back", () => {
|
|
280
|
+
const { ref, navigate } = mountTransitioner();
|
|
281
|
+
|
|
282
|
+
navigate(child, NAV_ACTION_PUSH);
|
|
283
|
+
finishAnimation();
|
|
284
|
+
|
|
285
|
+
const originalChildId = ref.current.state.scenes[1].screenUniqueId;
|
|
286
|
+
|
|
287
|
+
navigate(root, NAV_ACTION_BACK);
|
|
288
|
+
finishAnimation();
|
|
289
|
+
navigate(child, NAV_ACTION_PUSH);
|
|
290
|
+
finishAnimation();
|
|
291
|
+
|
|
292
|
+
const { scenes, to } = ref.current.state;
|
|
293
|
+
|
|
294
|
+
expect(sceneKeys(ref)).toEqual([root, child]);
|
|
295
|
+
expect(to.index).toBe(1);
|
|
296
|
+
expect(scenes[1].screenUniqueId).not.toBe(originalChildId);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("ignores the end of a back animation that a newer back superseded", () => {
|
|
300
|
+
const { ref, navigate } = mountTransitioner();
|
|
301
|
+
|
|
302
|
+
navigate(child, NAV_ACTION_PUSH);
|
|
303
|
+
finishAnimation();
|
|
304
|
+
navigate(grandchild, NAV_ACTION_PUSH);
|
|
305
|
+
finishAnimation();
|
|
306
|
+
|
|
307
|
+
navigate(child, NAV_ACTION_BACK);
|
|
308
|
+
|
|
309
|
+
const supersededEnd = runningAnimationEnd;
|
|
310
|
+
|
|
311
|
+
// Deliver the interrupted animation's end late, after the newer one started
|
|
312
|
+
Animated.Value.prototype.setValue.mockImplementation(() => {});
|
|
313
|
+
|
|
314
|
+
navigate(root, NAV_ACTION_BACK);
|
|
315
|
+
|
|
316
|
+
act(() => {
|
|
317
|
+
supersededEnd();
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
expect(ref.current.state.animating).toBe(true);
|
|
321
|
+
expect(sceneKeys(ref)).toEqual([root, child, grandchild]);
|
|
322
|
+
|
|
323
|
+
finishAnimation();
|
|
324
|
+
|
|
325
|
+
expect(sceneKeys(ref)).toEqual([root]);
|
|
326
|
+
expect(ref.current.state.animating).toBe(false);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it("drops the pushed scene when going back before its push animation ends", () => {
|
|
330
|
+
const { ref, navigate } = mountTransitioner();
|
|
331
|
+
|
|
332
|
+
navigate(child, NAV_ACTION_PUSH);
|
|
333
|
+
navigate(root, NAV_ACTION_BACK);
|
|
334
|
+
|
|
335
|
+
expect(ref.current.state.animating).toBe(true);
|
|
336
|
+
|
|
337
|
+
finishAnimation();
|
|
338
|
+
|
|
339
|
+
expect(sceneKeys(ref)).toEqual([root]);
|
|
340
|
+
expect(ref.current.state.animating).toBe(false);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it("pushes right after the visible scene even when stale scenes are left behind", () => {
|
|
344
|
+
const { ref, navigate } = mountTransitioner();
|
|
345
|
+
|
|
346
|
+
navigate(child, NAV_ACTION_PUSH);
|
|
347
|
+
finishAnimation();
|
|
348
|
+
|
|
349
|
+
// Leftover past to.index, as some other path could leave behind
|
|
350
|
+
act(() => {
|
|
351
|
+
ref.current.setState(({ scenes }) => ({
|
|
352
|
+
scenes: [
|
|
353
|
+
...scenes,
|
|
354
|
+
{
|
|
355
|
+
...scenes[1],
|
|
356
|
+
key: `${root}/river/stale`,
|
|
357
|
+
screenUniqueId: "stale",
|
|
358
|
+
},
|
|
359
|
+
],
|
|
360
|
+
}));
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
navigate(grandchild, NAV_ACTION_PUSH);
|
|
364
|
+
finishAnimation();
|
|
365
|
+
|
|
366
|
+
const { scenes, to } = ref.current.state;
|
|
367
|
+
|
|
368
|
+
expect(sceneKeys(ref)).toEqual([root, child, grandchild]);
|
|
369
|
+
expect(scenes[to.index].key).toBe(grandchild);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it("shows the replaced screen when going home during a back animation", () => {
|
|
373
|
+
const home = "/river/home";
|
|
374
|
+
const { ref, navigate } = mountTransitioner();
|
|
375
|
+
|
|
376
|
+
navigate(child, NAV_ACTION_PUSH);
|
|
377
|
+
finishAnimation();
|
|
378
|
+
|
|
379
|
+
navigate(root, NAV_ACTION_BACK);
|
|
380
|
+
navigate(home, NAV_ACTION_REPLACE);
|
|
381
|
+
|
|
382
|
+
const { scenes, to } = ref.current.state;
|
|
383
|
+
|
|
384
|
+
expect(scenes[to.index].key).toBe(home);
|
|
385
|
+
|
|
386
|
+
finishAnimation();
|
|
387
|
+
|
|
388
|
+
expect(sceneKeys(ref)).toEqual([home]);
|
|
389
|
+
expect(ref.current.state.animating).toBe(false);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it("shows the pushed screen when pushing during a back animation", () => {
|
|
393
|
+
const sibling = `${root}/river/sibling`;
|
|
394
|
+
const { ref, navigate } = mountTransitioner();
|
|
395
|
+
|
|
396
|
+
navigate(child, NAV_ACTION_PUSH);
|
|
397
|
+
finishAnimation();
|
|
398
|
+
|
|
399
|
+
navigate(root, NAV_ACTION_BACK);
|
|
400
|
+
navigate(sibling, NAV_ACTION_PUSH);
|
|
401
|
+
|
|
402
|
+
const { scenes, to } = ref.current.state;
|
|
403
|
+
|
|
404
|
+
expect(scenes[to.index].key).toBe(sibling);
|
|
405
|
+
|
|
406
|
+
finishAnimation();
|
|
407
|
+
|
|
408
|
+
expect(sceneKeys(ref)).not.toContain(child);
|
|
409
|
+
expect(ref.current.state.animating).toBe(false);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it("keeps both scenes when pushing during a push animation", () => {
|
|
413
|
+
const { ref, navigate } = mountTransitioner();
|
|
414
|
+
|
|
415
|
+
navigate(child, NAV_ACTION_PUSH);
|
|
416
|
+
navigate(grandchild, NAV_ACTION_PUSH);
|
|
417
|
+
|
|
418
|
+
expect(sceneKeys(ref)).toEqual([child, grandchild]);
|
|
419
|
+
expect(ref.current.state.to.index).toBe(1);
|
|
420
|
+
});
|
|
133
421
|
});
|
|
134
422
|
});
|
|
@@ -14,6 +14,7 @@ type Props = {
|
|
|
14
14
|
inline?: boolean;
|
|
15
15
|
docked?: boolean;
|
|
16
16
|
pip?: boolean;
|
|
17
|
+
vertical?: boolean;
|
|
17
18
|
isModal?: boolean;
|
|
18
19
|
fullscreen?: boolean;
|
|
19
20
|
isTabletPortrait?: boolean;
|
|
@@ -32,6 +33,7 @@ const PlayerWrapperComponent = (props: Props) => {
|
|
|
32
33
|
isTabletPortrait,
|
|
33
34
|
configuration,
|
|
34
35
|
pip,
|
|
36
|
+
vertical,
|
|
35
37
|
playerContent,
|
|
36
38
|
} = props;
|
|
37
39
|
|
|
@@ -42,6 +44,7 @@ const PlayerWrapperComponent = (props: Props) => {
|
|
|
42
44
|
isTabletPortrait,
|
|
43
45
|
configuration,
|
|
44
46
|
pip,
|
|
47
|
+
vertical,
|
|
45
48
|
});
|
|
46
49
|
|
|
47
50
|
const isTablet = useIsTablet();
|
|
@@ -52,7 +55,7 @@ const PlayerWrapperComponent = (props: Props) => {
|
|
|
52
55
|
|
|
53
56
|
return (
|
|
54
57
|
<WrapperView
|
|
55
|
-
edges={getEdges(isTablet, isInlineModal) as readonly Edge[]}
|
|
58
|
+
edges={getEdges(isTablet, isInlineModal, vertical) as readonly Edge[]}
|
|
56
59
|
style={playerDimensionsHack}
|
|
57
60
|
>
|
|
58
61
|
<View
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react-native";
|
|
2
|
+
|
|
3
|
+
import { useStyle } from "../playerWrapperStyle";
|
|
4
|
+
import { getScreenAspectRatio } from "../playerWrapperUtils";
|
|
5
|
+
|
|
6
|
+
let mockOrientation = "portrait";
|
|
7
|
+
|
|
8
|
+
jest.mock("@applicaster/zapp-react-native-utils/reactHooks", () => ({
|
|
9
|
+
useIsTablet: () => false,
|
|
10
|
+
useOrientation: () => mockOrientation,
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
const getAspectRatio = (props: {
|
|
14
|
+
vertical: boolean;
|
|
15
|
+
inline?: boolean;
|
|
16
|
+
isModal?: boolean;
|
|
17
|
+
}) => {
|
|
18
|
+
const { result } = renderHook(() =>
|
|
19
|
+
useStyle({
|
|
20
|
+
style: {},
|
|
21
|
+
inline: false,
|
|
22
|
+
isModal: true,
|
|
23
|
+
isTabletPortrait: false,
|
|
24
|
+
configuration: {},
|
|
25
|
+
pip: false,
|
|
26
|
+
...props,
|
|
27
|
+
})
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return result.current.containerDimensions.aspectRatio;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
describe("useStyle aspectRatio", () => {
|
|
34
|
+
const screenRatio = getScreenAspectRatio();
|
|
35
|
+
|
|
36
|
+
it("uses the screen ratio when not vertical", () => {
|
|
37
|
+
mockOrientation = "portrait";
|
|
38
|
+
expect(getAspectRatio({ vertical: false })).toBeCloseTo(screenRatio);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("inverts the screen ratio for vertical fullscreen in portrait", () => {
|
|
42
|
+
mockOrientation = "portrait";
|
|
43
|
+
expect(getAspectRatio({ vertical: true })).toBeCloseTo(1 / screenRatio);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("keeps the screen ratio for vertical fullscreen in landscape", () => {
|
|
47
|
+
mockOrientation = "landscape";
|
|
48
|
+
expect(getAspectRatio({ vertical: true })).toBeCloseTo(screenRatio);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("inverts 16:9 for the vertical inline modal in landscape", () => {
|
|
52
|
+
mockOrientation = "landscape";
|
|
53
|
+
|
|
54
|
+
expect(getAspectRatio({ vertical: true, inline: true })).toBeCloseTo(
|
|
55
|
+
9 / 16
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { getEdges } from "../playerWrapperUtils";
|
|
2
|
+
|
|
3
|
+
describe("getEdges", () => {
|
|
4
|
+
it("keeps the top edge in fullscreen when not vertical", () => {
|
|
5
|
+
expect(getEdges(false, false)).toEqual(["top"]);
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it("has no edges in vertical fullscreen", () => {
|
|
9
|
+
expect(getEdges(false, false, true)).toEqual([]);
|
|
10
|
+
expect(getEdges(true, false, true)).toEqual([]);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("keeps the top edge for the vertical inline modal", () => {
|
|
14
|
+
expect(getEdges(false, true, true)).toEqual(["top"]);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { StyleSheet, ViewStyle } from "react-native";
|
|
3
3
|
import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
useIsTablet,
|
|
6
|
+
useOrientation,
|
|
7
|
+
} from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
5
8
|
import { playerDimensionsHack } from "./utils";
|
|
6
9
|
import { getTabletWidth } from "@applicaster/zapp-react-native-utils/playerUtils";
|
|
7
10
|
import {
|
|
@@ -22,8 +25,10 @@ export const useStyle = ({
|
|
|
22
25
|
isTabletPortrait,
|
|
23
26
|
configuration,
|
|
24
27
|
pip,
|
|
28
|
+
vertical,
|
|
25
29
|
}) => {
|
|
26
30
|
const isTablet = useIsTablet();
|
|
31
|
+
const isLandscape = useOrientation() === "landscape";
|
|
27
32
|
|
|
28
33
|
const isInlineModal = inline && isModal;
|
|
29
34
|
|
|
@@ -40,10 +45,23 @@ export const useStyle = ({
|
|
|
40
45
|
);
|
|
41
46
|
|
|
42
47
|
const aspectRatio = React.useMemo(
|
|
43
|
-
() =>
|
|
48
|
+
() => {
|
|
49
|
+
const value = calculateAspectRatio(isInlineModal, pip);
|
|
50
|
+
|
|
51
|
+
if (!vertical) {
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// vertical fullscreen in landscape: the width is the long side, keep the screen ratio so the container matches the screen
|
|
56
|
+
if (isLandscape && !isInlineModal && !pip) {
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return 1 / value;
|
|
61
|
+
},
|
|
44
62
|
// ignoring insets - only initial needed
|
|
45
63
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
46
|
-
[isInlineModal, pip]
|
|
64
|
+
[isInlineModal, pip, vertical, isLandscape]
|
|
47
65
|
);
|
|
48
66
|
|
|
49
67
|
return React.useMemo(() => {
|
|
@@ -44,8 +44,14 @@ export const getScreenAspectRatio = () => {
|
|
|
44
44
|
|
|
45
45
|
export const getEdges = (
|
|
46
46
|
isTablet: boolean,
|
|
47
|
-
isInlineModal: boolean
|
|
47
|
+
isInlineModal: boolean,
|
|
48
|
+
vertical: boolean = false
|
|
48
49
|
): readonly Edge[] => {
|
|
50
|
+
// vertical fullscreen: the player takes the full screen height, a top inset would push it below the screen
|
|
51
|
+
if (vertical && !isInlineModal) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
|
|
49
55
|
if (isTablet) {
|
|
50
56
|
return ["top"];
|
|
51
57
|
}
|