Stop insights sub-pages from shifting after first paint

Weight, blood pressure and pulse painted no stat strip until their read
landed, so the read strip and chart jumped down a card's height; the
strip now holds its place with the skeleton the HealthKit pages already
used, shared from the strip itself. The tab strip's scroll arrows render
from the start and only hide while the row fits, instead of mounting once
it measured as overflowing and shoving every pill sideways. The sleep
page shows the stage chart's skeleton while loading rather than a
'no stage data yet' card. Load shift on weight, blood pressure and pulse
drops from 0.3 to under 0.01.
This commit is contained in:
Marc-André Bombeck committed 2026-10-08 17:02:01 +02:00
1 parent 3e1f630fa1
commit 4d18c4e101
9 files changed
+188 -72

No files matched your search

+6 -2
View File
@@ -46,8 +46,11 @@ export default function InsightsBlutdruckPage() {
const { t } = useTranslations();
const { compareBaseline } = useInsightsLayoutPrefs(user != null);
const { data: analytics, isEmpty } =
useInsightsAnalytics("BLOOD_PRESSURE_SYS");
const {
data: analytics,
isEmpty,
isLoading: analyticsLoading,
} = useInsightsAnalytics("BLOOD_PRESSURE_SYS");
// v1.12.4 — blood pressure is two series, so it stacks two stat strips
// (systolic / diastolic) rather than the single-series strip the other
// metrics carry. Both summaries ride the same `summaries` slice.
@@ -128,6 +131,7 @@ export default function InsightsBlutdruckPage() {
// brushing the single chart reports per-type windowed stats and each
// column reads its own half.
<MetricStatStrip
pending={analyticsLoading}
groupLabel={t("insights.bloodPressureSectionTitle")}
series={[
{
+6 -1
View File
@@ -63,7 +63,11 @@ export default function InsightsPulsPage() {
const { t } = useTranslations();
const { compareBaseline } = useInsightsLayoutPrefs(isAuthenticated);
const { data: analytics, isEmpty } = useInsightsAnalytics("PULSE");
const {
data: analytics,
isEmpty,
isLoading: analyticsLoading,
} = useInsightsAnalytics("PULSE");
// VO₂ max rides on the same `/api/analytics` bundle; we only need the
// sample count to decide whether to surface the cross-link to the
// dedicated cardio-fitness page.
@@ -123,6 +127,7 @@ export default function InsightsPulsPage() {
explainerMetric="pulse"
statStrip={
<MetricStatStrip
pending={analyticsLoading}
summary={pulseSummary}
unit="bpm"
seriesLabel={t("insights.pulseSectionTitle")}
+6 -1
View File
@@ -48,7 +48,11 @@ export default function InsightsGewichtPage() {
const { compareBaseline } = useInsightsLayoutPrefs(isAuthenticated);
const unitDisplay = useUnitDisplay();
const { data: analytics, isEmpty } = useInsightsAnalytics("WEIGHT");
const {
data: analytics,
isEmpty,
isLoading: analyticsLoading,
} = useInsightsAnalytics("WEIGHT");
const weightSummary = analytics?.summaries?.WEIGHT ?? null;
// v1.34 — the user's own weight target, read through the shared
@@ -170,6 +174,7 @@ export default function InsightsGewichtPage() {
explainerMetric="weight"
statStrip={
<MetricStatStrip
pending={analyticsLoading}
summary={displaySummary}
unit={weightUnit}
fractionDigits={weightTransform.decimals}