@applicaster/quick-brick-player 15.1.13-alpha.1034774223 → 15.1.13
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/package.json +5 -5
- package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +39 -17
- package/src/Player/AudioLayer/Layout/Controls/__tests__/utils.test.ts +4 -25
- package/src/Player/AudioLayer/Layout/Controls/index.tsx +8 -2
- package/src/Player/AudioLayer/Layout/Controls/utils.ts +0 -14
- package/src/Player/AudioLayer/Layout/TransportInfo/index.tsx +8 -69
- package/src/Player/AudioLayer/SleepTimerManager.ts +1 -5
- package/src/Player/AudioLayer/SpeedManager.ts +0 -4
- package/src/Player/AudioLayer/components/SleepTimerModalHeader/SleepTimerModalHeader.tsx +66 -0
- package/src/Player/AudioLayer/components/SleepTimerModalHeader/index.ts +1 -0
- package/src/Player/AudioLayer/useSleepTimerModal.ts +57 -20
- package/src/Player/AudioLayer/Layout/TransportInfo/__tests__/index.test.tsx +0 -257
- package/src/Player/AudioLayer/__tests__/SleepTimerManager.test.ts +0 -78
- package/src/Player/AudioLayer/__tests__/SpeedManager.test.ts +0 -57
- package/src/Player/AudioLayer/__tests__/playerActions.test.ts +0 -369
- package/src/Player/AudioLayer/playerActions.ts +0 -294
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { render, waitFor } from "@testing-library/react-native";
|
|
3
|
-
|
|
4
|
-
import { TransportInfo } from "../index";
|
|
5
|
-
import { resolvePlayerActions } from "@applicaster/zapp-react-native-utils/playerUtils";
|
|
6
|
-
import { usePlugins } from "@applicaster/zapp-react-native-redux/hooks";
|
|
7
|
-
import { TransportInfo as MobileTransportInfo } from "@applicaster/quick-brick-mobile-transport-controls";
|
|
8
|
-
import { uiActionsRegistry } from "@applicaster/zapp-react-native-utils/uiActionsRegistrator";
|
|
9
|
-
import { PLAYER_ACTION_IDENTIFIERS } from "../../../playerActions";
|
|
10
|
-
|
|
11
|
-
jest.mock("@applicaster/zapp-react-native-utils/reactHooks", () => ({
|
|
12
|
-
useIsTablet: () => false,
|
|
13
|
-
}));
|
|
14
|
-
|
|
15
|
-
jest.mock("react-native-safe-area-context", () => ({
|
|
16
|
-
useSafeAreaFrame: () => ({ height: 800, width: 400 }),
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
|
-
jest.mock(
|
|
20
|
-
"@applicaster/zapp-react-native-ui-components/Hooks/useEntryActionsExpander",
|
|
21
|
-
() => ({ useEntryActionsExpander: () => () => [] })
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
jest.mock("@applicaster/zapp-react-native-redux/hooks", () => ({
|
|
25
|
-
usePlugins: jest.fn(() => []),
|
|
26
|
-
}));
|
|
27
|
-
|
|
28
|
-
jest.mock("@applicaster/zapp-react-native-utils/playerUtils", () => {
|
|
29
|
-
const actual = jest.requireActual(
|
|
30
|
-
"@applicaster/zapp-react-native-utils/playerUtils"
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
...actual,
|
|
35
|
-
resolvePlayerActions: jest.fn(),
|
|
36
|
-
};
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
jest.mock("@applicaster/quick-brick-mobile-transport-controls", () => ({
|
|
40
|
-
TransportInfo: jest.fn(() => null),
|
|
41
|
-
}));
|
|
42
|
-
|
|
43
|
-
const MobileTransportInfoMock = MobileTransportInfo as unknown as jest.Mock;
|
|
44
|
-
const resolvePlayerActionsMock = resolvePlayerActions as jest.Mock;
|
|
45
|
-
const usePluginsMock = usePlugins as jest.Mock;
|
|
46
|
-
|
|
47
|
-
const action = (identifier: string) =>
|
|
48
|
-
({ identifier, action: { invokeAction: jest.fn() } }) as any;
|
|
49
|
-
|
|
50
|
-
const valueForLayout = (layout?: string): GetValue =>
|
|
51
|
-
((key: string) =>
|
|
52
|
-
key === "player_controls_layout" ? layout : null) as unknown as GetValue;
|
|
53
|
-
|
|
54
|
-
const entry: ZappEntry = {
|
|
55
|
-
id: "audio-1",
|
|
56
|
-
title: "Track",
|
|
57
|
-
type: { value: "audio" },
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const layoutState = { inline: false, docked: false, isModal: true };
|
|
61
|
-
|
|
62
|
-
describe("Audio TransportInfo overflow wiring", () => {
|
|
63
|
-
let getActionsSpy: jest.SpyInstance;
|
|
64
|
-
|
|
65
|
-
beforeEach(() => {
|
|
66
|
-
jest.clearAllMocks();
|
|
67
|
-
|
|
68
|
-
resolvePlayerActionsMock.mockReturnValue([
|
|
69
|
-
action("favorites"),
|
|
70
|
-
action("offline-content-button"),
|
|
71
|
-
action("native_share_action"),
|
|
72
|
-
]);
|
|
73
|
-
|
|
74
|
-
getActionsSpy = jest
|
|
75
|
-
.spyOn(uiActionsRegistry, "getActions")
|
|
76
|
-
.mockImplementation((identifier: string) => {
|
|
77
|
-
if (
|
|
78
|
-
identifier === PLAYER_ACTION_IDENTIFIERS.PLAYBACK_SPEED ||
|
|
79
|
-
identifier === PLAYER_ACTION_IDENTIFIERS.SLEEP_TIMER
|
|
80
|
-
) {
|
|
81
|
-
return [action(identifier)];
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return [];
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
afterEach(() => {
|
|
89
|
-
getActionsSpy.mockRestore();
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it("passes resolved actions as descriptors and enables overflow when the plugin is present", () => {
|
|
93
|
-
usePluginsMock.mockReturnValue([
|
|
94
|
-
{ identifier: "open-modal-bottom-sheet-cell-action" },
|
|
95
|
-
]);
|
|
96
|
-
|
|
97
|
-
render(
|
|
98
|
-
<TransportInfo
|
|
99
|
-
entry={entry}
|
|
100
|
-
layoutState={layoutState}
|
|
101
|
-
value={valueForLayout("playback_speed_and_sleep_timer")}
|
|
102
|
-
configuration={{ player_action_buttons: "a,b,c" }}
|
|
103
|
-
/>
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
expect(resolvePlayerActionsMock).toHaveBeenCalledWith({
|
|
107
|
-
configuration: { player_action_buttons: "a,b,c" },
|
|
108
|
-
entry,
|
|
109
|
-
expandEntryActions: expect.any(Function),
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
const props = MobileTransportInfoMock.mock.calls[0][0];
|
|
113
|
-
|
|
114
|
-
expect(props.actionButtonDescriptors).toEqual([
|
|
115
|
-
{
|
|
116
|
-
kind: "action",
|
|
117
|
-
action: expect.objectContaining({ identifier: "favorites" }),
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
kind: "action",
|
|
121
|
-
action: expect.objectContaining({
|
|
122
|
-
identifier: "offline-content-button",
|
|
123
|
-
}),
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
kind: "action",
|
|
127
|
-
action: expect.objectContaining({ identifier: "native_share_action" }),
|
|
128
|
-
},
|
|
129
|
-
]);
|
|
130
|
-
|
|
131
|
-
expect(props.actionButtons).toBeUndefined();
|
|
132
|
-
|
|
133
|
-
// Controls row renders speed and sleep timer itself in this layout.
|
|
134
|
-
expect(getActionsSpy).not.toHaveBeenCalled();
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it("appends playback speed and sleep timer when playlist controls layout is active", async () => {
|
|
138
|
-
usePluginsMock.mockReturnValue([
|
|
139
|
-
{ identifier: "open-modal-bottom-sheet-cell-action" },
|
|
140
|
-
]);
|
|
141
|
-
|
|
142
|
-
resolvePlayerActionsMock.mockReturnValue([
|
|
143
|
-
action("favorites"),
|
|
144
|
-
action("offline-content-button"),
|
|
145
|
-
]);
|
|
146
|
-
|
|
147
|
-
// The audio player registers these in a later effect than our first paint.
|
|
148
|
-
let registered = false;
|
|
149
|
-
|
|
150
|
-
getActionsSpy.mockImplementation((identifier: string) => {
|
|
151
|
-
if (
|
|
152
|
-
!registered ||
|
|
153
|
-
(identifier !== PLAYER_ACTION_IDENTIFIERS.PLAYBACK_SPEED &&
|
|
154
|
-
identifier !== PLAYER_ACTION_IDENTIFIERS.SLEEP_TIMER)
|
|
155
|
-
) {
|
|
156
|
-
return [];
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return [action(identifier)];
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
render(
|
|
163
|
-
<TransportInfo
|
|
164
|
-
entry={entry}
|
|
165
|
-
layoutState={layoutState}
|
|
166
|
-
value={valueForLayout("playlist_controls")}
|
|
167
|
-
configuration={{ player_action_buttons: "favorites,offline" }}
|
|
168
|
-
/>
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
expect(
|
|
172
|
-
MobileTransportInfoMock.mock.calls[0][0].actionButtonDescriptors
|
|
173
|
-
).toEqual([
|
|
174
|
-
{
|
|
175
|
-
kind: "action",
|
|
176
|
-
action: expect.objectContaining({ identifier: "favorites" }),
|
|
177
|
-
},
|
|
178
|
-
{
|
|
179
|
-
kind: "action",
|
|
180
|
-
action: expect.objectContaining({
|
|
181
|
-
identifier: "offline-content-button",
|
|
182
|
-
}),
|
|
183
|
-
},
|
|
184
|
-
]);
|
|
185
|
-
|
|
186
|
-
// Registering for real is what tells subscribers to re-read the registry.
|
|
187
|
-
registered = true;
|
|
188
|
-
|
|
189
|
-
const unregister = uiActionsRegistry.registerAction(
|
|
190
|
-
PLAYER_ACTION_IDENTIFIERS.PLAYBACK_SPEED,
|
|
191
|
-
{ invokeAction: jest.fn() }
|
|
192
|
-
);
|
|
193
|
-
|
|
194
|
-
await waitFor(() => {
|
|
195
|
-
const latest =
|
|
196
|
-
MobileTransportInfoMock.mock.calls[
|
|
197
|
-
MobileTransportInfoMock.mock.calls.length - 1
|
|
198
|
-
][0];
|
|
199
|
-
|
|
200
|
-
expect(latest.actionButtonDescriptors).toEqual([
|
|
201
|
-
{
|
|
202
|
-
kind: "action",
|
|
203
|
-
action: expect.objectContaining({ identifier: "favorites" }),
|
|
204
|
-
},
|
|
205
|
-
{
|
|
206
|
-
kind: "action",
|
|
207
|
-
action: expect.objectContaining({
|
|
208
|
-
identifier: "offline-content-button",
|
|
209
|
-
}),
|
|
210
|
-
},
|
|
211
|
-
{
|
|
212
|
-
kind: "action",
|
|
213
|
-
action: expect.objectContaining({
|
|
214
|
-
identifier: PLAYER_ACTION_IDENTIFIERS.PLAYBACK_SPEED,
|
|
215
|
-
}),
|
|
216
|
-
},
|
|
217
|
-
{
|
|
218
|
-
kind: "action",
|
|
219
|
-
action: expect.objectContaining({
|
|
220
|
-
identifier: PLAYER_ACTION_IDENTIFIERS.SLEEP_TIMER,
|
|
221
|
-
}),
|
|
222
|
-
},
|
|
223
|
-
]);
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
unregister();
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
it("does not duplicate playback speed or sleep timer if already configured", () => {
|
|
230
|
-
resolvePlayerActionsMock.mockReturnValue([
|
|
231
|
-
action("favorites"),
|
|
232
|
-
action(PLAYER_ACTION_IDENTIFIERS.PLAYBACK_SPEED),
|
|
233
|
-
action(PLAYER_ACTION_IDENTIFIERS.SLEEP_TIMER),
|
|
234
|
-
]);
|
|
235
|
-
|
|
236
|
-
render(
|
|
237
|
-
<TransportInfo
|
|
238
|
-
entry={entry}
|
|
239
|
-
layoutState={layoutState}
|
|
240
|
-
value={valueForLayout("playlist_controls")}
|
|
241
|
-
configuration={{}}
|
|
242
|
-
/>
|
|
243
|
-
);
|
|
244
|
-
|
|
245
|
-
const identifiers =
|
|
246
|
-
MobileTransportInfoMock.mock.calls[0][0].actionButtonDescriptors.map(
|
|
247
|
-
(descriptor: { action: { identifier: string } }) =>
|
|
248
|
-
descriptor.action.identifier
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
expect(identifiers).toEqual([
|
|
252
|
-
"favorites",
|
|
253
|
-
PLAYER_ACTION_IDENTIFIERS.PLAYBACK_SPEED,
|
|
254
|
-
PLAYER_ACTION_IDENTIFIERS.SLEEP_TIMER,
|
|
255
|
-
]);
|
|
256
|
-
});
|
|
257
|
-
});
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
|
|
2
|
-
import { SLEEP_TIMER_OFF, SleepTimerController } from "../SleepTimerManager";
|
|
3
|
-
|
|
4
|
-
jest.mock(
|
|
5
|
-
"@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage",
|
|
6
|
-
() => ({
|
|
7
|
-
localStorage: {
|
|
8
|
-
getItem: jest.fn(),
|
|
9
|
-
setItem: jest.fn(),
|
|
10
|
-
removeItem: jest.fn(),
|
|
11
|
-
},
|
|
12
|
-
})
|
|
13
|
-
);
|
|
14
|
-
|
|
15
|
-
describe("SleepTimerController", () => {
|
|
16
|
-
const player = {
|
|
17
|
-
startSleepTimer: jest.fn(),
|
|
18
|
-
cancelSleepTimer: jest.fn(),
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
let sleepController: SleepTimerController;
|
|
22
|
-
|
|
23
|
-
beforeEach(() => {
|
|
24
|
-
jest.clearAllMocks();
|
|
25
|
-
(localStorage.setItem as jest.Mock).mockResolvedValue(undefined);
|
|
26
|
-
(localStorage.removeItem as jest.Mock).mockResolvedValue(undefined);
|
|
27
|
-
(localStorage.getItem as jest.Mock).mockResolvedValue(undefined);
|
|
28
|
-
sleepController = new SleepTimerController(player as any);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("starts from the off state", () => {
|
|
32
|
-
expect(sleepController.currentState.timer).toEqual(SLEEP_TIMER_OFF);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("starts a sleep timer on the constructed player", async () => {
|
|
36
|
-
await sleepController.setSleepTimer({
|
|
37
|
-
label: "15 minutes",
|
|
38
|
-
value: 15,
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
expect(player.cancelSleepTimer).toHaveBeenCalled();
|
|
42
|
-
expect(player.startSleepTimer).toHaveBeenCalled();
|
|
43
|
-
expect(sleepController.currentState.timer.value).toBe(15);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("loadSleep restores an unexpired timer", async () => {
|
|
47
|
-
const futureSleepDate = Date.now() + 60 * 1000;
|
|
48
|
-
|
|
49
|
-
(localStorage.getItem as jest.Mock).mockResolvedValue({
|
|
50
|
-
label: "15 minutes",
|
|
51
|
-
value: 15,
|
|
52
|
-
futureSleepDate,
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
player.startSleepTimer.mockClear();
|
|
56
|
-
|
|
57
|
-
await sleepController.loadSleep();
|
|
58
|
-
|
|
59
|
-
expect(player.startSleepTimer).toHaveBeenCalledWith(futureSleepDate);
|
|
60
|
-
expect(sleepController.currentState.timer.value).toBe(15);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("loadSleep drops an expired timer without starting a native timer", async () => {
|
|
64
|
-
(localStorage.getItem as jest.Mock).mockResolvedValue({
|
|
65
|
-
label: "15 minutes",
|
|
66
|
-
value: 15,
|
|
67
|
-
futureSleepDate: Date.now() - 1000,
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
player.startSleepTimer.mockClear();
|
|
71
|
-
|
|
72
|
-
await sleepController.loadSleep();
|
|
73
|
-
|
|
74
|
-
expect(player.startSleepTimer).not.toHaveBeenCalled();
|
|
75
|
-
expect(localStorage.removeItem).toHaveBeenCalled();
|
|
76
|
-
expect(sleepController.currentState.timer).toEqual(SLEEP_TIMER_OFF);
|
|
77
|
-
});
|
|
78
|
-
});
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
|
|
2
|
-
import { DEFAULT_SPEED, SpeedController } from "../SpeedManager";
|
|
3
|
-
|
|
4
|
-
jest.mock(
|
|
5
|
-
"@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage",
|
|
6
|
-
() => ({
|
|
7
|
-
localStorage: {
|
|
8
|
-
getItem: jest.fn(),
|
|
9
|
-
setItem: jest.fn(),
|
|
10
|
-
removeItem: jest.fn(),
|
|
11
|
-
},
|
|
12
|
-
})
|
|
13
|
-
);
|
|
14
|
-
|
|
15
|
-
describe("SpeedController", () => {
|
|
16
|
-
const player = { setPlaybackRate: jest.fn() };
|
|
17
|
-
let speedController: SpeedController;
|
|
18
|
-
|
|
19
|
-
beforeEach(() => {
|
|
20
|
-
jest.clearAllMocks();
|
|
21
|
-
(localStorage.setItem as jest.Mock).mockResolvedValue(undefined);
|
|
22
|
-
(localStorage.getItem as jest.Mock).mockResolvedValue(undefined);
|
|
23
|
-
speedController = new SpeedController(player as any);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("requires a player in the constructor", () => {
|
|
27
|
-
expect(speedController.currentSpeed).toEqual(DEFAULT_SPEED);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("applies playback speed on the constructed player", async () => {
|
|
31
|
-
await speedController.setPlaybackSpeed({
|
|
32
|
-
asset: "speed_1_5",
|
|
33
|
-
value: 1.5,
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
expect(player.setPlaybackRate).toHaveBeenCalledWith(1.5);
|
|
37
|
-
|
|
38
|
-
expect(speedController.currentSpeed).toEqual({
|
|
39
|
-
asset: "speed_1_5",
|
|
40
|
-
value: 1.5,
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("loadSpeed restores a stored rate onto the player", async () => {
|
|
45
|
-
(localStorage.getItem as jest.Mock).mockResolvedValue({
|
|
46
|
-
asset: "speed_1_5",
|
|
47
|
-
value: 1.5,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
player.setPlaybackRate.mockClear();
|
|
51
|
-
|
|
52
|
-
await speedController.loadSpeed();
|
|
53
|
-
|
|
54
|
-
expect(player.setPlaybackRate).toHaveBeenCalledWith(1.5);
|
|
55
|
-
expect(speedController.currentSpeed.value).toBe(1.5);
|
|
56
|
-
});
|
|
57
|
-
});
|