mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-15 15:02:04 +00:00
Merge branch 'fix/v1436-oura' into release/v1.43.6
This commit is contained in:
commit
f05b2f71e7
17 files changed
+429
-54
No files matched your search
@@ -3,6 +3,7 @@ import { renderToStaticMarkup } from "react-dom/server";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
|
||||
import { I18nProvider } from "@/lib/i18n/context";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
import type { DataSummary } from "@/lib/analytics/trends";
|
||||
|
||||
/**
|
||||
@@ -45,6 +46,12 @@ function render(node: React.ReactNode) {
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: { queries: { retry: false, staleTime: Infinity } },
|
||||
});
|
||||
// The recovery page's empty note waits for the connection status; an
|
||||
// account with no integrations is the settled answer here.
|
||||
queryClient.setQueryData(queryKeys.integrationsStatus(), {
|
||||
threshold: 3,
|
||||
integrations: [],
|
||||
});
|
||||
return renderToStaticMarkup(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<I18nProvider initialLocale="en">{node}</I18nProvider>
|
||||
|
||||
@@ -9,10 +9,10 @@ import type { DataSummary } from "@/lib/analytics/trends";
|
||||
/**
|
||||
* The recovery page's empty note says what is missing for THIS account. An
|
||||
* Oura ring fills the page through its resilience level alone, so a connected
|
||||
* ring with an empty page is either a grant that leaves out the heart-health
|
||||
* data the level sits behind, or Oura not having sent a level yet. The
|
||||
* connection facts come from the server's integrations read; the page only
|
||||
* picks the sentence.
|
||||
* ring with an empty page asks for a reconnect only when the server recorded
|
||||
* that the grant leaves resilience out, and otherwise says only that no
|
||||
* level has arrived. The connection facts come from the server's
|
||||
* integrations read; the page only picks the sentence, and waits for it.
|
||||
*/
|
||||
|
||||
vi.mock("next/dynamic", () => ({
|
||||
@@ -57,14 +57,19 @@ const OURA_SUMMARIES = {
|
||||
HRV_RMSSD: summary(30),
|
||||
};
|
||||
|
||||
function render(oura: Record<string, unknown> | null) {
|
||||
function render(
|
||||
oura: Record<string, unknown> | null,
|
||||
opts: { statusLoaded?: boolean } = {},
|
||||
) {
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: { queries: { retry: false, staleTime: Infinity } },
|
||||
});
|
||||
queryClient.setQueryData(queryKeys.integrationsStatus(), {
|
||||
threshold: 3,
|
||||
integrations: oura ? [{ integration: "oura", ...oura }] : [],
|
||||
});
|
||||
if (opts.statusLoaded !== false) {
|
||||
queryClient.setQueryData(queryKeys.integrationsStatus(), {
|
||||
threshold: 3,
|
||||
integrations: oura ? [{ integration: "oura", ...oura }] : [],
|
||||
});
|
||||
}
|
||||
return renderToStaticMarkup(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<I18nProvider initialLocale="en">
|
||||
@@ -104,16 +109,34 @@ describe("<RecoverySection> empty note", () => {
|
||||
expect(html).toContain('href="/settings/integrations"');
|
||||
});
|
||||
|
||||
it("says Oura has sent no level yet when the grant covers it", () => {
|
||||
it("says only that no level has arrived when no withheld permission is recorded", () => {
|
||||
const html = render({
|
||||
connected: true,
|
||||
health: health([{ item: "spo2", cause: "permission" }]),
|
||||
});
|
||||
expect(html).toContain('data-empty-case="oura-no-resilience"');
|
||||
expect(html).toContain("has not sent a resilience level");
|
||||
expect(html).toContain("no resilience level has arrived");
|
||||
// The server cannot prove the grant covers resilience, so the note
|
||||
// never says it does.
|
||||
expect(html).not.toContain("permission covers");
|
||||
expect(html).not.toContain('href="/settings/integrations"');
|
||||
});
|
||||
|
||||
it("stays neutral when the connection carries no health block", () => {
|
||||
const html = render({ connected: true });
|
||||
expect(html).toContain('data-empty-case="oura-no-resilience"');
|
||||
expect(html).not.toContain("permission covers");
|
||||
});
|
||||
|
||||
it("shows no note until the connection status has loaded", () => {
|
||||
const html = render(
|
||||
{ connected: true, health: health([]) },
|
||||
{ statusLoaded: false },
|
||||
);
|
||||
expect(html).toContain('data-slot="recovery-loading"');
|
||||
expect(html).not.toContain("data-empty-case");
|
||||
});
|
||||
|
||||
it("keeps the general note for an account without Oura", () => {
|
||||
const html = render(null);
|
||||
expect(html).toContain('data-empty-case="general"');
|
||||
|
||||
@@ -49,11 +49,12 @@ interface BlockGroup {
|
||||
/**
|
||||
* Why this account's recovery page is empty, from the server's view of its
|
||||
* Oura connection. An Oura ring feeds this page through its resilience level
|
||||
* alone, so a connected ring with an empty page has exactly two causes: the
|
||||
* grant does not cover the heart-health data the level sits behind (the
|
||||
* server records that per collection and publishes it as an unavailable
|
||||
* item), or Oura has not sent a level for this account. Everyone else gets
|
||||
* the general note naming what fills the page.
|
||||
* alone. When the server records that the grant leaves resilience out, the
|
||||
* note asks for one reconnect. Otherwise it says only that no level has
|
||||
* arrived: the server cannot tell "the grant covers it" from "not recorded
|
||||
* yet" (the scope note is cleared on every reconnect and written only by a
|
||||
* sync that reached Oura), so the note never claims coverage. Everyone else
|
||||
* gets the general note naming what fills the page.
|
||||
*/
|
||||
type RecoveryEmptyCase = "general" | "oura-permission" | "oura-no-resilience";
|
||||
|
||||
@@ -222,7 +223,9 @@ export function RecoverySection() {
|
||||
);
|
||||
const emptyCase = recoveryEmptyCase(pickStatus(statuses.data, "oura"));
|
||||
|
||||
if (isLoading || summaries == null) {
|
||||
// The empty note waits for the connection status, so it never reads as
|
||||
// the general note first and then changes under the reader.
|
||||
if (isLoading || summaries == null || (!hasAny && statuses.isPending)) {
|
||||
return (
|
||||
<div className="space-y-3" data-slot="recovery-loading">
|
||||
<ChartSkeleton />
|
||||
|
||||
Reference in new issue
Block a user