@applicaster/zapp-react-native-ui-components 16.0.0-alpha.1453660071 → 16.0.0-alpha.1454930193
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/AudioPlayer/tv/__tests__/audioPlayer.test.js +19 -0
- package/Components/AudioPlayer/tv/__tests__/index.test.tsx +120 -0
- package/Components/AudioPlayer/tv/index.tsx +5 -1
- package/Components/Cell/Cell.tsx +3 -6
- package/Components/Cell/TvOSCellComponent.tsx +0 -1
- package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +15 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/skipExpression.test.ts +294 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +64 -0
- package/Components/GeneralContentScreen/hookAdapter/skipExpression.ts +252 -0
- package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +18 -0
- package/Components/LinkHandler/LinkHandler.tsx +2 -7
- package/Components/MasterCell/DefaultComponents/ActionButton.tsx +5 -1
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +47 -21
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +19 -4
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +43 -1
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +34 -6
- package/Components/MasterCell/DefaultComponents/Button.tsx +1 -0
- package/Components/MasterCell/DefaultComponents/Image/hoc/utils/__tests__/withDimensions.test.ts +420 -125
- package/Components/MasterCell/DefaultComponents/Image/hoc/utils/index.ts +114 -19
- package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +19 -8
- package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -3
- package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +2 -4
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +2 -3
- package/Components/MasterCell/DefaultComponents/PressableView.tsx +29 -9
- package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +4 -0
- package/Components/MasterCell/DefaultComponents/Toggle.tsx +36 -14
- package/Components/MasterCell/DefaultComponents/__tests__/Toggle.test.tsx +167 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +154 -100
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +15 -4
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +56 -22
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +6 -4
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +3 -1
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +32 -21
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +15 -4
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +23 -16
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +8 -3
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +2 -2
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +2 -2
- package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/factory.test.ts +32 -0
- package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -2
- package/Components/MasterCell/contexts/PressedStateContext.ts +3 -0
- package/Components/MasterCell/hooks/index.ts +2 -0
- package/Components/MasterCell/hooks/usePressedState.ts +4 -0
- package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +56 -0
- package/Components/ModalComponent/ActionsBottomSheet/__tests__/openActionsBottomSheet.test.ts +38 -0
- package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +24 -0
- package/Components/ModalComponent/ActionsBottomSheet/index.ts +9 -0
- package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +61 -0
- package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +392 -0
- package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +772 -0
- package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +728 -0
- package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +1007 -0
- package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +205 -0
- package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +113 -0
- package/Components/ModalComponent/AudioPlayer/Components/__tests__/NowPlayingHeaderSection.test.tsx +36 -0
- package/Components/ModalComponent/AudioPlayer/Components/index.ts +9 -0
- package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +356 -0
- package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +521 -0
- package/Components/ModalComponent/BottomSheetDragArea.tsx +33 -0
- package/Components/ModalComponent/BottomSheetModalContent.tsx +505 -50
- package/Components/ModalComponent/Button/index.tsx +5 -3
- package/Components/ModalComponent/Header/CloseButton.tsx +12 -0
- package/Components/ModalComponent/Header/__tests__/CloseButton.test.tsx +72 -0
- package/Components/ModalComponent/Header/index.tsx +20 -7
- package/Components/ModalComponent/Header/utils.ts +1 -1
- package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
- package/Components/ModalComponent/SortableList.tsx +156 -0
- package/Components/ModalComponent/SortableList.web.tsx +34 -0
- package/Components/ModalComponent/TextInputContent.tsx +135 -0
- package/Components/ModalComponent/__tests__/BottomSheetDragArea.test.tsx +74 -0
- package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +343 -0
- package/Components/ModalComponent/__tests__/SortableList.web.test.tsx +16 -0
- package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +169 -0
- package/Components/ModalComponent/__tests__/showTextInput.test.ts +95 -0
- package/Components/ModalComponent/index.tsx +18 -9
- package/Components/ModalComponent/presets/__tests__/getCell.test.ts +90 -0
- package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
- package/Components/ModalComponent/presets/index.ts +27 -0
- package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
- package/Components/ModalComponent/presets/standardCell.tsx +41 -0
- package/Components/ModalComponent/presets/types.ts +35 -0
- package/Components/ModalComponent/showTextInput.ts +52 -0
- package/Components/ModalComponent/utils.ts +82 -21
- package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +4 -85
- package/Components/OfflineHandler/__tests__/index.test.tsx +78 -18
- package/Components/OfflineHandler/hooks.ts +111 -0
- package/Components/OfflineHandler/index.tsx +51 -73
- package/Components/OfflineHandler/utils/index.ts +1 -13
- package/Components/PlayerContainer/PlayerContainer.tsx +11 -7
- package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -4
- package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +66 -13
- package/Components/PreloaderWrapper/index.tsx +1 -1
- package/Components/River/ComponentsMap/ComponentsMap.tsx +85 -59
- package/Components/River/River.tsx +4 -0
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +140 -75
- package/Components/River/__tests__/componentsMap.test.js +8 -3
- package/Components/River/__tests__/resultCallback.test.tsx +47 -0
- package/Components/Screen/__tests__/Screen.test.tsx +24 -0
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -0
- package/Components/Screen/index.tsx +5 -0
- package/Components/Screen/navigationHandler.ts +8 -8
- package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +5 -3
- package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +1 -1
- package/Components/ScreenRevealManager/ScreenRevealManager.ts +18 -2
- package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +36 -0
- package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +56 -0
- package/Components/ScreenRevealManager/withScreenRevealManager.tsx +21 -4
- package/Components/TopCutoffOverlay/index.tsx +2 -2
- package/Components/Transitioner/Transitioner.tsx +30 -20
- package/Components/Transitioner/__tests__/transitioner.test.js +83 -2
- package/Components/VideoLive/LiveImageManager.ts +9 -3
- package/Components/VideoLive/__tests__/LiveImageManager.test.ts +59 -0
- package/Contexts/ModalNavigationContext/__tests__/index.test.tsx +15 -0
- package/Contexts/ModalNavigationContext/index.tsx +2 -2
- package/Hooks/useEntryActionsExpander.ts +63 -0
- package/assets/empty-Info.png +0 -0
- package/package.json +6 -5
- package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +0 -112
- package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +0 -107
- package/Components/OfflineHandler/NotificationView/NotificationView.tsx +0 -153
- package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -66
- package/Components/OfflineHandler/NotificationView/utils.ts +0 -34
- /package/Decorators/ZappPipesDataConnector/__tests__/{Hero.js → Hero.tsx} +0 -0
|
@@ -42,9 +42,25 @@ export class ScreenRevealManager {
|
|
|
42
42
|
private subject$ = new Subject<void>();
|
|
43
43
|
private subscription: Subscription;
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
/**
|
|
46
|
+
* @param initialNumberToLoad how many components to wait for before revealing
|
|
47
|
+
* the screen. Staggering exists so a screen full of network-backed components
|
|
48
|
+
* does not load them all at once. A caller that has nothing to stagger - see
|
|
49
|
+
* `disableIncrementalLoading` on the HOC - passes the full count here so the
|
|
50
|
+
* reveal waits for exactly what gets rendered. Omitted, the count is derived
|
|
51
|
+
* from the components themselves.
|
|
52
|
+
*/
|
|
53
|
+
constructor(
|
|
54
|
+
componentsToRender: ZappUIComponent[],
|
|
55
|
+
callback: Callback,
|
|
56
|
+
initialNumberToLoad?: number
|
|
57
|
+
) {
|
|
46
58
|
this.numberOfComponentsWaitToLoadBeforePresent =
|
|
47
|
-
|
|
59
|
+
initialNumberToLoad == null
|
|
60
|
+
? getNumberOfComponentsWaitToLoadBeforePresent(componentsToRender)
|
|
61
|
+
: // Clamped like the computed path: waiting for components that do not
|
|
62
|
+
// exist would hold the screen behind its overlay until the timeout.
|
|
63
|
+
Math.min(initialNumberToLoad, componentsToRender.length);
|
|
48
64
|
|
|
49
65
|
this.renderingState = makeListOf<ComponentLoadingState>(
|
|
50
66
|
COMPONENT_LOADING_STATE.UNKNOWN,
|
|
@@ -22,6 +22,42 @@ import { makeListOf } from "@applicaster/zapp-react-native-utils/arrayUtils";
|
|
|
22
22
|
import { isFirstComponentGallery } from "@applicaster/zapp-react-native-utils/componentsUtils";
|
|
23
23
|
import { withTimeout$ } from "@applicaster/zapp-react-native-utils/idleUtils";
|
|
24
24
|
|
|
25
|
+
describe("ScreenRevealManager explicit component count", () => {
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
(isFirstComponentGallery as jest.Mock).mockReturnValue(false);
|
|
28
|
+
|
|
29
|
+
(makeListOf as jest.Mock).mockImplementation((value, length) =>
|
|
30
|
+
Array(length).fill(value)
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
(withTimeout$ as jest.Mock).mockReturnValue(new Subject());
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("waits for the number of components it is given, not the computed default", () => {
|
|
37
|
+
const components = Array(6).fill({}) as any;
|
|
38
|
+
|
|
39
|
+
const manager = new ScreenRevealManager(components, jest.fn(), 6);
|
|
40
|
+
|
|
41
|
+
expect(manager.numberOfComponentsWaitToLoadBeforePresent).toBe(6);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("never waits for more components than it was handed", () => {
|
|
45
|
+
const components = Array(3).fill({}) as any;
|
|
46
|
+
|
|
47
|
+
const manager = new ScreenRevealManager(components, jest.fn(), 10);
|
|
48
|
+
|
|
49
|
+
expect(manager.numberOfComponentsWaitToLoadBeforePresent).toBe(3);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("falls back to the computed default when given no explicit count", () => {
|
|
53
|
+
const components = Array(6).fill({}) as any;
|
|
54
|
+
|
|
55
|
+
const manager = new ScreenRevealManager(components, jest.fn());
|
|
56
|
+
|
|
57
|
+
expect(manager.numberOfComponentsWaitToLoadBeforePresent).toBe(3);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
25
61
|
describe("ScreenRevealManager", () => {
|
|
26
62
|
let mockCallback: jest.Mock;
|
|
27
63
|
let timeout$: Subject<void>;
|
|
@@ -9,6 +9,12 @@ import {
|
|
|
9
9
|
TIMEOUT,
|
|
10
10
|
} from "../withScreenRevealManager";
|
|
11
11
|
|
|
12
|
+
jest.mock("@applicaster/zapp-react-native-utils/theme", () => ({
|
|
13
|
+
useTheme: () => ({ app_background_color: "#000000" }),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
jest.mock("../Overlay", () => ({ Overlay: () => null }));
|
|
17
|
+
|
|
12
18
|
// jest.mock("react-native/Libraries/Animated/NativeAnimatedHelper");
|
|
13
19
|
|
|
14
20
|
const MockComponent = ({
|
|
@@ -37,6 +43,56 @@ const MockComponent = ({
|
|
|
37
43
|
|
|
38
44
|
const WrappedComponent = withScreenRevealManager(MockComponent);
|
|
39
45
|
|
|
46
|
+
describe("withScreenRevealManager disableIncrementalLoading", () => {
|
|
47
|
+
const components = (count: number) =>
|
|
48
|
+
Array(count).fill({ component_type: "grid-qb" });
|
|
49
|
+
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
jest.clearAllMocks();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("asks for every component at once when incremental loading is disabled", () => {
|
|
55
|
+
render(
|
|
56
|
+
<WrappedComponent
|
|
57
|
+
componentsToRender={components(6)}
|
|
58
|
+
disableIncrementalLoading
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
expect(screen.getByTestId("mock-component").props.initialNumberToLoad).toBe(
|
|
63
|
+
6
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("still tracks the component count after it changes", () => {
|
|
68
|
+
const { rerender } = render(
|
|
69
|
+
<WrappedComponent
|
|
70
|
+
componentsToRender={components(6)}
|
|
71
|
+
disableIncrementalLoading
|
|
72
|
+
/>
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
rerender(
|
|
76
|
+
<WrappedComponent
|
|
77
|
+
componentsToRender={components(9)}
|
|
78
|
+
disableIncrementalLoading
|
|
79
|
+
/>
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
expect(screen.getByTestId("mock-component").props.initialNumberToLoad).toBe(
|
|
83
|
+
9
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("loads incrementally when the flag is not set", () => {
|
|
88
|
+
render(<WrappedComponent componentsToRender={components(6)} />);
|
|
89
|
+
|
|
90
|
+
expect(screen.getByTestId("mock-component").props.initialNumberToLoad).toBe(
|
|
91
|
+
3
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
40
96
|
describe.skip("withScreenRevealManager", () => {
|
|
41
97
|
beforeEach(() => {
|
|
42
98
|
jest.clearAllMocks();
|
|
@@ -21,11 +21,23 @@ export const SHOWN = 1; // opacity = 1
|
|
|
21
21
|
type Props = {
|
|
22
22
|
componentsToRender: ZappUIComponent[];
|
|
23
23
|
backgroundColor?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Renders every component at once instead of revealing them a few at a time.
|
|
26
|
+
*
|
|
27
|
+
* For a screen that builds its own components and holds all of them already -
|
|
28
|
+
* a form, say - staggering buys nothing and costs: each rebuild of the list
|
|
29
|
+
* tears it back down to the first few and lets the rest crawl in again.
|
|
30
|
+
*
|
|
31
|
+
* This is a flag rather than a count on purpose. A count would have to be
|
|
32
|
+
* re-read whenever the number of components changes, while "do not stagger"
|
|
33
|
+
* stays true whatever the screen ends up holding.
|
|
34
|
+
*/
|
|
35
|
+
disableIncrementalLoading?: boolean;
|
|
24
36
|
};
|
|
25
37
|
|
|
26
38
|
export const withScreenRevealManager = (Component) => {
|
|
27
39
|
return function WithScreenRevealManager(props: Props) {
|
|
28
|
-
const { componentsToRender } = props;
|
|
40
|
+
const { componentsToRender, disableIncrementalLoading } = props;
|
|
29
41
|
|
|
30
42
|
const [isContentReadyToBeShown, setIsContentReadyToBeShown] =
|
|
31
43
|
React.useState(false);
|
|
@@ -42,7 +54,8 @@ export const withScreenRevealManager = (Component) => {
|
|
|
42
54
|
() =>
|
|
43
55
|
new ScreenRevealManager(
|
|
44
56
|
componentsToRender,
|
|
45
|
-
handleSetIsContentReadyToBeShown
|
|
57
|
+
handleSetIsContentReadyToBeShown,
|
|
58
|
+
disableIncrementalLoading ? componentsToRender.length : undefined
|
|
46
59
|
)
|
|
47
60
|
);
|
|
48
61
|
|
|
@@ -72,7 +85,7 @@ export const withScreenRevealManager = (Component) => {
|
|
|
72
85
|
|
|
73
86
|
if (isFirstComponentScreenPicker(componentsToRender)) {
|
|
74
87
|
// for screen-picker with have additional internal ComponentsMap, no need to add this wrapper
|
|
75
|
-
return <Component {...props} />;
|
|
88
|
+
return <Component {...props} initialNumberToLoad={1} />;
|
|
76
89
|
}
|
|
77
90
|
|
|
78
91
|
return (
|
|
@@ -80,7 +93,11 @@ export const withScreenRevealManager = (Component) => {
|
|
|
80
93
|
<Component
|
|
81
94
|
{...props}
|
|
82
95
|
initialNumberToLoad={
|
|
83
|
-
|
|
96
|
+
// Recomputed on every render, so the count follows the components
|
|
97
|
+
// even though the manager settled its own expectation at mount.
|
|
98
|
+
disableIncrementalLoading
|
|
99
|
+
? componentsToRender.length
|
|
100
|
+
: managerRef.current.numberOfComponentsWaitToLoadBeforePresent
|
|
84
101
|
}
|
|
85
102
|
onLoadFinishedFromScreenRevealManager={
|
|
86
103
|
managerRef.current.onLoadFinished
|
|
@@ -7,7 +7,7 @@ import { useMarginTop } from "./hooks";
|
|
|
7
7
|
|
|
8
8
|
interface IProps {
|
|
9
9
|
targetScreenId?: string;
|
|
10
|
-
children
|
|
10
|
+
children: React.ReactElement | null;
|
|
11
11
|
applyTopCutoff?: boolean;
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -21,7 +21,7 @@ export const TopCutoffOverlay: React.FC<IProps> = ({
|
|
|
21
21
|
targetScreenId,
|
|
22
22
|
children,
|
|
23
23
|
applyTopCutoff = true,
|
|
24
|
-
}
|
|
24
|
+
}) => {
|
|
25
25
|
const cutoffHeight = useMarginTop(targetScreenId);
|
|
26
26
|
|
|
27
27
|
const { backgroundColor: screenBackgroundColor } =
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { View } from "react-native";
|
|
3
|
-
import { append, compose, identity,
|
|
3
|
+
import { append, compose, identity, tail, take, update } from "ramda";
|
|
4
4
|
|
|
5
5
|
import { AnimationManager } from "./AnimationManager";
|
|
6
6
|
import { Scene } from "./Scene";
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
shouldSkipAnimationForPreviousWebViewScene,
|
|
11
11
|
} from "./utils";
|
|
12
12
|
import { v4 as uuid_v4 } from "uuid";
|
|
13
|
+
import { isScreenActiveForRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useIsScreenActive";
|
|
13
14
|
|
|
14
15
|
export const NAV_ACTION_PUSH = "PUSH";
|
|
15
16
|
|
|
@@ -50,16 +51,16 @@ function scenesForBackTransition(props: SceneProps, { scenes }) {
|
|
|
50
51
|
: [prepareScene(props.children), scenes[0]];
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
const isModalClosed = (navigator) => {
|
|
54
|
-
return !!isNil(navigator?.modalData);
|
|
55
|
-
};
|
|
56
|
-
|
|
57
54
|
type Props = {
|
|
58
55
|
transitionConfig: any;
|
|
59
56
|
children: React.ReactNode;
|
|
60
57
|
navigator: {
|
|
61
58
|
previousAction: string;
|
|
62
59
|
currentRoute: string;
|
|
60
|
+
videoModalState?: {
|
|
61
|
+
visible?: boolean;
|
|
62
|
+
mode?: string;
|
|
63
|
+
};
|
|
63
64
|
screenData: {
|
|
64
65
|
[key: string]: any;
|
|
65
66
|
};
|
|
@@ -128,8 +129,7 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
|
|
|
128
129
|
* The last scene in the array is removed in the "backTransitionEnd" callback,
|
|
129
130
|
* only after the animation is complete.
|
|
130
131
|
*/
|
|
131
|
-
backTransitionStart() {
|
|
132
|
-
const previousRoute = this.props.navigator?.currentRoute;
|
|
132
|
+
backTransitionStart(previousRoute: string) {
|
|
133
133
|
const currentRoute = this.props.navigator?.currentRoute;
|
|
134
134
|
|
|
135
135
|
const diffLengthRoutes =
|
|
@@ -241,7 +241,7 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
|
|
|
241
241
|
break;
|
|
242
242
|
|
|
243
243
|
case NAV_ACTION_BACK: {
|
|
244
|
-
this.backTransitionStart();
|
|
244
|
+
this.backTransitionStart(previousRoute);
|
|
245
245
|
break;
|
|
246
246
|
}
|
|
247
247
|
|
|
@@ -283,8 +283,10 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
|
|
|
283
283
|
// so only a single scene should be rendered,
|
|
284
284
|
// instead of dangerous nulls or duplicates.
|
|
285
285
|
renderSingleScene({ scenes, to }) {
|
|
286
|
-
const { contentStyle } = this.props;
|
|
287
|
-
|
|
286
|
+
const { contentStyle, navigator, children } = this.props;
|
|
287
|
+
|
|
288
|
+
const { currentRoute, videoModalState } = navigator;
|
|
289
|
+
const screenData = children.props.screenData;
|
|
288
290
|
const pathname = scenes[to.index].key;
|
|
289
291
|
|
|
290
292
|
return (
|
|
@@ -293,7 +295,11 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
|
|
|
293
295
|
<Scene
|
|
294
296
|
{...{ style: to?.style || {} }}
|
|
295
297
|
pathname={pathname}
|
|
296
|
-
isActive={
|
|
298
|
+
isActive={isScreenActiveForRoute(
|
|
299
|
+
pathname,
|
|
300
|
+
currentRoute,
|
|
301
|
+
videoModalState
|
|
302
|
+
)}
|
|
297
303
|
contentStyle={contentStyle}
|
|
298
304
|
key={scenes[to.index].key}
|
|
299
305
|
screenUniqueId={scenes[to.index].screenUniqueId}
|
|
@@ -311,16 +317,19 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
|
|
|
311
317
|
// Looks redundant, but when written like this
|
|
312
318
|
// we do not need to pass or generate keys.
|
|
313
319
|
renderScenes({ scenes, from, to }) {
|
|
314
|
-
const { contentStyle } = this.props;
|
|
320
|
+
const { contentStyle, navigator } = this.props;
|
|
321
|
+
|
|
322
|
+
const { currentRoute, videoModalState } = navigator;
|
|
315
323
|
|
|
316
324
|
const fromScene = scenes[from.index] && (
|
|
317
325
|
<Scene
|
|
318
326
|
key={scenes[from.index].key}
|
|
319
327
|
pathname={scenes[from.index].key}
|
|
320
|
-
isActive={
|
|
321
|
-
scenes[from.index].key
|
|
322
|
-
|
|
323
|
-
|
|
328
|
+
isActive={isScreenActiveForRoute(
|
|
329
|
+
scenes[from.index].key,
|
|
330
|
+
currentRoute,
|
|
331
|
+
videoModalState
|
|
332
|
+
)}
|
|
324
333
|
style={from.style}
|
|
325
334
|
pointerEvents="none"
|
|
326
335
|
overlayStyle={from.overlayStyle}
|
|
@@ -336,10 +345,11 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
|
|
|
336
345
|
const toScene = scenes[to.index] && (
|
|
337
346
|
<Scene
|
|
338
347
|
pathname={scenes[to.index].key}
|
|
339
|
-
isActive={
|
|
340
|
-
scenes[to.index].key
|
|
341
|
-
|
|
342
|
-
|
|
348
|
+
isActive={isScreenActiveForRoute(
|
|
349
|
+
scenes[to.index].key,
|
|
350
|
+
currentRoute,
|
|
351
|
+
videoModalState
|
|
352
|
+
)}
|
|
343
353
|
key={scenes[to.index].key}
|
|
344
354
|
style={to.style}
|
|
345
355
|
overlayStyle={to.overlayStyle}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Text, View } from "react-native";
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { AnimationManager } from "../AnimationManager";
|
|
5
|
+
import {
|
|
6
|
+
NAV_ACTION_BACK,
|
|
7
|
+
NAV_ACTION_PUSH,
|
|
8
|
+
TransitionerComponent,
|
|
9
|
+
} from "../Transitioner";
|
|
10
|
+
import { act, render } from "@testing-library/react-native";
|
|
6
11
|
|
|
7
12
|
jest.mock("../Scene", () => {
|
|
8
13
|
const View = jest.requireActual("react-native").View;
|
|
@@ -40,6 +45,27 @@ const transitionerComponentProps = {
|
|
|
40
45
|
|
|
41
46
|
jest.mock("uuid");
|
|
42
47
|
|
|
48
|
+
const sceneTransitionConfig = () => ({
|
|
49
|
+
push: {
|
|
50
|
+
duration: 0,
|
|
51
|
+
easing: jest.fn(),
|
|
52
|
+
from: { style: {} },
|
|
53
|
+
to: { style: {} },
|
|
54
|
+
},
|
|
55
|
+
back: {
|
|
56
|
+
duration: 0,
|
|
57
|
+
easing: jest.fn(),
|
|
58
|
+
from: { style: {} },
|
|
59
|
+
to: { style: {} },
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const sceneFor = (route) => (
|
|
64
|
+
<View pathname={route}>
|
|
65
|
+
<Text>{route}</Text>
|
|
66
|
+
</View>
|
|
67
|
+
);
|
|
68
|
+
|
|
43
69
|
describe("<TransitionerComponent />", () => {
|
|
44
70
|
it("renders correctly", () => {
|
|
45
71
|
const wrapper = render(
|
|
@@ -50,4 +76,59 @@ describe("<TransitionerComponent />", () => {
|
|
|
50
76
|
|
|
51
77
|
expect(wrapper.toJSON()).toMatchSnapshot();
|
|
52
78
|
});
|
|
79
|
+
|
|
80
|
+
it("removes both intermediate scenes when backing out to the tab root", () => {
|
|
81
|
+
jest
|
|
82
|
+
.spyOn(AnimationManager.prototype, "animate")
|
|
83
|
+
.mockImplementation((_, done) => done());
|
|
84
|
+
|
|
85
|
+
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
|
+
|
|
104
|
+
const { rerender } = render(
|
|
105
|
+
<TransitionerComponent ref={ref} {...propsFor(root, "REPLACE")} />
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
act(() => {
|
|
109
|
+
rerender(
|
|
110
|
+
<TransitionerComponent
|
|
111
|
+
ref={ref}
|
|
112
|
+
{...propsFor(child, NAV_ACTION_PUSH)}
|
|
113
|
+
/>
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
act(() => {
|
|
118
|
+
rerender(
|
|
119
|
+
<TransitionerComponent
|
|
120
|
+
ref={ref}
|
|
121
|
+
{...propsFor(grandchild, NAV_ACTION_PUSH)}
|
|
122
|
+
/>
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
act(() => {
|
|
127
|
+
rerender(
|
|
128
|
+
<TransitionerComponent ref={ref} {...propsFor(root, NAV_ACTION_BACK)} />
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
expect(ref.current.state.scenes.map((scene) => scene.key)).toEqual([root]);
|
|
133
|
+
});
|
|
53
134
|
});
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from "@applicaster/zapp-react-native-utils/appUtils/playerManager";
|
|
6
6
|
import { setUserCellPlayerMutedPreference } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/userCellPlayerMutedPreference";
|
|
7
7
|
import { playerFactory } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/playerFactory";
|
|
8
|
-
import { PlayerRole } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/
|
|
8
|
+
import { PlayerRole } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/const";
|
|
9
9
|
import { loggerLiveImageManager } from "./loggerHelper";
|
|
10
10
|
import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
11
11
|
import {
|
|
@@ -213,10 +213,16 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
213
213
|
log_debug(
|
|
214
214
|
`pauseItem: live-image not ready, ${item.playerId} - ${item.tag}`
|
|
215
215
|
);
|
|
216
|
-
} else {
|
|
217
|
-
item.player.pause();
|
|
218
216
|
}
|
|
219
217
|
|
|
218
|
+
// Pause even when the player is not ready yet: the underlying player may
|
|
219
|
+
// still be loading and would otherwise start playing on its own once it
|
|
220
|
+
// has buffered, leaving the live-image audible underneath the primary
|
|
221
|
+
// player. Note this is best effort - the web controller's pause() only
|
|
222
|
+
// logs while the player component is unmounted - so the player itself also
|
|
223
|
+
// refuses buffer-driven starts when it was never asked to play.
|
|
224
|
+
item.player.pause();
|
|
225
|
+
|
|
220
226
|
// Fake close event, because we unmount native view
|
|
221
227
|
item.player.onPlayerClose();
|
|
222
228
|
item.setMode?.(LiveImageType.Image);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const { LiveImageManager } = require("../LiveImageManager");
|
|
2
2
|
|
|
3
3
|
describe("LiveImageManager", () => {
|
|
4
|
+
const flushPromises = () => new Promise(setImmediate);
|
|
5
|
+
|
|
4
6
|
it("should subscribe to LiveImageManager events", () => {
|
|
5
7
|
const sampleListener = {
|
|
6
8
|
id: "1",
|
|
@@ -78,4 +80,61 @@ describe("LiveImageManager", () => {
|
|
|
78
80
|
expect(mockOnEnd1).toHaveBeenCalledWith(eventBody);
|
|
79
81
|
expect(mockOnEnd2).toHaveBeenCalledWith(eventBody);
|
|
80
82
|
});
|
|
83
|
+
|
|
84
|
+
it("should start a prepared live image when the mounted player loads", async () => {
|
|
85
|
+
const manager = new LiveImageManager();
|
|
86
|
+
|
|
87
|
+
const item = {
|
|
88
|
+
playerId: "live-image",
|
|
89
|
+
tag: "Live image",
|
|
90
|
+
player: {
|
|
91
|
+
playerId: "live-image",
|
|
92
|
+
playerState: { isReadyToPlay: false },
|
|
93
|
+
getEntry: jest.fn(),
|
|
94
|
+
play: jest.fn(),
|
|
95
|
+
},
|
|
96
|
+
getPlayer: jest.fn(),
|
|
97
|
+
prepareForPlayback: jest.fn().mockResolvedValue(true),
|
|
98
|
+
setMode: jest.fn(),
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
manager.playLiveImage(item);
|
|
102
|
+
await flushPromises();
|
|
103
|
+
|
|
104
|
+
expect(item.setMode).toHaveBeenCalledWith("video");
|
|
105
|
+
expect(item.player.play).not.toHaveBeenCalled();
|
|
106
|
+
|
|
107
|
+
manager.onLiveImageVideoLoaded(item);
|
|
108
|
+
|
|
109
|
+
expect(item.player.play).toHaveBeenCalledTimes(1);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("should not start a live image that was paused before the player loaded", async () => {
|
|
113
|
+
const manager = new LiveImageManager();
|
|
114
|
+
|
|
115
|
+
const item = {
|
|
116
|
+
playerId: "live-image",
|
|
117
|
+
tag: "Live image",
|
|
118
|
+
player: {
|
|
119
|
+
playerId: "live-image",
|
|
120
|
+
playerState: { isReadyToPlay: false },
|
|
121
|
+
getEntry: jest.fn(),
|
|
122
|
+
play: jest.fn(),
|
|
123
|
+
pause: jest.fn(),
|
|
124
|
+
onPlayerClose: jest.fn(),
|
|
125
|
+
},
|
|
126
|
+
getPlayer: jest.fn(),
|
|
127
|
+
prepareForPlayback: jest.fn().mockResolvedValue(true),
|
|
128
|
+
setMode: jest.fn(),
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
manager.playLiveImage(item);
|
|
132
|
+
await flushPromises();
|
|
133
|
+
manager.pauseLiveImage(item);
|
|
134
|
+
manager.onLiveImageVideoLoaded(item);
|
|
135
|
+
|
|
136
|
+
expect(item.player.pause).toHaveBeenCalledTimes(1);
|
|
137
|
+
expect(item.player.play).not.toHaveBeenCalled();
|
|
138
|
+
expect(item.setMode).toHaveBeenLastCalledWith("image");
|
|
139
|
+
});
|
|
81
140
|
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@testing-library/react-native";
|
|
3
|
+
import { withModalNavigationContextProvider } from "../";
|
|
4
|
+
|
|
5
|
+
describe("withModalNavigationContextProvider", () => {
|
|
6
|
+
it("does not re-render the wrapped component when props are unchanged", () => {
|
|
7
|
+
const Component = jest.fn(() => null);
|
|
8
|
+
const WrappedComponent = withModalNavigationContextProvider(Component);
|
|
9
|
+
const { rerender } = render(<WrappedComponent title="Modal" />);
|
|
10
|
+
|
|
11
|
+
rerender(<WrappedComponent title="Modal" />);
|
|
12
|
+
|
|
13
|
+
expect(Component).toHaveBeenCalledTimes(1);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -6,11 +6,11 @@ export function useModalNavigationContext() {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export function withModalNavigationContextProvider(Component) {
|
|
9
|
-
return function WrappedWithModalNavigationContextProvider(props) {
|
|
9
|
+
return React.memo(function WrappedWithModalNavigationContextProvider(props) {
|
|
10
10
|
return (
|
|
11
11
|
<ModalNavigationContext.Provider value={true}>
|
|
12
12
|
<Component {...props} />
|
|
13
13
|
</ModalNavigationContext.Provider>
|
|
14
14
|
);
|
|
15
|
-
};
|
|
15
|
+
});
|
|
16
16
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { ActionExecutorContext } from "@applicaster/zapp-react-native-utils/actionsExecutor/ActionExecutorContext";
|
|
4
|
+
import {
|
|
5
|
+
buildEntryActions,
|
|
6
|
+
EntryActionDeps,
|
|
7
|
+
} from "@applicaster/zapp-react-native-utils/entryActions";
|
|
8
|
+
import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
|
|
9
|
+
import { useScreenStateStore } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useScreenStateStore";
|
|
10
|
+
import {
|
|
11
|
+
useCurrentScreenData,
|
|
12
|
+
useScreenContext,
|
|
13
|
+
} from "@applicaster/zapp-react-native-utils/reactHooks/screen";
|
|
14
|
+
import { RegisteredAction } from "@applicaster/zapp-react-native-utils/uiActionsRegistrator";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Expands `entry.extensions.entry_action` into invokable actions, against the
|
|
18
|
+
* screen this hook is called from.
|
|
19
|
+
*
|
|
20
|
+
* Entry actions are data on the entry, not an installed capability, so there is
|
|
21
|
+
* nothing to look up by identifier: the surface showing the entry expands them.
|
|
22
|
+
* That also decides which screen they execute against — the one whose tree this
|
|
23
|
+
* hook runs in, rather than whichever screen happened to be current when a
|
|
24
|
+
* global provider was registered.
|
|
25
|
+
*
|
|
26
|
+
* Pass the result to `resolveActionIdentifiers` / `resolvePlayerActions` as
|
|
27
|
+
* `expandEntryActions`.
|
|
28
|
+
*/
|
|
29
|
+
export function useEntryActionsExpander(): (
|
|
30
|
+
entry: ZappEntry | ZappFeed
|
|
31
|
+
) => RegisteredAction[] {
|
|
32
|
+
const { pathname } = useRoute();
|
|
33
|
+
|
|
34
|
+
const screenContext = useScreenContext();
|
|
35
|
+
const screenData = useCurrentScreenData();
|
|
36
|
+
const screenState = screenContext?.options;
|
|
37
|
+
const screenStateStore = useScreenStateStore();
|
|
38
|
+
const actionExecutor = React.useContext(ActionExecutorContext);
|
|
39
|
+
|
|
40
|
+
// Read through a ref so the returned expander keeps a stable identity: it
|
|
41
|
+
// feeds `useMemo` dependency lists on surfaces that re-resolve their actions
|
|
42
|
+
// on every registry change.
|
|
43
|
+
const depsRef = React.useRef<EntryActionDeps>();
|
|
44
|
+
|
|
45
|
+
depsRef.current = {
|
|
46
|
+
actionExecutor,
|
|
47
|
+
actionContext: {
|
|
48
|
+
screenData,
|
|
49
|
+
screenState,
|
|
50
|
+
screenRoute: pathname,
|
|
51
|
+
screenStateStore,
|
|
52
|
+
// From the screen context, the way `useCellClick` builds the same bag.
|
|
53
|
+
// The ZappPipes entry context is a route-keyed map, so reading `.data`
|
|
54
|
+
// off it was always undefined.
|
|
55
|
+
screenEntry: screenContext?.entry,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return React.useCallback(
|
|
60
|
+
(entry: ZappEntry | ZappFeed) => buildEntryActions(entry, depsRef.current),
|
|
61
|
+
[]
|
|
62
|
+
);
|
|
63
|
+
}
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-ui-components",
|
|
3
|
-
"version": "16.0.0-alpha.
|
|
3
|
+
"version": "16.0.0-alpha.1454930193",
|
|
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",
|
|
@@ -28,12 +28,13 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@applicaster/applicaster-types": "16.0.0-alpha.
|
|
32
|
-
"@applicaster/zapp-react-native-bridge": "16.0.0-alpha.
|
|
33
|
-
"@applicaster/zapp-react-native-redux": "16.0.0-alpha.
|
|
34
|
-
"@applicaster/zapp-react-native-utils": "16.0.0-alpha.
|
|
31
|
+
"@applicaster/applicaster-types": "16.0.0-alpha.1454930193",
|
|
32
|
+
"@applicaster/zapp-react-native-bridge": "16.0.0-alpha.1454930193",
|
|
33
|
+
"@applicaster/zapp-react-native-redux": "16.0.0-alpha.1454930193",
|
|
34
|
+
"@applicaster/zapp-react-native-utils": "16.0.0-alpha.1454930193",
|
|
35
35
|
"fast-json-stable-stringify": "^2.1.0",
|
|
36
36
|
"promise": "^8.3.0",
|
|
37
|
+
"react-native-sortables": "1.7.1",
|
|
37
38
|
"url": "^0.11.0",
|
|
38
39
|
"uuid": "^3.3.2"
|
|
39
40
|
},
|