Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 25 additions & 15 deletions apps/mobile/src/features/settings/ClaudeAccounts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
type ServerProvider,
} from "@t3tools/contracts";
import { useRef, useState } from "react";
import { TextInput, View } from "react-native";
import { TextInput, useWindowDimensions, View } from "react-native";

import * as Dialog from "../../components/ui/Dialog";
import { AppText as Text } from "../../components/ui/AppText";
Expand Down Expand Up @@ -68,15 +68,18 @@ export function ClaudeAccount({
const instance = environment.serverConfig.settings.providerInstances[provider.instanceId];
const configDir = claudeConfigDir(instance);
const name = provider.displayName ?? "Claude";
// A signed-in account needs no label; the row only speaks up when something is off.
const status = !provider.enabled
? "Turned off"
: provider.auth.status === "authenticated"
? "Signed in"
? null
: provider.auth.status === "unauthenticated"
? "Not signed in"
: !provider.installed
? "Not installed"
: null;
: "Sign-in not checked";
// Matches ListRowLayout: large text stacks the status below instead of squeezing the name.
const stacked = useWindowDimensions().fontScale >= 1.5;
const removable =
provider.instanceId !== DEFAULT_CLAUDE_INSTANCE_ID &&
!isManagedSettingPath(
Expand Down Expand Up @@ -114,14 +117,26 @@ export function ClaudeAccount({
}
return (
<View className="border-t border-border-subtle">
<View className="min-h-16 flex-row items-center gap-3 px-4 py-3">
<View className="min-w-0 shrink grow gap-0.5">
<Text textRole="body" className="text-foreground" numberOfLines={1}>
<View className="min-h-14 flex-row items-center gap-3 px-4 py-1.5">
<View
className={
stacked
? "min-w-0 shrink grow gap-0.5"
: "min-w-0 shrink grow flex-row items-center gap-3"
}
>
<Text
textRole="body"
className={stacked ? "text-foreground" : "min-w-0 shrink grow text-foreground"}
numberOfLines={1}
>
{name}
</Text>
<Text textRole="footnote" className="text-foreground-muted" numberOfLines={2}>
{[status, configDir || "Default config folder"].filter(Boolean).join(" · ")}
</Text>
{status ? (
<Text textRole="footnote" className="text-foreground-muted">
{status}
</Text>
) : null}
</View>
{editing ? null : (
<Menu
Expand Down Expand Up @@ -172,12 +187,7 @@ export function AddClaudeAccount({ environment }: { readonly environment: Settin
onDone={() => setAdding(false)}
/>
) : (
<ListActionRow
icon="plus"
label="Add Claude account"
description="Use a Claude config folder that is already signed in on this server."
onPress={() => setAdding(true)}
/>
<ListActionRow icon="plus" label="Add Claude account" onPress={() => setAdding(true)} />
)}
</View>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,22 +243,23 @@ function ProviderAccount({
const usesCredentials =
provider.driver === "antigravity" &&
(provider.auth.type === "gemini-api-key" || provider.auth.type === "agent-platform");
const status =
active || state?.phase === "failed" || state?.phase === "cancelled"
? state.message
: !provider.enabled
? "Turned off"
: signedIn
? "Signed in"
: isDiscovering
? "Discovering sign-in methods…"
: needsExternalSetup
? "No in-app sign-in advertised. Follow the provider's docs to finish setup."
: !provider.installed
? "Not installed"
: usesCredentials
? "Connect to load models"
: "Not signed in";
const showsFlow = active || state?.phase === "failed" || state?.phase === "cancelled";
const turnedOff = !showsFlow && !provider.enabled;
const status = showsFlow
? state.message
: turnedOff
? "Turned off"
: signedIn
? "Signed in"
: isDiscovering
? "Discovering sign-in methods…"
: needsExternalSetup
? "No in-app sign-in advertised. Follow the provider's docs to finish setup."
: !provider.installed
? "Not installed"
: usesCredentials
? "Connect to load models"
: "Not signed in";
// A provider with several sign-in methods picks one from a menu on the same control.
const methodActions: MenuAction[] = methods.map((method) => ({
id: `method:${method.id}`,
Expand Down Expand Up @@ -402,10 +403,18 @@ function ProviderAccount({
className={
stacked
? "min-h-16 items-start gap-3 px-4 py-3"
: "min-h-16 flex-row items-center gap-3 px-4 py-3"
: turnedOff
? "min-h-14 flex-row items-center gap-3 px-4 py-1.5"
: "min-h-16 flex-row items-center gap-3 px-4 py-3"
}
>
<ProviderAccountSummary key={email} name={name} status={status} email={email} />
<ProviderAccountSummary
key={email}
name={name}
status={status}
email={email}
inline={turnedOff && !stacked}
/>
{trailing}
</View>
{interaction?.type === "deviceCode" ||
Expand Down Expand Up @@ -511,15 +520,25 @@ function ProviderAccountSummary({
name,
status,
email,
inline,
}: {
readonly name: string;
readonly status: string | null | undefined;
readonly email: string | undefined;
/** Puts a short status beside the name, so the row is one line tall. */
readonly inline: boolean;
}) {
const [revealed, setRevealed] = useState(false);
const layout = inline
? "min-w-0 shrink grow flex-row items-center gap-3"
: "min-w-0 shrink grow gap-0.5";
const content = (
<>
<Text textRole="body" className="text-foreground" numberOfLines={1}>
<Text
textRole="body"
className={inline ? "min-w-0 shrink grow text-foreground" : "text-foreground"}
numberOfLines={1}
>
{name}
</Text>
<Text accessibilityLiveRegion="polite" textRole="footnote" className="text-foreground-muted">
Expand All @@ -532,7 +551,7 @@ function ProviderAccountSummary({
<View
accessible
accessibilityLabel={[name, status].filter(Boolean).join(", ")}
className="min-w-0 shrink grow gap-0.5"
className={layout}
>
{content}
</View>
Expand All @@ -544,7 +563,7 @@ function ProviderAccountSummary({
accessibilityLabel={[name, status, revealed ? email : null].filter(Boolean).join(", ")}
accessibilityHint={revealed ? "Hides the account email" : "Reveals the account email"}
onPress={() => setRevealed((value) => !value)}
className="min-w-0 shrink grow gap-0.5"
className={layout}
>
{content}
</Pressable>
Expand Down
Loading