diff --git a/apps/mobile/src/features/cloud/cloudEnvironmentPresentation.test.ts b/apps/mobile/src/features/cloud/cloudEnvironmentPresentation.test.ts index f78cecc0671824cca3a99d787fa657dcb625443e..8277463dae3a3ac35cb57cc771fe2764e0ea3c55 100644 --- a/apps/mobile/src/features/cloud/cloudEnvironmentPresentation.test.ts +++ b/apps/mobile/src/features/cloud/cloudEnvironmentPresentation.test.ts @@ -48,7 +48,7 @@ describe("available cloud environment presentation", () => { }), ).toMatchObject({ connectionState: "unsupported", - statusText: "Client not supported", + statusText: expect.stringContaining("Update your app"), }); }); diff --git a/apps/mobile/src/features/cloud/cloudEnvironmentPresentation.ts b/apps/mobile/src/features/cloud/cloudEnvironmentPresentation.ts index 49e1e790c50ed511a57911dd5681f52239ed6919..448fab09188f81f47935ee8e264990ca42d77e19 100644 --- a/apps/mobile/src/features/cloud/cloudEnvironmentPresentation.ts +++ b/apps/mobile/src/features/cloud/cloudEnvironmentPresentation.ts @@ -27,7 +27,7 @@ export function availableCloudEnvironmentPresentation(input: { connectionError: compatibilityError.message, connectionErrorTraceId: null, connectionState: "unsupported", - statusText: "Client not supported", + statusText: compatibilityError.message, }; } if (input.status?.status === "online") { diff --git a/apps/mobile/src/features/connection/CloudEnvironmentRows.tsx b/apps/mobile/src/features/connection/CloudEnvironmentRows.tsx index 86df2e9158635aa7b5eb6d48183051c4f1390ff5..4d4dbbc424da4ef886ecb01d5e7baf527a682d42 100644 --- a/apps/mobile/src/features/connection/CloudEnvironmentRows.tsx +++ b/apps/mobile/src/features/connection/CloudEnvironmentRows.tsx @@ -2,10 +2,7 @@ import { AppActivityIndicator } from "../../components/AppActivityIndicator"; import { ConnectionTraceId } from "./ConnectionTraceId"; import { useAuth } from "@clerk/expo"; import { SymbolView } from "../../components/AppSymbol"; -import { - connectionStatusText, - type EnvironmentConnectionPhase, -} from "@t3tools/client-runtime/connection"; +import { type EnvironmentConnectionPhase } from "@t3tools/client-runtime/connection"; import { type EnvironmentId, type EnvironmentMachineKind, @@ -26,6 +23,7 @@ import { serverEnvironment } from "../../state/server"; import { availableCloudEnvironmentPresentation } from "../cloud/cloudEnvironmentPresentation"; import { hasCloudPublicConfig } from "../cloud/publicConfig"; import { ConnectionStatusDot } from "./ConnectionStatusDot"; +import { environmentStatusText } from "./environmentStatusText"; import { type RelayEnvironmentView, useConnectionController } from "./useConnectionController"; interface CloudEnvironmentRowsProps { @@ -307,7 +305,7 @@ function CloudEnvironmentRowShell(props: { const shouldPulse = isRetrying; const statusText = props.statusText ?? - connectionStatusText({ + environmentStatusText({ phase: props.connectionState, error: props.connectionError, traceId: props.connectionErrorTraceId, diff --git a/apps/mobile/src/features/connection/ConnectionEnvironmentRow.tsx b/apps/mobile/src/features/connection/ConnectionEnvironmentRow.tsx index f4a682c6c14c357f9876b4ee1e9cd4b63928b764..ad02b885be80859941c0fd2b8489a7468939de61 100644 --- a/apps/mobile/src/features/connection/ConnectionEnvironmentRow.tsx +++ b/apps/mobile/src/features/connection/ConnectionEnvironmentRow.tsx @@ -1,6 +1,5 @@ import { ConnectionTraceId } from "./ConnectionTraceId"; import { SymbolView } from "../../components/AppSymbol"; -import { connectionStatusText } from "@t3tools/client-runtime/connection"; import type { AtomCommandResult } from "@t3tools/client-runtime/state/runtime"; import { type EnvironmentId, resolveEnvironmentMachineKind } from "@t3tools/contracts"; import { useAtomValue } from "@effect/atom-react"; @@ -20,12 +19,13 @@ import type { ConnectedEnvironmentSummary } from "../../state/remote-runtime-typ import { serverEnvironment } from "../../state/server"; import { ConnectionFormField } from "./ConnectionFormField"; import { ConnectionStatusDot } from "./ConnectionStatusDot"; +import { environmentStatusText } from "./environmentStatusText"; function connectionStatusLabel(environment: ConnectedEnvironmentSummary): string | null { if (!environment.isEnabled && environment.connectionState !== "unsupported") { return "Off"; } - return connectionStatusText({ + return environmentStatusText({ phase: environment.connectionState, error: environment.connectionError, traceId: environment.connectionErrorTraceId, diff --git a/apps/mobile/src/features/connection/environmentStatusText.test.ts b/apps/mobile/src/features/connection/environmentStatusText.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..e573138beef363ac15ce241e048ad26912126422 --- /dev/null +++ b/apps/mobile/src/features/connection/environmentStatusText.test.ts @@ -0,0 +1,39 @@ +import { EnvironmentId, ORCHESTRATION_PROTOCOL_VERSION } from "@t3tools/contracts"; +import { describe, expect, it } from "vite-plus/test"; + +import { orchestrationProtocolCompatibilityError } from "@t3tools/client-runtime/connection"; + +import { environmentStatusText } from "./environmentStatusText"; + +function compatibilityMessage(serverProtocolVersion: number): string { + const error = orchestrationProtocolCompatibilityError({ + environmentId: EnvironmentId.make("environment-preview"), + label: "Studio Mac", + platform: { os: "darwin", arch: "arm64" }, + serverVersion: "1.0.0", + orchestrationProtocolVersion: serverProtocolVersion, + capabilities: { repositoryIdentity: true }, + }); + if (error === null) throw new Error("expected an incompatible protocol"); + return error.message; +} + +describe("environment status text", () => { + it.each([ + ["a newer server", ORCHESTRATION_PROTOCOL_VERSION + 1, "Update your app"], + ["an older server", ORCHESTRATION_PROTOCOL_VERSION - 1, "Update T3 Code on Studio Mac"], + ])("tells the user what to update for %s", (_, serverProtocolVersion, guidance) => { + const text = environmentStatusText({ + phase: "unsupported", + error: compatibilityMessage(serverProtocolVersion), + traceId: null, + }); + expect(text).toContain(guidance); + }); + + it("falls back to the generic label when there is no detail", () => { + expect(environmentStatusText({ phase: "unsupported", error: null, traceId: null })).toBe( + "Client not supported", + ); + }); +}); diff --git a/apps/mobile/src/features/connection/environmentStatusText.ts b/apps/mobile/src/features/connection/environmentStatusText.ts new file mode 100644 index 0000000000000000000000000000000000000000..b3340a2f22be304897d5c2bc3b2a45dfa06ad1dc --- /dev/null +++ b/apps/mobile/src/features/connection/environmentStatusText.ts @@ -0,0 +1,15 @@ +import { + connectionStatusText, + type EnvironmentConnectionPresentation, +} from "@t3tools/client-runtime/connection"; + +/** + * Status line for mobile environment rows. Mobile has no tooltip like web, so + * an unsupported environment shows its compatibility detail, which says + * whether to update the app or the host. + */ +export function environmentStatusText(connection: EnvironmentConnectionPresentation): string { + return connection.phase === "unsupported" && connection.error !== null + ? connection.error + : connectionStatusText(connection); +}