@maestro_io/maestro-web-sdk 5.1.0 → 5.1.2
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/dist/12.maestro-web-sdk.esm.js +1 -1
- package/dist/12.maestro-web-sdk.esm.js.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/maestro-web-sdk.esm.js +1 -1
- package/dist/maestro-web-sdk.esm.js.map +1 -1
- package/dist/maestro-web-sdk.umd.js +2 -2
- package/dist/maestro-web-sdk.umd.js.map +1 -1
- package/dist/src/interfaces/IMaestroEventDelegate.d.ts +8 -1
- package/dist/src/modules/espn/fantasy/view-model/FantasyViewModel.js +7 -0
- package/dist/src/modules/fox/multi-view/types.d.ts +62 -0
- package/dist/src/modules/fox/multi-view/types.js +89 -0
- package/dist/src/modules/fox/multi-view/view/components/LayoutSelector/LayoutSelector.d.ts +13 -0
- package/dist/src/modules/fox/multi-view/view/components/LayoutSelector/LayoutSelector.js +43 -0
- package/dist/src/modules/fox/multi-view/view/components/LayoutSelector/index.d.ts +2 -0
- package/dist/src/modules/fox/multi-view/view/components/LayoutSelector/index.js +2 -0
- package/dist/src/services/StaticService/StaticService.js +1 -0
- package/package.json +1 -1
|
@@ -8,7 +8,14 @@ export type RequestLoginPanelEvent = {
|
|
|
8
8
|
onLoginSuccess: () => void;
|
|
9
9
|
};
|
|
10
10
|
};
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Generic panel event type that supports client-specific events
|
|
13
|
+
* without exposing specific event structures in the SDK interface
|
|
14
|
+
*/
|
|
15
|
+
export type PanelEvent = {
|
|
16
|
+
type: string;
|
|
17
|
+
payload: unknown;
|
|
18
|
+
};
|
|
12
19
|
/**
|
|
13
20
|
* @description Interface for the client app to implement in order to handle Maestro TV SDK events.
|
|
14
21
|
*/
|
|
@@ -154,8 +154,10 @@ export class FantasyViewModel {
|
|
|
154
154
|
await this.fetchData();
|
|
155
155
|
}
|
|
156
156
|
async fetchData() {
|
|
157
|
+
this.isDataLoading.value = true;
|
|
157
158
|
const isAuthenticated = await this.delegate.userIsAuthenticated();
|
|
158
159
|
if (!isAuthenticated) {
|
|
160
|
+
this.isDataLoading.value = false;
|
|
159
161
|
this.append({
|
|
160
162
|
id: 'not-logged-in',
|
|
161
163
|
type: 'notLoggedIn',
|
|
@@ -211,8 +213,11 @@ export class FantasyViewModel {
|
|
|
211
213
|
this.itemToLeagueKey.clear();
|
|
212
214
|
this._fantasyLeagues = [];
|
|
213
215
|
this._tournamentChallenges = [];
|
|
216
|
+
this._promoItem = null;
|
|
214
217
|
this._tcProcessingCount = 0;
|
|
215
218
|
this.isLoadingTc.value = false;
|
|
219
|
+
this.fantasyItems.value = [];
|
|
220
|
+
this.hasEverSuccessfullyLoaded.value = false;
|
|
216
221
|
}
|
|
217
222
|
/**
|
|
218
223
|
* Track panel view event
|
|
@@ -334,6 +339,7 @@ export class FantasyViewModel {
|
|
|
334
339
|
// Mark successful load
|
|
335
340
|
this.hasEverSuccessfullyLoaded.value = true;
|
|
336
341
|
this.errorState.value = 'none';
|
|
342
|
+
this.isDataLoading.value = false;
|
|
337
343
|
// Initial rebuild even if empty, to ensure state is clean
|
|
338
344
|
this.rebuildFantasyItems();
|
|
339
345
|
}
|
|
@@ -342,6 +348,7 @@ export class FantasyViewModel {
|
|
|
342
348
|
context: 'FantasyViewModel.processFantasy',
|
|
343
349
|
});
|
|
344
350
|
console.error('[FANTASY] Error processing fantasy data:', error);
|
|
351
|
+
this.isDataLoading.value = false;
|
|
345
352
|
if (!this.hasEverSuccessfullyLoaded.value) {
|
|
346
353
|
this.errorState.value = 'tabLoadFailure';
|
|
347
354
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description Represents a single stream/camera listing available for multi-view
|
|
3
|
+
*/
|
|
4
|
+
export interface Listing {
|
|
5
|
+
id: string;
|
|
6
|
+
title: string;
|
|
7
|
+
thumbnailUrl: string;
|
|
8
|
+
streamUrl: string;
|
|
9
|
+
homeTeam?: {
|
|
10
|
+
name: string;
|
|
11
|
+
abbreviation: string;
|
|
12
|
+
logoUrl: string;
|
|
13
|
+
score?: number;
|
|
14
|
+
};
|
|
15
|
+
awayTeam?: {
|
|
16
|
+
name: string;
|
|
17
|
+
abbreviation: string;
|
|
18
|
+
logoUrl: string;
|
|
19
|
+
score?: number;
|
|
20
|
+
};
|
|
21
|
+
status?: 'live' | 'upcoming' | 'ended';
|
|
22
|
+
metadata?: Record<string, unknown>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* @description Defines a multi-view layout configuration with positioned slots
|
|
26
|
+
*/
|
|
27
|
+
export interface MultiViewLayout {
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
type: '50-50' | '60-40' | '70-30' | 'pip' | 'quad';
|
|
31
|
+
slots: Array<{
|
|
32
|
+
index: number;
|
|
33
|
+
x: number;
|
|
34
|
+
y: number;
|
|
35
|
+
width: number;
|
|
36
|
+
height: number;
|
|
37
|
+
}>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* @description Current multi-view state including listings and layout
|
|
41
|
+
*/
|
|
42
|
+
export interface MultiViewConfig {
|
|
43
|
+
listings: Listing[];
|
|
44
|
+
layout: MultiViewLayout;
|
|
45
|
+
primaryAudioSlot?: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* @description Represents a pre-configured curated multiview experience
|
|
49
|
+
*/
|
|
50
|
+
export interface CuratedMultiview {
|
|
51
|
+
id: string;
|
|
52
|
+
title: string;
|
|
53
|
+
description?: string;
|
|
54
|
+
thumbnailUrl?: string;
|
|
55
|
+
listings: Listing[];
|
|
56
|
+
defaultLayout: MultiViewLayout;
|
|
57
|
+
metadata?: Record<string, unknown>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* @description Available layouts organized by stream count
|
|
61
|
+
*/
|
|
62
|
+
export declare const LAYOUTS_BY_COUNT: Record<2 | 3 | 4, MultiViewLayout[]>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description Available layouts organized by stream count
|
|
3
|
+
*/
|
|
4
|
+
export const LAYOUTS_BY_COUNT = {
|
|
5
|
+
2: [
|
|
6
|
+
{
|
|
7
|
+
id: 'layout-2-50-50',
|
|
8
|
+
name: '50/50',
|
|
9
|
+
type: '50-50',
|
|
10
|
+
slots: [
|
|
11
|
+
{ index: 0, x: 0, y: 0, width: 50, height: 100 },
|
|
12
|
+
{ index: 1, x: 50, y: 0, width: 50, height: 100 },
|
|
13
|
+
],
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: 'layout-2-60-40',
|
|
17
|
+
name: '60/40',
|
|
18
|
+
type: '60-40',
|
|
19
|
+
slots: [
|
|
20
|
+
{ index: 0, x: 0, y: 0, width: 60, height: 100 },
|
|
21
|
+
{ index: 1, x: 60, y: 0, width: 40, height: 100 },
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'layout-2-70-30',
|
|
26
|
+
name: '70/30',
|
|
27
|
+
type: '70-30',
|
|
28
|
+
slots: [
|
|
29
|
+
{ index: 0, x: 0, y: 0, width: 70, height: 100 },
|
|
30
|
+
{ index: 1, x: 70, y: 0, width: 30, height: 100 },
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: 'layout-2-pip',
|
|
35
|
+
name: 'PIP',
|
|
36
|
+
type: 'pip',
|
|
37
|
+
slots: [
|
|
38
|
+
{ index: 0, x: 0, y: 0, width: 100, height: 100 },
|
|
39
|
+
{ index: 1, x: 70, y: 70, width: 25, height: 25 },
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
3: [
|
|
44
|
+
{
|
|
45
|
+
id: 'layout-3-main-side',
|
|
46
|
+
name: 'Main + Side',
|
|
47
|
+
type: '60-40',
|
|
48
|
+
slots: [
|
|
49
|
+
{ index: 0, x: 0, y: 0, width: 60, height: 100 },
|
|
50
|
+
{ index: 1, x: 60, y: 0, width: 40, height: 50 },
|
|
51
|
+
{ index: 2, x: 60, y: 50, width: 40, height: 50 },
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: 'layout-3-horizontal',
|
|
56
|
+
name: 'Horizontal',
|
|
57
|
+
type: '50-50',
|
|
58
|
+
slots: [
|
|
59
|
+
{ index: 0, x: 0, y: 0, width: 100, height: 33.33 },
|
|
60
|
+
{ index: 1, x: 0, y: 33.33, width: 100, height: 33.33 },
|
|
61
|
+
{ index: 2, x: 0, y: 66.66, width: 100, height: 33.34 },
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
4: [
|
|
66
|
+
{
|
|
67
|
+
id: 'layout-4-quad',
|
|
68
|
+
name: 'Quad',
|
|
69
|
+
type: 'quad',
|
|
70
|
+
slots: [
|
|
71
|
+
{ index: 0, x: 0, y: 0, width: 50, height: 50 },
|
|
72
|
+
{ index: 1, x: 50, y: 0, width: 50, height: 50 },
|
|
73
|
+
{ index: 2, x: 0, y: 50, width: 50, height: 50 },
|
|
74
|
+
{ index: 3, x: 50, y: 50, width: 50, height: 50 },
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'layout-4-main-grid',
|
|
79
|
+
name: 'Main + Grid',
|
|
80
|
+
type: '60-40',
|
|
81
|
+
slots: [
|
|
82
|
+
{ index: 0, x: 0, y: 0, width: 60, height: 100 },
|
|
83
|
+
{ index: 1, x: 60, y: 0, width: 40, height: 33.33 },
|
|
84
|
+
{ index: 2, x: 60, y: 33.33, width: 40, height: 33.33 },
|
|
85
|
+
{ index: 3, x: 60, y: 66.66, width: 40, height: 33.34 },
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import './LayoutSelector.style.css';
|
|
2
|
+
import { WithFocusableProps } from '@/external/spatial-navigation';
|
|
3
|
+
import { Direction } from '@/external/spatial-navigation/utils';
|
|
4
|
+
import { MultiViewLayout } from '../../../types';
|
|
5
|
+
export type LayoutSelectorProps = {
|
|
6
|
+
streamCount: 2 | 3 | 4;
|
|
7
|
+
selectedLayoutId?: string;
|
|
8
|
+
onLayoutSelect?: (layout: MultiViewLayout) => void;
|
|
9
|
+
onLayoutArrowPress?: (direction: Direction) => void;
|
|
10
|
+
className?: string;
|
|
11
|
+
};
|
|
12
|
+
declare const LayoutSelector: (props: LayoutSelectorProps & WithFocusableProps) => JSX.Element;
|
|
13
|
+
export default LayoutSelector;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
2
|
+
import './LayoutSelector.style.css';
|
|
3
|
+
import { withFocusable, } from '@/external/spatial-navigation';
|
|
4
|
+
import { FocusableItem, useScrollableContainer, } from '@/components/core/ScrollableContainer';
|
|
5
|
+
import SDK from '@/index';
|
|
6
|
+
import { LAYOUTS_BY_COUNT } from '../../../types';
|
|
7
|
+
const LayoutOptionRowComponent = (props) => {
|
|
8
|
+
const { layout, selected, focusable, focused } = props;
|
|
9
|
+
console.log('selected', selected);
|
|
10
|
+
return (React.createElement(FocusableItem, null,
|
|
11
|
+
React.createElement("div", { className: "layout-selector-option-focus focus-ring", "data-focusable": focusable, "data-focused": focused },
|
|
12
|
+
React.createElement("div", { className: "layout-selector-row" },
|
|
13
|
+
React.createElement("div", { className: "layout-selector-cell layout-selector-cell-preview" },
|
|
14
|
+
React.createElement("div", { className: "layout-selector-preview" }, layout.slots.map((slot) => (React.createElement("div", { key: `${layout.id}-slot-${slot.index}`, className: "layout-selector-slot", style: {
|
|
15
|
+
left: `${slot.x}%`,
|
|
16
|
+
top: `${slot.y}%`,
|
|
17
|
+
width: `${slot.width}%`,
|
|
18
|
+
height: `${slot.height}%`,
|
|
19
|
+
} }))))),
|
|
20
|
+
React.createElement("div", { className: "layout-selector-cell layout-selector-cell-label" },
|
|
21
|
+
React.createElement("div", { className: "layout-selector-label T30 FONT_BOLD" }, layout.name)),
|
|
22
|
+
React.createElement("div", { className: "layout-selector-cell layout-selector-cell-radio" },
|
|
23
|
+
React.createElement("div", { className: `layout-selector-radio ${selected ? 'selected' : ''}`, "aria-hidden": true }, selected ? (React.createElement("span", { className: "layout-selector-radio-check" }, "\u2713")) : null))))));
|
|
24
|
+
};
|
|
25
|
+
const LayoutOptionRow = withFocusable()(LayoutOptionRowComponent);
|
|
26
|
+
const LayoutSelector = (props) => {
|
|
27
|
+
const { scrollToFocusedElement } = useScrollableContainer();
|
|
28
|
+
const { streamCount, selectedLayoutId, onLayoutSelect } = props;
|
|
29
|
+
const layouts = LAYOUTS_BY_COUNT[streamCount] || [];
|
|
30
|
+
const onBecameFocused = useCallback((layout) => {
|
|
31
|
+
scrollToFocusedElement(layout);
|
|
32
|
+
}, [scrollToFocusedElement]);
|
|
33
|
+
const onArrowPress = useCallback((d, _props) => {
|
|
34
|
+
if (d === 'left') {
|
|
35
|
+
SDK.getMaestroEventViewModel().deliverFocus();
|
|
36
|
+
}
|
|
37
|
+
}, []);
|
|
38
|
+
return (React.createElement(React.Fragment, null, layouts.length === 0 ? (React.createElement("div", { className: "layout-selector-empty T10" },
|
|
39
|
+
"No layouts available for ",
|
|
40
|
+
streamCount,
|
|
41
|
+
" streams.")) : (React.createElement("div", { className: "layout-selector-options" }, layouts.map((layout) => (React.createElement(LayoutOptionRow, { key: layout.id, focusKey: `fox-multiview-layout-${layout.id}`, layout: layout, selected: layout.id === selectedLayoutId, onEnterPress: () => onLayoutSelect?.(layout), onArrowPress: onArrowPress, onBecameFocused: onBecameFocused })))))));
|
|
42
|
+
};
|
|
43
|
+
export default LayoutSelector;
|
|
@@ -4,6 +4,7 @@ import { captureException } from '@/sentry';
|
|
|
4
4
|
import NetworkManager from '../NetworkManager/NetworkManager';
|
|
5
5
|
const SITE_ID_STATIC_BASE_URL_MAP = {
|
|
6
6
|
'664b9c57d59a7a431542d814': 'https://static-prod.us-central1-espn-prod.gcp.maestro.io',
|
|
7
|
+
'6930c214fcb9633b5484de81': 'https://static-prod.us-central1-espn-prod.gcp.maestro.io',
|
|
7
8
|
};
|
|
8
9
|
export default class StaticService {
|
|
9
10
|
constructor(siteId) {
|