@sensiblestats/widget-react 0.48.0 → 0.48.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/CHANGELOG.md +6 -0
- package/dist/index.cjs +20 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +20 -59
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @sensiblestats/widget-react
|
|
2
2
|
|
|
3
|
+
## 0.48.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- A live match no longer loses its minute and drop out of the live section just because its score has not changed recently. The per-match staleness check read a last-changed timestamp as if it were a last-polled one.
|
|
8
|
+
|
|
3
9
|
## 0.48.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
package/dist/index.cjs
CHANGED
|
@@ -1548,43 +1548,8 @@ function dueMatchIds(groups, states, nowMs) {
|
|
|
1548
1548
|
return [...due];
|
|
1549
1549
|
}
|
|
1550
1550
|
|
|
1551
|
-
// src/liveClock.ts
|
|
1552
|
-
var STALE_READING_MS = 5 * 6e4;
|
|
1553
|
-
function parseMinute(label) {
|
|
1554
|
-
if (!label) return null;
|
|
1555
|
-
const plus = /^(\d+)\+(\d+)'$/.exec(label);
|
|
1556
|
-
if (plus) return Number(plus[1]) + Number(plus[2]);
|
|
1557
|
-
const plain = /^(\d+)'$/.exec(label);
|
|
1558
|
-
return plain ? Number(plain[1]) : null;
|
|
1559
|
-
}
|
|
1560
|
-
function readingAgeMs(state, nowMs) {
|
|
1561
|
-
if (!state?.stateAsOfUtc) return null;
|
|
1562
|
-
const t = Date.parse(state.stateAsOfUtc);
|
|
1563
|
-
return Number.isNaN(t) ? null : Math.max(0, nowMs - t);
|
|
1564
|
-
}
|
|
1565
|
-
function isReadingStale(state, nowMs) {
|
|
1566
|
-
if (state?.isHalfTime) return false;
|
|
1567
|
-
const age = readingAgeMs(state, nowMs);
|
|
1568
|
-
return age !== null && age > STALE_READING_MS;
|
|
1569
|
-
}
|
|
1570
|
-
function displayMinute(state, nowMs) {
|
|
1571
|
-
if (!state || state.phase !== "live") return null;
|
|
1572
|
-
if (state.isHalfTime) return null;
|
|
1573
|
-
if (isReadingStale(state, nowMs)) return null;
|
|
1574
|
-
const base = parseMinute(state.minuteLabel);
|
|
1575
|
-
if (base === null) return null;
|
|
1576
|
-
const age = readingAgeMs(state, nowMs);
|
|
1577
|
-
if (age === null) return state.minuteLabel ?? null;
|
|
1578
|
-
const advanced = base + Math.floor(age / 6e4);
|
|
1579
|
-
return `${advanced}'`;
|
|
1580
|
-
}
|
|
1581
|
-
function isTreatedLive(state, pollStale, nowMs) {
|
|
1582
|
-
return state?.phase === "live" && !pollStale && !isReadingStale(state, nowMs);
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
1551
|
// src/useMatchDiscovery.ts
|
|
1586
1552
|
var POLL_MS = 6e4;
|
|
1587
|
-
var CLOCK_MS = 1e4;
|
|
1588
1553
|
var ROLLOVER_CHECK_MS = 6e4;
|
|
1589
1554
|
var STALE_AFTER_FAILURES = 2;
|
|
1590
1555
|
function localDateOf(now) {
|
|
@@ -1597,7 +1562,6 @@ function useMatchDiscovery(opts) {
|
|
|
1597
1562
|
const [groups, setGroups] = (0, import_react4.useState)([]);
|
|
1598
1563
|
const [states, setStates] = (0, import_react4.useState)({});
|
|
1599
1564
|
const [failures, setFailures] = (0, import_react4.useState)(0);
|
|
1600
|
-
const [nowMs, setNowMs] = (0, import_react4.useState)(() => Date.now());
|
|
1601
1565
|
const [expanded, setExpanded] = (0, import_react4.useState)({});
|
|
1602
1566
|
const [touched, setTouched] = (0, import_react4.useState)({});
|
|
1603
1567
|
const groupsRef = (0, import_react4.useRef)(groups);
|
|
@@ -1675,21 +1639,16 @@ function useMatchDiscovery(opts) {
|
|
|
1675
1639
|
}, POLL_MS);
|
|
1676
1640
|
return () => clearInterval(id);
|
|
1677
1641
|
}, [opts.isOpen, tick, groups]);
|
|
1678
|
-
(0, import_react4.useEffect)(() => {
|
|
1679
|
-
if (!opts.isOpen) return;
|
|
1680
|
-
const id = setInterval(() => setNowMs(Date.now()), CLOCK_MS);
|
|
1681
|
-
return () => clearInterval(id);
|
|
1682
|
-
}, [opts.isOpen]);
|
|
1683
1642
|
const stale = failures >= STALE_AFTER_FAILURES;
|
|
1684
1643
|
const liveCount = (0, import_react4.useMemo)(
|
|
1685
|
-
() => stale ? 0 : Object.values(states).filter((s) => s.phase === "live"
|
|
1686
|
-
[states, stale
|
|
1644
|
+
() => stale ? 0 : Object.values(states).filter((s) => s.phase === "live").length,
|
|
1645
|
+
[states, stale]
|
|
1687
1646
|
);
|
|
1688
1647
|
const toggleGroup = (0, import_react4.useCallback)((competitionId) => {
|
|
1689
1648
|
setTouched((prev) => ({ ...prev, [competitionId]: true }));
|
|
1690
1649
|
setExpanded((prev) => ({ ...prev, [competitionId]: !prev[competitionId] }));
|
|
1691
1650
|
}, []);
|
|
1692
|
-
return { groups, states,
|
|
1651
|
+
return { groups, states, liveCount, stale, expanded, toggleGroup, _tick: tick };
|
|
1693
1652
|
}
|
|
1694
1653
|
|
|
1695
1654
|
// src/eventLogContext.ts
|
|
@@ -3047,7 +3006,7 @@ function computeDisplayMinute(minute, clockRunning, receivedAtMs) {
|
|
|
3047
3006
|
return minute + Math.min(Math.max(0, elapsedMinutes), MAX_LIVE_TICK_MINUTES);
|
|
3048
3007
|
}
|
|
3049
3008
|
function useLiveClock(minute, clockRunning, receivedAtMs) {
|
|
3050
|
-
const [
|
|
3009
|
+
const [displayMinute, setDisplayMinute] = (0, import_react19.useState)(
|
|
3051
3010
|
() => computeDisplayMinute(minute, clockRunning, receivedAtMs)
|
|
3052
3011
|
);
|
|
3053
3012
|
(0, import_react19.useEffect)(() => {
|
|
@@ -3058,7 +3017,7 @@ function useLiveClock(minute, clockRunning, receivedAtMs) {
|
|
|
3058
3017
|
}, TICK_MS);
|
|
3059
3018
|
return () => window.clearInterval(id);
|
|
3060
3019
|
}, [minute, clockRunning, receivedAtMs]);
|
|
3061
|
-
return
|
|
3020
|
+
return displayMinute;
|
|
3062
3021
|
}
|
|
3063
3022
|
|
|
3064
3023
|
// src/components/dashboard/LiveScoreCard.tsx
|
|
@@ -3090,7 +3049,7 @@ function LiveScoreCard({
|
|
|
3090
3049
|
if (!seedRef.current || seedRef.current.key !== seedKey) {
|
|
3091
3050
|
seedRef.current = { key: seedKey, receivedAtMs: Date.now() };
|
|
3092
3051
|
}
|
|
3093
|
-
const
|
|
3052
|
+
const displayMinute = useLiveClock(data.minute, data.clockRunning, seedRef.current.receivedAtMs);
|
|
3094
3053
|
const minuteSuffix = data.minuteLabel.replace(/^\d+/, "");
|
|
3095
3054
|
const scorers = scorersLine(goalEvents);
|
|
3096
3055
|
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "ss-w-fandash-card ss-w-fandash-live-score", children: [
|
|
@@ -3100,7 +3059,7 @@ function LiveScoreCard({
|
|
|
3100
3059
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "ss-w-fandash-live-dot", "aria-hidden": "true" }),
|
|
3101
3060
|
data.statusLabel
|
|
3102
3061
|
] }),
|
|
3103
|
-
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "ss-w-fandash-live-minute", children: `${
|
|
3062
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "ss-w-fandash-live-minute", children: `${displayMinute}${minuteSuffix}` }),
|
|
3104
3063
|
data.competition ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "ss-w-fandash-live-competition", children: data.competition }) : null
|
|
3105
3064
|
] }),
|
|
3106
3065
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "ss-w-fandash-live-main", children: [
|
|
@@ -3782,11 +3741,10 @@ function Side({ name, crest }) {
|
|
|
3782
3741
|
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "ss-w-drow-nm", children: name })
|
|
3783
3742
|
] });
|
|
3784
3743
|
}
|
|
3785
|
-
function FixtureRow({ match, state, stale,
|
|
3786
|
-
const
|
|
3787
|
-
const isLive = state?.phase === "live" && !stale && !readingStale;
|
|
3744
|
+
function FixtureRow({ match, state, stale, ui, monitor }) {
|
|
3745
|
+
const isLive = state?.phase === "live" && !stale;
|
|
3788
3746
|
const hasScore = (state?.phase === "live" || state?.phase === "fullTime") && typeof state.homeScore === "number" && typeof state.awayScore === "number";
|
|
3789
|
-
const ticked =
|
|
3747
|
+
const ticked = state?.phase === "live" ? state.minuteLabel ?? null : null;
|
|
3790
3748
|
const isFinished = state?.phase === "fullTime";
|
|
3791
3749
|
const when = state?.isHalfTime && !stale ? ui.halfTime : isLive ? ticked ?? "\u2014" : state?.phase === "live" ? "\u2014" : isFinished ? ui.fullTime : kickoffLabel(match.kickoffUnix);
|
|
3792
3750
|
const pin = { kind: "match", matchId: match.matchId };
|
|
@@ -3802,10 +3760,15 @@ function FixtureRow({ match, state, stale, nowMs, ui, monitor }) {
|
|
|
3802
3760
|
] });
|
|
3803
3761
|
}
|
|
3804
3762
|
|
|
3763
|
+
// src/liveClock.ts
|
|
3764
|
+
function isTreatedLive(state, pollStale) {
|
|
3765
|
+
return state?.phase === "live" && !pollStale;
|
|
3766
|
+
}
|
|
3767
|
+
|
|
3805
3768
|
// src/components/CompetitionGroup.tsx
|
|
3806
3769
|
var import_jsx_runtime50 = require("react/jsx-runtime");
|
|
3807
|
-
function CompetitionGroup({ group, open, onToggle, states, stale,
|
|
3808
|
-
const own = group.matches.filter((m) => !isTreatedLive(states[m.matchId], stale
|
|
3770
|
+
function CompetitionGroup({ group, open, onToggle, states, stale, ui, monitor, disambiguate = false }) {
|
|
3771
|
+
const own = group.matches.filter((m) => !isTreatedLive(states[m.matchId], stale));
|
|
3809
3772
|
const hasLive = own.length !== group.matches.length;
|
|
3810
3773
|
if (own.length === 0) {
|
|
3811
3774
|
return null;
|
|
@@ -3824,15 +3787,15 @@ function CompetitionGroup({ group, open, onToggle, states, stale, nowMs, ui, mon
|
|
|
3824
3787
|
] }),
|
|
3825
3788
|
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "ss-w-grp-count", children: own.length })
|
|
3826
3789
|
] }),
|
|
3827
|
-
open && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "ss-w-grp-body", children: own.map((m) => /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(FixtureRow, { match: m, state: states[m.matchId], stale,
|
|
3790
|
+
open && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "ss-w-grp-body", children: own.map((m) => /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(FixtureRow, { match: m, state: states[m.matchId], stale, ui, monitor }, m.matchId)) })
|
|
3828
3791
|
] });
|
|
3829
3792
|
}
|
|
3830
3793
|
|
|
3831
3794
|
// src/components/TodayPanel.tsx
|
|
3832
3795
|
var import_jsx_runtime51 = require("react/jsx-runtime");
|
|
3833
3796
|
function TodayPanel({ discovery, ui, monitor }) {
|
|
3834
|
-
const { groups, states,
|
|
3835
|
-
const liveRows = stale ? [] : groups.flatMap((group) => group.matches.filter((m) => isTreatedLive(states[m.matchId], stale
|
|
3797
|
+
const { groups, states, stale, expanded, toggleGroup } = discovery;
|
|
3798
|
+
const liveRows = stale ? [] : groups.flatMap((group) => group.matches.filter((m) => isTreatedLive(states[m.matchId], stale)).map((match) => ({ match, rank: group.rank }))).sort((a, b) => a.rank - b.rank);
|
|
3836
3799
|
const nameCounts = groups.reduce((acc, g) => {
|
|
3837
3800
|
acc[g.competitionName] = (acc[g.competitionName] ?? 0) + 1;
|
|
3838
3801
|
return acc;
|
|
@@ -3850,7 +3813,6 @@ function TodayPanel({ discovery, ui, monitor }) {
|
|
|
3850
3813
|
match,
|
|
3851
3814
|
state: states[match.matchId],
|
|
3852
3815
|
stale,
|
|
3853
|
-
nowMs,
|
|
3854
3816
|
ui,
|
|
3855
3817
|
monitor
|
|
3856
3818
|
},
|
|
@@ -3866,7 +3828,6 @@ function TodayPanel({ discovery, ui, monitor }) {
|
|
|
3866
3828
|
disambiguate: (nameCounts[group.competitionName] ?? 0) > 1,
|
|
3867
3829
|
states,
|
|
3868
3830
|
stale,
|
|
3869
|
-
nowMs,
|
|
3870
3831
|
ui,
|
|
3871
3832
|
monitor
|
|
3872
3833
|
},
|