@applicaster/zapp-react-native-ui-components 15.0.0-rc.98 → 15.1.0-rc.1
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/BaseFocusable/index.ios.ts +2 -12
- package/Components/Cell/FocusableWrapper.tsx +0 -3
- package/Components/Cell/TvOSCellComponent.tsx +0 -5
- package/Components/Focusable/Focusable.tsx +2 -4
- package/Components/Focusable/FocusableTvOS.tsx +1 -18
- package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +0 -1
- package/Components/FocusableGroup/FocusableTvOS.tsx +1 -30
- package/Components/GeneralContentScreen/GeneralContentScreen.tsx +39 -28
- package/Components/GeneralContentScreen/__tests__/GeneralContentScreen.test.tsx +104 -0
- package/Components/GeneralContentScreen/utils/__tests__/getScreenDataSource.test.ts +19 -0
- package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
- package/Components/GeneralContentScreen/utils/getScreenDataSource.ts +9 -0
- package/Components/HandlePlayable/HandlePlayable.tsx +24 -42
- package/Components/HandlePlayable/utils.ts +31 -0
- package/Components/HookRenderer/HookRenderer.tsx +40 -10
- package/Components/HookRenderer/__tests__/HookRenderer.test.tsx +60 -0
- package/Components/Layout/TV/LayoutBackground.tsx +2 -5
- package/Components/Layout/TV/ScreenContainer.tsx +6 -2
- package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +5 -0
- package/Components/Layout/TV/index.tsx +4 -3
- package/Components/Layout/TV/index.web.tsx +4 -3
- package/Components/LinkHandler/LinkHandler.tsx +2 -2
- package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +10 -4
- package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +1 -5
- package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +3 -11
- package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +1 -9
- package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +14 -15
- package/Components/MasterCell/DefaultComponents/LiveImage/__tests__/prepareEntry.test.ts +352 -0
- package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +136 -0
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +34 -16
- package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +6 -7
- package/Components/MasterCell/DefaultComponents/Text/index.tsx +2 -6
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +2 -6
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +11 -233
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +15 -19
- package/Components/Navigator/StackNavigator.tsx +6 -0
- package/Components/OfflineHandler/NotificationView/NotificationView.tsx +2 -2
- package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +18 -17
- package/Components/OfflineHandler/__tests__/index.test.tsx +18 -27
- package/Components/PlayerContainer/PlayerContainer.tsx +14 -32
- package/Components/PreloaderWrapper/__tests__/index.test.tsx +26 -0
- package/Components/PreloaderWrapper/index.tsx +15 -0
- package/Components/River/ComponentsMap/ComponentsMap.tsx +15 -0
- package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
- package/Components/River/RefreshControl.tsx +9 -3
- package/Components/River/RiverItem.tsx +26 -20
- package/Components/River/TV/River.tsx +14 -31
- package/Components/River/TV/index.tsx +4 -8
- package/Components/River/TV/withTVEventHandler.tsx +36 -0
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -0
- package/Components/Screen/TV/index.web.tsx +2 -4
- package/Components/Screen/__tests__/Screen.test.tsx +43 -65
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +44 -68
- package/Components/Screen/hooks.ts +76 -5
- package/Components/Screen/index.tsx +10 -3
- package/Components/Screen/orientationHandler.ts +3 -3
- package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +46 -0
- package/Components/ScreenFeedLoader/__tests__/ScreenFeedLoader.test.tsx +94 -0
- package/Components/ScreenFeedLoader/index.ts +1 -0
- package/Components/ScreenResolver/__tests__/screenResolver.test.js +24 -0
- package/Components/ScreenResolver/hooks/index.ts +3 -0
- package/Components/ScreenResolver/hooks/useGetComponent.ts +15 -0
- package/Components/ScreenResolver/hooks/useScreenComponentResolver.tsx +90 -0
- package/Components/ScreenResolver/index.tsx +9 -115
- package/Components/ScreenResolver/utils/__tests__/getScreenTypeProps.test.ts +45 -0
- package/Components/ScreenResolver/utils/getScreenTypeProps.ts +43 -0
- package/Components/ScreenResolver/utils/index.ts +1 -0
- package/Components/ScreenResolver/withDefaultScreenContext.tsx +16 -0
- package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +25 -0
- package/Components/ScreenResolverFeedProvider/__tests__/ScreenResolverFeedProvider.test.tsx +44 -0
- package/Components/ScreenResolverFeedProvider/index.ts +1 -0
- package/Components/ScreenRevealManager/ScreenRevealManager.ts +8 -40
- package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +69 -86
- package/Components/ScreenRevealManager/withScreenRevealManager.tsx +4 -1
- package/Components/Tabs/TabContent.tsx +4 -7
- package/Components/Transitioner/Scene.tsx +9 -15
- package/Components/Transitioner/index.js +3 -3
- package/Components/VideoLive/LiveImageManager.ts +199 -54
- package/Components/VideoLive/PlayerLiveImageComponent.tsx +31 -33
- package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +2 -17
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +5 -5
- package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +7 -15
- package/Components/VideoModal/utils.ts +9 -12
- package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
- package/Components/ZappFrameworkComponents/BarView/BarView.tsx +6 -4
- package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
- package/Components/ZappUIComponent/index.tsx +12 -6
- package/Components/index.js +1 -1
- package/Contexts/ScreenContext/__tests__/index.test.tsx +57 -0
- package/Contexts/ScreenContext/index.tsx +64 -26
- package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
- package/Contexts/ZappPipesContext/ZappPipesContextFactory.tsx +18 -7
- package/Decorators/Analytics/index.tsx +5 -6
- package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +0 -1
- package/Decorators/ConfigurationWrapper/const.ts +0 -1
- package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -7
- package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +212 -5
- package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
- package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
- package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
- package/Helpers/DataSourceHelper/index.js +19 -0
- package/events/index.ts +2 -0
- package/events/scrollEndReached.ts +15 -0
- package/package.json +5 -5
- package/Components/MasterCell/DefaultComponents/Text/utils/__tests__/withAdjustedLineHeight.test.ts +0 -46
- package/Components/MasterCell/DefaultComponents/Text/utils/index.ts +0 -21
- package/Components/PlayerContainer/ErrorDisplay/ErrorDisplay.tsx +0 -57
- package/Components/PlayerContainer/ErrorDisplay/index.ts +0 -9
- package/Components/PlayerContainer/useRestrictMobilePlayback.tsx +0 -101
- package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +0 -30
- package/Components/River/TV/utils/index.ts +0 -4
- package/Components/River/TV/withFocusableGroupForContent.tsx +0 -71
- package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +0 -80
- package/Helpers/DataSourceHelper/index.ts +0 -19
- /package/Components/HookRenderer/{index.tsx → index.ts} +0 -0
|
@@ -7,15 +7,30 @@ import { UrlFeedResolver } from "../resolvers/UrlFeedResolver";
|
|
|
7
7
|
import { NullFeedResolver } from "../resolvers/NullFeedResolver";
|
|
8
8
|
|
|
9
9
|
jest.mock("../resolvers/StaticFeedResolver", () => ({
|
|
10
|
-
StaticFeedResolver: jest.fn(() =>
|
|
10
|
+
StaticFeedResolver: jest.fn(({ children }) =>
|
|
11
|
+
children({
|
|
12
|
+
zappPipesData: { loading: true, data: null, error: null },
|
|
13
|
+
reloadData: jest.fn(),
|
|
14
|
+
})
|
|
15
|
+
),
|
|
11
16
|
}));
|
|
12
17
|
|
|
13
18
|
jest.mock("../resolvers/UrlFeedResolver", () => ({
|
|
14
|
-
UrlFeedResolver: jest.fn(() =>
|
|
19
|
+
UrlFeedResolver: jest.fn(({ children }) =>
|
|
20
|
+
children({
|
|
21
|
+
zappPipesData: { loading: true, data: null, error: null },
|
|
22
|
+
reloadData: jest.fn(),
|
|
23
|
+
})
|
|
24
|
+
),
|
|
15
25
|
}));
|
|
16
26
|
|
|
17
27
|
jest.mock("../resolvers/NullFeedResolver", () => ({
|
|
18
|
-
NullFeedResolver: jest.fn(() =>
|
|
28
|
+
NullFeedResolver: jest.fn(({ children }) =>
|
|
29
|
+
children({
|
|
30
|
+
zappPipesData: { loading: false, data: null, error: null },
|
|
31
|
+
reloadData: jest.fn(),
|
|
32
|
+
})
|
|
33
|
+
),
|
|
19
34
|
}));
|
|
20
35
|
|
|
21
36
|
const testPlugin = {
|
|
@@ -52,7 +67,7 @@ describe("ResolverSelector", () => {
|
|
|
52
67
|
expect.objectContaining({
|
|
53
68
|
getStaticComponentFeed: props.getStaticComponentFeed,
|
|
54
69
|
component: mockComponent,
|
|
55
|
-
children:
|
|
70
|
+
children: expect.any(Function),
|
|
56
71
|
}),
|
|
57
72
|
expect.anything()
|
|
58
73
|
);
|
|
@@ -151,7 +166,10 @@ describe("ResolverSelector", () => {
|
|
|
151
166
|
render(<ResolverSelector {...props} />);
|
|
152
167
|
|
|
153
168
|
expect(StaticFeedResolver).toHaveBeenCalledWith(
|
|
154
|
-
expect.objectContaining(
|
|
169
|
+
expect.objectContaining({
|
|
170
|
+
...props,
|
|
171
|
+
children: expect.any(Function),
|
|
172
|
+
}),
|
|
155
173
|
expect.anything()
|
|
156
174
|
);
|
|
157
175
|
});
|
|
@@ -202,4 +220,193 @@ describe("ResolverSelector", () => {
|
|
|
202
220
|
expect(UrlFeedResolver).not.toHaveBeenCalled();
|
|
203
221
|
expect(NullFeedResolver).not.toHaveBeenCalled();
|
|
204
222
|
});
|
|
223
|
+
|
|
224
|
+
describe("Fallback Logic", () => {
|
|
225
|
+
it("should fallback to UrlFeedResolver when StaticFeedResolver returns null and feedUrl is present", () => {
|
|
226
|
+
const props = {
|
|
227
|
+
getStaticComponentFeed: jest.fn(),
|
|
228
|
+
feedUrl: "https://example.com/feed",
|
|
229
|
+
component: mockComponent,
|
|
230
|
+
children: mockChildren,
|
|
231
|
+
riverId: "test-river",
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
(StaticFeedResolver as jest.Mock).mockImplementation(({ children }) =>
|
|
235
|
+
children({
|
|
236
|
+
zappPipesData: { loading: false, data: null, error: null },
|
|
237
|
+
reloadData: jest.fn(),
|
|
238
|
+
})
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
render(<ResolverSelector {...props} />);
|
|
242
|
+
|
|
243
|
+
expect(StaticFeedResolver).toHaveBeenCalled();
|
|
244
|
+
|
|
245
|
+
expect(UrlFeedResolver).toHaveBeenCalledWith(
|
|
246
|
+
expect.objectContaining({
|
|
247
|
+
feedUrl: props.feedUrl,
|
|
248
|
+
component: mockComponent,
|
|
249
|
+
children: mockChildren,
|
|
250
|
+
}),
|
|
251
|
+
expect.anything()
|
|
252
|
+
);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("should fallback to UrlFeedResolver when StaticFeedResolver returns null and component.data.source is present", () => {
|
|
256
|
+
const componentWithSource = {
|
|
257
|
+
...mockComponent,
|
|
258
|
+
data: {
|
|
259
|
+
source: "data-source",
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const props = {
|
|
264
|
+
getStaticComponentFeed: jest.fn(),
|
|
265
|
+
component: componentWithSource,
|
|
266
|
+
children: mockChildren,
|
|
267
|
+
riverId: "test-river",
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
(StaticFeedResolver as jest.Mock).mockImplementation(({ children }) =>
|
|
271
|
+
children({
|
|
272
|
+
zappPipesData: { loading: false, data: null, error: null },
|
|
273
|
+
reloadData: jest.fn(),
|
|
274
|
+
})
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
render(<ResolverSelector {...props} />);
|
|
278
|
+
|
|
279
|
+
expect(StaticFeedResolver).toHaveBeenCalled();
|
|
280
|
+
|
|
281
|
+
expect(UrlFeedResolver).toHaveBeenCalledWith(
|
|
282
|
+
expect.objectContaining({
|
|
283
|
+
component: componentWithSource,
|
|
284
|
+
children: mockChildren,
|
|
285
|
+
}),
|
|
286
|
+
expect.anything()
|
|
287
|
+
);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it("should fallback to NullFeedResolver when StaticFeedResolver returns null and no feedUrl/source is present", () => {
|
|
291
|
+
const props = {
|
|
292
|
+
getStaticComponentFeed: jest.fn(),
|
|
293
|
+
component: mockComponent,
|
|
294
|
+
children: mockChildren,
|
|
295
|
+
riverId: "test-river",
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
(StaticFeedResolver as jest.Mock).mockImplementation(({ children }) =>
|
|
299
|
+
children({
|
|
300
|
+
zappPipesData: { loading: false, data: null, error: null },
|
|
301
|
+
reloadData: jest.fn(),
|
|
302
|
+
})
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
render(<ResolverSelector {...props} />);
|
|
306
|
+
|
|
307
|
+
expect(StaticFeedResolver).toHaveBeenCalled();
|
|
308
|
+
expect(UrlFeedResolver).not.toHaveBeenCalled();
|
|
309
|
+
|
|
310
|
+
expect(NullFeedResolver).toHaveBeenCalledWith(
|
|
311
|
+
expect.objectContaining({
|
|
312
|
+
children: mockChildren,
|
|
313
|
+
}),
|
|
314
|
+
expect.anything()
|
|
315
|
+
);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it("should NOT fallback and call children with static data when StaticFeedResolver returns data", () => {
|
|
319
|
+
const staticData = { entry: [{ id: "1" }] };
|
|
320
|
+
|
|
321
|
+
const props = {
|
|
322
|
+
getStaticComponentFeed: jest.fn(),
|
|
323
|
+
feedUrl: "https://example.com/feed",
|
|
324
|
+
component: mockComponent,
|
|
325
|
+
children: mockChildren,
|
|
326
|
+
riverId: "test-river",
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
(StaticFeedResolver as jest.Mock).mockImplementation(({ children }) =>
|
|
330
|
+
children({
|
|
331
|
+
zappPipesData: { loading: false, data: staticData, error: null },
|
|
332
|
+
reloadData: jest.fn(),
|
|
333
|
+
})
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
render(<ResolverSelector {...props} />);
|
|
337
|
+
|
|
338
|
+
expect(StaticFeedResolver).toHaveBeenCalled();
|
|
339
|
+
expect(UrlFeedResolver).not.toHaveBeenCalled();
|
|
340
|
+
|
|
341
|
+
expect(mockChildren).toHaveBeenCalledWith(
|
|
342
|
+
expect.objectContaining({
|
|
343
|
+
zappPipesData: expect.objectContaining({
|
|
344
|
+
data: staticData,
|
|
345
|
+
}),
|
|
346
|
+
})
|
|
347
|
+
);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it("should NOT fallback and call children with loading state when StaticFeedResolver is loading", () => {
|
|
351
|
+
const props = {
|
|
352
|
+
getStaticComponentFeed: jest.fn(),
|
|
353
|
+
feedUrl: "https://example.com/feed",
|
|
354
|
+
component: mockComponent,
|
|
355
|
+
children: mockChildren,
|
|
356
|
+
riverId: "test-river",
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
(StaticFeedResolver as jest.Mock).mockImplementation(({ children }) =>
|
|
360
|
+
children({
|
|
361
|
+
zappPipesData: { loading: true, data: null, error: null },
|
|
362
|
+
reloadData: jest.fn(),
|
|
363
|
+
})
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
render(<ResolverSelector {...props} />);
|
|
367
|
+
|
|
368
|
+
expect(StaticFeedResolver).toHaveBeenCalled();
|
|
369
|
+
expect(UrlFeedResolver).not.toHaveBeenCalled();
|
|
370
|
+
|
|
371
|
+
expect(mockChildren).toHaveBeenCalledWith(
|
|
372
|
+
expect.objectContaining({
|
|
373
|
+
zappPipesData: expect.objectContaining({
|
|
374
|
+
loading: true,
|
|
375
|
+
}),
|
|
376
|
+
})
|
|
377
|
+
);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it("should NOT fallback and call children with error when StaticFeedResolver returns error", () => {
|
|
381
|
+
const error = new Error("Static error");
|
|
382
|
+
|
|
383
|
+
const props = {
|
|
384
|
+
getStaticComponentFeed: jest.fn(),
|
|
385
|
+
feedUrl: "https://example.com/feed",
|
|
386
|
+
component: mockComponent,
|
|
387
|
+
children: mockChildren,
|
|
388
|
+
riverId: "test-river",
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
(StaticFeedResolver as jest.Mock).mockImplementation(({ children }) =>
|
|
392
|
+
children({
|
|
393
|
+
zappPipesData: { loading: false, data: null, error },
|
|
394
|
+
reloadData: jest.fn(),
|
|
395
|
+
})
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
render(<ResolverSelector {...props} />);
|
|
399
|
+
|
|
400
|
+
expect(StaticFeedResolver).toHaveBeenCalled();
|
|
401
|
+
expect(UrlFeedResolver).not.toHaveBeenCalled();
|
|
402
|
+
|
|
403
|
+
expect(mockChildren).toHaveBeenCalledWith(
|
|
404
|
+
expect.objectContaining({
|
|
405
|
+
zappPipesData: expect.objectContaining({
|
|
406
|
+
error,
|
|
407
|
+
}),
|
|
408
|
+
})
|
|
409
|
+
);
|
|
410
|
+
});
|
|
411
|
+
});
|
|
205
412
|
});
|
|
@@ -3,7 +3,7 @@ import { renderWithProviders } from "@applicaster/zapp-react-native-utils/testUt
|
|
|
3
3
|
|
|
4
4
|
import * as zappPipesRedux from "@applicaster/zapp-react-native-redux/ZappPipes";
|
|
5
5
|
import configureStore from "redux-mock-store";
|
|
6
|
-
import
|
|
6
|
+
import thunk from "redux-thunk";
|
|
7
7
|
|
|
8
8
|
import { zappPipesDataConnector } from "../index";
|
|
9
9
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/// <reference types="@applicaster/zapp-react-native-ui-components" />
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
|
|
5
|
-
import {
|
|
5
|
+
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
6
6
|
import { ZappPipesSearchContext } from "@applicaster/zapp-react-native-ui-components/Contexts";
|
|
7
7
|
import { useScreenContext } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
|
|
8
8
|
import { ResolverSelector } from "./ResolverSelector";
|
|
@@ -23,7 +23,7 @@ export function zappPipesDataConnector(
|
|
|
23
23
|
) {
|
|
24
24
|
return function WrappedWithZappPipesData(props: Props) {
|
|
25
25
|
const { screenData } = useRoute();
|
|
26
|
-
const plugins =
|
|
26
|
+
const { plugins } = usePickFromState(["plugins"]);
|
|
27
27
|
const screenContextData = useScreenContext();
|
|
28
28
|
|
|
29
29
|
const {
|
|
@@ -76,7 +76,7 @@ export function StaticFeedResolver({
|
|
|
76
76
|
|
|
77
77
|
const zappPipesDataProps = useMemo(
|
|
78
78
|
() => ({
|
|
79
|
-
zappPipesData: { url, loading, data, error },
|
|
79
|
+
zappPipesData: { url, loading, data, error },
|
|
80
80
|
reloadData,
|
|
81
81
|
loadNextData: undefined, // Static resolver doesn't support pagination
|
|
82
82
|
}),
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as R from "ramda";
|
|
2
|
+
|
|
3
|
+
export function itemLimitForData(entry = [], component) {
|
|
4
|
+
const itemLimitValue = Number(R.path(["rules", "item_limit"], component));
|
|
5
|
+
|
|
6
|
+
const itemLimit =
|
|
7
|
+
itemLimitValue && itemLimitValue > 0
|
|
8
|
+
? itemLimitValue
|
|
9
|
+
: Number.MAX_SAFE_INTEGER;
|
|
10
|
+
|
|
11
|
+
const isInRange = (min, max) => R.both(R.gte(R.__, min), R.lt(R.__, max));
|
|
12
|
+
|
|
13
|
+
const entryShouldBeSliced = (entry) =>
|
|
14
|
+
isInRange(0, R.length(entry))(itemLimit);
|
|
15
|
+
|
|
16
|
+
const sliceEntriesUpToItemLimit = R.slice(0, itemLimit);
|
|
17
|
+
|
|
18
|
+
return R.when(entryShouldBeSliced, sliceEntriesUpToItemLimit)(entry);
|
|
19
|
+
}
|
package/events/index.ts
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Subject } from "rxjs";
|
|
2
|
+
import { throttleTime } from "rxjs/operators";
|
|
3
|
+
|
|
4
|
+
const SCROLL_END_THROTTLE_MS = 1000;
|
|
5
|
+
const scrollEndReachedSubject = new Subject<void>();
|
|
6
|
+
|
|
7
|
+
/* Throttle so we only emit at most once per second; RN often fires onEndReached repeatedly (e.g. on Android) when near the bottom. */
|
|
8
|
+
export const scrollEndReached$ = scrollEndReachedSubject.pipe(
|
|
9
|
+
throttleTime(SCROLL_END_THROTTLE_MS)
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
/* Call from scroll container (ComponentsMap or Tabs) when scroll reaches end. */
|
|
13
|
+
export const emitScrollEndReached = (): void => {
|
|
14
|
+
scrollEndReachedSubject.next();
|
|
15
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-ui-components",
|
|
3
|
-
"version": "15.
|
|
3
|
+
"version": "15.1.0-rc.1",
|
|
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,10 +28,10 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@applicaster/applicaster-types": "15.
|
|
32
|
-
"@applicaster/zapp-react-native-bridge": "15.
|
|
33
|
-
"@applicaster/zapp-react-native-redux": "15.
|
|
34
|
-
"@applicaster/zapp-react-native-utils": "15.
|
|
31
|
+
"@applicaster/applicaster-types": "15.1.0-rc.1",
|
|
32
|
+
"@applicaster/zapp-react-native-bridge": "15.1.0-rc.1",
|
|
33
|
+
"@applicaster/zapp-react-native-redux": "15.1.0-rc.1",
|
|
34
|
+
"@applicaster/zapp-react-native-utils": "15.1.0-rc.1",
|
|
35
35
|
"fast-json-stable-stringify": "^2.1.0",
|
|
36
36
|
"promise": "^8.3.0",
|
|
37
37
|
"url": "^0.11.0",
|
package/Components/MasterCell/DefaultComponents/Text/utils/__tests__/withAdjustedLineHeight.test.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { PixelRatio } from "react-native";
|
|
2
|
-
import { withAdjustedLineHeight } from "..";
|
|
3
|
-
|
|
4
|
-
const FONT_SCALE = 1.118;
|
|
5
|
-
jest.spyOn(PixelRatio, "getFontScale").mockReturnValue(FONT_SCALE);
|
|
6
|
-
|
|
7
|
-
describe("withAdjustedLineHeight", () => {
|
|
8
|
-
it("with provided fontScale and styles", () => {
|
|
9
|
-
// setup
|
|
10
|
-
const styles = {
|
|
11
|
-
lineHeight: 10,
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
// run
|
|
15
|
-
const result = withAdjustedLineHeight(styles);
|
|
16
|
-
|
|
17
|
-
// verify
|
|
18
|
-
expect(result).toEqual({
|
|
19
|
-
lineHeight: styles.lineHeight * FONT_SCALE,
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("with provided fontScale and empty styles", () => {
|
|
24
|
-
// setup
|
|
25
|
-
const styles = {};
|
|
26
|
-
|
|
27
|
-
// run
|
|
28
|
-
const result = withAdjustedLineHeight(styles);
|
|
29
|
-
|
|
30
|
-
// verify
|
|
31
|
-
expect(result).toEqual(styles);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it("with non-number lineHeight", () => {
|
|
35
|
-
// setup
|
|
36
|
-
const styles = {
|
|
37
|
-
lineHeight: NaN,
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
// run
|
|
41
|
-
const result = withAdjustedLineHeight(styles);
|
|
42
|
-
|
|
43
|
-
// verify
|
|
44
|
-
expect(result).toEqual(styles);
|
|
45
|
-
});
|
|
46
|
-
});
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { PixelRatio } from "react-native";
|
|
2
|
-
import { isNil, identity } from "ramda";
|
|
3
|
-
import { toNumber } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
4
|
-
import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
5
|
-
|
|
6
|
-
// HACK - to prevent text flickering
|
|
7
|
-
export const withAdjustedLineHeight = (styles) => {
|
|
8
|
-
const fontScale = PixelRatio.getFontScale();
|
|
9
|
-
const lineHeight = toNumber(styles.lineHeight);
|
|
10
|
-
|
|
11
|
-
if (isNil(lineHeight)) {
|
|
12
|
-
return styles;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return { ...styles, lineHeight: lineHeight * fontScale };
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export const withScaledLineHeight = platformSelect({
|
|
19
|
-
ios: withAdjustedLineHeight,
|
|
20
|
-
default: identity,
|
|
21
|
-
});
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import * as R from "ramda";
|
|
3
|
-
import { Text, TextStyle, View, ViewStyle } from "react-native";
|
|
4
|
-
|
|
5
|
-
import { getLocalizations } from "@applicaster/zapp-react-native-utils/localizationUtils";
|
|
6
|
-
import { getAppStylesColor } from "@applicaster/zapp-react-native-utils/stylesUtils";
|
|
7
|
-
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
8
|
-
import { styleKeys } from "@applicaster/zapp-react-native-utils/styleKeysUtils";
|
|
9
|
-
|
|
10
|
-
type Props = {
|
|
11
|
-
styles: {};
|
|
12
|
-
error: {};
|
|
13
|
-
remoteConfigurations: { localizations: {} };
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const defaultAppStyles = {
|
|
17
|
-
loading_error_label: {
|
|
18
|
-
color: "#aaa",
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const textStyles = (appStyles = defaultAppStyles): TextStyle => ({
|
|
23
|
-
color: getAppStylesColor("loading_error_label", appStyles),
|
|
24
|
-
fontSize: 36,
|
|
25
|
-
textAlign: "center",
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
const errorStyles = ({ backgroundColor }): ViewStyle => ({
|
|
29
|
-
flex: 1,
|
|
30
|
-
width: "100%",
|
|
31
|
-
height: "100%",
|
|
32
|
-
justifyContent: "center",
|
|
33
|
-
alignItems: "center",
|
|
34
|
-
position: "absolute",
|
|
35
|
-
zIndex: 100,
|
|
36
|
-
backgroundColor,
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
export function ErrorDisplayComponent({
|
|
40
|
-
styles,
|
|
41
|
-
remoteConfigurations: { localizations },
|
|
42
|
-
}: Props) {
|
|
43
|
-
const theme = useTheme();
|
|
44
|
-
const backgroundColor = theme?.app_background_color;
|
|
45
|
-
|
|
46
|
-
const { stream_error_message = "Cannot play stream" } = getLocalizations({
|
|
47
|
-
localizations,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
const appStyles = R.prop(styleKeys.style_namespace, styles);
|
|
51
|
-
|
|
52
|
-
return (
|
|
53
|
-
<View style={errorStyles({ backgroundColor })}>
|
|
54
|
-
<Text style={textStyles(appStyles)}>{stream_error_message}</Text>
|
|
55
|
-
</View>
|
|
56
|
-
);
|
|
57
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import * as R from "ramda";
|
|
2
|
-
|
|
3
|
-
import { connectToStore } from "@applicaster/zapp-react-native-redux/utils/connectToStore";
|
|
4
|
-
|
|
5
|
-
import { ErrorDisplayComponent } from "./ErrorDisplay";
|
|
6
|
-
|
|
7
|
-
export const ErrorDisplay = R.compose(
|
|
8
|
-
connectToStore(R.pick(["remoteConfigurations"]))
|
|
9
|
-
)(ErrorDisplayComponent);
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { Player } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/player";
|
|
2
|
-
import NetInfo from "@react-native-community/netinfo";
|
|
3
|
-
|
|
4
|
-
import { useEffect, useMemo, useRef, useState } from "react";
|
|
5
|
-
import { showAlertDialog } from "@applicaster/zapp-react-native-utils/alertUtils";
|
|
6
|
-
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
7
|
-
import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
8
|
-
import { log_info } from "./logger";
|
|
9
|
-
|
|
10
|
-
type RestrictMobilePlaybackProps = {
|
|
11
|
-
player?: Player;
|
|
12
|
-
entry?: ZappEntry;
|
|
13
|
-
pluginConfiguration?: Record<string, string>;
|
|
14
|
-
close: () => void;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export const useRestrictMobilePlayback = ({
|
|
18
|
-
player,
|
|
19
|
-
entry,
|
|
20
|
-
pluginConfiguration,
|
|
21
|
-
close,
|
|
22
|
-
}: RestrictMobilePlaybackProps): { isRestricted: boolean } => {
|
|
23
|
-
const dialogVisibleRef = useRef<boolean>(false);
|
|
24
|
-
const theme = useTheme();
|
|
25
|
-
|
|
26
|
-
useEffect(() => {
|
|
27
|
-
return () => {
|
|
28
|
-
if (isTV()) {
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
dialogVisibleRef.current = false;
|
|
33
|
-
};
|
|
34
|
-
}, []);
|
|
35
|
-
|
|
36
|
-
const isConnectionRestricted = useMemo(() => {
|
|
37
|
-
if (isTV()) {
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return player && entry?.extensions?.connection_restricted;
|
|
42
|
-
}, [player, entry]);
|
|
43
|
-
|
|
44
|
-
const [isRestricted, setIsRestricted] = useState<boolean>(
|
|
45
|
-
isConnectionRestricted
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
if (!isConnectionRestricted) {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const stopPlayer = () => {
|
|
54
|
-
log_info(
|
|
55
|
-
"Stopping player due to mobile restriction, connection_restricted: true"
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
player?.close();
|
|
59
|
-
|
|
60
|
-
dialogVisibleRef.current = true;
|
|
61
|
-
|
|
62
|
-
showAlertDialog({
|
|
63
|
-
title:
|
|
64
|
-
pluginConfiguration?.mobile_connection_restricted_alert_title ||
|
|
65
|
-
"Restricted Connection Type",
|
|
66
|
-
message:
|
|
67
|
-
pluginConfiguration?.mobile_connection_restricted_alert_message ||
|
|
68
|
-
"This content can only be viewed over a Wi-Fi or LAN network.",
|
|
69
|
-
okButtonText: theme.ok_button || "OK",
|
|
70
|
-
completion: () => {
|
|
71
|
-
dialogVisibleRef.current = false;
|
|
72
|
-
|
|
73
|
-
close();
|
|
74
|
-
},
|
|
75
|
-
});
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
return NetInfo.addEventListener((state) => {
|
|
79
|
-
if (state.type === "cellular") {
|
|
80
|
-
setIsRestricted(true);
|
|
81
|
-
|
|
82
|
-
if (dialogVisibleRef.current) {
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
stopPlayer();
|
|
87
|
-
} else {
|
|
88
|
-
setIsRestricted(false);
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
}, [
|
|
92
|
-
close,
|
|
93
|
-
entry?.extensions?.connection_restricted,
|
|
94
|
-
pluginConfiguration,
|
|
95
|
-
player,
|
|
96
|
-
theme.ok_button,
|
|
97
|
-
isConnectionRestricted,
|
|
98
|
-
]);
|
|
99
|
-
|
|
100
|
-
return { isRestricted };
|
|
101
|
-
};
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { toStringOrEmpty } from "..";
|
|
2
|
-
|
|
3
|
-
describe("toStringOrEmpty", () => {
|
|
4
|
-
test("returns empty string for undefined", () => {
|
|
5
|
-
expect(toStringOrEmpty(undefined)).toBe("");
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
test("returns empty string for null", () => {
|
|
9
|
-
expect(toStringOrEmpty(null)).toBe("");
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
test("converts number to string", () => {
|
|
13
|
-
expect(toStringOrEmpty(0)).toBe("0");
|
|
14
|
-
expect(toStringOrEmpty(123)).toBe("123");
|
|
15
|
-
expect(toStringOrEmpty(-42)).toBe("-42");
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
test("returns string as is", () => {
|
|
19
|
-
expect(toStringOrEmpty("hello")).toBe("hello");
|
|
20
|
-
expect(toStringOrEmpty("")).toBe("");
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test("works with numeric strings", () => {
|
|
24
|
-
expect(toStringOrEmpty("123")).toBe("123");
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test("does not throw on falsy values like 0", () => {
|
|
28
|
-
expect(toStringOrEmpty(0)).toBe("0");
|
|
29
|
-
});
|
|
30
|
-
});
|