mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-15 06:52:04 +00:00
fix(daily): decide the line under the Today lead on the server
The muted line under the lead repeated what the lead had just said: a lead about blood pressure sat above "Your latest blood pressure is sitting in the optimal band · ↓ ~10 mmHg…". The web client only dropped the headline when the lead quoted it word for word, and other clients had no rule at all. The digest now carries `signalLine`, built next to the lead: when the lead already talks about the top signal's metric (matched in every shipped language) or says its headline, only the delta remains, and with no delta there is no line. The hero renders the field verbatim.
This commit is contained in:
1 parent
20085d5759
commit
524975d5f3
13 files changed
+265
-27
No files matched your search
@@ -354,6 +354,7 @@ const DIGEST: DailyDigest = {
|
||||
sleepPending: false,
|
||||
score: null,
|
||||
topSignal: null,
|
||||
signalLine: null,
|
||||
briefingLead: "A steady week so far.",
|
||||
lead: { text: "A steady week so far.", source: "briefing" },
|
||||
today: [],
|
||||
|
||||
@@ -41,6 +41,7 @@ function digest(over: Partial<DailyDigest> = {}): DailyDigest {
|
||||
sleepPending: false,
|
||||
score: { value: 82, band: "green", delta: 3 },
|
||||
topSignal: null,
|
||||
signalLine: null,
|
||||
briefingLead: "Your week is trending steady.",
|
||||
lead: { text: "Your week is trending steady.", source: "briefing" },
|
||||
today: [],
|
||||
|
||||
@@ -71,6 +71,12 @@ function digest(over: Partial<DailyDigest> = {}): DailyDigest {
|
||||
nudge: "Take it again after a calm five minutes.",
|
||||
delta: "+6 mmHg vs your 30-day average",
|
||||
},
|
||||
// Resolved on the server: the lead says nothing about blood pressure, so
|
||||
// the whole signal rides under it.
|
||||
signalLine: {
|
||||
headline: "Blood pressure a touch high this morning",
|
||||
delta: "+6 mmHg vs your 30-day average",
|
||||
},
|
||||
briefingLead: "Your week is trending steady.",
|
||||
lead: { text: "Your week is trending steady.", source: "briefing" },
|
||||
today: [],
|
||||
@@ -209,6 +215,7 @@ describe("<TodayHero>", () => {
|
||||
lead: null,
|
||||
briefingLead: null,
|
||||
topSignal: null,
|
||||
signalLine: null,
|
||||
reactionLine: "Score 82 today.",
|
||||
})}
|
||||
/>,
|
||||
@@ -233,6 +240,7 @@ describe("<TodayHero>", () => {
|
||||
nudge: "",
|
||||
delta: null,
|
||||
},
|
||||
signalLine: null,
|
||||
})}
|
||||
/>,
|
||||
);
|
||||
@@ -270,6 +278,7 @@ describe("<TodayHero>", () => {
|
||||
digest={digest({
|
||||
score: null,
|
||||
topSignal: null,
|
||||
signalLine: null,
|
||||
briefingLead: null,
|
||||
lead: null,
|
||||
line: "Nothing needs your attention today — everything's tracking normally.",
|
||||
@@ -289,6 +298,7 @@ describe("<TodayHero>", () => {
|
||||
digest={digest({
|
||||
score: null,
|
||||
topSignal: null,
|
||||
signalLine: null,
|
||||
briefingLead: null,
|
||||
lead: null,
|
||||
line: "Nothing needs your attention today — everything's tracking normally.",
|
||||
@@ -340,6 +350,10 @@ describe("<TodayHero>", () => {
|
||||
text: "Blood pressure a touch high this morning.",
|
||||
source: "briefing",
|
||||
},
|
||||
signalLine: {
|
||||
headline: null,
|
||||
delta: "+6 mmHg vs your 30-day average",
|
||||
},
|
||||
})}
|
||||
/>,
|
||||
);
|
||||
@@ -355,6 +369,7 @@ describe("<TodayHero>", () => {
|
||||
<TodayHero
|
||||
digest={digest({
|
||||
topSignal: null,
|
||||
signalLine: null,
|
||||
briefingLead: null,
|
||||
lead: null,
|
||||
reactionLine: null,
|
||||
@@ -378,6 +393,7 @@ describe("<TodayHero>", () => {
|
||||
<TodayHero
|
||||
digest={digest({
|
||||
topSignal: null,
|
||||
signalLine: null,
|
||||
worthALook: [],
|
||||
})}
|
||||
/>,
|
||||
@@ -443,6 +459,7 @@ describe("<TodayHero>", () => {
|
||||
digest={digest({
|
||||
score: null,
|
||||
topSignal: null,
|
||||
signalLine: null,
|
||||
briefingLead: null,
|
||||
lead: null,
|
||||
worthALook: [],
|
||||
@@ -596,6 +613,7 @@ describe("<TodayHero> Today overview", () => {
|
||||
digest={digest({
|
||||
score: null,
|
||||
topSignal: null,
|
||||
signalLine: null,
|
||||
briefingLead: null,
|
||||
lead: null,
|
||||
worthALook: [],
|
||||
@@ -615,6 +633,7 @@ describe("<TodayHero> Today overview", () => {
|
||||
text: "All 4 of your latest vitals sit inside their usual range.",
|
||||
source: "signal",
|
||||
},
|
||||
signalLine: null,
|
||||
})}
|
||||
/>,
|
||||
);
|
||||
|
||||
@@ -74,20 +74,6 @@ function formatDelta(
|
||||
return t("daily.today.deltaVsBaseline", { delta: signed });
|
||||
}
|
||||
|
||||
/** Lowercase letters and digits only, for a wording-insensitive compare. */
|
||||
function normalise(text: string): string {
|
||||
return text
|
||||
.toLowerCase()
|
||||
.replace(/[^\p{L}\p{N}]+/gu, " ")
|
||||
.trim();
|
||||
}
|
||||
|
||||
/** Whether `lead` already says what `headline` says. */
|
||||
function restates(lead: string, headline: string): boolean {
|
||||
const h = normalise(headline);
|
||||
return h.length > 0 && normalise(lead).includes(h);
|
||||
}
|
||||
|
||||
export function TodayHero({
|
||||
digest,
|
||||
renderFilteredAllClear = false,
|
||||
@@ -148,18 +134,12 @@ export function TodayHero({
|
||||
digest.lead === undefined
|
||||
? (digest.reactionLine ?? digest.briefingLead ?? null)
|
||||
: (digest.lead?.text ?? null);
|
||||
const topSignal = digest.topSignal;
|
||||
// The briefing's top signal rides under an AI lead as its supporting line,
|
||||
// and only with what the lead does not already say: a headline the lead
|
||||
// restates is dropped and its delta, when there is one, stands alone.
|
||||
const signalLine =
|
||||
topSignal && lead && digest.lead?.source !== "signal"
|
||||
? restates(lead, topSignal.headline)
|
||||
? topSignal.delta
|
||||
? { headline: null, delta: topSignal.delta }
|
||||
: null
|
||||
: { headline: topSignal.headline, delta: topSignal.delta }
|
||||
: null;
|
||||
// The line under the lead is decided on the server (`digest.signalLine`):
|
||||
// the briefing's top signal minus whatever the lead already says, so the
|
||||
// headline goes when the lead talks about its metric and the delta stands
|
||||
// alone. A digest the service worker cached before the field existed simply
|
||||
// shows no line.
|
||||
const signalLine = lead ? (digest.signalLine ?? null) : null;
|
||||
// A score-only digest with nothing else to say has no narrative content for
|
||||
// the leading column. Keeping the full md ring in that two-column shell left
|
||||
// a blank column beside a 168 px dial and pushed the all-clear read below
|
||||
|
||||
Reference in new issue
Block a user