Skip to content

[codex] Restore T3 Connect account controls#3492

Open
juliusmarminge wants to merge 1 commit into
mainfrom
codex/investigate-account-mobile-relay
Open

[codex] Restore T3 Connect account controls#3492
juliusmarminge wants to merge 1 commit into
mainfrom
codex/investigate-account-mobile-relay

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Jun 21, 2026

Copy link
Copy Markdown
Member

Summary

  • add a Clerk UserButton.UserProfilePage for account-level mobile clients
  • show each registered client's push notification, Live Activity, app version, and notification-event status
  • restore Publish agent activity as a nested setting under the local environment's T3 Connect exposure switch
  • serialize preference changes with link/unlink operations and restore stale T3 Connect naming

Root cause

The dedicated cloud settings page originally owned both notification-device status and the activity publication preference. The T3 Connect rebrand removed that page without moving the device view into Clerk. A later connection architecture rewrite also replaced the T3 Connect exposure row and dropped its nested publication toggle, while leaving the relay device APIs and server preference endpoint intact.

Impact

Signed-in users can inspect registered mobile clients from the Clerk account component again. Linked server environments can explicitly opt into publishing agent activity so mobile push notifications and Live Activities receive updates.

Validation

  • vp test run apps/web/src/components/clerk/MobileClientsUserProfilePage.logic.test.ts apps/web/src/cloud/linkEnvironment.test.ts
  • vp check
  • vp run typecheck
  • vp run --filter @t3tools/web build

Note

Restore T3 Connect account controls with mobile clients page and agent activity toggle

  • Renames all user-facing copy from 'T3 Cloud' to 'T3 Connect' across connection settings, toasts, and error messages.
  • Adds a 'Publish agent activity' toggle in ConnectionsSettings.tsx that updates the primary cloud preference via the new updatePrimaryEnvironmentPreferences runtime command.
  • Adds a 'Mobile clients' page to the Clerk user menu (MobileClientsUserProfilePage.tsx) that lists registered iOS devices with push notification status, live activity badges, and a refresh/error-retry flow.
  • Introduces formatting utilities for device platform labels, notification detail summaries, and localized 'updated at' timestamps in MobileClientsUserProfilePage.logic.ts.

Macroscope summarized 5c536c8.


Note

Low Risk
Mostly UI and preference wiring against existing connect APIs; no new auth paths, with modest risk if preference toggles race link state in edge cases.

Overview
Restores T3 Connect account and environment controls that were dropped during earlier UI refactors, and renames user-facing T3 Cloud copy to T3 Connect in connection settings.

A new Mobile clients tab on the Clerk UserButton lists relay-registered iOS devices (push/Live Activity badges, notification detail, refresh and error retry) via useManagedRelayDevices, with presentation helpers covered by unit tests.

For the local primary environment, a nested Publish agent activity switch appears when T3 Connect is linked; it POSTs publishAgentActivity through updatePrimaryCloudPreferences / updatePrimaryEnvironmentPreferences, serialized with link/unlink on the same environment scheduler.

Reviewed by Cursor Bugbot for commit 5c536c8. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 1ec9a0cf-b35b-41f5-ad79-c7e7bc6f36e1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/investigate-account-mobile-relay

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Jun 21, 2026
@juliusmarminge juliusmarminge marked this pull request as ready for review June 21, 2026 21:11

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.

Fix All in Cursor

Bugbot Autofix prepared fixes for all 3 issues found in the latest run.

  • ✅ Fixed: Error shows empty clients state
    • Added a hasErrorWithoutData guard that renders nothing in place of EmptyMobileClients when an error is present but no cached data exists, so only the error banner is shown.
  • ✅ Fixed: Preference switch stale after update
    • Added optimistic local state (optimisticPublishActivity) that is set immediately on successful update and used as the switch's checked value until the async refresh catches up, preventing the stale display.
  • ✅ Fixed: False empty before relay session
    • Extended the isInitialLoad condition to also be true when devicesState.accountId is falsy (relay session not yet established), showing the loading skeleton instead of the false empty state.

Create PR

Or push these changes by commenting:

@cursor push 85a49a0654
Preview (85a49a0654)
diff --git a/apps/web/src/components/clerk/MobileClientsUserProfilePage.tsx b/apps/web/src/components/clerk/MobileClientsUserProfilePage.tsx
--- a/apps/web/src/components/clerk/MobileClientsUserProfilePage.tsx
+++ b/apps/web/src/components/clerk/MobileClientsUserProfilePage.tsx
@@ -107,7 +107,9 @@
 export function MobileClientsUserProfilePage() {
   const devicesState = useManagedRelayDevices();
   const devices = devicesState.data ?? [];
-  const isInitialLoad = devicesState.data === null && !devicesState.error;
+  const isInitialLoad =
+    !devicesState.accountId || (devicesState.data === null && !devicesState.error);
+  const hasErrorWithoutData = devicesState.error !== null && devicesState.data === null;
 
   return (
     <div className="flex min-h-[30rem] w-full flex-col bg-background text-foreground">
@@ -149,7 +151,7 @@
 
         {isInitialLoad ? (
           <MobileClientsSkeleton />
-        ) : devices.length > 0 ? (
+        ) : hasErrorWithoutData ? null : devices.length > 0 ? (
           <ul className="space-y-3">
             {devices.map((device) => (
               <MobileClientRow key={device.deviceId} device={device} />

diff --git a/apps/web/src/components/settings/ConnectionsSettings.tsx b/apps/web/src/components/settings/ConnectionsSettings.tsx
--- a/apps/web/src/components/settings/ConnectionsSettings.tsx
+++ b/apps/web/src/components/settings/ConnectionsSettings.tsx
@@ -1586,7 +1586,15 @@
   const [operationError, setOperationError] = useState<string | null>(null);
   const [isUpdating, setIsUpdating] = useState(false);
   const [isUpdatingPreference, setIsUpdatingPreference] = useState(false);
+  const [optimisticPublishActivity, setOptimisticPublishActivity] = useState<boolean | null>(null);
 
+  const serverPublishActivity = primaryCloudLinkState.data?.publishAgentActivity ?? null;
+  useEffect(() => {
+    if (optimisticPublishActivity !== null && serverPublishActivity === optimisticPublishActivity) {
+      setOptimisticPublishActivity(null);
+    }
+  }, [serverPublishActivity, optimisticPublishActivity]);
+
   const reportUpdateFailure = (cause: unknown) => {
     const message = cause instanceof Error ? cause.message : "Could not update T3 Connect access.";
     const traceId = findErrorTraceId(cause);
@@ -1689,6 +1697,7 @@
     }
 
     primaryCloudLinkState.refresh();
+    setOptimisticPublishActivity(enabled);
     toastManager.add({
       type: "success",
       title: enabled ? "Agent activity enabled" : "Agent activity disabled",
@@ -1734,7 +1743,11 @@
           control={
             <Switch
               aria-label="Publish agent activity to mobile clients"
-              checked={primaryCloudLinkState.data?.publishAgentActivity ?? false}
+              checked={
+                optimisticPublishActivity ??
+                primaryCloudLinkState.data?.publishAgentActivity ??
+                false
+              }
               disabled={
                 !canManageRelay ||
                 !isSignedIn ||

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit 5c536c8. Configure here.

</ul>
) : (
<EmptyMobileClients />
)}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error shows empty clients state

Medium Severity

When the mobile client list fails to load, the UI displays both the error banner and the "No mobile clients" empty state. This occurs because isInitialLoad is false when an error is present, causing the page to suggest there are no devices rather than indicating a loading failure.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5c536c8. Configure here.

? "This environment can publish agent activity to your mobile clients."
: "This environment will stop publishing agent activity.",
});
setIsUpdatingPreference(false);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preference switch stale after update

Medium Severity

The "Publish agent activity" switch can display an incorrect state immediately after a successful update. The UI relies on primaryCloudLinkState which updates asynchronously, discarding the updatePrimaryEnvironmentPreferences command's immediate return value. This causes a brief mismatch between the UI and the actual state, even after a success toast.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5c536c8. Configure here.

export function MobileClientsUserProfilePage() {
const devicesState = useManagedRelayDevices();
const devices = devicesState.data ?? [];
const isInitialLoad = devicesState.data === null && !devicesState.error;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

False empty before relay session

Medium Severity

isInitialLoad treats only data === null as loading, but useManagedRelayDevices substitutes a successful empty array when managedRelaySessionAtom has no accountId. During Clerk sign-in or account activation, the page can show “No mobile clients” instead of the loading skeleton until the relay session is registered.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5c536c8. Configure here.

@macroscopeapp

macroscopeapp Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces new user-facing features (mobile clients management page, agent activity publishing toggle) rather than simple bug fixes or refactors. Additionally, there are three unresolved review comments identifying potential UI state bugs that should be addressed before merging.

You can customize Macroscope's approvability policy. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant