fix(cloud-auth): keep Sign in clickable during a pending browser wait

Closing the cloud sign-in tab used to leave every Sign in button disabled
as "Signing in…" until the 5-minute loopback timeout. A second click now
starts another wait, the first tab can still complete, and the first
successful callback wins.

STA-7610
This commit is contained in:
Jinwoo-H
2026-09-16 14:56:34 -04:00
parent 36cdb34097
commit 331e4bc29b
19 changed files with 165 additions and 133 deletions
@@ -148,4 +148,37 @@ describe('Orca cloud PKCE flow', () => {
await readHttp(callbackUrl(redirectUri, { code: 'real-code', state }))
await expect(flow).resolves.toMatchObject({ code: 'real-code', nonce })
})
it('keeps the first loopback alive when a second sign-in starts', async () => {
const first = beginOrcaCloudPkceFlow(config, 'local-default')
await vi.waitFor(() => expect(openExternalMock).toHaveBeenCalledTimes(1))
const firstUrl = new URL(String(openExternalMock.mock.calls[0]?.[0]))
const firstRedirectUri = firstUrl.searchParams.get('redirect_uri')
const firstState = firstUrl.searchParams.get('state')
if (!firstRedirectUri || !firstState) {
throw new Error('Expected the first PKCE flow to create redirect_uri and state')
}
const second = beginOrcaCloudPkceFlow(config, 'local-default')
await vi.waitFor(() => expect(openExternalMock).toHaveBeenCalledTimes(2))
const secondUrl = new URL(String(openExternalMock.mock.calls[1]?.[0]))
const secondRedirectUri = secondUrl.searchParams.get('redirect_uri')
const secondState = secondUrl.searchParams.get('state')
if (!secondRedirectUri || !secondState) {
throw new Error('Expected the second PKCE flow to create redirect_uri and state')
}
expect(secondRedirectUri).not.toBe(firstRedirectUri)
const firstResponse = await readHttp(
callbackUrl(firstRedirectUri, { code: 'first-code', state: firstState })
)
expect(firstResponse.statusCode).toBe(200)
await expect(first).resolves.toMatchObject({ code: 'first-code', state: firstState })
const secondResponse = await readHttp(
callbackUrl(secondRedirectUri, { code: 'second-code', state: secondState })
)
expect(secondResponse.statusCode).toBe(200)
await expect(second).resolves.toMatchObject({ code: 'second-code', state: secondState })
})
})
@@ -48,7 +48,6 @@ export function UnexpectedSignoutCard(): React.JSX.Element | null {
const persistedDismissedVersion = useAppStore((s) => s.dismissedUnexpectedSignoutVersion)
const dismissedVersions = useAppStore((s) => s.unexpectedSignoutDismissedVersions)
const dismissForVersion = useAppStore((s) => s.dismissUnexpectedSignoutCard)
const connecting = useAppStore((s) => s.orcaProfileConnecting)
const connect = useAppStore((s) => s.connectCurrentOrcaProfile)
const [appVersion, setAppVersion] = useState<string | null>(null)
const [authRefreshReady, setAuthRefreshReady] = useState(false)
@@ -236,12 +235,10 @@ export function UnexpectedSignoutCard(): React.JSX.Element | null {
variant="default"
size="sm"
className="flex-1"
disabled={!canConnect || connecting}
disabled={!canConnect}
onClick={() => void connect()}
>
{connecting
? translate('auto.components.UnexpectedSignoutCard.7e1a9c4d2f', 'Signing in…')
: translate('auto.components.UnexpectedSignoutCard.c5b3e8a17d', 'Sign in to Orca')}
{translate('auto.components.UnexpectedSignoutCard.c5b3e8a17d', 'Sign in to Orca')}
</Button>
</div>
</div>
@@ -17,7 +17,6 @@ const mocks = vi.hoisted(() => ({
openPopover: null as ((open: boolean) => void) | null,
state: {
orcaProfileAuthStatus: { configured: true, state: 'connected' } as Record<string, unknown>,
orcaProfileConnecting: false,
settings: { artifactSharingEnabled: true }
}
}))
@@ -83,7 +82,6 @@ describe('ArtifactPublishButton', () => {
mocks.copyLink.mockResolvedValue(true)
mocks.openPopover = null
mocks.state.orcaProfileAuthStatus = { configured: true, state: 'connected' }
mocks.state.orcaProfileConnecting = false
mocks.state.settings = { artifactSharingEnabled: true }
})
@@ -35,7 +35,6 @@ export function ArtifactPublishButton({
const lookupSequence = useRef(0)
const popoverContentRef = useRef<HTMLDivElement>(null)
const authStatus = useAppStore((state) => state.orcaProfileAuthStatus)
const connecting = useAppStore((state) => state.orcaProfileConnecting)
const connect = useAppStore((state) => state.connectCurrentOrcaProfile)
const openSettingsPage = useAppStore((state) => state.openSettingsPage)
const openSettingsTarget = useAppStore((state) => state.openSettingsTarget)
@@ -56,7 +55,7 @@ export function ArtifactPublishButton({
const checkingLink =
signedIn && currentLookup?.status !== 'loaded' && currentLookup?.status !== 'error'
const publishedLink = currentLookup?.status === 'loaded' ? currentLookup.shareUrl : null
const busy = publishing || connecting
const busy = publishing
const blocked = disabled || busy
useEffect(() => {
@@ -184,23 +183,15 @@ export function ArtifactPublishButton({
type="button"
variant="outline"
size="xs"
disabled={connecting || authStatus?.configured !== true}
disabled={authStatus?.configured !== true}
onClick={() => void connect()}
>
{connecting
{authStatus?.state === 'reconnect-required'
? translate(
'auto.components.artifacts.ArtifactPublishButton.signingIn',
'Signing in'
'auto.components.artifacts.ArtifactPublishButton.signInAgain',
'Sign in again'
)
: authStatus?.state === 'reconnect-required'
? translate(
'auto.components.artifacts.ArtifactPublishButton.signInAgain',
'Sign in again'
)
: translate(
'auto.components.artifacts.ArtifactPublishButton.signIn',
'Sign in'
)}
: translate('auto.components.artifacts.ArtifactPublishButton.signIn', 'Sign in')}
</Button>
</div>
) : null}
@@ -63,7 +63,7 @@ function storeState(): Record<string, unknown> {
closeArtifactsPage: mocks.closePage,
connectCurrentOrcaProfile: mocks.connect,
orcaProfileAuthStatus: mocks.authStatus,
orcaProfileConnecting: false,
refreshCurrentOrcaProfileAuth: mocks.refreshAuth,
settings: mocks.settings,
updateSettings: mocks.updateSettings,
@@ -20,7 +20,6 @@ const LOCAL_RUNTIME = { kind: 'local' } as const
export default function ArtifactsPage(): React.JSX.Element {
const closePage = useAppStore((state) => state.closeArtifactsPage)
const authStatus = useAppStore((state) => state.orcaProfileAuthStatus)
const connecting = useAppStore((state) => state.orcaProfileConnecting)
const connect = useAppStore((state) => state.connectCurrentOrcaProfile)
const refreshAuth = useAppStore((state) => state.refreshCurrentOrcaProfileAuth)
const openSettingsPage = useAppStore((state) => state.openSettingsPage)
@@ -192,7 +191,6 @@ export default function ArtifactsPage(): React.JSX.Element {
) : null}
{!signedIn ? (
<ArtifactsPageAuthState
connecting={connecting}
needsReconnect={needsReconnect}
configured={authStatus?.configured === true}
onConnect={() => void connect()}
@@ -22,13 +22,11 @@ export function ArtifactsPageErrorBanner({
}
export function ArtifactsPageAuthState({
connecting,
needsReconnect,
configured,
onConnect,
onOpenAccountSettings
}: {
connecting: boolean
needsReconnect: boolean
configured: boolean
onConnect: () => void
@@ -62,15 +60,13 @@ export function ArtifactsPageAuthState({
</p>
</div>
{configured ? (
<Button size="sm" disabled={connecting} onClick={onConnect}>
{connecting
? translate('auto.components.artifacts.ArtifactsPage.signingIn', 'Signing in…')
: needsReconnect
? translate(
'auto.components.artifacts.ArtifactsPage.signInAgainAction',
'Sign in again'
)
: translate('auto.components.artifacts.ArtifactsPage.signIn', 'Sign in to Orca')}
<Button size="sm" onClick={onConnect}>
{needsReconnect
? translate(
'auto.components.artifacts.ArtifactsPage.signInAgainAction',
'Sign in again'
)
: translate('auto.components.artifacts.ArtifactsPage.signIn', 'Sign in to Orca')}
</Button>
) : (
<div className="flex flex-col items-center gap-2">
@@ -15,7 +15,6 @@ const mocks = vi.hoisted(() => ({
configured: true,
state: 'connected'
} as Record<string, unknown> | null,
orcaProfileConnecting: false,
isWebClient: false
}
}))
@@ -46,7 +45,6 @@ describe('ArtifactsSettingsPane', () => {
mocks.fetchAuthStatus.mockReset()
mocks.openArtifactsPage.mockReset()
mocks.state.orcaProfileAuthStatus = { configured: true, state: 'connected' }
mocks.state.orcaProfileConnecting = false
mocks.state.isWebClient = false
})
@@ -92,19 +90,11 @@ describe('ArtifactsSettingsPane', () => {
expect(mocks.connect).toHaveBeenCalledOnce()
})
it('shows reconnect and connecting states', () => {
it('keeps sign in clickable while reconnect is required', () => {
mocks.state.orcaProfileAuthStatus = { configured: true, state: 'reconnect-required' }
const { rerender } = render(
<ArtifactsSettingsPane settings={getDefaultSettings('/tmp')} updateSettings={vi.fn()} />
)
render(<ArtifactsSettingsPane settings={getDefaultSettings('/tmp')} updateSettings={vi.fn()} />)
expect(screen.getByRole('button', { name: 'Sign in again' })).toBeEnabled()
mocks.state.orcaProfileConnecting = true
rerender(
<ArtifactsSettingsPane settings={getDefaultSettings('/tmp')} updateSettings={vi.fn()} />
)
expect(screen.getByRole('button', { name: 'Signing in…' })).toBeDisabled()
})
it('loads missing account status and disables sign in until configured', () => {
@@ -18,7 +18,6 @@ export function ArtifactsSettingsPane({
}): React.JSX.Element {
const openArtifactsPage = useAppStore((state) => state.openArtifactsPage)
const authStatus = useAppStore((state) => state.orcaProfileAuthStatus)
const connecting = useAppStore((state) => state.orcaProfileConnecting)
const connect = useAppStore((state) => state.connectCurrentOrcaProfile)
const signedIn = authStatus?.state === 'connected'
// Why: the capability lives in the desktop host's store and is deliberately absent from the
@@ -128,14 +127,12 @@ export function ArtifactsSettingsPane({
<Button
type="button"
size="sm"
disabled={connecting || authStatus?.configured !== true}
disabled={authStatus?.configured !== true}
onClick={() => void connect()}
>
{connecting
? translate('auto.components.settings.artifacts.signingIn', 'Signing in')
: authStatus?.state === 'reconnect-required'
? translate('auto.components.settings.artifacts.signInAgain', 'Sign in again')
: translate('auto.components.settings.artifacts.signIn', 'Sign in to Orca')}
{authStatus?.state === 'reconnect-required'
? translate('auto.components.settings.artifacts.signInAgain', 'Sign in again')
: translate('auto.components.settings.artifacts.signIn', 'Sign in to Orca')}
</Button>
</section>
) : null}
@@ -139,7 +139,6 @@ function showDeleteFailureToast(): void {
// progress; this surfaces it (and its status) in dev when the env vars are set.
function OrcaCloudDevSubsection(): React.JSX.Element {
const authStatus = useAppStore((s) => s.orcaProfileAuthStatus)
const connecting = useAppStore((s) => s.orcaProfileConnecting)
const connect = useAppStore((s) => s.connectCurrentOrcaProfile)
const signOut = useAppStore((s) => s.signOutCurrentOrcaProfile)
const refresh = useAppStore((s) => s.fetchOrcaProfileAuthStatus)
@@ -170,23 +169,11 @@ function OrcaCloudDevSubsection(): React.JSX.Element {
</p>
<div className="flex flex-wrap gap-2">
{connected ? (
<Button
type="button"
variant="outline"
size="sm"
disabled={connecting}
onClick={() => void signOut()}
>
<Button type="button" variant="outline" size="sm" onClick={() => void signOut()}>
{translate('auto.components.settings.DevToolsPane.orcaCloudSignOut', 'Sign out')}
</Button>
) : (
<Button
type="button"
variant="outline"
size="sm"
disabled={connecting}
onClick={() => void connect()}
>
<Button type="button" variant="outline" size="sm" onClick={() => void connect()}>
{translate(
'auto.components.settings.DevToolsPane.orcaCloudConnect',
'Connect profile'
@@ -12,7 +12,6 @@ import { MobilePairingConnectionOptions } from './MobilePairingConnectionOptions
type MobileRelayStoreState = {
orcaProfileAuthStatus: OrcaProfileAuthStatus | null
orcaProfileConnecting: boolean
connectCurrentOrcaProfile: () => Promise<null>
fetchOrcaProfileAuthStatus: () => Promise<OrcaProfileAuthStatus | null>
}
@@ -73,7 +72,6 @@ describe('MobilePairingConnectionOptions', () => {
state: 'local',
persistence: 'none'
},
orcaProfileConnecting: false,
connectCurrentOrcaProfile: connect,
fetchOrcaProfileAuthStatus: fetchAuthStatus
}
@@ -222,7 +220,6 @@ describe('MobilePairingConnectionOptions', () => {
state: 'connected',
persistence: 'encrypted'
},
orcaProfileConnecting: false,
connectCurrentOrcaProfile: connect,
fetchOrcaProfileAuthStatus: fetchAuthStatus
}
@@ -1,5 +1,4 @@
import { useEffect, useRef, useState } from 'react'
import { Loader2 } from 'lucide-react'
import { Badge } from '../ui/badge'
import { Button } from '../ui/button'
import { translate } from '../../i18n/i18n'
@@ -66,7 +65,6 @@ export function MobilePairingConnectionOptions({
relayMintRetrying?: boolean
}): React.JSX.Element {
const authStatus = useAppStore((state) => state.orcaProfileAuthStatus)
const connecting = useAppStore((state) => state.orcaProfileConnecting)
const connect = useAppStore((state) => state.connectCurrentOrcaProfile)
const [relayStatus, setRelayStatus] = useState<MobileRelayStatus>('offline')
const [relayCellUrl, setRelayCellUrl] = useState<string | undefined>(undefined)
@@ -227,13 +225,11 @@ export function MobilePairingConnectionOptions({
type="button"
size="sm"
className="shrink-0"
disabled={connecting}
onClick={() => {
onChange('automatic')
void connect()
}}
>
{connecting ? <Loader2 className="animate-spin" /> : null}
{reconnectRequired
? translate(
'auto.components.settings.MobilePairingConnectionOptions.signInAgain',
@@ -6,19 +6,27 @@ import { cleanup, render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
const mocks = vi.hoisted(() => ({
connect: vi.fn(),
fetchAuthStatus: vi.fn(),
signOut: vi.fn(),
state: {
orcaProfileAuthStatus: {
configured: true,
state: 'connected',
cloud: { displayName: 'Ada Lovelace', email: 'ada@example.com' }
} as Record<string, unknown> | null,
orcaProfileConnecting: false
type MockAuthStatus = {
configured: boolean
state: string
cloud?: { displayName: string; email: string }
} | null
const mocks = vi.hoisted(() => {
const connectedAuth: MockAuthStatus = {
configured: true,
state: 'connected',
cloud: { displayName: 'Ada Lovelace', email: 'ada@example.com' }
}
}))
return {
connect: vi.fn(),
fetchAuthStatus: vi.fn(),
signOut: vi.fn(),
state: {
orcaProfileAuthStatus: connectedAuth
}
}
})
vi.mock('@/i18n/i18n', () => ({
translate: (_key: string, fallback: string) => fallback
@@ -58,7 +66,6 @@ describe('OrcaAccountSettingsPane', () => {
state: 'connected',
cloud: { displayName: 'Ada Lovelace', email: 'ada@example.com' }
}
mocks.state.orcaProfileConnecting = false
})
afterEach(cleanup)
@@ -89,6 +96,9 @@ describe('OrcaAccountSettingsPane', () => {
).toBeInTheDocument()
await user.click(screen.getByRole('button', { name: 'Sign in to Orca' }))
expect(mocks.connect).toHaveBeenCalledOnce()
expect(screen.getByRole('button', { name: 'Sign in to Orca' })).toBeEnabled()
await user.click(screen.getByRole('button', { name: 'Sign in to Orca' }))
expect(mocks.connect).toHaveBeenCalledTimes(2)
})
it('loads account status when it is not hydrated yet', () => {
@@ -60,7 +60,6 @@ function AccountBenefit({
export function OrcaAccountSettingsPane(): React.JSX.Element {
const authStatus = useAppStore((state) => state.orcaProfileAuthStatus)
const connecting = useAppStore((state) => state.orcaProfileConnecting)
const connect = useAppStore((state) => state.connectCurrentOrcaProfile)
const signOut = useAppStore((state) => state.signOutCurrentOrcaProfile)
const [signOutOpen, setSignOutOpen] = useState(false)
@@ -117,17 +116,10 @@ export function OrcaAccountSettingsPane(): React.JSX.Element {
{translate('auto.components.settings.orcaAccount.signOut', 'Sign out')}
</Button>
) : (
<Button
type="button"
size="sm"
disabled={!canConnect || connecting}
onClick={() => void connect()}
>
{connecting
? translate('auto.components.settings.orcaAccount.signingIn', 'Signing in…')
: authStatus?.state === 'reconnect-required'
? translate('auto.components.settings.orcaAccount.signInAgain', 'Sign in again')
: translate('auto.components.settings.orcaAccount.signIn', 'Sign in to Orca')}
<Button type="button" size="sm" disabled={!canConnect} onClick={() => void connect()}>
{authStatus?.state === 'reconnect-required'
? translate('auto.components.settings.orcaAccount.signInAgain', 'Sign in again')
: translate('auto.components.settings.orcaAccount.signIn', 'Sign in to Orca')}
</Button>
)}
</div>
@@ -16,7 +16,6 @@ const mocks = vi.hoisted(() => ({
string,
unknown
> | null,
orcaProfileConnecting: false,
isWebClient: false,
settings: { showSkillsButton: false, agentSkillSharingEnabled: false }
}
@@ -50,7 +49,6 @@ describe('ShareSkillsSettingsPane', () => {
mocks.openSkillsPage.mockReset()
mocks.updateSettings.mockReset()
mocks.state.orcaProfileAuthStatus = { configured: true, state: 'connected' }
mocks.state.orcaProfileConnecting = false
mocks.state.isWebClient = false
Object.defineProperty(window, 'api', {
configurable: true,
@@ -14,7 +14,6 @@ export function ShareSkillsSettingsPane(): React.JSX.Element {
const settings = useAppStore((state) => state.settings)
const updateSettings = useAppStore((state) => state.updateSettings)
const authStatus = useAppStore((state) => state.orcaProfileAuthStatus)
const connecting = useAppStore((state) => state.orcaProfileConnecting)
const connect = useAppStore((state) => state.connectCurrentOrcaProfile)
const signedIn = authStatus?.state === 'connected'
const isWebClient = isWebClientLocation()
@@ -135,14 +134,12 @@ export function ShareSkillsSettingsPane(): React.JSX.Element {
<Button
type="button"
size="sm"
disabled={connecting || authStatus?.configured !== true}
disabled={authStatus?.configured !== true}
onClick={() => void connect()}
>
{connecting
? translate('auto.components.settings.shareSkills.signingIn', 'Signing in')
: authStatus?.state === 'reconnect-required'
? translate('auto.components.settings.shareSkills.signInAgain', 'Sign in again')
: translate('auto.components.settings.shareSkills.signIn', 'Sign in to Orca')}
{authStatus?.state === 'reconnect-required'
? translate('auto.components.settings.shareSkills.signInAgain', 'Sign in again')
: translate('auto.components.settings.shareSkills.signIn', 'Sign in to Orca')}
</Button>
) : null}
</section>
@@ -1,5 +1,4 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { createTestStore } from './store-test-helpers'
import type {
ConnectCurrentOrcaProfileResult,
CreateCloudLinkedOrcaProfileResult,
@@ -9,6 +8,21 @@ import type {
SelectOrcaProfileOrgResult,
SignOutCurrentOrcaProfileResult
} from '../../../../shared/orca-profiles'
import { createTestStore } from './store-test-helpers'
const { toastErrorMock, toastSuccessMock } = vi.hoisted(() => ({
toastErrorMock: vi.fn(),
toastSuccessMock: vi.fn()
}))
vi.mock('sonner', () => ({
toast: {
error: toastErrorMock,
info: vi.fn(),
success: toastSuccessMock,
warning: vi.fn()
}
}))
const listState: OrcaProfileListState = {
activeProfileId: 'local-default',
@@ -73,6 +87,8 @@ const orcaProfilesApi = {
describe('orca profile auth actions slice', () => {
beforeEach(() => {
vi.resetAllMocks()
toastErrorMock.mockReset()
toastSuccessMock.mockReset()
orcaProfilesApi.authStatus.mockResolvedValue(localAuthStatus)
vi.stubGlobal('window', {
api: {
@@ -98,13 +114,51 @@ describe('orca profile auth actions slice', () => {
orcaProfilesApi.connectCurrent.mockResolvedValue(result)
const store = createTestStore()
const pending = store.getState().connectCurrentOrcaProfile()
expect(store.getState().orcaProfileConnecting).toBe(true)
await expect(pending).resolves.toEqual(result)
expect(store.getState().orcaProfileConnecting).toBe(false)
await expect(store.getState().connectCurrentOrcaProfile()).resolves.toEqual(result)
expect(store.getState().orcaProfileAuthStatus).toEqual(connectedAuthStatus)
expect(store.getState().orcaProfiles).toEqual(connectedProfiles)
expect(toastSuccessMock).toHaveBeenCalledOnce()
})
it('starts a second sign-in while the first browser wait is still open', async () => {
const connectedProfiles = [
{
...listState.profiles[0],
kind: 'cloud-linked' as const,
cloud: connectedAuthStatus.cloud
}
]
const connected: ConnectCurrentOrcaProfileResult = {
status: 'connected',
auth: connectedAuthStatus,
activeProfileId: 'local-default',
profiles: connectedProfiles
}
const cancelled: ConnectCurrentOrcaProfileResult = {
status: 'cancelled',
auth: connectedAuthStatus
}
let finishFirst!: (value: ConnectCurrentOrcaProfileResult) => void
orcaProfilesApi.connectCurrent
.mockReturnValueOnce(
new Promise<ConnectCurrentOrcaProfileResult>((resolve) => {
finishFirst = resolve
})
)
.mockResolvedValueOnce(connected)
const store = createTestStore()
const first = store.getState().connectCurrentOrcaProfile()
const second = store.getState().connectCurrentOrcaProfile()
expect(orcaProfilesApi.connectCurrent).toHaveBeenCalledTimes(2)
await expect(second).resolves.toEqual(connected)
expect(toastSuccessMock).toHaveBeenCalledOnce()
finishFirst(cancelled)
await expect(first).resolves.toEqual(cancelled)
expect(toastErrorMock).not.toHaveBeenCalled()
expect(toastSuccessMock).toHaveBeenCalledOnce()
expect(store.getState().orcaProfileAuthStatus).toEqual(connectedAuthStatus)
})
it('refreshes current profile auth and stores fresh capability flags', async () => {
@@ -70,14 +70,12 @@ export const createOrcaProfilesAuthActions: StateCreator<
},
connectCurrentOrcaProfile: async () => {
if (get().orcaProfileConnecting) {
return null
}
set({ orcaProfileConnecting: true })
try {
// Why: a pending browser callback must not block retry. Another click
// starts a second PKCE wait; the earlier tab can still complete.
const alreadyConnected = get().orcaProfileAuthStatus?.state === 'connected'
const result = await window.api.orcaProfiles.connectCurrent()
set({
orcaProfileConnecting: false,
orcaProfileAuthStatus: result.auth,
...(result.status === 'connected'
? {
@@ -96,24 +94,29 @@ export const createOrcaProfilesAuthActions: StateCreator<
description: result.auth.setupMessage
}
)
} else if (result.status === 'failed') {
} else if (
result.status === 'failed' &&
!alreadyConnected &&
result.auth.state !== 'connected'
) {
toast.error(
translate('auto.store.slices.orca.profiles.33290e88ed', 'Failed to connect profile'),
{ description: result.error }
)
} else if (result.status === 'connected') {
} else if (result.status === 'connected' && !alreadyConnected) {
toast.success(translate('auto.store.slices.orca.profiles.9fcb07a796', 'Profile connected'))
}
return result
} catch (err) {
console.error('Failed to connect Orca profile:', err)
set({ orcaProfileConnecting: false })
toast.error(
translate('auto.store.slices.orca.profiles.33290e88ed', 'Failed to connect profile'),
{
description: err instanceof Error ? err.message : String(err)
}
)
if (get().orcaProfileAuthStatus?.state !== 'connected') {
toast.error(
translate('auto.store.slices.orca.profiles.33290e88ed', 'Failed to connect profile'),
{
description: err instanceof Error ? err.message : String(err)
}
)
}
return null
}
},
@@ -21,7 +21,6 @@ export type OrcaProfilesSlice = OrcaProfilesAuthActions & {
orcaProfilesMultiProfileUi: boolean
orcaProfilesLoading: boolean
orcaProfileSwitching: boolean
orcaProfileConnecting: boolean
fetchOrcaProfiles: () => Promise<void>
fetchOrcaProfileAuthStatus: () => Promise<OrcaProfileAuthStatus | null>
createLocalOrcaProfile: (name?: string) => Promise<OrcaProfileSummary | null>
@@ -42,7 +41,6 @@ export const createOrcaProfilesSlice: StateCreator<AppState, [], [], OrcaProfile
orcaProfilesMultiProfileUi: false,
orcaProfilesLoading: false,
orcaProfileSwitching: false,
orcaProfileConnecting: false,
fetchOrcaProfiles: async () => {
set({ orcaProfilesLoading: true })