@applicaster/quick-brick-core 16.0.0-rc.113 → 16.0.0-rc.114
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.
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
useGetScreenOrientation,
|
|
14
14
|
} from "@applicaster/zapp-react-native-utils/appUtils/orientationHelper";
|
|
15
15
|
import { focusManager } from "@applicaster/zapp-react-native-utils/focusManager/FocusManager";
|
|
16
|
+
import { setXrayNavigator } from "@applicaster/zapp-react-native-utils/navigationUtils/xrayNavigator";
|
|
17
|
+
import { registerNavToolbox } from "./navToolbox";
|
|
16
18
|
|
|
17
19
|
import reducer, {
|
|
18
20
|
ACTIONS,
|
|
@@ -760,11 +762,11 @@ export function NavigationProvider({ children }: Props) {
|
|
|
760
762
|
const getStackForPathname = (
|
|
761
763
|
pathname: string
|
|
762
764
|
): NavigationScreenState | undefined => {
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
765
|
+
// Topmost match: the active screen if a route is on the stack twice.
|
|
766
|
+
return R.findLast(
|
|
767
|
+
({ route }) => route === pathname,
|
|
768
|
+
state?.stack?.mainStack ?? []
|
|
769
|
+
);
|
|
768
770
|
};
|
|
769
771
|
|
|
770
772
|
const navigator: QuickBrickAppNavigator = React.useMemo(
|
|
@@ -829,6 +831,13 @@ export function NavigationProvider({ children }: Props) {
|
|
|
829
831
|
navigator.state = state;
|
|
830
832
|
}
|
|
831
833
|
|
|
834
|
+
React.useLayoutEffect(() => {
|
|
835
|
+
setXrayNavigator(navigator);
|
|
836
|
+
registerNavToolbox();
|
|
837
|
+
|
|
838
|
+
return () => setXrayNavigator(undefined);
|
|
839
|
+
}, [navigator]);
|
|
840
|
+
|
|
832
841
|
React.useLayoutEffect(() => {
|
|
833
842
|
if (pathname === "/" && appLaunched) {
|
|
834
843
|
goHome(true);
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
const mockRegister = jest.fn().mockResolvedValue(undefined);
|
|
2
|
+
|
|
3
|
+
// The X-Ray mock (__mocks__/@applicaster/quick-brick-xray) has no toolbox API: give it one.
|
|
4
|
+
require("@applicaster/quick-brick-xray").registerReactToolbox = mockRegister;
|
|
5
|
+
|
|
6
|
+
// Required after the toolbox API is in place: loading them can register the player toolbox.
|
|
7
|
+
const {
|
|
8
|
+
modalStore,
|
|
9
|
+
} = require("@applicaster/zapp-react-native-utils/modalState");
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
setXrayNavigator,
|
|
13
|
+
} = require("@applicaster/zapp-react-native-utils/navigationUtils/xrayNavigator");
|
|
14
|
+
|
|
15
|
+
const {
|
|
16
|
+
playerManager,
|
|
17
|
+
} = require("@applicaster/zapp-react-native-utils/appUtils/playerManager");
|
|
18
|
+
|
|
19
|
+
const { registerNavToolbox } = require("../navToolbox");
|
|
20
|
+
|
|
21
|
+
const navigator = (overrides = {}) => {
|
|
22
|
+
const nav = {
|
|
23
|
+
getPathname: () => "/river/a/river/b",
|
|
24
|
+
getStack: () => [{ route: "/river/a" }, { route: "/river/a/river/b" }],
|
|
25
|
+
canGoBack: () => true,
|
|
26
|
+
goBack: jest.fn(),
|
|
27
|
+
goHome: jest.fn(),
|
|
28
|
+
getPlayNextOverlay: () => null,
|
|
29
|
+
getModalState: () => modalStore.getState().modalState,
|
|
30
|
+
dismissModal: jest.fn(),
|
|
31
|
+
videoModalState: { visible: false },
|
|
32
|
+
minimiseVideoModal: jest.fn(),
|
|
33
|
+
maximiseVideoModal: jest.fn(),
|
|
34
|
+
closeVideoModal: jest.fn(),
|
|
35
|
+
...overrides,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
setXrayNavigator(nav as unknown as QuickBrickAppNavigator);
|
|
39
|
+
|
|
40
|
+
return nav;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const videoModal = (mode: string) => ({
|
|
44
|
+
videoModalState: { visible: true, mode, item: { id: "v1", title: "V" } },
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const call = (name: string) => {
|
|
48
|
+
// Other toolboxes (player) may register first.
|
|
49
|
+
const { tools } = mockRegister.mock.calls.find(
|
|
50
|
+
([toolbox]) => toolbox.name === "nav"
|
|
51
|
+
)[0];
|
|
52
|
+
|
|
53
|
+
return tools.find((tool) => tool.name === name).handler({});
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
describe("navToolbox", () => {
|
|
57
|
+
beforeAll(() => registerNavToolbox());
|
|
58
|
+
|
|
59
|
+
afterEach(() => {
|
|
60
|
+
modalStore.getState().setModalState({ visible: false, screen: null });
|
|
61
|
+
jest.restoreAllMocks();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("throws until the NavigationProvider sets the navigator", () => {
|
|
65
|
+
setXrayNavigator(undefined);
|
|
66
|
+
|
|
67
|
+
expect(() => call("stack")).toThrow("Navigation is not available yet");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("goes back one screen", () => {
|
|
71
|
+
const nav = navigator();
|
|
72
|
+
|
|
73
|
+
expect(call("back")).toEqual({ from: "/river/a/river/b", wentBack: true });
|
|
74
|
+
expect(nav.goBack).toHaveBeenCalledWith(false);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("does not go back from the root", () => {
|
|
78
|
+
const nav = navigator({ canGoBack: () => false });
|
|
79
|
+
|
|
80
|
+
expect(call("back")).toMatchObject({ wentBack: false });
|
|
81
|
+
expect(nav.goBack).not.toHaveBeenCalled();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("does not pop the screen under a modal or the video modal", () => {
|
|
85
|
+
const nav = navigator(videoModal("MAXIMIZED"));
|
|
86
|
+
|
|
87
|
+
expect(call("back")).toEqual({
|
|
88
|
+
from: "video-modal/v1",
|
|
89
|
+
wentBack: false,
|
|
90
|
+
blockedBy: "video-modal",
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
modalStore.getState().openModal({ item: { id: "m" } });
|
|
94
|
+
|
|
95
|
+
expect(call("back")).toMatchObject({ blockedBy: "modal" });
|
|
96
|
+
expect(nav.goBack).not.toHaveBeenCalled();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("only dismisses Play Next when it shows", () => {
|
|
100
|
+
const nav = navigator({ getPlayNextOverlay: () => ({}) });
|
|
101
|
+
|
|
102
|
+
expect(call("back")).toMatchObject({
|
|
103
|
+
wentBack: false,
|
|
104
|
+
dismissed: "playNext",
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
expect(nav.goBack).toHaveBeenCalledWith(false);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("leaves a hook screen to cancel itself", () => {
|
|
111
|
+
const nav = navigator({ getPathname: () => "/river/a/hooks/login" });
|
|
112
|
+
|
|
113
|
+
expect(call("back")).toMatchObject({ wentBack: false, blockedBy: "hook" });
|
|
114
|
+
expect(nav.goBack).not.toHaveBeenCalled();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("goes home", () => {
|
|
118
|
+
const nav = navigator();
|
|
119
|
+
|
|
120
|
+
expect(call("home")).toEqual({ from: "/river/a/river/b", wentHome: true });
|
|
121
|
+
expect(nav.goHome).toHaveBeenCalled();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("does not go home under a modal or the video modal", () => {
|
|
125
|
+
const nav = navigator(videoModal("MAXIMIZED"));
|
|
126
|
+
|
|
127
|
+
expect(call("home")).toEqual({
|
|
128
|
+
from: "video-modal/v1",
|
|
129
|
+
wentHome: false,
|
|
130
|
+
blockedBy: "video-modal",
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
modalStore.getState().openModal({ item: { id: "m" } });
|
|
134
|
+
|
|
135
|
+
expect(call("home")).toMatchObject({ blockedBy: "modal" });
|
|
136
|
+
expect(nav.goHome).not.toHaveBeenCalled();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("goes home with the mini player docked", () => {
|
|
140
|
+
const nav = navigator(videoModal("MINIMIZED"));
|
|
141
|
+
|
|
142
|
+
expect(call("home")).toMatchObject({ wentHome: true });
|
|
143
|
+
expect(nav.goHome).toHaveBeenCalled();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("dismisses the modal store's modal, not the video modal", () => {
|
|
147
|
+
const nav = navigator(videoModal("MAXIMIZED"));
|
|
148
|
+
|
|
149
|
+
expect(() => call("dismissModal")).toThrow("No modal is open");
|
|
150
|
+
|
|
151
|
+
modalStore.getState().openModal({ item: { id: "m" } });
|
|
152
|
+
|
|
153
|
+
expect(call("dismissModal")).toBe("OK");
|
|
154
|
+
expect(nav.dismissModal).toHaveBeenCalled();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("minimises only a maximised video modal", () => {
|
|
158
|
+
const fullscreen = navigator(videoModal("FULLSCREEN"));
|
|
159
|
+
|
|
160
|
+
expect(() => call("minimizeVideoModal")).toThrow("mode is FULLSCREEN");
|
|
161
|
+
expect(fullscreen.minimiseVideoModal).not.toHaveBeenCalled();
|
|
162
|
+
|
|
163
|
+
const maximized = navigator(videoModal("MAXIMIZED"));
|
|
164
|
+
|
|
165
|
+
call("minimizeVideoModal");
|
|
166
|
+
|
|
167
|
+
expect(maximized.minimiseVideoModal).toHaveBeenCalled();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("enters and leaves fullscreen through the player, which also rotates the phone", () => {
|
|
171
|
+
const invoke = jest.spyOn(playerManager, "invokeHandler");
|
|
172
|
+
const playerContainer = jest.fn();
|
|
173
|
+
|
|
174
|
+
playerManager.on("toggleFullscreen", playerContainer);
|
|
175
|
+
|
|
176
|
+
navigator(videoModal("MAXIMIZED"));
|
|
177
|
+
call("fullscreenVideoModal");
|
|
178
|
+
|
|
179
|
+
expect(invoke).toHaveBeenCalledWith("toggleFullscreen", {
|
|
180
|
+
fullscreen: true,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const fullscreen = navigator(videoModal("FULLSCREEN"));
|
|
184
|
+
|
|
185
|
+
call("maximizeVideoModal");
|
|
186
|
+
|
|
187
|
+
expect(invoke).toHaveBeenCalledWith("toggleFullscreen", {
|
|
188
|
+
fullscreen: false,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
expect(fullscreen.maximiseVideoModal).not.toHaveBeenCalled();
|
|
192
|
+
|
|
193
|
+
playerManager.removeHandler("toggleFullscreen", playerContainer);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("does not claim fullscreen without a player, or from the mini player", () => {
|
|
197
|
+
navigator(videoModal("MAXIMIZED"));
|
|
198
|
+
|
|
199
|
+
expect(() => call("fullscreenVideoModal")).toThrow("shows no player");
|
|
200
|
+
|
|
201
|
+
navigator(videoModal("MINIMIZED"));
|
|
202
|
+
|
|
203
|
+
expect(() => call("fullscreenVideoModal")).toThrow("mode is MINIMIZED");
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("expands the mini player", () => {
|
|
207
|
+
const nav = navigator(videoModal("MINIMIZED"));
|
|
208
|
+
|
|
209
|
+
call("maximizeVideoModal");
|
|
210
|
+
|
|
211
|
+
expect(nav.maximiseVideoModal).toHaveBeenCalled();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("reports the video modal", () => {
|
|
215
|
+
navigator(videoModal("MINIMIZED"));
|
|
216
|
+
|
|
217
|
+
expect(call("videoModal")).toMatchObject({
|
|
218
|
+
visible: true,
|
|
219
|
+
mode: "MINIMIZED",
|
|
220
|
+
entry: { id: "v1", title: "V" },
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
navigator();
|
|
224
|
+
|
|
225
|
+
expect(call("videoModal")).toMatchObject({ visible: false });
|
|
226
|
+
});
|
|
227
|
+
});
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { Dimensions } from "react-native";
|
|
2
|
+
import {
|
|
3
|
+
registerXrayToolbox,
|
|
4
|
+
XrayToolSpec,
|
|
5
|
+
} from "@applicaster/zapp-react-native-utils/logger/xrayToolbox";
|
|
6
|
+
import { getXrayNavigator } from "@applicaster/zapp-react-native-utils/navigationUtils/xrayNavigator";
|
|
7
|
+
import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager";
|
|
8
|
+
import { getTopPathname } from "@applicaster/zapp-react-native-ui-components/Decorators/ZappPipesDataConnector/screenToolbox";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* X-Ray `nav.*` toolbox: lets an agent navigate by intent and read or drive the video modal,
|
|
12
|
+
* which is navigation state. The swipes behind some of these (minimising the player, dragging a
|
|
13
|
+
* sheet) are react-native-gesture-handler recognisers that no public API can feed touches to, so
|
|
14
|
+
* the outcome is offered instead. Pairs with `screen.current` to check where an action landed.
|
|
15
|
+
*
|
|
16
|
+
* Registered by the NavigationProvider, which keeps the navigator in a plain module variable
|
|
17
|
+
* (xrayNavigator), so updating it never renders anything.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const requireNavigator = (): QuickBrickAppNavigator => {
|
|
21
|
+
const navigator = getXrayNavigator();
|
|
22
|
+
|
|
23
|
+
if (!navigator) throw new Error("Navigation is not available yet");
|
|
24
|
+
|
|
25
|
+
return navigator;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** The player's own fullscreen switch: it also rotates the phone, as the fullscreen button does. */
|
|
29
|
+
const toggleFullscreen = (fullscreen: boolean) => {
|
|
30
|
+
// Only a mounted PlayerContainer handles it: not while the video modal runs its hooks or casts.
|
|
31
|
+
if (!playerManager.hasHandler("toggleFullscreen")) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
"The video modal shows no player (hooks running or casting)"
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
playerManager.invokeHandler("toggleFullscreen", { fullscreen });
|
|
38
|
+
|
|
39
|
+
return "OK: the player switches on its next tick, check with videoModal";
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const requireVideoModal = () => {
|
|
43
|
+
const nav = requireNavigator();
|
|
44
|
+
|
|
45
|
+
if (!nav.videoModalState?.visible) {
|
|
46
|
+
throw new Error("No video modal is open");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return nav;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const tools: XrayToolSpec[] = [
|
|
53
|
+
{
|
|
54
|
+
name: "stack",
|
|
55
|
+
description: "Routes on the navigation stack, root first.",
|
|
56
|
+
handler: () => ({
|
|
57
|
+
routes: requireNavigator()
|
|
58
|
+
.getStack()
|
|
59
|
+
.map(({ route }) => route),
|
|
60
|
+
}),
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "back",
|
|
64
|
+
description:
|
|
65
|
+
"Go back one screen, as the nav bar back button does: it pops the stack and does not run " +
|
|
66
|
+
"hardware back listeners (Android back key), so a screen plugin's own back handling is skipped. " +
|
|
67
|
+
"Not while a modal, the video modal (unless docked) or a hook screen is on top: blockedBy says " +
|
|
68
|
+
"which; close it first, or cancel a hook with the device back key. With Play Next showing it " +
|
|
69
|
+
"only dismisses that. Check with screen.current.",
|
|
70
|
+
handler: () => {
|
|
71
|
+
const nav = requireNavigator();
|
|
72
|
+
const from = getTopPathname() ?? nav.getPathname();
|
|
73
|
+
|
|
74
|
+
// A presented layer (hooks-modal/, modal/, video-modal/) is on top: popping the stack would
|
|
75
|
+
// change the screen underneath it, which the user's back never does.
|
|
76
|
+
if (!from.startsWith("/")) {
|
|
77
|
+
return { from, wentBack: false, blockedBy: from.split("/")[0] };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (nav.getPlayNextOverlay()) {
|
|
81
|
+
nav.goBack(false);
|
|
82
|
+
|
|
83
|
+
return { from, wentBack: false, dismissed: "playNext" };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// The hook screen answers the user's back itself (cancels its flow); a pop would skip that.
|
|
87
|
+
if (from.split("/").slice(-2)[0] === "hooks") {
|
|
88
|
+
return { from, wentBack: false, blockedBy: "hook" };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!nav.canGoBack()) return { from, wentBack: false };
|
|
92
|
+
|
|
93
|
+
nav.goBack(false);
|
|
94
|
+
|
|
95
|
+
return { from, wentBack: true };
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: "home",
|
|
100
|
+
description:
|
|
101
|
+
"Go to the home screen. Not while a modal, the video modal (unless docked) or the hooks modal is " +
|
|
102
|
+
"on top: blockedBy says which; close it first. On TV it does not move focus to the top menu as " +
|
|
103
|
+
"the remote's home does.",
|
|
104
|
+
handler: () => {
|
|
105
|
+
const nav = requireNavigator();
|
|
106
|
+
const from = getTopPathname() ?? nav.getPathname();
|
|
107
|
+
|
|
108
|
+
// goHome only replaces the stack: a presented layer would stay on top of the home screen,
|
|
109
|
+
// and it covers the menu the user would press home in.
|
|
110
|
+
if (!from.startsWith("/")) {
|
|
111
|
+
return { from, wentHome: false, blockedBy: from.split("/")[0] };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
nav.goHome();
|
|
115
|
+
|
|
116
|
+
return { from, wentHome: true };
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: "dismissModal",
|
|
121
|
+
description:
|
|
122
|
+
"Close the open modal screen or bottom sheet, as dragging it down does. Not the video modal: use closeVideoModal.",
|
|
123
|
+
handler: () => {
|
|
124
|
+
const nav = requireNavigator();
|
|
125
|
+
|
|
126
|
+
if (!nav.getModalState()?.visible) throw new Error("No modal is open");
|
|
127
|
+
|
|
128
|
+
nav.dismissModal();
|
|
129
|
+
|
|
130
|
+
return "OK";
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: "videoModal",
|
|
135
|
+
description:
|
|
136
|
+
"The video modal: visible, mode (MAXIMIZED, MINIMIZED = mini player, FULLSCREEN, PIP), its entry, " +
|
|
137
|
+
"and the screen orientation.",
|
|
138
|
+
handler: () => {
|
|
139
|
+
const videoModal = requireNavigator().videoModalState;
|
|
140
|
+
const { width, height } = Dimensions.get("window");
|
|
141
|
+
const orientation = width > height ? "landscape" : "portrait";
|
|
142
|
+
|
|
143
|
+
if (!videoModal?.visible) return { visible: false, orientation };
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
visible: true,
|
|
147
|
+
mode: videoModal.mode,
|
|
148
|
+
entry: videoModal.item
|
|
149
|
+
? { id: videoModal.item.id, title: videoModal.item.title }
|
|
150
|
+
: null,
|
|
151
|
+
orientation,
|
|
152
|
+
};
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: "minimizeVideoModal",
|
|
157
|
+
description:
|
|
158
|
+
"Minimise the maximised video modal to the mini player, as swiping it down does.",
|
|
159
|
+
handler: () => {
|
|
160
|
+
const nav = requireVideoModal();
|
|
161
|
+
const { mode } = nav.videoModalState;
|
|
162
|
+
|
|
163
|
+
// The swipe exists only on the maximised modal; other modes have no way to the mini player.
|
|
164
|
+
if (mode !== "MAXIMIZED") {
|
|
165
|
+
throw new Error(
|
|
166
|
+
`Only a maximised video modal minimises (mode is ${mode})`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
nav.minimiseVideoModal();
|
|
171
|
+
|
|
172
|
+
return "OK";
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: "maximizeVideoModal",
|
|
177
|
+
description: "Expand the mini player or leave fullscreen.",
|
|
178
|
+
handler: () => {
|
|
179
|
+
const nav = requireVideoModal();
|
|
180
|
+
|
|
181
|
+
if (nav.videoModalState.mode === "FULLSCREEN") {
|
|
182
|
+
return toggleFullscreen(false);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
nav.maximiseVideoModal();
|
|
186
|
+
|
|
187
|
+
return "OK";
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
name: "fullscreenVideoModal",
|
|
192
|
+
description:
|
|
193
|
+
"Put the video modal in fullscreen, as the player's fullscreen button does.",
|
|
194
|
+
handler: () => {
|
|
195
|
+
const { mode } = requireVideoModal().videoModalState;
|
|
196
|
+
|
|
197
|
+
// The fullscreen button is on the maximised player, not on the mini player.
|
|
198
|
+
if (mode !== "MAXIMIZED") {
|
|
199
|
+
throw new Error(
|
|
200
|
+
`Only a maximised video modal goes fullscreen (mode is ${mode})`
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return toggleFullscreen(true);
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: "closeVideoModal",
|
|
209
|
+
description: "Close the video modal and its player.",
|
|
210
|
+
handler: () => {
|
|
211
|
+
requireVideoModal().closeVideoModal();
|
|
212
|
+
|
|
213
|
+
return "OK";
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
];
|
|
217
|
+
|
|
218
|
+
export const registerNavToolbox = () =>
|
|
219
|
+
registerXrayToolbox({
|
|
220
|
+
name: "nav",
|
|
221
|
+
description:
|
|
222
|
+
"Navigate the app (stack, back, home, modals) and drive the video modal.",
|
|
223
|
+
tools,
|
|
224
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/quick-brick-core",
|
|
3
|
-
"version": "16.0.0-rc.
|
|
3
|
+
"version": "16.0.0-rc.114",
|
|
4
4
|
"description": "Core package for Applicaster's Quick Brick App",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -28,13 +28,13 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@applicaster/applicaster-types": "16.0.0-rc.
|
|
32
|
-
"@applicaster/quick-brick-core-plugins": "16.0.0-rc.
|
|
33
|
-
"@applicaster/zapp-pipes-v2-client": "16.0.0-rc.
|
|
34
|
-
"@applicaster/zapp-react-native-bridge": "16.0.0-rc.
|
|
35
|
-
"@applicaster/zapp-react-native-redux": "16.0.0-rc.
|
|
36
|
-
"@applicaster/zapp-react-native-ui-components": "16.0.0-rc.
|
|
37
|
-
"@applicaster/zapp-react-native-utils": "16.0.0-rc.
|
|
31
|
+
"@applicaster/applicaster-types": "16.0.0-rc.114",
|
|
32
|
+
"@applicaster/quick-brick-core-plugins": "16.0.0-rc.114",
|
|
33
|
+
"@applicaster/zapp-pipes-v2-client": "16.0.0-rc.114",
|
|
34
|
+
"@applicaster/zapp-react-native-bridge": "16.0.0-rc.114",
|
|
35
|
+
"@applicaster/zapp-react-native-redux": "16.0.0-rc.114",
|
|
36
|
+
"@applicaster/zapp-react-native-ui-components": "16.0.0-rc.114",
|
|
37
|
+
"@applicaster/zapp-react-native-utils": "16.0.0-rc.114",
|
|
38
38
|
"atob": "^2.1.2",
|
|
39
39
|
"axios": "^0.28.0",
|
|
40
40
|
"btoa": "^1.2.1",
|