feat(dashboard): make the v1.10 additive metrics pinnable to Home

Add eight widget ids (cardio recovery, six-minute walk, stair ascent/descent speed, breathing disturbances, wrist temperature, falls, walking steadiness) to the dashboard widget catalogue + default layout (hidden by default) so they can be pinned via the layout PUT.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marc-André BombeckandClaude Opus 4.8 committed 2026-06-04 12:53:38 +02:00
1 parent 1f3b0ff5b0
commit 0949fadfb0
3 files changed
+49 -9

No files matched your search

@@ -132,6 +132,16 @@ const WIDGET_LABEL_KEYS: Record<DashboardWidgetId, string> = {
vo2Max: "dashboard.vo2Max",
// v1.4.32 — Recent workouts tile (default-on).
recentWorkouts: "dashboard.recentWorkouts.title",
// v1.11.2 B5 — v1.10 additive HealthKit signals, now pinnable. Each
// reuses the existing measurement-type label key.
cardioRecovery: "measurements.typeCardioRecovery",
sixMinuteWalk: "measurements.typeSixMinuteWalkDistance",
stairAscentSpeed: "measurements.typeStairAscentSpeed",
stairDescentSpeed: "measurements.typeStairDescentSpeed",
breathingDisturbances: "measurements.typeBreathingDisturbances",
wristTemperature: "measurements.typeWristTemperature",
falls: "measurements.typeFallCount",
walkingSteadiness: "measurements.typeWalkingSteadiness",
};
export function DashboardLayoutSection({ id }: { id: string }) {
+11 -9
View File
@@ -382,11 +382,13 @@ describe("resolveDashboardLayout() — chartOverlayPrefs (v1.4.18)", () => {
* iOS-only ids extend it without touching the writable PUT enum.
*/
describe("DASHBOARD_WIDGET_CATALOGUE_IDS — 27-id catalogue", () => {
it("carries exactly 27 distinct ids (16 server-known + 11 iOS-only)", () => {
expect(DASHBOARD_WIDGET_IDS).toHaveLength(16);
it("carries exactly 35 distinct ids (24 server-known + 11 iOS-only)", () => {
// v1.11.2 B5 — the 8 v1.10 additive metrics became web-writable, so
// the server-known set grew 16 → 24 and the catalogue 27 → 35.
expect(DASHBOARD_WIDGET_IDS).toHaveLength(24);
expect(DASHBOARD_IOS_ONLY_WIDGET_IDS).toHaveLength(11);
expect(DASHBOARD_WIDGET_CATALOGUE_IDS).toHaveLength(27);
expect(new Set(DASHBOARD_WIDGET_CATALOGUE_IDS).size).toBe(27);
expect(DASHBOARD_WIDGET_CATALOGUE_IDS).toHaveLength(35);
expect(new Set(DASHBOARD_WIDGET_CATALOGUE_IDS).size).toBe(35);
});
it("is a superset of the server-known ids in declaration order", () => {
@@ -483,17 +485,17 @@ describe("resolveDashboardLayout() — iOS-only id retention (v1.7.0)", () => {
expect(ids).not.toContain("totally-made-up");
});
it("keeps the default layout at the 16 web tiles (no iOS-only seeded)", () => {
it("keeps the default layout at the 24 web tiles (no iOS-only seeded)", () => {
const ids = DEFAULT_DASHBOARD_LAYOUT.widgets.map((w) => w.id);
expect(ids).toHaveLength(16);
expect(ids).toHaveLength(24);
for (const iosId of DASHBOARD_IOS_ONLY_WIDGET_IDS) {
expect(ids).not.toContain(iosId);
}
});
it("does NOT auto-append iOS-only ids when a web-only layout is read", () => {
// A web account that saved only `weight` must auto-upgrade to the 16
// web defaults — never to the 27 catalogue. iOS-only ids appear only
// A web account that saved only `weight` must auto-upgrade to the 24
// web defaults — never to the 35 catalogue. iOS-only ids appear only
// once a native client has explicitly sent them.
const partial = {
version: 1,
@@ -501,7 +503,7 @@ describe("resolveDashboardLayout() — iOS-only id retention (v1.7.0)", () => {
};
const resolved = resolveDashboardLayout(partial);
const ids = resolved.widgets.map((w) => w.id);
expect(ids).toHaveLength(16);
expect(ids).toHaveLength(24);
for (const iosId of DASHBOARD_IOS_ONLY_WIDGET_IDS) {
expect(ids).not.toContain(iosId);
}
+28
View File
@@ -42,6 +42,18 @@ export const DASHBOARD_WIDGET_IDS = [
// cue is discoverable from the dashboard rather than hidden
// behind Settings.
"recentWorkouts",
// v1.11.2 B5 — the v1.10 additive HealthKit signals become pinnable.
// Each maps 1:1 to a MeasurementType the server already stores;
// default-invisible so existing dashboards stay unchanged until the
// user opts in via Settings → Dashboard.
"cardioRecovery",
"sixMinuteWalk",
"stairAscentSpeed",
"stairDescentSpeed",
"breathingDisturbances",
"wristTemperature",
"falls",
"walkingSteadiness",
] as const;
export type DashboardWidgetId = (typeof DASHBOARD_WIDGET_IDS)[number];
@@ -323,6 +335,22 @@ export const DEFAULT_DASHBOARD_LAYOUT: DashboardLayout = {
// workouts list and renders an Apple-Health onboarding hint
// otherwise.
{ id: "recentWorkouts", visible: true, tileVisible: true, order: 15 },
// v1.11.2 B5 — v1.10 additive HealthKit signals, pinnable but
// default-invisible on both surfaces. The user opts in via
// Settings → Dashboard; each tile self-gates on having any sample.
{ id: "cardioRecovery", visible: false, tileVisible: false, order: 16 },
{ id: "sixMinuteWalk", visible: false, tileVisible: false, order: 17 },
{ id: "stairAscentSpeed", visible: false, tileVisible: false, order: 18 },
{ id: "stairDescentSpeed", visible: false, tileVisible: false, order: 19 },
{
id: "breathingDisturbances",
visible: false,
tileVisible: false,
order: 20,
},
{ id: "wristTemperature", visible: false, tileVisible: false, order: 21 },
{ id: "falls", visible: false, tileVisible: false, order: 22 },
{ id: "walkingSteadiness", visible: false, tileVisible: false, order: 23 },
],
};