@24i/bigscreen-sdk 1.0.12-alpha.2254 → 1.0.12-alpha.2260
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 +6 -2
- package/packages/analytics/README.md +1 -58
- package/packages/analytics/src/A.ts +1 -51
- package/packages/analytics/src/Analytics.ts +1 -114
- package/packages/analytics/src/analyticsOnScroll.ts +1 -11
- package/packages/analytics/src/clients/ConsoleAnalytics/ConsoleAnalytics.ts +1 -12
- package/packages/analytics/src/constants.ts +1 -56
- package/packages/analytics/src/interface.ts +1 -82
- package/packages/driver-webos/src/DeviceWebos.ts +8 -0
- package/packages/epg/README.md +3 -1
- package/packages/epg/src/v2/Cell.tsx +16 -2
- package/packages/epg/src/v2/DataManager.ts +4 -1
- package/packages/epg/src/v2/Epg.tsx +3 -1
- package/packages/epg/src/v2/Row.tsx +3 -1
- package/packages/epg/src/v2/basic/CellsManager.tsx +7 -2
- package/packages/epg/src/v2/basic/RowsManager.tsx +3 -1
- package/packages/epg/src/v2/interface.ts +13 -0
- package/packages/events-manager/src/EventsManager.ts +1 -56
- package/packages/list/src/Base/Base.tsx +6 -1
- package/packages/list/src/Base/interface.ts +2 -2
- package/packages/list/src/EdgeOffsetList/EdgeOffsetList.tsx +2 -2
- package/packages/logger/src/loggers/SentryLogger/Sentry.ts +1 -0
- package/packages/logger/src/loggers/SentryLogger/SentryLogger.ts +2 -2
- package/packages/perf-utils/README.md +1 -120
- package/packages/perf-utils/src/array/filter.ts +1 -54
- package/packages/perf-utils/src/array/find.ts +1 -43
- package/packages/perf-utils/src/array/findIndex.ts +1 -43
- package/packages/perf-utils/src/array/forEach.ts +1 -48
- package/packages/perf-utils/src/array/includes.ts +1 -41
- package/packages/perf-utils/src/array/includesNaN.ts +1 -32
- package/packages/perf-utils/src/array/index.ts +1 -7
- package/packages/perf-utils/src/array/map.ts +1 -52
- package/packages/player-ui/src/PlayerUI.tsx +15 -9
- package/packages/player-ui/src/Seekbar.tsx +4 -2
- package/packages/player-ui/src/index.ts +1 -0
- package/packages/player-ui/src/mocks.ts +3 -3
- package/packages/player-ui/src/types.ts +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@24i/bigscreen-sdk",
|
|
3
|
-
"version": "1.0.12-alpha.
|
|
3
|
+
"version": "1.0.12-alpha.2260",
|
|
4
4
|
"description": "SmartApps BIGscreen SDK monorepo",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -37,9 +37,13 @@
|
|
|
37
37
|
},
|
|
38
38
|
"homepage": "https://github.com/24i/smartapps-bigscreen-sdk#readme",
|
|
39
39
|
"dependencies": {
|
|
40
|
+
"@24i/appstage-shared-analytics": "1.0.7",
|
|
41
|
+
"@24i/appstage-shared-events-manager": "1.0.5",
|
|
42
|
+
"@24i/appstage-shared-perf-utils": "1.0.5",
|
|
40
43
|
"@24i/player-base": "6.10.0",
|
|
41
44
|
"@24i/smartapps-datalayer": "3.0.6-alpha.978",
|
|
42
|
-
"@sentry/browser": "
|
|
45
|
+
"@sentry/browser": "7.35.0",
|
|
46
|
+
"@sentry/types": "7.35.0",
|
|
43
47
|
"@types/gtag.js": "^0.0.12",
|
|
44
48
|
"@types/prop-types": "^15.7.5",
|
|
45
49
|
"@types/scheduler": "^0.16.2",
|
|
@@ -5,61 +5,4 @@ hide_title: true
|
|
|
5
5
|
sidebar_label: Analytics
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
This package contains general implementation of analytics and implementation for these clients:
|
|
10
|
-
- ConsoleAnalytics - debugging analytics that dump triggers to the console.
|
|
11
|
-
|
|
12
|
-
## analytics
|
|
13
|
-
Single instance `analytics` is a general manager for analytics clients. It provides a unified
|
|
14
|
-
interface for use by the app without it needing to know any specific analytics client implementation.
|
|
15
|
-
It also adds some general app info to each trigger call (e.g. resolution, language, etc.)
|
|
16
|
-
|
|
17
|
-
## A
|
|
18
|
-
`A` is a shorthand utility function to simplify sending analytics information. It allows setting
|
|
19
|
-
a basic payload that is sent with each trigger call and shallowly merged with trigger payload.
|
|
20
|
-
Trigger payload can override values inside this basic payload.
|
|
21
|
-
|
|
22
|
-
### Usage
|
|
23
|
-
```typescript
|
|
24
|
-
import { A } from '@24i/bigscreen-sdk/analytics';
|
|
25
|
-
|
|
26
|
-
class DetailScene {
|
|
27
|
-
a = A.of({ sceneType: SceneType.DETAIL });
|
|
28
|
-
|
|
29
|
-
activate(params: RouteParams) {
|
|
30
|
-
this.a.addToPayload({
|
|
31
|
-
sceneId: `/detail/${params.assetType}/${params.id}`,
|
|
32
|
-
sceneName: `Detail - ${data.type} - [unknown]`,
|
|
33
|
-
asset: undefined,
|
|
34
|
-
});
|
|
35
|
-
// ...
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async loadAndRenderData() {
|
|
39
|
-
this.a('buffering_start');
|
|
40
|
-
const data = await dataProvider();
|
|
41
|
-
this.a.addToPayload({
|
|
42
|
-
sceneName: `Detail - ${data.type} - ${data.name}`,
|
|
43
|
-
asset: data,
|
|
44
|
-
});
|
|
45
|
-
this.a('buffering_stop');
|
|
46
|
-
// render
|
|
47
|
-
this.a('scene_view');
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
```
|
|
51
|
-
It is better to store shorthand `a` function inside the component instance. Some scenes can be
|
|
52
|
-
instantiated multiple times by the router (e.g. Browse in WL). Therefore it cannot use a single
|
|
53
|
-
instance from a local `./analytics'` import for example.
|
|
54
|
-
|
|
55
|
-
## Clients
|
|
56
|
-
Analytics client is the integration to the specific analytics provider (e.g. Google Analytics).
|
|
57
|
-
It needs to implement `AnalyticsClient` interface and register its instance to receive the events.
|
|
58
|
-
|
|
59
|
-
Clients have to opt-in to events they wish to listen to. This can be done by listing all events or by
|
|
60
|
-
using a regular expression.
|
|
61
|
-
|
|
62
|
-
```typescript
|
|
63
|
-
analytics.addClient(new GoogleAnalytics(), ['scene_view', 'buffering_start', 'buffering_stop']);
|
|
64
|
-
analytics.addClient(new ConsoleAnalytics(), /.*/);
|
|
65
|
-
```
|
|
8
|
+
https://github.com/24i/appstage-shared-packages/blob/main/packages/analytics/README.md
|
|
@@ -1,51 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { AnalyticsEventsMap, AnalyticsEventsNames, AnyPayload, AFunction } from './interface';
|
|
3
|
-
|
|
4
|
-
interface PrivateAFunction extends AFunction {
|
|
5
|
-
payload: AnyPayload,
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export class A {
|
|
9
|
-
/**
|
|
10
|
-
* Creates an analytics 'a' function for easy triggering of events.
|
|
11
|
-
* @param payload basic payload that is added to each payload of the returned
|
|
12
|
-
* function call.
|
|
13
|
-
* @returns a function for triggering analytics events
|
|
14
|
-
* @example
|
|
15
|
-
* ```typescript
|
|
16
|
-
* // Creates a function and sets the basic payload.
|
|
17
|
-
* const a = A.of({ sceneType: 'HOME', sceneId: 'home' });
|
|
18
|
-
*
|
|
19
|
-
* // Sends 'scene_view' event with the basic payload.
|
|
20
|
-
* a('scene_view');
|
|
21
|
-
*
|
|
22
|
-
* // Sends 'buffering_stop' with shallow merge of the basic payload and { asset } object.
|
|
23
|
-
* a('buffering_stop', { asset });
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
static of(payload: AnyPayload) {
|
|
27
|
-
const a: PrivateAFunction = (<
|
|
28
|
-
EventName extends AnalyticsEventsNames,
|
|
29
|
-
P extends Partial<Parameters<AnalyticsEventsMap[EventName]>[0]>,
|
|
30
|
-
>(eventName: EventName, eventPayload: P) => {
|
|
31
|
-
analytics.triggerEvent(eventName, {
|
|
32
|
-
...a.payload,
|
|
33
|
-
...eventPayload,
|
|
34
|
-
} as unknown as Parameters<AnalyticsEventsMap[EventName]>[0]);
|
|
35
|
-
}) as unknown as PrivateAFunction;
|
|
36
|
-
a.payload = payload;
|
|
37
|
-
a.addToPayload = (extendedPayload: AnyPayload) => {
|
|
38
|
-
a.payload = {
|
|
39
|
-
...a.payload,
|
|
40
|
-
...extendedPayload,
|
|
41
|
-
};
|
|
42
|
-
};
|
|
43
|
-
a.setPayload = (newPayload: AnyPayload) => {
|
|
44
|
-
a.payload = newPayload;
|
|
45
|
-
};
|
|
46
|
-
a.clearPayload = () => {
|
|
47
|
-
a.payload = {};
|
|
48
|
-
};
|
|
49
|
-
return a as AFunction;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
1
|
+
export { A } from '@24i/appstage-shared-analytics';
|
|
@@ -1,114 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { i18n } from '@24i/bigscreen-sdk/i18n';
|
|
3
|
-
import type {
|
|
4
|
-
AnalyticsEventsMap,
|
|
5
|
-
AnalyticsEventsNames,
|
|
6
|
-
AnalyticsClient,
|
|
7
|
-
GeneralAppData,
|
|
8
|
-
AppInfo,
|
|
9
|
-
UserInfo,
|
|
10
|
-
} from './interface';
|
|
11
|
-
import { EVENT_NAMES, ONLY_ONCE_EVENTS, UNREPEATABLE_EVENTS } from './constants';
|
|
12
|
-
|
|
13
|
-
class Analytics {
|
|
14
|
-
private events = new EventsManager<AnalyticsEventsMap>();
|
|
15
|
-
|
|
16
|
-
private onlyOnceAlreadySent: string[] = [];
|
|
17
|
-
|
|
18
|
-
private lastPayloadForEvent: Partial<Record<AnalyticsEventsNames, string>> = {};
|
|
19
|
-
|
|
20
|
-
private clients: Record<string, {
|
|
21
|
-
client: AnalyticsClient
|
|
22
|
-
registeredEvents: Partial<Record<AnalyticsEventsNames, Function>>,
|
|
23
|
-
}> = {};
|
|
24
|
-
|
|
25
|
-
private generalAppData: Partial<GeneralAppData> = {
|
|
26
|
-
language: i18n.getLanguage(),
|
|
27
|
-
resolution: `${window.innerWidth}x${window.innerHeight}`,
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
private appInfo: Partial<AppInfo> = {};
|
|
31
|
-
|
|
32
|
-
private userInfo: Partial<UserInfo> = {};
|
|
33
|
-
|
|
34
|
-
constructor() {
|
|
35
|
-
i18n.addEventListener('languagechange', this.onLanguageChange);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
addClient(client: AnalyticsClient, events: AnalyticsEventsNames[] | RegExp) {
|
|
39
|
-
if (this.clients[client.name]) {
|
|
40
|
-
throw new Error(`${client.name} analytics client is already registered.`);
|
|
41
|
-
}
|
|
42
|
-
const eventsToRegister = Array.isArray(events)
|
|
43
|
-
? events
|
|
44
|
-
: EVENT_NAMES.filter(events.test.bind(events));
|
|
45
|
-
const registeredEvents: Partial<Record<AnalyticsEventsNames, Function>> = {};
|
|
46
|
-
eventsToRegister.forEach((eventName) => {
|
|
47
|
-
const listener = (payload: any) => {
|
|
48
|
-
client.onEvent(eventName, payload);
|
|
49
|
-
};
|
|
50
|
-
this.events.addEventListener(eventName, listener);
|
|
51
|
-
registeredEvents[eventName] = listener;
|
|
52
|
-
});
|
|
53
|
-
this.clients[client.name] = {
|
|
54
|
-
client,
|
|
55
|
-
registeredEvents,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
removeClient(client: AnalyticsClient) {
|
|
60
|
-
if (this.clients[client.name]) {
|
|
61
|
-
const { registeredEvents } = this.clients[client.name];
|
|
62
|
-
Object.keys(registeredEvents).forEach((eventName) => {
|
|
63
|
-
// @ts-ignore - specific payload typing
|
|
64
|
-
this.events.removeEventListener(eventName, registeredEvents[eventName]!);
|
|
65
|
-
});
|
|
66
|
-
delete this.clients[client.name];
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
triggerEvent<Event extends AnalyticsEventsNames>(
|
|
71
|
-
event: Event,
|
|
72
|
-
triggerPayload: Parameters<AnalyticsEventsMap[Event]>[0] = undefined,
|
|
73
|
-
) {
|
|
74
|
-
const fullPayload = {
|
|
75
|
-
...this.generalAppData,
|
|
76
|
-
...this.appInfo,
|
|
77
|
-
...this.userInfo,
|
|
78
|
-
...triggerPayload,
|
|
79
|
-
};
|
|
80
|
-
if (ONLY_ONCE_EVENTS.includes(event)) {
|
|
81
|
-
const serializedPayload = `${event} - ${JSON.stringify(fullPayload)}`;
|
|
82
|
-
if (this.onlyOnceAlreadySent.includes(serializedPayload)) return;
|
|
83
|
-
this.onlyOnceAlreadySent.push(serializedPayload);
|
|
84
|
-
}
|
|
85
|
-
if (UNREPEATABLE_EVENTS.includes(event)) {
|
|
86
|
-
const serializedPayload = JSON.stringify(fullPayload);
|
|
87
|
-
if (this.lastPayloadForEvent[event] === serializedPayload) return;
|
|
88
|
-
this.lastPayloadForEvent[event] = serializedPayload;
|
|
89
|
-
}
|
|
90
|
-
this.tryFillUpMissingData();
|
|
91
|
-
// @ts-ignore - payload typing
|
|
92
|
-
this.events.triggerEvent(event, fullPayload);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
setAppInfo(data: Partial<AppInfo>) {
|
|
96
|
-
this.appInfo = data;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
setUserInfo(data: Partial<UserInfo>) {
|
|
100
|
-
this.userInfo = data;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
private onLanguageChange = () => {
|
|
104
|
-
this.generalAppData.language = i18n.getLanguage();
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
private tryFillUpMissingData() {
|
|
108
|
-
if (!this.generalAppData.language) {
|
|
109
|
-
this.generalAppData.language = i18n.getLanguage();
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export const analytics = new Analytics();
|
|
1
|
+
export { analytics } from '@24i/appstage-shared-analytics';
|
|
@@ -1,11 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export const analyticsOnScroll = (a: AFunction, itemsTotal: number) => (_: any, index: number) => {
|
|
4
|
-
const percent = (index + 1) / itemsTotal;
|
|
5
|
-
/* eslint-disable no-magic-numbers */
|
|
6
|
-
if (percent >= 0.9) a('scroll_90');
|
|
7
|
-
else if (percent >= 0.75) a('scroll_75');
|
|
8
|
-
else if (percent >= 0.50) a('scroll_50');
|
|
9
|
-
else if (percent >= 0.25) a('scroll_25');
|
|
10
|
-
/* eslint-enable no-magic-numbers */
|
|
11
|
-
};
|
|
1
|
+
export { analyticsOnScroll } from '@24i/appstage-shared-analytics';
|
|
@@ -1,12 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Simple debugging client that dumps all triggers to the console.
|
|
5
|
-
*/
|
|
6
|
-
export class ConsoleAnalytics implements AnalyticsClient {
|
|
7
|
-
name = 'ConsoleAnalytics';
|
|
8
|
-
|
|
9
|
-
onEvent(eventName: string, payload: any) {
|
|
10
|
-
console.info('CA: ', eventName, payload);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
1
|
+
export { ConsoleAnalytics } from '@24i/appstage-shared-analytics/dist/clients/ConsoleAnalytics';
|
|
@@ -1,56 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export const EVENT_NAMES = [
|
|
4
|
-
// App
|
|
5
|
-
'app_close',
|
|
6
|
-
|
|
7
|
-
// Data loading / Player buffering
|
|
8
|
-
'buffering_start',
|
|
9
|
-
'buffering_stop',
|
|
10
|
-
|
|
11
|
-
// Heartbeat
|
|
12
|
-
'heartbeat_5',
|
|
13
|
-
|
|
14
|
-
// Playback
|
|
15
|
-
'playback_10',
|
|
16
|
-
'playback_25',
|
|
17
|
-
'playback_50',
|
|
18
|
-
'playback_75',
|
|
19
|
-
'playback_90',
|
|
20
|
-
'playback_pause',
|
|
21
|
-
'playback_start',
|
|
22
|
-
'playback_stop',
|
|
23
|
-
|
|
24
|
-
// Player
|
|
25
|
-
'player_close',
|
|
26
|
-
'player_open',
|
|
27
|
-
|
|
28
|
-
// Scenes
|
|
29
|
-
'scene_view',
|
|
30
|
-
|
|
31
|
-
// Scrolling in scene
|
|
32
|
-
'scroll_25',
|
|
33
|
-
'scroll_50',
|
|
34
|
-
'scroll_75',
|
|
35
|
-
'scroll_90',
|
|
36
|
-
] as const;
|
|
37
|
-
|
|
38
|
-
export const ONLY_ONCE_EVENTS: AnalyticsEventsNames[] = [
|
|
39
|
-
// Playback
|
|
40
|
-
'playback_10',
|
|
41
|
-
'playback_25',
|
|
42
|
-
'playback_50',
|
|
43
|
-
'playback_75',
|
|
44
|
-
'playback_90',
|
|
45
|
-
|
|
46
|
-
// Scrolling in scene
|
|
47
|
-
'scroll_25',
|
|
48
|
-
'scroll_50',
|
|
49
|
-
'scroll_75',
|
|
50
|
-
'scroll_90',
|
|
51
|
-
];
|
|
52
|
-
|
|
53
|
-
export const UNREPEATABLE_EVENTS: AnalyticsEventsNames[] = [
|
|
54
|
-
// Scenes
|
|
55
|
-
'scene_view',
|
|
56
|
-
];
|
|
1
|
+
export * from '@24i/appstage-shared-analytics/dist/constants';
|
|
@@ -1,82 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
sceneId: string,
|
|
3
|
-
sceneType: string,
|
|
4
|
-
sceneName: string,
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
export type AppInfo = {
|
|
8
|
-
applicationId: string,
|
|
9
|
-
serviceId: string,
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export type UserInfo = {
|
|
13
|
-
userId: string,
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export type GeneralAppData = {
|
|
17
|
-
language: string,
|
|
18
|
-
resolution: string,
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* List of all tracked events.
|
|
23
|
-
*/
|
|
24
|
-
export type AnalyticsEventsMap = {
|
|
25
|
-
// App
|
|
26
|
-
'app_close': (payload: void) => void,
|
|
27
|
-
|
|
28
|
-
// Data loading / Player buffering
|
|
29
|
-
'buffering_start': (payload: SceneInfo) => void,
|
|
30
|
-
'buffering_stop': (payload: SceneInfo) => void,
|
|
31
|
-
|
|
32
|
-
// Heartbeat
|
|
33
|
-
'heartbeat_5': (payload: void) => void,
|
|
34
|
-
|
|
35
|
-
// Playback
|
|
36
|
-
'playback_10': (payload: void) => void,
|
|
37
|
-
'playback_25': (payload: void) => void,
|
|
38
|
-
'playback_50': (payload: void) => void,
|
|
39
|
-
'playback_75': (payload: void) => void,
|
|
40
|
-
'playback_90': (payload: void) => void,
|
|
41
|
-
'playback_pause': (payload: void) => void,
|
|
42
|
-
'playback_start': (payload: void) => void,
|
|
43
|
-
'playback_stop': (payload: void) => void,
|
|
44
|
-
|
|
45
|
-
// Player
|
|
46
|
-
'player_close': (payload: void) => void,
|
|
47
|
-
'player_open': (payload: void) => void,
|
|
48
|
-
|
|
49
|
-
// Scenes
|
|
50
|
-
'scene_view': (payload: SceneInfo) => void,
|
|
51
|
-
|
|
52
|
-
// Scrolling in scene
|
|
53
|
-
'scroll_25': (payload: SceneInfo) => void,
|
|
54
|
-
'scroll_50': (payload: SceneInfo) => void,
|
|
55
|
-
'scroll_75': (payload: SceneInfo) => void,
|
|
56
|
-
'scroll_90': (payload: SceneInfo) => void,
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export type AnalyticsEventsNames = keyof AnalyticsEventsMap;
|
|
60
|
-
|
|
61
|
-
export interface AnalyticsClient {
|
|
62
|
-
name: string,
|
|
63
|
-
onEvent<Event extends keyof AnalyticsEventsMap>(
|
|
64
|
-
eventName: Event,
|
|
65
|
-
payload: Parameters<AnalyticsEventsMap[Event]>,
|
|
66
|
-
): void;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export type AnyPayload = Record<string, any>;
|
|
70
|
-
|
|
71
|
-
export interface AFunction {
|
|
72
|
-
<
|
|
73
|
-
EventName extends AnalyticsEventsNames,
|
|
74
|
-
P extends Partial<Parameters<AnalyticsEventsMap[EventName]>[0]>,
|
|
75
|
-
>(eventName: EventName, payload?: P): void,
|
|
76
|
-
|
|
77
|
-
addToPayload(payload: AnyPayload): void,
|
|
78
|
-
|
|
79
|
-
setPayload(payload: AnyPayload): void,
|
|
80
|
-
|
|
81
|
-
clearPayload(): void,
|
|
82
|
-
}
|
|
1
|
+
export * from '@24i/appstage-shared-analytics/dist/interface';
|
|
@@ -95,7 +95,15 @@ export class DeviceWebos extends DeviceBase {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
initMouseActiveListener() {
|
|
98
|
+
// checks if mouse is already being used since startup - PRDSAPPSBW-1005
|
|
99
|
+
const mouseActiveAtStartupHandler = () => {
|
|
100
|
+
document.removeEventListener('mousemove', mouseActiveAtStartupHandler);
|
|
101
|
+
this.isMouseActive = true;
|
|
102
|
+
this.triggerEvent('mouseactive', { isMouseActive: true });
|
|
103
|
+
};
|
|
104
|
+
document.addEventListener('mousemove', mouseActiveAtStartupHandler);
|
|
98
105
|
document.addEventListener('cursorStateChange', (event) => {
|
|
106
|
+
document.removeEventListener('mousemove', mouseActiveAtStartupHandler);
|
|
99
107
|
// @ts-ignore - the LG event has this structure.
|
|
100
108
|
this.isMouseActive = event.detail.visibility;
|
|
101
109
|
this.triggerEvent('mouseactive', {
|
package/packages/epg/README.md
CHANGED
|
@@ -18,6 +18,7 @@ This package contains a prepared Electronic Program Guide component.
|
|
|
18
18
|
- `channelHeaderWidthPx` - the width of the channel headers (the part before programs in a row) in pixels
|
|
19
19
|
- `channelData` - the array of channels of a type that extends `EpgChannel` from the data layer
|
|
20
20
|
- `fetchPrograms` - the function to fetch more programs for a channel in a given time range
|
|
21
|
+
- `smallestAllowedCellPx` - optional - cells with width less than this are marked with `.too-small` class (defaults to `0`)
|
|
21
22
|
- `timelineWidthMs` - optional - the width of the timeline (the part where the programs are) in milliseconds (defaults to `90 minutes`)
|
|
22
23
|
- `scrollStepMs` - optional - the amount of movement step left/right in milliseconds (defaults to `0.5 hours`)
|
|
23
24
|
- `idleTimerMs` - optional - the amount of ms without user input after which epg jumps to the current time (defaults to `scrollStepMs`)
|
|
@@ -80,10 +81,11 @@ then the initial time of EPG will be `13:30` (if not set otherwise)
|
|
|
80
81
|
- `endTime` is always taken as excluded = not the part of the program
|
|
81
82
|
- so the program that gets focused/marked as live / etc. is the one that is just starting
|
|
82
83
|
- when the Program displayed by the cell is currently live, the cell has an `.live` class
|
|
83
|
-
- the Cell has also these following classes based on its width
|
|
84
|
+
- the Cell has also these following classes based on its width, breakpoints defined by `programTextModeResolver` and `smallestAllowedCellPx`
|
|
84
85
|
- `.with-text` - this cell should display full or ellipsis truncated text
|
|
85
86
|
- `.ellipsis-only` - this cell should display only ellipsis. You can use this class to center the text.
|
|
86
87
|
- `.no-text` - this cell is so small that it should be empty
|
|
88
|
+
- `.too-small` - this cell is too small to render the usual way, as in its only several pixels wide, less than the needed margin/border etc.
|
|
87
89
|
- the element of the currently focused row has an `.active` class
|
|
88
90
|
|
|
89
91
|
## Styles
|
|
@@ -17,6 +17,7 @@ const LIVE_CLASS = 'live';
|
|
|
17
17
|
const NO_TEXT_CLASS = 'no-text';
|
|
18
18
|
const ELLIPSIS_ONLY_CLASS = 'ellipsis-only';
|
|
19
19
|
const WITH_TEXT_CLASS = 'with-text';
|
|
20
|
+
const TOO_SMALL_CLASS = 'too-small';
|
|
20
21
|
|
|
21
22
|
const getClassByTextMode = (textMode: ProgramTextMode) => {
|
|
22
23
|
switch (textMode) {
|
|
@@ -40,6 +41,8 @@ export class Cell<
|
|
|
40
41
|
|
|
41
42
|
widthPx = this.props.widthPx;
|
|
42
43
|
|
|
44
|
+
smallestAllowedCellPx = this.props.smallestAllowedCellPx;
|
|
45
|
+
|
|
43
46
|
xOffsetPx = this.props.xOffsetPx;
|
|
44
47
|
|
|
45
48
|
textMode = this.props.programTextModeResolver(this.widthPx);
|
|
@@ -137,6 +140,14 @@ export class Cell<
|
|
|
137
140
|
this.textMode = textMode;
|
|
138
141
|
}
|
|
139
142
|
|
|
143
|
+
private setTooSmallClass() {
|
|
144
|
+
if (this.widthPx < this.smallestAllowedCellPx) {
|
|
145
|
+
addClass(this.div, TOO_SMALL_CLASS);
|
|
146
|
+
} else {
|
|
147
|
+
removeClass(this.div, TOO_SMALL_CLASS);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
140
151
|
focus(options?: FocusOptions) {
|
|
141
152
|
if (!this.wasFocusedWithCurrentItem && document.activeElement === this.div.current) {
|
|
142
153
|
this.onFocusChange();
|
|
@@ -169,6 +180,7 @@ export class Cell<
|
|
|
169
180
|
this.item = item;
|
|
170
181
|
this.setLiveClass();
|
|
171
182
|
this.setTextModeClass(textMode);
|
|
183
|
+
this.setTooSmallClass();
|
|
172
184
|
this.programComponent.current!.updateData(
|
|
173
185
|
this.item!,
|
|
174
186
|
textMode,
|
|
@@ -179,13 +191,15 @@ export class Cell<
|
|
|
179
191
|
|
|
180
192
|
render() {
|
|
181
193
|
const {
|
|
182
|
-
isLoading, xOffsetPx, widthPx,
|
|
194
|
+
isLoading, xOffsetPx, widthPx, smallestAllowedCellPx,
|
|
195
|
+
item, programTextModeResolver, renderProgram,
|
|
183
196
|
} = this.props;
|
|
184
197
|
const textMode = programTextModeResolver(widthPx);
|
|
198
|
+
const tooSmall = widthPx < smallestAllowedCellPx ? TOO_SMALL_CLASS : '';
|
|
185
199
|
return (
|
|
186
200
|
<Interactable
|
|
187
201
|
ref={this.div}
|
|
188
|
-
className={`epg-cell ${getClassByTextMode(textMode)}`}
|
|
202
|
+
className={`epg-cell ${getClassByTextMode(textMode)} ${tooSmall}`}
|
|
189
203
|
style={{
|
|
190
204
|
width: `${widthPx}px`,
|
|
191
205
|
...getXOffsetStyleObject(xOffsetPx),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { filter, find, findIndex, map } from '@24i/bigscreen-sdk/perf-utils/array';
|
|
2
2
|
import { Asset } from '@24i/smartapps-datalayer/models';
|
|
3
3
|
import { i18n } from '@24i/bigscreen-sdk/i18n';
|
|
4
|
+
import { MINUTE_IN_MS } from '@24i/bigscreen-sdk/utils/timeConstants';
|
|
4
5
|
import { isStartVisible, isEndVisible, isOverFullWidth } from './utils';
|
|
5
6
|
import {
|
|
6
7
|
DataRange,
|
|
@@ -14,6 +15,7 @@ const START = 0;
|
|
|
14
15
|
const END = 1;
|
|
15
16
|
|
|
16
17
|
const TIMELINE_WIDTHS_TO_FETCH = 3;
|
|
18
|
+
const MINIMUM_BREAK_LENGTH = MINUTE_IN_MS;
|
|
17
19
|
|
|
18
20
|
export class DataManager implements IDataManager {
|
|
19
21
|
private readonly data: Asset[] = [];
|
|
@@ -355,7 +357,8 @@ export class DataManager implements IDataManager {
|
|
|
355
357
|
for (let i = data.length - 1; i > 0; i--) {
|
|
356
358
|
const current = data[i];
|
|
357
359
|
const previous = data[i - 1];
|
|
358
|
-
|
|
360
|
+
const breakLength = current.program!.startTime - previous.program!.endTime;
|
|
361
|
+
if (breakLength > 0 && breakLength >= MINIMUM_BREAK_LENGTH) {
|
|
359
362
|
data.splice(
|
|
360
363
|
i,
|
|
361
364
|
0,
|
|
@@ -83,6 +83,7 @@ export class Epg<
|
|
|
83
83
|
timeProvider: Date.now,
|
|
84
84
|
scrollStepMs: HALF_HOUR_IN_MS,
|
|
85
85
|
timelineSectionWidthMs: HALF_HOUR_IN_MS,
|
|
86
|
+
smallestAllowedCellPx: 0,
|
|
86
87
|
limitFromMs: -Infinity,
|
|
87
88
|
limitToMs: Infinity,
|
|
88
89
|
initialChannelIndex: 0,
|
|
@@ -382,7 +383,7 @@ export class Epg<
|
|
|
382
383
|
const {
|
|
383
384
|
datePickerProps, timelineWidthPx, channelHeaderWidthPx, scrollStepMs, rowHeightPx,
|
|
384
385
|
timelineSectionWidthMs, timelineWidthMs, initialChannelIndex, plugins,
|
|
385
|
-
navigatableRows, additionalRows, programTextModeResolver,
|
|
386
|
+
navigatableRows, additionalRows, programTextModeResolver, smallestAllowedCellPx,
|
|
386
387
|
renderDay, renderTime, renderProgram, renderChannelHeader, renderNowLineHeader,
|
|
387
388
|
} = this.props;
|
|
388
389
|
return (
|
|
@@ -442,6 +443,7 @@ export class Epg<
|
|
|
442
443
|
renderChannelHeader={renderChannelHeader}
|
|
443
444
|
renderProgram={renderProgram}
|
|
444
445
|
isFocusInRows={this.isFocusInRows}
|
|
446
|
+
smallestAllowedCellPx={smallestAllowedCellPx}
|
|
445
447
|
/>
|
|
446
448
|
<EpgMouseNavigation
|
|
447
449
|
// @ts-ignore - needed to convert fallback (false) to interface
|
|
@@ -109,7 +109,8 @@ export class Row<
|
|
|
109
109
|
|
|
110
110
|
render() {
|
|
111
111
|
const {
|
|
112
|
-
timelineWidthMs,
|
|
112
|
+
timelineWidthMs, timestamp, time,
|
|
113
|
+
pixelInMs, timelineWidthPx, smallestAllowedCellPx,
|
|
113
114
|
programTextModeResolver, onMouseEnter, renderProgram,
|
|
114
115
|
} = this.props;
|
|
115
116
|
return (
|
|
@@ -126,6 +127,7 @@ export class Row<
|
|
|
126
127
|
onItemPress={this.onItemPress}
|
|
127
128
|
onMouseEnter={onMouseEnter}
|
|
128
129
|
renderProgram={renderProgram}
|
|
130
|
+
smallestAllowedCellPx={smallestAllowedCellPx}
|
|
129
131
|
/>
|
|
130
132
|
);
|
|
131
133
|
}
|
|
@@ -180,7 +180,8 @@ export class CellsManager<
|
|
|
180
180
|
|
|
181
181
|
private addCell(item: Asset) {
|
|
182
182
|
const {
|
|
183
|
-
time,
|
|
183
|
+
time, smallestAllowedCellPx,
|
|
184
|
+
programTextModeResolver, onMouseEnter, onItemPress, onFocusChange, renderProgram,
|
|
184
185
|
} = this.props;
|
|
185
186
|
const cell = createRef<ICell<T>>();
|
|
186
187
|
this.mountBefore((
|
|
@@ -195,6 +196,7 @@ export class CellsManager<
|
|
|
195
196
|
onFocusChange={onFocusChange}
|
|
196
197
|
onMouseEnter={onMouseEnter}
|
|
197
198
|
renderProgram={renderProgram}
|
|
199
|
+
smallestAllowedCellPx={smallestAllowedCellPx}
|
|
198
200
|
/>
|
|
199
201
|
), this.endLoadingCell.current!.div);
|
|
200
202
|
this.cells.push(cell);
|
|
@@ -241,7 +243,7 @@ export class CellsManager<
|
|
|
241
243
|
|
|
242
244
|
render() {
|
|
243
245
|
const {
|
|
244
|
-
timelineWidthPx, time, dataGetter, programTextModeResolver,
|
|
246
|
+
timelineWidthPx, smallestAllowedCellPx, time, dataGetter, programTextModeResolver,
|
|
245
247
|
onMouseEnter, onItemPress, onFocusChange, renderProgram,
|
|
246
248
|
} = this.props;
|
|
247
249
|
const visibleData = dataGetter(this.getRange());
|
|
@@ -257,6 +259,7 @@ export class CellsManager<
|
|
|
257
259
|
onFocusChange={onFocusChange}
|
|
258
260
|
onMouseEnter={onMouseEnter}
|
|
259
261
|
renderProgram={renderProgram}
|
|
262
|
+
smallestAllowedCellPx={smallestAllowedCellPx}
|
|
260
263
|
isLoading
|
|
261
264
|
/>
|
|
262
265
|
{map(visibleData, (item) => {
|
|
@@ -274,6 +277,7 @@ export class CellsManager<
|
|
|
274
277
|
onFocusChange={onFocusChange}
|
|
275
278
|
onMouseEnter={onMouseEnter}
|
|
276
279
|
renderProgram={renderProgram}
|
|
280
|
+
smallestAllowedCellPx={smallestAllowedCellPx}
|
|
277
281
|
/>
|
|
278
282
|
);
|
|
279
283
|
})}
|
|
@@ -288,6 +292,7 @@ export class CellsManager<
|
|
|
288
292
|
onMouseEnter={onMouseEnter}
|
|
289
293
|
renderProgram={renderProgram}
|
|
290
294
|
isLoading
|
|
295
|
+
smallestAllowedCellPx={smallestAllowedCellPx}
|
|
291
296
|
/>
|
|
292
297
|
</div>
|
|
293
298
|
);
|
|
@@ -465,7 +465,8 @@ export class RowsManager<
|
|
|
465
465
|
|
|
466
466
|
render() {
|
|
467
467
|
const {
|
|
468
|
-
timelineWidthMs,
|
|
468
|
+
timelineWidthMs, timestamp, time, scrollStepMs,
|
|
469
|
+
pixelInMs, timelineWidthPx, smallestAllowedCellPx,
|
|
469
470
|
programTextModeResolver, onItemPress, onMouseEnter, renderProgram, renderChannelHeader,
|
|
470
471
|
} = this.props;
|
|
471
472
|
const visibleData = this.getVisibleChannels();
|
|
@@ -492,6 +493,7 @@ export class RowsManager<
|
|
|
492
493
|
onFocusChange={this.onFocusChange}
|
|
493
494
|
onMouseEnter={onMouseEnter}
|
|
494
495
|
renderProgram={renderProgram}
|
|
496
|
+
smallestAllowedCellPx={smallestAllowedCellPx}
|
|
495
497
|
/>
|
|
496
498
|
))}
|
|
497
499
|
</Scroller>
|