Skip to content

Commit 1c31293

Browse files
committed
Fix onboarding request drop on effect rerun and surface web discovery failures alongside rows
1 parent cac134c commit 1c31293

2 files changed

Lines changed: 46 additions & 28 deletions

File tree

apps/mobile/src/features/cloud/CloudAuthProvider.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ function CloudAuthBridge(props: { readonly children: ReactNode }) {
5757
} | null>(null);
5858
const observedAccountRef = useRef<string | null | undefined>(undefined);
5959
const accountTransitionRef = useRef<Promise<void> | null>(null);
60+
const pendingOnboardingAccountRef = useRef<string | null>(null);
6061

6162
useEffect(() => {
6263
let cancelled = false;
@@ -77,8 +78,15 @@ function CloudAuthBridge(props: { readonly children: ReactNode }) {
7778
// environments; sign-out drops any not-yet-presented request instead.
7879
const isAccountTransition =
7980
previousObservedAccount !== undefined && previousObservedAccount !== nextAccount;
80-
if (isAccountTransition && nextAccount === null) {
81-
clearConnectOnboardingRequest();
81+
if (isAccountTransition) {
82+
// The pending request lives in a ref rather than this run's closure: a
83+
// rerun with the same account (e.g. a new getToken identity) cancels the
84+
// run that observed the transition, and its activation must still
85+
// request onboarding.
86+
pendingOnboardingAccountRef.current = nextAccount;
87+
if (nextAccount === null) {
88+
clearConnectOnboardingRequest();
89+
}
8290
}
8391

8492
const queueAccountCleanup = (
@@ -128,7 +136,8 @@ function CloudAuthBridge(props: { readonly children: ReactNode }) {
128136
}
129137
previousTokenProviderRef.current = { userId, provider: tokenProvider };
130138
activateCloudRelayAccount(userId, tokenProvider);
131-
if (isAccountTransition) {
139+
if (pendingOnboardingAccountRef.current === userId) {
140+
pendingOnboardingAccountRef.current = null;
132141
requestConnectOnboarding(userId);
133142
}
134143
};

apps/web/src/components/cloud/CloudEnvironmentConnectList.tsx

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -135,34 +135,36 @@ export function CloudEnvironmentConnectRows({
135135
return <RemoteEnvironmentRowsSkeleton />;
136136
}
137137

138+
// A failed or offline discovery is not "no environments" — misreporting it
139+
// as empty would read as the user's devices having disappeared. The card is
140+
// also rendered alongside any already-discovered rows, so a failed refresh
141+
// never hides behind an otherwise-healthy list.
142+
const discoveryProblem = environmentsState.offline
143+
? "You appear to be offline."
144+
: (Option.getOrNull(environmentsState.error)?.message ?? null);
145+
const discoveryFailure =
146+
discoveryProblem !== null && !environmentsState.refreshing ? (
147+
<div className={ITEM_ROW_CLASSNAME}>
148+
<p className="text-sm font-medium text-destructive">
149+
Could not load T3 Connect environments
150+
</p>
151+
<p className="mt-1 text-xs text-muted-foreground">{discoveryProblem}</p>
152+
<Button
153+
size="sm"
154+
variant="outline"
155+
className="mt-3"
156+
onClick={() => void refreshRelayEnvironments()}
157+
>
158+
Try again
159+
</Button>
160+
</div>
161+
) : null;
162+
138163
if (standalone && visibleEnvironments.length === 0) {
139-
// A failed or offline discovery is not "no environments" — misreporting it
140-
// as empty would read as the user's devices having disappeared.
141-
const discoveryProblem = environmentsState.offline
142-
? "You appear to be offline."
143-
: (Option.getOrNull(environmentsState.error)?.message ?? null);
144-
if (discoveryProblem !== null && !environmentsState.refreshing) {
145-
return (
146-
<div className={ITEM_ROW_CLASSNAME}>
147-
<p className="text-sm font-medium text-destructive">
148-
Could not load T3 Connect environments
149-
</p>
150-
<p className="mt-1 text-xs text-muted-foreground">{discoveryProblem}</p>
151-
<Button
152-
size="sm"
153-
variant="outline"
154-
className="mt-3"
155-
onClick={() => void refreshRelayEnvironments()}
156-
>
157-
Try again
158-
</Button>
159-
</div>
160-
);
161-
}
162-
return empty;
164+
return discoveryFailure ?? empty;
163165
}
164166

165-
return visibleEnvironments.map(({ environment, availability, error }) => {
167+
const rows = visibleEnvironments.map(({ environment, availability, error }) => {
166168
const alreadyConnected = savedIds.has(environment.environmentId);
167169
return (
168170
<div key={environment.environmentId} className={ITEM_ROW_CLASSNAME}>
@@ -224,4 +226,11 @@ export function CloudEnvironmentConnectRows({
224226
</div>
225227
);
226228
});
229+
230+
return (
231+
<>
232+
{rows}
233+
{discoveryFailure}
234+
</>
235+
);
227236
}

0 commit comments

Comments
 (0)