@sensiblestats/widget-react 0.61.3 → 0.62.0
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/CHANGELOG.md +11 -0
- package/dist/index.cjs +300 -175
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +215 -90
- package/dist/index.js.map +1 -1
- package/dist/styles.css +53 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/StatsWidget.tsx
|
|
2
|
-
import { useCallback as
|
|
2
|
+
import { useCallback as useCallback10, useEffect as useEffect19, useMemo as useMemo3, useRef as useRef15, useState as useState38 } from "react";
|
|
3
3
|
import { StatsWidgetClient } from "@sensiblestats/widget-sdk";
|
|
4
4
|
|
|
5
5
|
// src/kickoff.ts
|
|
@@ -614,6 +614,10 @@ var STRINGS = {
|
|
|
614
614
|
function resolveLocaleStrings(locale) {
|
|
615
615
|
return STRINGS[locale] ?? STRINGS.en;
|
|
616
616
|
}
|
|
617
|
+
function isEnglishLocale(locale) {
|
|
618
|
+
const lang = (locale ?? "").trim().toLowerCase().split(/[-_]/)[0] ?? "";
|
|
619
|
+
return lang === "en" || !Object.prototype.hasOwnProperty.call(STRINGS, lang);
|
|
620
|
+
}
|
|
617
621
|
|
|
618
622
|
// src/config.ts
|
|
619
623
|
function normalizeUi(cfg) {
|
|
@@ -623,6 +627,7 @@ function normalizeUi(cfg) {
|
|
|
623
627
|
accent: cfg.accent,
|
|
624
628
|
logoUrl: cfg.logoUrl,
|
|
625
629
|
headerTitle: cfg.headerTitle,
|
|
630
|
+
rawBettingLabels: isEnglishLocale(cfg.locale),
|
|
626
631
|
cta: cfg.cta,
|
|
627
632
|
surface: cfg.surface,
|
|
628
633
|
ink: cfg.ink,
|
|
@@ -738,6 +743,18 @@ function toUserContext(cfg) {
|
|
|
738
743
|
|
|
739
744
|
// src/slip.ts
|
|
740
745
|
var ADD_TO_SLIP_TIMEOUT_MS = 1e4;
|
|
746
|
+
var MAX_SELECTION = 64;
|
|
747
|
+
var MAX_LINE = 32;
|
|
748
|
+
var MAX_ODDS = 1e4;
|
|
749
|
+
function slipPickFields(selection, label, line) {
|
|
750
|
+
const fields = {};
|
|
751
|
+
if (Number.isSafeInteger(selection.matchId) && selection.matchId > 0) fields.matchId = selection.matchId;
|
|
752
|
+
if (label && label.trim() && label.length <= MAX_SELECTION) fields.selection = label;
|
|
753
|
+
if (line && line.trim() && line.length <= MAX_LINE) fields.line = line;
|
|
754
|
+
const odds = selection.oddsDecimal;
|
|
755
|
+
if (Number.isFinite(odds) && odds > 1 && odds <= MAX_ODDS) fields.odds = odds;
|
|
756
|
+
return fields;
|
|
757
|
+
}
|
|
741
758
|
function wasAdded(result) {
|
|
742
759
|
if (!result) return true;
|
|
743
760
|
if (result.status === "price_changed") return result.accepted;
|
|
@@ -1024,6 +1041,8 @@ function toMarketOddsVM(card) {
|
|
|
1024
1041
|
return {
|
|
1025
1042
|
key: `${selection}:${str(r.line) ?? ""}:${i}`,
|
|
1026
1043
|
selection,
|
|
1044
|
+
selectionLabel: str(r.selectionLabel),
|
|
1045
|
+
line: str(r.line),
|
|
1027
1046
|
odds: price !== void 0 ? price.toFixed(2) : "",
|
|
1028
1047
|
oddsDecimal: price,
|
|
1029
1048
|
matchOddsId: typeof r.matchOddsId === "number" ? r.matchOddsId : void 0,
|
|
@@ -1777,6 +1796,7 @@ function useLiveMonitor(opts) {
|
|
|
1777
1796
|
const inFlightRef = useRef3(false);
|
|
1778
1797
|
const timerRef = useRef3(void 0);
|
|
1779
1798
|
const disabledRef = useRef3(false);
|
|
1799
|
+
const mountedRef = useRef3(false);
|
|
1780
1800
|
const getLiveMonitorRef = useRef3(opts.getLiveMonitor);
|
|
1781
1801
|
getLiveMonitorRef.current = opts.getLiveMonitor;
|
|
1782
1802
|
const tick = useCallback2(async () => {
|
|
@@ -1784,6 +1804,7 @@ function useLiveMonitor(opts) {
|
|
|
1784
1804
|
inFlightRef.current = true;
|
|
1785
1805
|
try {
|
|
1786
1806
|
const snap = await getLiveMonitorRef.current(pinsRef.current);
|
|
1807
|
+
if (!mountedRef.current) return;
|
|
1787
1808
|
if (snap === null) {
|
|
1788
1809
|
disabledRef.current = true;
|
|
1789
1810
|
applySnapshot(null);
|
|
@@ -1805,10 +1826,16 @@ function useLiveMonitor(opts) {
|
|
|
1805
1826
|
});
|
|
1806
1827
|
scheduleRef.current = () => {
|
|
1807
1828
|
window.clearTimeout(timerRef.current);
|
|
1808
|
-
if (disabledRef.current || pinsRef.current.length === 0 || document.visibilityState === "hidden") return;
|
|
1829
|
+
if (!mountedRef.current || disabledRef.current || pinsRef.current.length === 0 || document.visibilityState === "hidden") return;
|
|
1809
1830
|
const hinted = (snapshotRef.current?.nextPollSeconds ?? DEFAULT_POLL_MS / 1e3) * 1e3;
|
|
1810
1831
|
timerRef.current = window.setTimeout(() => void tick(), Math.max(MIN_POLL_MS, hinted));
|
|
1811
1832
|
};
|
|
1833
|
+
useEffect3(() => {
|
|
1834
|
+
mountedRef.current = true;
|
|
1835
|
+
return () => {
|
|
1836
|
+
mountedRef.current = false;
|
|
1837
|
+
};
|
|
1838
|
+
}, []);
|
|
1812
1839
|
const prevPinsLengthRef = useRef3(0);
|
|
1813
1840
|
useEffect3(() => {
|
|
1814
1841
|
const wasEmpty = prevPinsLengthRef.current === 0;
|
|
@@ -2203,8 +2230,31 @@ function useEventLog() {
|
|
|
2203
2230
|
return useContext(EventLogContext);
|
|
2204
2231
|
}
|
|
2205
2232
|
|
|
2233
|
+
// src/popupTelemetry.ts
|
|
2234
|
+
import { useCallback as useCallback6, useEffect as useEffect7, useRef as useRef7 } from "react";
|
|
2235
|
+
function usePopupTelemetry(log, isOpen, popupText, dismissPopup) {
|
|
2236
|
+
const shownRef = useRef7(/* @__PURE__ */ new Set());
|
|
2237
|
+
const closedTextRef = useRef7(null);
|
|
2238
|
+
useEffect7(() => {
|
|
2239
|
+
if (isOpen) {
|
|
2240
|
+
if (closedTextRef.current) log?.log({ name: "popup_opened" });
|
|
2241
|
+
closedTextRef.current = null;
|
|
2242
|
+
return;
|
|
2243
|
+
}
|
|
2244
|
+
closedTextRef.current = popupText;
|
|
2245
|
+
if (popupText && !shownRef.current.has(popupText)) {
|
|
2246
|
+
shownRef.current.add(popupText);
|
|
2247
|
+
log?.log({ name: "popup_shown" });
|
|
2248
|
+
}
|
|
2249
|
+
}, [log, isOpen, popupText]);
|
|
2250
|
+
return useCallback6(() => {
|
|
2251
|
+
log?.log({ name: "popup_dismissed" });
|
|
2252
|
+
dismissPopup();
|
|
2253
|
+
}, [log, dismissPopup]);
|
|
2254
|
+
}
|
|
2255
|
+
|
|
2206
2256
|
// src/components/ChatFab.tsx
|
|
2207
|
-
import { useEffect as
|
|
2257
|
+
import { useEffect as useEffect8, useRef as useRef8, useState as useState7 } from "react";
|
|
2208
2258
|
|
|
2209
2259
|
// src/components/BrandMark.tsx
|
|
2210
2260
|
import { useState as useState6 } from "react";
|
|
@@ -2282,8 +2332,8 @@ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
|
2282
2332
|
function ChatFab({ label, greeting, popupText, logoUrl, onOpen, onDismissPopup, dismissLabel = "Dismiss", introAllowed = false, onIntroStart }) {
|
|
2283
2333
|
const bubble = popupText ?? greeting;
|
|
2284
2334
|
const [intro, setIntro] = useState7(false);
|
|
2285
|
-
const armedRef =
|
|
2286
|
-
|
|
2335
|
+
const armedRef = useRef8(introAllowed);
|
|
2336
|
+
useEffect8(() => {
|
|
2287
2337
|
if (armedRef.current && bubble) {
|
|
2288
2338
|
armedRef.current = false;
|
|
2289
2339
|
setIntro(true);
|
|
@@ -2297,10 +2347,10 @@ function ChatFab({ label, greeting, popupText, logoUrl, onOpen, onDismissPopup,
|
|
|
2297
2347
|
}
|
|
2298
2348
|
|
|
2299
2349
|
// src/components/ChatPanel.tsx
|
|
2300
|
-
import { useEffect as
|
|
2350
|
+
import { useEffect as useEffect18, useState as useState37 } from "react";
|
|
2301
2351
|
|
|
2302
2352
|
// src/components/ChatHeader.tsx
|
|
2303
|
-
import { useEffect as
|
|
2353
|
+
import { useEffect as useEffect9, useRef as useRef9, useState as useState8 } from "react";
|
|
2304
2354
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2305
2355
|
function ChatHeader({
|
|
2306
2356
|
onClose,
|
|
@@ -2313,14 +2363,14 @@ function ChatHeader({
|
|
|
2313
2363
|
moreOptionsLabel = "More options"
|
|
2314
2364
|
}) {
|
|
2315
2365
|
const [menuOpen, setMenuOpen] = useState8(false);
|
|
2316
|
-
const wrapRef =
|
|
2317
|
-
const kebabRef =
|
|
2318
|
-
const firstItemRef =
|
|
2366
|
+
const wrapRef = useRef9(null);
|
|
2367
|
+
const kebabRef = useRef9(null);
|
|
2368
|
+
const firstItemRef = useRef9(null);
|
|
2319
2369
|
const hasMenu = !!(canReset && onNewConversation);
|
|
2320
|
-
|
|
2370
|
+
useEffect9(() => {
|
|
2321
2371
|
if (menuOpen) firstItemRef.current?.focus();
|
|
2322
2372
|
}, [menuOpen]);
|
|
2323
|
-
|
|
2373
|
+
useEffect9(() => {
|
|
2324
2374
|
if (!menuOpen) return;
|
|
2325
2375
|
const onPointer = (e) => {
|
|
2326
2376
|
if (!wrapRef.current?.contains(e.target)) setMenuOpen(false);
|
|
@@ -2376,7 +2426,7 @@ function ChatHeader({
|
|
|
2376
2426
|
}
|
|
2377
2427
|
|
|
2378
2428
|
// src/components/MessageList.tsx
|
|
2379
|
-
import { useEffect as
|
|
2429
|
+
import { useEffect as useEffect13, useRef as useRef11 } from "react";
|
|
2380
2430
|
|
|
2381
2431
|
// src/components/Markdown.tsx
|
|
2382
2432
|
import ReactMarkdown from "react-markdown";
|
|
@@ -2506,15 +2556,15 @@ function EntityCardList({ cards, disabled, showLessLabel = "Show less", showMore
|
|
|
2506
2556
|
import { useState as useState13 } from "react";
|
|
2507
2557
|
|
|
2508
2558
|
// src/components/AddToSlipButton.tsx
|
|
2509
|
-
import { useCallback as
|
|
2559
|
+
import { useCallback as useCallback7, useState as useState10 } from "react";
|
|
2510
2560
|
import { isAddToSlipResult } from "@sensiblestats/widget-sdk";
|
|
2511
2561
|
|
|
2512
2562
|
// src/components/Toast.tsx
|
|
2513
|
-
import { useEffect as
|
|
2563
|
+
import { useEffect as useEffect10 } from "react";
|
|
2514
2564
|
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
2515
2565
|
var DISMISS_MS = 4e3;
|
|
2516
2566
|
function Toast({ toast, onDismiss }) {
|
|
2517
|
-
|
|
2567
|
+
useEffect10(() => {
|
|
2518
2568
|
if (!toast) return;
|
|
2519
2569
|
const timer = setTimeout(onDismiss, DISMISS_MS);
|
|
2520
2570
|
return () => clearTimeout(timer);
|
|
@@ -2531,18 +2581,19 @@ function withTimeout(value) {
|
|
|
2531
2581
|
new Promise((_, reject) => setTimeout(() => reject(new Error("add-to-slip timed out")), ADD_TO_SLIP_TIMEOUT_MS))
|
|
2532
2582
|
]);
|
|
2533
2583
|
}
|
|
2534
|
-
function SlipButton({ selection, shownOdds,
|
|
2584
|
+
function SlipButton({ selection, shownOdds, marketDeveloperName, selectionLabel, line, slip, ui }) {
|
|
2535
2585
|
const [pending, setPending] = useState10(false);
|
|
2536
2586
|
const [settled, setSettled] = useState10(false);
|
|
2537
2587
|
const [toast, setToast] = useState10(null);
|
|
2538
|
-
const dismiss =
|
|
2588
|
+
const dismiss = useCallback7(() => setToast(null), []);
|
|
2539
2589
|
const log = useEventLog();
|
|
2540
2590
|
const handler = slip.onAddToSlip;
|
|
2541
2591
|
if (!ui || !handler || !selection) return null;
|
|
2542
2592
|
const onClick = async () => {
|
|
2543
2593
|
setPending(true);
|
|
2594
|
+
const pick = slipPickFields(selection, ui.rawBettingLabels ? selectionLabel : void 0, line);
|
|
2544
2595
|
if (marketDeveloperName !== void 0) {
|
|
2545
|
-
log?.log({ name: "slip_click", entityType: "market", marketDeveloperName, entityId: marketDeveloperName });
|
|
2596
|
+
log?.log({ name: "slip_click", entityType: "market", marketDeveloperName, entityId: marketDeveloperName, ...pick });
|
|
2546
2597
|
}
|
|
2547
2598
|
try {
|
|
2548
2599
|
const raw = await withTimeout(Promise.resolve(handler(selection)));
|
|
@@ -2554,20 +2605,15 @@ function SlipButton({ selection, shownOdds, marketName, marketDeveloperName, sli
|
|
|
2554
2605
|
entityType: "market",
|
|
2555
2606
|
marketDeveloperName,
|
|
2556
2607
|
entityId: marketDeveloperName,
|
|
2557
|
-
outcome: slipOutcome(result)
|
|
2608
|
+
outcome: slipOutcome(result),
|
|
2609
|
+
...pick
|
|
2558
2610
|
});
|
|
2559
2611
|
}
|
|
2560
2612
|
setToast(resultToToast(result, shownOdds || selection.oddsDecimal.toFixed(2), ui));
|
|
2561
2613
|
setSettled(isTerminal(result));
|
|
2562
|
-
if (wasAdded(result) && slip.onMarketClicked) {
|
|
2563
|
-
try {
|
|
2564
|
-
slip.onMarketClicked(marketName);
|
|
2565
|
-
} catch {
|
|
2566
|
-
}
|
|
2567
|
-
}
|
|
2568
2614
|
} catch {
|
|
2569
2615
|
if (marketDeveloperName !== void 0) {
|
|
2570
|
-
log?.log({ name: "slip_result", entityType: "market", marketDeveloperName, entityId: marketDeveloperName, outcome: "timeout" });
|
|
2616
|
+
log?.log({ name: "slip_result", entityType: "market", marketDeveloperName, entityId: marketDeveloperName, outcome: "timeout", ...pick });
|
|
2571
2617
|
}
|
|
2572
2618
|
setToast({ kind: "error", text: ui.slipFailed });
|
|
2573
2619
|
} finally {
|
|
@@ -2595,8 +2641,9 @@ function AddToSlipButton({ it, slip, ui }) {
|
|
|
2595
2641
|
{
|
|
2596
2642
|
selection,
|
|
2597
2643
|
shownOdds: it.odds ?? "",
|
|
2598
|
-
marketName: it.market,
|
|
2599
2644
|
marketDeveloperName: it.marketDeveloperName,
|
|
2645
|
+
selectionLabel: it.selectionLabel,
|
|
2646
|
+
line: it.line,
|
|
2600
2647
|
slip,
|
|
2601
2648
|
ui
|
|
2602
2649
|
}
|
|
@@ -2640,7 +2687,7 @@ function Crest2({ url, name, kind }) {
|
|
|
2640
2687
|
}
|
|
2641
2688
|
|
|
2642
2689
|
// src/components/PinToggle.tsx
|
|
2643
|
-
import { useCallback as
|
|
2690
|
+
import { useCallback as useCallback8, useState as useState12 } from "react";
|
|
2644
2691
|
import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2645
2692
|
var PIN_GLYPHS = {
|
|
2646
2693
|
pin: { viewBox: "0 0 24 24", strokeWidth: 1.4, transform: "scale(1.5)", d: "M9.5 1.5 14.5 6.5 13 8l-.5-.5-3 3 .3 2.2-1.4 1.4L5.6 11.3l-3.3 3.3-1-1 3.3-3.3L1.9 7.5l1.4-1.4 2.2.3 3-3L8 2.9z" },
|
|
@@ -2649,7 +2696,7 @@ var PIN_GLYPHS = {
|
|
|
2649
2696
|
};
|
|
2650
2697
|
function PinToggle({ pin, monitor, ui }) {
|
|
2651
2698
|
const [toast, setToast] = useState12(null);
|
|
2652
|
-
const dismiss =
|
|
2699
|
+
const dismiss = useCallback8(() => setToast(null), []);
|
|
2653
2700
|
const log = useEventLog();
|
|
2654
2701
|
if (!monitor || !monitor.enabled) return null;
|
|
2655
2702
|
const key = pinKey(pin);
|
|
@@ -2853,7 +2900,7 @@ function OddsRow({ vm, row, ui, slip, inSlipIds }) {
|
|
|
2853
2900
|
"\u2713 ",
|
|
2854
2901
|
ui.inSlipLabel
|
|
2855
2902
|
] }) : null,
|
|
2856
|
-
/* @__PURE__ */ jsx14(SlipButton, { selection, shownOdds: row.odds,
|
|
2903
|
+
/* @__PURE__ */ jsx14(SlipButton, { selection, shownOdds: row.odds, marketDeveloperName: vm.marketDeveloperName, selectionLabel: row.selectionLabel, line: row.line, slip, ui })
|
|
2857
2904
|
] });
|
|
2858
2905
|
}
|
|
2859
2906
|
function MarketOddsCard({ vm, ui, slip, monitor, inSlipIds, hideHeader, pinRef }) {
|
|
@@ -3025,7 +3072,37 @@ function ActionButtons({ actions, loading, disabled, onAct }) {
|
|
|
3025
3072
|
// src/components/dashboard/DashboardTile.tsx
|
|
3026
3073
|
import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3027
3074
|
var MAX_CRESTS = 3;
|
|
3028
|
-
|
|
3075
|
+
var ODDS_TAIL = /\s*·\s*@(\d+(?:\.\d+)?)\s*$/;
|
|
3076
|
+
function splitOdds(subtitle) {
|
|
3077
|
+
const m = ODDS_TAIL.exec(subtitle);
|
|
3078
|
+
return m ? { text: subtitle.slice(0, m.index), odds: m[1] } : { text: subtitle };
|
|
3079
|
+
}
|
|
3080
|
+
function HeroTile({ tile, onTap, oddsLabel }) {
|
|
3081
|
+
const crests = (tile.imageUrls ?? []).filter(Boolean).slice(0, 2);
|
|
3082
|
+
const sub = tile.subtitle ? splitOdds(tile.subtitle) : null;
|
|
3083
|
+
return /* @__PURE__ */ jsxs13("button", { type: "button", className: "ss-w-dash-tile ss-w-dash-tile--full ss-w-dash-hero", onClick: onTap, children: [
|
|
3084
|
+
/* @__PURE__ */ jsxs13("span", { className: "ss-w-dash-hero-top", children: [
|
|
3085
|
+
tile.badgeText ? /* @__PURE__ */ jsx17("span", { className: "ss-w-dash-hero-badge", children: tile.badgeText }) : null,
|
|
3086
|
+
sub?.odds ? /* @__PURE__ */ jsxs13("span", { className: "ss-w-dash-hero-price", children: [
|
|
3087
|
+
/* @__PURE__ */ jsxs13("span", { className: "ss-g-sr", children: [
|
|
3088
|
+
oddsLabel,
|
|
3089
|
+
" "
|
|
3090
|
+
] }),
|
|
3091
|
+
sub.odds
|
|
3092
|
+
] }) : null,
|
|
3093
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-dash-hero-chevron", children: /* @__PURE__ */ jsx17(ChevronIcon, {}) })
|
|
3094
|
+
] }),
|
|
3095
|
+
/* @__PURE__ */ jsxs13("span", { className: "ss-w-dash-hero-main", children: [
|
|
3096
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-dash-hero-visual", "aria-hidden": "true", children: crests.length > 0 ? crests.map((url, i) => /* @__PURE__ */ jsx17("span", { className: "ss-w-dash-hero-crest", style: { backgroundImage: `url(${url})` } }, `${url}-${i}`)) : /* @__PURE__ */ jsx17("span", { className: "ss-w-dash-hero-icon", children: /* @__PURE__ */ jsx17(DashboardIcon, { name: tile.icon }) }) }),
|
|
3097
|
+
/* @__PURE__ */ jsxs13("span", { className: "ss-w-dash-hero-body", children: [
|
|
3098
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-dash-hero-title", children: tile.title }),
|
|
3099
|
+
sub?.text ? /* @__PURE__ */ jsx17("span", { className: "ss-w-dash-hero-subtitle", children: sub.text }) : null
|
|
3100
|
+
] })
|
|
3101
|
+
] })
|
|
3102
|
+
] });
|
|
3103
|
+
}
|
|
3104
|
+
function DashboardTile({ tile, onTap, oddsLabel = "Odds" }) {
|
|
3105
|
+
if (tile.emphasis === "hero") return /* @__PURE__ */ jsx17(HeroTile, { tile, onTap, oddsLabel });
|
|
3029
3106
|
const spanClass = tile.span === "full" ? "ss-w-dash-tile--full" : "ss-w-dash-tile--half";
|
|
3030
3107
|
const crests = (tile.imageUrls ?? []).slice(0, MAX_CRESTS);
|
|
3031
3108
|
const hasMeta = Boolean(tile.badgeText) || crests.length > 0;
|
|
@@ -3058,13 +3135,13 @@ var LAYOUT_CLASSES = {
|
|
|
3058
3135
|
function layoutClass(layout) {
|
|
3059
3136
|
return Object.prototype.hasOwnProperty.call(LAYOUT_CLASSES, layout) ? LAYOUT_CLASSES[layout] : LAYOUT_CLASSES.goal;
|
|
3060
3137
|
}
|
|
3061
|
-
function GreetingDashboard({ dashboard, onTap }) {
|
|
3138
|
+
function GreetingDashboard({ dashboard, onTap, oddsLabel }) {
|
|
3062
3139
|
return /* @__PURE__ */ jsxs14("div", { className: `ss-w-dash ${layoutClass(dashboard.layout)}`, children: [
|
|
3063
3140
|
/* @__PURE__ */ jsxs14("div", { className: "ss-w-dash-head", children: [
|
|
3064
3141
|
/* @__PURE__ */ jsx18("span", { className: "ss-w-dash-title", children: dashboard.title }),
|
|
3065
3142
|
dashboard.subtitle ? /* @__PURE__ */ jsx18("span", { className: "ss-w-dash-subtitle", children: dashboard.subtitle }) : null
|
|
3066
3143
|
] }),
|
|
3067
|
-
/* @__PURE__ */ jsx18("div", { className: "ss-w-dash-grid", children: dashboard.tiles.map((t, i) => /* @__PURE__ */ jsx18(DashboardTile, { tile: t, onTap: () => onTap(t, i) }, `${t.title}-${i}`)) }),
|
|
3144
|
+
/* @__PURE__ */ jsx18("div", { className: "ss-w-dash-grid", children: dashboard.tiles.map((t, i) => /* @__PURE__ */ jsx18(DashboardTile, { tile: t, onTap: () => onTap(t, i), oddsLabel }, `${t.title}-${i}`)) }),
|
|
3068
3145
|
dashboard.footerTip ? /* @__PURE__ */ jsx18("span", { className: "ss-w-dash-foot", children: dashboard.footerTip }) : null
|
|
3069
3146
|
] });
|
|
3070
3147
|
}
|
|
@@ -3160,7 +3237,7 @@ function FixtureCard({ card }) {
|
|
|
3160
3237
|
}
|
|
3161
3238
|
|
|
3162
3239
|
// src/components/dashboard/CountdownCard.tsx
|
|
3163
|
-
import { Fragment as Fragment6, useEffect as
|
|
3240
|
+
import { Fragment as Fragment6, useEffect as useEffect11, useState as useState15 } from "react";
|
|
3164
3241
|
import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
3165
3242
|
var DAY_MS = 864e5;
|
|
3166
3243
|
function remainingSegments(kickoffUnix, nowMs, withSeconds) {
|
|
@@ -3184,7 +3261,7 @@ function CountdownCard({ card }) {
|
|
|
3184
3261
|
() => typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
|
3185
3262
|
);
|
|
3186
3263
|
const withinDay = data.kickoffUnix * 1e3 - nowMs < DAY_MS;
|
|
3187
|
-
|
|
3264
|
+
useEffect11(() => {
|
|
3188
3265
|
if (reduced) return;
|
|
3189
3266
|
const id = window.setInterval(() => setNowMs(Date.now()), withinDay ? 1e3 : 6e4);
|
|
3190
3267
|
return () => window.clearInterval(id);
|
|
@@ -3541,10 +3618,10 @@ function RestingFormCard({ card }) {
|
|
|
3541
3618
|
}
|
|
3542
3619
|
|
|
3543
3620
|
// src/components/dashboard/LiveScoreCard.tsx
|
|
3544
|
-
import { useRef as
|
|
3621
|
+
import { useRef as useRef10, useState as useState19 } from "react";
|
|
3545
3622
|
|
|
3546
3623
|
// src/useLiveClock.ts
|
|
3547
|
-
import { useEffect as
|
|
3624
|
+
import { useEffect as useEffect12, useState as useState18 } from "react";
|
|
3548
3625
|
var TICK_MS = 6e4;
|
|
3549
3626
|
var MAX_LIVE_TICK_MINUTES = 15;
|
|
3550
3627
|
function computeDisplayMinute(minute, clockRunning, receivedAtMs) {
|
|
@@ -3556,7 +3633,7 @@ function useLiveClock(minute, clockRunning, receivedAtMs) {
|
|
|
3556
3633
|
const [displayMinute, setDisplayMinute] = useState18(
|
|
3557
3634
|
() => computeDisplayMinute(minute, clockRunning, receivedAtMs)
|
|
3558
3635
|
);
|
|
3559
|
-
|
|
3636
|
+
useEffect12(() => {
|
|
3560
3637
|
setDisplayMinute(computeDisplayMinute(minute, clockRunning, receivedAtMs));
|
|
3561
3638
|
if (!clockRunning) return;
|
|
3562
3639
|
const id = window.setInterval(() => {
|
|
@@ -3592,7 +3669,7 @@ function LiveScoreCard({
|
|
|
3592
3669
|
}) {
|
|
3593
3670
|
const { data } = card;
|
|
3594
3671
|
const seedKey = `${data.minute}:${data.homeScore}:${data.awayScore}`;
|
|
3595
|
-
const seedRef =
|
|
3672
|
+
const seedRef = useRef10(null);
|
|
3596
3673
|
if (!seedRef.current || seedRef.current.key !== seedKey) {
|
|
3597
3674
|
seedRef.current = { key: seedKey, receivedAtMs: Date.now() };
|
|
3598
3675
|
}
|
|
@@ -3881,7 +3958,7 @@ function ChatMessage({ message, disabled, onAct, onTap, ui, slip, monitor }) {
|
|
|
3881
3958
|
};
|
|
3882
3959
|
return /* @__PURE__ */ jsxs37("div", { className: "ss-w-msg ss-w-bot", "data-msg-id": message.id, children: [
|
|
3883
3960
|
message.text && !message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ jsx41(Markdown, { text: message.text }) : null,
|
|
3884
|
-
message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ jsx41(GreetingDashboard, { dashboard: message.greetingDashboard, onTap }) : null,
|
|
3961
|
+
message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ jsx41(GreetingDashboard, { dashboard: message.greetingDashboard, onTap, oddsLabel: ui?.oddsLabel }) : null,
|
|
3885
3962
|
message.clubFanDashboard ? /* @__PURE__ */ jsx41(ClubFanDashboard, { dashboard: message.clubFanDashboard, onTap: onFanDashboardTap, ui }) : null,
|
|
3886
3963
|
/* @__PURE__ */ jsx41(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
|
|
3887
3964
|
/* @__PURE__ */ jsx41(BettingInsightList, { insights: message.insights, ui, slip, monitor }),
|
|
@@ -3914,11 +3991,11 @@ function turnAnchor(messages) {
|
|
|
3914
3991
|
}
|
|
3915
3992
|
function MessageList({ controller, ui, slip, onDashboardTap, monitor }) {
|
|
3916
3993
|
const { state, send } = controller;
|
|
3917
|
-
const listRef =
|
|
3918
|
-
const endRef =
|
|
3919
|
-
const pinnedRef =
|
|
3920
|
-
const anchoredTurnRef =
|
|
3921
|
-
|
|
3994
|
+
const listRef = useRef11(null);
|
|
3995
|
+
const endRef = useRef11(null);
|
|
3996
|
+
const pinnedRef = useRef11(true);
|
|
3997
|
+
const anchoredTurnRef = useRef11(null);
|
|
3998
|
+
useEffect13(() => {
|
|
3922
3999
|
const anchor = turnAnchor(state.messages);
|
|
3923
4000
|
const list = listRef.current;
|
|
3924
4001
|
if (anchor && anchor.turnId !== anchoredTurnRef.current) {
|
|
@@ -4207,8 +4284,8 @@ function MonitorMatchCard({ match, promotedPins, monitor, ui, slip }) {
|
|
|
4207
4284
|
{
|
|
4208
4285
|
selection,
|
|
4209
4286
|
shownOdds: typeof row.market.price === "number" ? row.market.price.toFixed(2) : "",
|
|
4210
|
-
marketName: row.market.marketLabel,
|
|
4211
4287
|
marketDeveloperName: row.market.marketDeveloperName,
|
|
4288
|
+
selectionLabel: row.market.selectionLabel,
|
|
4212
4289
|
slip,
|
|
4213
4290
|
ui
|
|
4214
4291
|
}
|
|
@@ -4400,7 +4477,7 @@ function TodayPanel({ discovery, ui, monitor }) {
|
|
|
4400
4477
|
}
|
|
4401
4478
|
|
|
4402
4479
|
// src/components/GamesPanel.tsx
|
|
4403
|
-
import { useEffect as
|
|
4480
|
+
import { useEffect as useEffect17, useState as useState35 } from "react";
|
|
4404
4481
|
|
|
4405
4482
|
// src/components/GamesLoginWall.tsx
|
|
4406
4483
|
import { jsx as jsx52 } from "react/jsx-runtime";
|
|
@@ -4409,7 +4486,7 @@ function GamesLoginWall({ ui }) {
|
|
|
4409
4486
|
}
|
|
4410
4487
|
|
|
4411
4488
|
// src/components/GamesHub.tsx
|
|
4412
|
-
import { useEffect as
|
|
4489
|
+
import { useEffect as useEffect14, useState as useState22 } from "react";
|
|
4413
4490
|
|
|
4414
4491
|
// src/components/games/ui/gameMeta.ts
|
|
4415
4492
|
function prettifyGameId(gameId) {
|
|
@@ -4595,7 +4672,7 @@ function pickHeroGame(games) {
|
|
|
4595
4672
|
var tileKey = (g) => `${g.gameId}-${g.gameDay}-${g.fixtureId}`;
|
|
4596
4673
|
function GamesHub({ games, myWeeklyRow, weeklyPrize, onOpen, ui }) {
|
|
4597
4674
|
const [, setTick] = useState22(0);
|
|
4598
|
-
|
|
4675
|
+
useEffect14(() => {
|
|
4599
4676
|
const id = setInterval(() => setTick((t) => t + 1), 6e4);
|
|
4600
4677
|
return () => clearInterval(id);
|
|
4601
4678
|
}, []);
|
|
@@ -4655,10 +4732,10 @@ function GamesHub({ games, myWeeklyRow, weeklyPrize, onOpen, ui }) {
|
|
|
4655
4732
|
}
|
|
4656
4733
|
|
|
4657
4734
|
// src/components/GameScreenHost.tsx
|
|
4658
|
-
import { useEffect as
|
|
4735
|
+
import { useEffect as useEffect16, useRef as useRef14, useState as useState34 } from "react";
|
|
4659
4736
|
|
|
4660
4737
|
// src/useGamePlay.ts
|
|
4661
|
-
import { useCallback as
|
|
4738
|
+
import { useCallback as useCallback9, useRef as useRef12, useState as useState23 } from "react";
|
|
4662
4739
|
function toSubmitResult(payload) {
|
|
4663
4740
|
return {
|
|
4664
4741
|
// 'pending' means the play is recorded but awaiting resolution (fixture games)
|
|
@@ -4673,14 +4750,14 @@ function useGamePlay(conversation, { gameId, gameDay, fixtureId, locale, refresh
|
|
|
4673
4750
|
const [result, setResult] = useState23(null);
|
|
4674
4751
|
const [submitting, setSubmitting] = useState23(false);
|
|
4675
4752
|
const [rawStatus, setRawStatus] = useState23(null);
|
|
4676
|
-
const inFlightRef =
|
|
4677
|
-
const convRef =
|
|
4753
|
+
const inFlightRef = useRef12(false);
|
|
4754
|
+
const convRef = useRef12(conversation);
|
|
4678
4755
|
convRef.current = conversation;
|
|
4679
|
-
const optsRef =
|
|
4756
|
+
const optsRef = useRef12({ gameId, gameDay, fixtureId, locale, refresh });
|
|
4680
4757
|
optsRef.current = { gameId, gameDay, fixtureId, locale, refresh };
|
|
4681
|
-
const resultRef =
|
|
4758
|
+
const resultRef = useRef12(null);
|
|
4682
4759
|
resultRef.current = result;
|
|
4683
|
-
const refetch =
|
|
4760
|
+
const refetch = useCallback9(async () => {
|
|
4684
4761
|
const conv = convRef.current;
|
|
4685
4762
|
if (!conv) return;
|
|
4686
4763
|
const opts = optsRef.current;
|
|
@@ -4696,7 +4773,7 @@ function useGamePlay(conversation, { gameId, gameDay, fixtureId, locale, refresh
|
|
|
4696
4773
|
} catch {
|
|
4697
4774
|
}
|
|
4698
4775
|
}, []);
|
|
4699
|
-
const submit =
|
|
4776
|
+
const submit = useCallback9(async (answerJson) => {
|
|
4700
4777
|
const conv = convRef.current;
|
|
4701
4778
|
if (!conv || inFlightRef.current) return;
|
|
4702
4779
|
inFlightRef.current = true;
|
|
@@ -4723,7 +4800,7 @@ function useGamePlay(conversation, { gameId, gameDay, fixtureId, locale, refresh
|
|
|
4723
4800
|
setSubmitting(false);
|
|
4724
4801
|
}
|
|
4725
4802
|
}, [refetch]);
|
|
4726
|
-
const hydrate =
|
|
4803
|
+
const hydrate = useCallback9(() => {
|
|
4727
4804
|
void refetch();
|
|
4728
4805
|
}, [refetch]);
|
|
4729
4806
|
return { result, submitting, submit, hydrate, rawStatus };
|
|
@@ -5608,7 +5685,7 @@ function CareerPathGame({
|
|
|
5608
5685
|
}
|
|
5609
5686
|
|
|
5610
5687
|
// src/components/games/BeatTheGridGame.tsx
|
|
5611
|
-
import React, { useEffect as
|
|
5688
|
+
import React, { useEffect as useEffect15, useId as useId4, useRef as useRef13, useState as useState28 } from "react";
|
|
5612
5689
|
import { Fragment as Fragment14, jsx as jsx71, jsxs as jsxs59 } from "react/jsx-runtime";
|
|
5613
5690
|
function criterionIconKind(label, key) {
|
|
5614
5691
|
const l = (key ?? label).toLowerCase();
|
|
@@ -5691,8 +5768,8 @@ function SearchSheet({
|
|
|
5691
5768
|
const s = ui.games.beatTheGrid;
|
|
5692
5769
|
const [query, setQuery] = useState28("");
|
|
5693
5770
|
const inputId = useId4();
|
|
5694
|
-
const inputRef =
|
|
5695
|
-
|
|
5771
|
+
const inputRef = useRef13(null);
|
|
5772
|
+
useEffect15(() => {
|
|
5696
5773
|
inputRef.current?.focus();
|
|
5697
5774
|
}, [row, col]);
|
|
5698
5775
|
const filtered = playerPool.filter(
|
|
@@ -6994,7 +7071,7 @@ function GameScreenHost({
|
|
|
6994
7071
|
}) {
|
|
6995
7072
|
const { gameId, fixtureId, gameDay = "", lockAtUtc = null } = selected;
|
|
6996
7073
|
const [configJson, setConfigJson] = useState34(null);
|
|
6997
|
-
|
|
7074
|
+
useEffect16(() => {
|
|
6998
7075
|
setConfigJson(null);
|
|
6999
7076
|
if (!conversation) return;
|
|
7000
7077
|
let cancelled = false;
|
|
@@ -7017,12 +7094,12 @@ function GameScreenHost({
|
|
|
7017
7094
|
fixtureId: fixtureId || void 0,
|
|
7018
7095
|
locale: ui.games.lang
|
|
7019
7096
|
});
|
|
7020
|
-
|
|
7097
|
+
useEffect16(() => {
|
|
7021
7098
|
if (conversation) hydrate();
|
|
7022
7099
|
}, [conversation, gameId]);
|
|
7023
|
-
const hydrateRef =
|
|
7100
|
+
const hydrateRef = useRef14(hydrate);
|
|
7024
7101
|
hydrateRef.current = hydrate;
|
|
7025
|
-
|
|
7102
|
+
useEffect16(() => {
|
|
7026
7103
|
if (!FIXTURE_GAME_IDS.has(gameId)) return;
|
|
7027
7104
|
if (rawStatus !== "pending") return;
|
|
7028
7105
|
const timerId = setInterval(() => {
|
|
@@ -7158,7 +7235,7 @@ function GamesPanel({
|
|
|
7158
7235
|
refreshHub
|
|
7159
7236
|
}) {
|
|
7160
7237
|
const [selected, setSelected] = useState35(null);
|
|
7161
|
-
|
|
7238
|
+
useEffect17(() => {
|
|
7162
7239
|
ensureGamesFonts();
|
|
7163
7240
|
}, []);
|
|
7164
7241
|
if (!loggedIn) return /* @__PURE__ */ jsx80(GamesLoginWall, { ui });
|
|
@@ -7358,7 +7435,7 @@ import { jsx as jsx85, jsxs as jsxs70 } from "react/jsx-runtime";
|
|
|
7358
7435
|
var FULLSCREEN_QUERY = "(max-width: 639px)";
|
|
7359
7436
|
function useIsModal() {
|
|
7360
7437
|
const [modal, setModal] = useState37(false);
|
|
7361
|
-
|
|
7438
|
+
useEffect18(() => {
|
|
7362
7439
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
7363
7440
|
const mql = window.matchMedia(FULLSCREEN_QUERY);
|
|
7364
7441
|
const sync = () => setModal(mql.matches);
|
|
@@ -7371,7 +7448,7 @@ function useIsModal() {
|
|
|
7371
7448
|
function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, gamesResult, leaderboardResult, loggedIn, conversation }) {
|
|
7372
7449
|
const { state, close, stop, send, reset } = controller;
|
|
7373
7450
|
const isModal = useIsModal();
|
|
7374
|
-
|
|
7451
|
+
useEffect18(() => {
|
|
7375
7452
|
const onKey = (e) => {
|
|
7376
7453
|
if (e.key === "Escape") close();
|
|
7377
7454
|
};
|
|
@@ -7382,7 +7459,7 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
|
|
|
7382
7459
|
monitor?.setActiveTab("chat");
|
|
7383
7460
|
send(text);
|
|
7384
7461
|
};
|
|
7385
|
-
|
|
7462
|
+
useEffect18(() => {
|
|
7386
7463
|
if (monitor && monitor.activeTab !== "chat" && state.isStreaming) monitor.markChatActivity();
|
|
7387
7464
|
}, [monitor, state.isStreaming, state.messages.length]);
|
|
7388
7465
|
return /* @__PURE__ */ jsxs70("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
|
|
@@ -7993,6 +8070,59 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
7993
8070
|
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(2) { background: color-mix(in srgb, #3a82f6 10%, var(--ss-w-panel)); }
|
|
7994
8071
|
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(3) { background: color-mix(in srgb, #f5a623 10%, var(--ss-w-panel)); }
|
|
7995
8072
|
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(4) { background: color-mix(in srgb, #a855f7 10%, var(--ss-w-panel)); }
|
|
8073
|
+
/* A hero tile would take the first tint position; count only regular tiles where \`of S\` parses. */
|
|
8074
|
+
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(1 of :not(.ss-w-dash-hero)) { background: color-mix(in srgb, #34c759 10%, var(--ss-w-panel)); }
|
|
8075
|
+
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(2 of :not(.ss-w-dash-hero)) { background: color-mix(in srgb, #3a82f6 10%, var(--ss-w-panel)); }
|
|
8076
|
+
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(3 of :not(.ss-w-dash-hero)) { background: color-mix(in srgb, #f5a623 10%, var(--ss-w-panel)); }
|
|
8077
|
+
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(4 of :not(.ss-w-dash-hero)) { background: color-mix(in srgb, #a855f7 10%, var(--ss-w-panel)); }
|
|
8078
|
+
|
|
8079
|
+
/* Hero: the personalized lead tile. Declared after the layout rules and at three-class
|
|
8080
|
+
specificity so it keeps one look in every layout -- no market rail, no goal pill, no
|
|
8081
|
+
discovery tint or stacking. Colours derive from accent/panel/ink so an operator surface
|
|
8082
|
+
and dark mode both work without their own rules. */
|
|
8083
|
+
.ss-w-dash .ss-w-dash-grid .ss-w-dash-hero {
|
|
8084
|
+
display: flex; flex-direction: column; align-items: stretch; gap: 10px;
|
|
8085
|
+
padding: 12px 14px 14px; border-radius: 16px;
|
|
8086
|
+
border: 1px solid color-mix(in srgb, var(--ss-w-accent) 38%, var(--ss-w-line));
|
|
8087
|
+
background:
|
|
8088
|
+
radial-gradient(120% 140% at 0% 0%, color-mix(in srgb, var(--ss-w-accent) 18%, transparent) 0%, transparent 60%),
|
|
8089
|
+
var(--ss-w-panel);
|
|
8090
|
+
box-shadow: 0 10px 24px -18px color-mix(in srgb, var(--ss-w-accent) 80%, transparent);
|
|
8091
|
+
}
|
|
8092
|
+
.ss-w-dash .ss-w-dash-grid .ss-w-dash-hero:hover { border-color: var(--ss-w-accent); }
|
|
8093
|
+
.ss-w-dash .ss-w-dash-grid .ss-w-dash-hero:focus-visible { outline: 2px solid var(--ss-w-accent); outline-offset: 2px; }
|
|
8094
|
+
.ss-w-dash-hero-top { display: flex; align-items: center; gap: 8px; min-height: 20px; }
|
|
8095
|
+
.ss-w-dash-hero-badge {
|
|
8096
|
+
font-size: 10.5px; font-weight: 750; letter-spacing: .06em; text-transform: uppercase;
|
|
8097
|
+
color: var(--ss-w-panel); background: color-mix(in srgb, var(--ss-w-accent) 45%, var(--ss-w-ink)); border-radius: 999px; padding: 3px 10px;
|
|
8098
|
+
white-space: nowrap;
|
|
8099
|
+
}
|
|
8100
|
+
.ss-w-dash-hero-chevron { flex: none; width: 14px; height: 14px; color: var(--ss-w-faint); }
|
|
8101
|
+
.ss-w-dash-hero-badge { margin-right: auto; }
|
|
8102
|
+
.ss-w-dash-hero-chevron:only-child { margin-left: auto; }
|
|
8103
|
+
.ss-w-dash-hero-main { display: flex; align-items: center; gap: 12px; min-width: 0; }
|
|
8104
|
+
.ss-w-dash-hero-visual { flex: none; display: flex; align-items: center; }
|
|
8105
|
+
.ss-w-dash-hero-crest {
|
|
8106
|
+
width: 40px; height: 40px; border-radius: 50%;
|
|
8107
|
+
background-color: color-mix(in srgb, var(--ss-w-ink) 4%, var(--ss-w-panel));
|
|
8108
|
+
background-size: 72%; background-position: center; background-repeat: no-repeat;
|
|
8109
|
+
box-shadow: 0 0 0 2px var(--ss-w-panel), 0 0 0 3px color-mix(in srgb, var(--ss-w-ink) 10%, var(--ss-w-panel));
|
|
8110
|
+
}
|
|
8111
|
+
.ss-w-dash-hero-crest + .ss-w-dash-hero-crest { margin-left: -8px; }
|
|
8112
|
+
.ss-w-dash-hero-icon {
|
|
8113
|
+
display: grid; place-items: center; width: 40px; height: 40px; border-radius: 50%; font-size: 22px;
|
|
8114
|
+
background: color-mix(in srgb, var(--ss-w-accent) 14%, var(--ss-w-panel));
|
|
8115
|
+
}
|
|
8116
|
+
.ss-w-dash-hero-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
|
|
8117
|
+
.ss-w-dash-hero-title { font-size: 15.5px; font-weight: 700; line-height: 1.25; color: var(--ss-w-ink); overflow-wrap: anywhere; }
|
|
8118
|
+
.ss-w-dash-hero-subtitle { font-size: 12px; color: var(--ss-w-faint); overflow-wrap: anywhere; }
|
|
8119
|
+
.ss-w-dash-hero-price {
|
|
8120
|
+
flex: none; margin-left: auto;
|
|
8121
|
+
font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-size: 15px; font-weight: 750; line-height: 1;
|
|
8122
|
+
color: color-mix(in srgb, var(--ss-w-accent) 45%, var(--ss-w-ink)); padding: 5px 9px; border-radius: 8px;
|
|
8123
|
+
background: color-mix(in srgb, var(--ss-w-accent) 12%, var(--ss-w-panel));
|
|
8124
|
+
border: 1px solid color-mix(in srgb, var(--ss-w-accent) 30%, var(--ss-w-panel));
|
|
8125
|
+
}
|
|
7996
8126
|
|
|
7997
8127
|
/* club fan pre-match dashboard */
|
|
7998
8128
|
.ss-w-fandash { display: flex; flex-direction: column; gap: 10px; padding: 4px 0; }
|
|
@@ -8900,9 +9030,9 @@ function StatsWidget(props) {
|
|
|
8900
9030
|
[built?.client]
|
|
8901
9031
|
);
|
|
8902
9032
|
const ui = useMemo3(() => normalizeUi(props.config ?? { operatorId: "", publicKey: "" }), [props.config]);
|
|
8903
|
-
const rootRef =
|
|
8904
|
-
const introDoneRef =
|
|
8905
|
-
const markIntroDone =
|
|
9033
|
+
const rootRef = useRef15(null);
|
|
9034
|
+
const introDoneRef = useRef15(false);
|
|
9035
|
+
const markIntroDone = useCallback10(() => {
|
|
8906
9036
|
introDoneRef.current = true;
|
|
8907
9037
|
}, []);
|
|
8908
9038
|
const convStorageKey = `ss-w-conv:${cfg?.operatorId ?? ""}:${cfg?.userContext?.operatorUserId ?? "anon"}`;
|
|
@@ -8915,11 +9045,11 @@ function StatsWidget(props) {
|
|
|
8915
9045
|
isOpen: controller.state.isOpen,
|
|
8916
9046
|
hasToday
|
|
8917
9047
|
});
|
|
8918
|
-
const getSchedule =
|
|
9048
|
+
const getSchedule = useCallback10(
|
|
8919
9049
|
(localDate, utcOffsetMinutes) => built ? built.conversation.getSchedule(localDate, utcOffsetMinutes) : Promise.resolve(null),
|
|
8920
9050
|
[built]
|
|
8921
9051
|
);
|
|
8922
|
-
const getStates =
|
|
9052
|
+
const getStates = useCallback10(
|
|
8923
9053
|
(matchIds) => built ? built.conversation.getStates(matchIds) : Promise.resolve(null),
|
|
8924
9054
|
[built]
|
|
8925
9055
|
);
|
|
@@ -8935,14 +9065,14 @@ function StatsWidget(props) {
|
|
|
8935
9065
|
pinnedMatchIds
|
|
8936
9066
|
});
|
|
8937
9067
|
const todayHasContent = visibleGroupsOf(discovery.groups, discovery.states).length > 0;
|
|
8938
|
-
|
|
9068
|
+
useEffect19(() => {
|
|
8939
9069
|
setHasToday(todayHasContent);
|
|
8940
9070
|
}, [todayHasContent]);
|
|
8941
9071
|
const gamesResult = useGames(built?.conversation ?? null, { activeTab: monitor.activeTab });
|
|
8942
9072
|
const leaderboardResult = useLeaderboard(built?.conversation ?? null, { activeTab: monitor.activeTab });
|
|
8943
9073
|
const markTodayActivity = monitor.markTodayActivity;
|
|
8944
|
-
const prevLiveCountRef =
|
|
8945
|
-
|
|
9074
|
+
const prevLiveCountRef = useRef15(0);
|
|
9075
|
+
useEffect19(() => {
|
|
8946
9076
|
const rose = prevLiveCountRef.current === 0 && discovery.liveCount > 0;
|
|
8947
9077
|
prevLiveCountRef.current = discovery.liveCount;
|
|
8948
9078
|
if (rose) markTodayActivity();
|
|
@@ -8959,29 +9089,24 @@ function StatsWidget(props) {
|
|
|
8959
9089
|
if (wasAdded(result)) monitor.markInSlip(sel.matchOddsId);
|
|
8960
9090
|
return result;
|
|
8961
9091
|
}),
|
|
8962
|
-
conversationId: controller.state.conversationId ?? void 0
|
|
8963
|
-
onMarketClicked: (marketName) => {
|
|
8964
|
-
try {
|
|
8965
|
-
void built?.conversation.sendSignal({ signalType: "InsightMarketClicked", entityType: "Market", entityName: marketName }).catch(() => void 0);
|
|
8966
|
-
} catch {
|
|
8967
|
-
}
|
|
8968
|
-
}
|
|
9092
|
+
conversationId: controller.state.conversationId ?? void 0
|
|
8969
9093
|
};
|
|
8970
9094
|
},
|
|
8971
|
-
[props.config?.onAddToSlip, controller.state.conversationId,
|
|
9095
|
+
[props.config?.onAddToSlip, controller.state.conversationId, monitor.markInSlip]
|
|
8972
9096
|
);
|
|
8973
9097
|
const onDashboardTap = useMemo3(
|
|
8974
9098
|
() => built ? createDashboardTapSignal((input) => built.conversation.sendSignal(input), logger) : void 0,
|
|
8975
9099
|
[built, logger]
|
|
8976
9100
|
);
|
|
9101
|
+
const dismissPopup = usePopupTelemetry(logger, controller.state.isOpen, controller.state.popupText, controller.dismissPopup);
|
|
8977
9102
|
const operatorUserId = cfg?.userContext?.operatorUserId;
|
|
8978
9103
|
const anonymousUserId = cfg?.userContext?.anonymousUserId;
|
|
8979
9104
|
const conversationId = controller.state.conversationId ?? void 0;
|
|
8980
|
-
|
|
9105
|
+
useEffect19(() => {
|
|
8981
9106
|
if (!logger) return;
|
|
8982
9107
|
logger.setIdentity({ operatorUserId, anonymousUserId, conversationId });
|
|
8983
9108
|
}, [logger, operatorUserId, anonymousUserId, conversationId]);
|
|
8984
|
-
|
|
9109
|
+
useEffect19(() => {
|
|
8985
9110
|
if (!logger) return;
|
|
8986
9111
|
const onHide = () => {
|
|
8987
9112
|
logger.flushOnHide();
|
|
@@ -8998,7 +9123,7 @@ function StatsWidget(props) {
|
|
|
8998
9123
|
logger.dispose();
|
|
8999
9124
|
};
|
|
9000
9125
|
}, [logger]);
|
|
9001
|
-
|
|
9126
|
+
useEffect19(() => {
|
|
9002
9127
|
if (!built || !ui.injectStyles) return;
|
|
9003
9128
|
const node = rootRef.current?.getRootNode();
|
|
9004
9129
|
if (node) injectStyles(node);
|
|
@@ -9027,7 +9152,7 @@ function StatsWidget(props) {
|
|
|
9027
9152
|
popupText: controller.state.popupText,
|
|
9028
9153
|
logoUrl: ui.logoUrl,
|
|
9029
9154
|
onOpen: controller.open,
|
|
9030
|
-
onDismissPopup:
|
|
9155
|
+
onDismissPopup: dismissPopup,
|
|
9031
9156
|
dismissLabel: ui.dismissGreeting,
|
|
9032
9157
|
introAllowed: !introDoneRef.current,
|
|
9033
9158
|
onIntroStart: markIntroDone
|