authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-17 20:18:19-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-17 20:18:19-07:00
log05a8c6016a4070b92f5d51e7a79b9c76814947df
tree43a8c20dfe0299c5525db554a0f3e3bf69e59920
parent5a39013313a7e6e9d6fbeeb915b8c176ea3dd126
signature Signed by PGP key B5690EEEBB952194

fix(mobile): share accessible connection trace controls (#12371)


4 files changed, 69 insertions(+), 51 deletions(-)

apps/mobile/src/features/connection/CloudEnvironmentRows.tsx+10-17
...@@ -1,3 +1,4 @@...@@ -1,3 +1,4 @@
1import { ConnectionTraceId } from "./ConnectionTraceId";
1import { useAuth } from "@clerk/expo";2import { useAuth } from "@clerk/expo";
2import { SymbolView } from "../../components/AppSymbol";3import { SymbolView } from "../../components/AppSymbol";
3import {4import {
...@@ -405,23 +406,15 @@ function CloudEnvironmentRowShell(props: {...@@ -405,23 +406,15 @@ function CloudEnvironmentRowShell(props: {
405 >406 >
406 {statusText}407 {statusText}
407 {errorTraceId ? (408 {errorTraceId ? (
408 <>409 <ConnectionTraceId
409 {" Trace ID: "}410 traceId={errorTraceId}
410 <Text411 tone={
411 accessibilityHint="Copies the trace ID"412 props.connectionError && props.connectionState !== "unsupported"
412 accessibilityRole="button"413 ? "danger"
413 className={cn("text-xs underline decoration-dotted", statusClassName)}414 : "muted"
414 onLongPress={(event) => {415 }
415 event.stopPropagation();416 activation="longPress"
416 copyTextWithHaptic(errorTraceId, { target: "connection-trace-id" });417 />
417 }}
418 onPress={(event) => {
419 event.stopPropagation();
420 }}
421 >
422 {errorTraceId}
423 </Text>
424 </>
425 ) : null}418 ) : null}
426 </Text>419 </Text>
427 {errorCanExpand ? (420 {errorCanExpand ? (
apps/mobile/src/features/connection/ConnectionEnvironmentRow.tsx+6-18
...@@ -1,3 +1,4 @@...@@ -1,3 +1,4 @@
1import { ConnectionTraceId } from "./ConnectionTraceId";
1import { SymbolView } from "../../components/AppSymbol";2import { SymbolView } from "../../components/AppSymbol";
2import { connectionStatusText } from "@t3tools/client-runtime/connection";3import { connectionStatusText } from "@t3tools/client-runtime/connection";
3import type { AtomCommandResult } from "@t3tools/client-runtime/state/runtime";4import type { AtomCommandResult } from "@t3tools/client-runtime/state/runtime";
...@@ -15,7 +16,6 @@ import { MaterialButton } from "../../components/MaterialButton";...@@ -15,7 +16,6 @@ import { MaterialButton } from "../../components/MaterialButton";
15import { MaterialIconButton } from "../../components/MaterialIconButton";16import { MaterialIconButton } from "../../components/MaterialIconButton";
16import { ThemedSwitch } from "../../components/ThemedSwitch";17import { ThemedSwitch } from "../../components/ThemedSwitch";
17import { cn } from "../../lib/cn";18import { cn } from "../../lib/cn";
18import { copyTextWithHaptic } from "../../lib/copyTextWithHaptic";
19import type { ConnectedEnvironmentSummary } from "../../state/remote-runtime-types";19import type { ConnectedEnvironmentSummary } from "../../state/remote-runtime-types";
20import { serverEnvironment } from "../../state/server";20import { serverEnvironment } from "../../state/server";
21import { ConnectionStatusDot } from "./ConnectionStatusDot";21import { ConnectionStatusDot } from "./ConnectionStatusDot";
...@@ -114,23 +114,11 @@ export function ConnectionEnvironmentRow(props: {...@@ -114,23 +114,11 @@ export function ConnectionEnvironmentRow(props: {
114 >114 >
115 {statusLabel}115 {statusLabel}
116 {statusTraceId ? (116 {statusTraceId ? (
117 <>117 <ConnectionTraceId
118 {" Trace ID: "}118 traceId={statusTraceId}
119 <Text119 tone={hasConnectionFailure ? "danger" : "muted"}
120 accessibilityHint="Copies the trace ID"120 activation="longPress"
121 accessibilityRole="button"121 />
122 className="underline decoration-dotted"
123 onLongPress={(event) => {
124 event.stopPropagation();
125 copyTextWithHaptic(statusTraceId, { target: "connection-trace-id" });
126 }}
127 onPress={(event) => {
128 event.stopPropagation();
129 }}
130 >
131 {statusTraceId}
132 </Text>
133 </>
134 ) : null}122 ) : null}
135 </Text>123 </Text>
136 ) : null}124 ) : null}
apps/mobile/src/features/connection/ConnectionTraceId.tsx created+51
...@@ -0,0 +1,51 @@
1import { AppText as Text } from "../../components/AppText";
2import { cn } from "../../lib/cn";
3import { copyTextWithHaptic } from "../../lib/copyTextWithHaptic";
4
5/** Inline trace control; disclosure rows reserve ordinary taps for their own navigation. */
6export function ConnectionTraceId({
7 traceId,
8 tone = "muted",
9 activation = "press",
10}: {
11 readonly traceId: string;
12 readonly tone?: "muted" | "danger";
13 readonly activation?: "press" | "longPress";
14}) {
15 const copy = () => copyTextWithHaptic(traceId, { target: "connection-trace-id" });
16 return (
17 <>
18 {" Trace ID: "}
19 <Text
20 accessibilityHint={
21 activation === "longPress" ? "Long press to copy the trace ID" : "Copies the trace ID"
22 }
23 accessibilityLabel={`Copy trace ID ${traceId}`}
24 accessibilityRole="button"
25 accessibilityActions={[{ name: "activate", label: "Copy trace ID" }]}
26 onAccessibilityAction={(event) => {
27 event.stopPropagation();
28 if (event.nativeEvent.actionName === "activate") copy();
29 }}
30 className={cn(
31 "underline decoration-dotted",
32 tone === "danger" ? "text-danger-foreground" : "text-foreground-muted",
33 )}
34 onLongPress={
35 activation === "longPress"
36 ? (event) => {
37 event.stopPropagation();
38 copy();
39 }
40 : undefined
41 }
42 onPress={(event) => {
43 event.stopPropagation();
44 if (activation === "press") copy();
45 }}
46 >
47 {traceId}
48 </Text>
49 </>
50 );
51}
apps/mobile/src/features/connection/EnvironmentConnectionNotice.tsx+2-16
...@@ -1,3 +1,4 @@...@@ -1,3 +1,4 @@
1import { ConnectionTraceId } from "./ConnectionTraceId";
1import {2import {
2 type EnvironmentConnectionPhase,3 type EnvironmentConnectionPhase,
3 type EnvironmentConnectionPresentation,4 type EnvironmentConnectionPresentation,
...@@ -6,7 +7,6 @@ import { SymbolView } from "../../components/AppSymbol";...@@ -6,7 +7,6 @@ import { SymbolView } from "../../components/AppSymbol";
6import { ActivityIndicator, Pressable, View } from "react-native";7import { ActivityIndicator, Pressable, View } from "react-native";
78
8import { AppText as Text } from "../../components/AppText";9import { AppText as Text } from "../../components/AppText";
9import { copyTextWithHaptic } from "../../lib/copyTextWithHaptic";
1010
11function noticeTitle(phase: EnvironmentConnectionPhase, environmentLabel: string): string {11function noticeTitle(phase: EnvironmentConnectionPhase, environmentLabel: string): string {
12 switch (phase) {12 switch (phase) {
...@@ -81,21 +81,7 @@ export function EnvironmentConnectionNotice(props: {...@@ -81,21 +81,7 @@ export function EnvironmentConnectionNotice(props: {
81 <Text className="text-center text-sm leading-normal text-foreground-muted">81 <Text className="text-center text-sm leading-normal text-foreground-muted">
82 {noticeDetail(props.connection.phase, props.resourceName, props.connection.error)}82 {noticeDetail(props.connection.phase, props.resourceName, props.connection.error)}
83 {props.connection.traceId ? (83 {props.connection.traceId ? (
84 <>84 <ConnectionTraceId traceId={props.connection.traceId} />
85 {" Trace ID: "}
86 <Text
87 accessibilityHint="Copies the trace ID"
88 accessibilityRole="button"
89 className="underline decoration-dotted"
90 onPress={() =>
91 copyTextWithHaptic(props.connection.traceId!, {
92 target: "connection-trace-id",
93 })
94 }
95 >
96 {props.connection.traceId}
97 </Text>
98 </>
99 ) : null}85 ) : null}
100 </Text>86 </Text>
10187