feat(landing): signed-out cloud workspaces sign-in card (V3-08)

In builds that offer Orca Cloud, when signed out (never, or the session
ended) with projects and no cloud list, Home shows a quiet 'Cloud
workspaces · Sign in to see your team's cloud workspaces' card where the
list would be. It stands in for the trial card while signed out. The base
landing (wordmark, no-project Home) is unchanged pending FR1.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Neil
2026-11-12 11:46:13 -08:00
co-authored by Claude
parent a57aecc3f8
commit 4a67fcdbde
6 changed files with 195 additions and 1 deletions
+11 -1
View File
@@ -1,4 +1,5 @@
import { LandingCloudCard } from './landing/LandingCloudCard'
import { LandingCloudSignInCard, useSignedOutOfOrcaCloud } from './landing/LandingCloudSignInCard'
import {
CloudWorkspacesHome,
useCloudWorkspacesPresent
@@ -225,6 +226,10 @@ export default function Landing(): React.JSX.Element {
const cloudListPresent = useCloudWorkspacesPresent()
const repos = useAppStore((s) => s.repos)
const openModal = useAppStore((s) => s.openModal)
const signedOutOfCloud = useSignedOutOfOrcaCloud()
const reconnectRequired = useAppStore(
(s) => s.orcaProfileAuthStatus?.state === 'reconnect-required'
)
const createActionLabel =
repos.length > 0 && repos.every((repo) => isGitRepoKind(repo))
@@ -298,7 +303,12 @@ export default function Landing(): React.JSX.Element {
onCreate={() => openModal('new-workspace-composer', { telemetrySource: 'unknown' })}
/>
{hasProjects ? <LandingCloudCard cloudWorkspacesPresent={cloudListPresent} /> : null}
{/* V3-08: signed out, the team's cloud workspaces can't be listed; say so where they'd be. */}
{hasProjects && signedOutOfCloud && !cloudListPresent ? (
<LandingCloudSignInCard reconnect={reconnectRequired} />
) : hasProjects ? (
<LandingCloudCard cloudWorkspacesPresent={cloudListPresent} />
) : null}
{!cloudListPresent && <LandingShortcutList shortcuts={shortcuts} />}
</div>
@@ -65,6 +65,13 @@ export function cloudWorkspacesAvailable(): Promise<boolean> {
return availability
}
/** Fixtures only: forget the cached answer so the next probe reads the stubbed bridge. */
export function resetCloudWorkspacesAvailabilityForFixtures(): void {
availability = null
availableValue = null
availabilityListeners.forEach((listener) => listener())
}
/** null until the first availability answer arrives. */
export function useCloudWorkspacesAvailable(): boolean | null {
return useSyncExternalStore(
@@ -0,0 +1,77 @@
// V3-08: Home signed out of Orca. Cloud builds with projects show the "Cloud workspaces · Sign in"
// card where the list would be; without projects, and in local builds, Home is unchanged.
import type { Repo } from '../../../../shared/repo-types'
import type { OrcaProfileAuthStatus } from '../../../../shared/orca-profiles'
import { defineFixture } from '../../component-fixtures/component-fixture'
import { resetCloudWorkspacesAvailabilityForFixtures } from '../cloud-workspaces/use-cloud-workspaces'
import { makeRepo } from '../worktree-jump/worktree-jump-palette-test-fixtures'
import Landing from '../Landing'
const repo: Repo = { ...makeRepo(), displayName: 'orca' }
const auth = (
configured: boolean,
state: OrcaProfileAuthStatus['state']
): OrcaProfileAuthStatus => ({ activeProfileId: 'default', configured, state, persistence: 'none' })
const signedOut = auth(true, 'local')
const sessionEnded = auth(true, 'reconnect-required')
const localBuild = auth(false, 'unconfigured')
const build = (cloud: boolean) => (): void => {
window.api.cloudWorkspaces = { ...window.api.cloudWorkspaces, available: async () => cloud }
resetCloudWorkspacesAvailabilityForFixtures()
}
// A healthy machine, so the shots show only Home's cloud area.
const healthy = {
preflightStatus: {
git: { installed: true },
gh: { installed: true, authenticated: true },
glab: { installed: true, authenticated: true }
},
refreshPreflightStatus: async () => {}
}
const home = (): React.JSX.Element => (
<div className="relative" style={{ height: 620 }}>
<Landing />
</div>
)
/** Cloud build, signed out, has projects: the sign-in card. */
export const cloudSignedOutWithProjects = defineFixture({
width: 760,
setup: build(true),
store: { ...healthy, repos: [repo], orcaProfileAuthStatus: signedOut },
render: home
})
/** Cloud build, the session ended: same card, "Sign in again". */
export const cloudSessionEndedWithProjects = defineFixture({
width: 760,
setup: build(true),
store: { ...healthy, repos: [repo], orcaProfileAuthStatus: sessionEnded },
render: home
})
/** Cloud build, signed out, no projects: unchanged (the welcome screen is V3-05, not built). */
export const cloudSignedOutNoProjects = defineFixture({
width: 760,
setup: build(true),
store: { ...healthy, repos: [], orcaProfileAuthStatus: signedOut },
render: home
})
/** Local build with projects: unchanged, no cloud card. */
export const localBuildWithProjects = defineFixture({
width: 760,
setup: build(false),
store: { ...healthy, repos: [repo], orcaProfileAuthStatus: localBuild },
render: home
})
/** Local build, no projects: unchanged. */
export const localBuildNoProjects = defineFixture({
width: 760,
setup: build(false),
store: { ...healthy, repos: [], orcaProfileAuthStatus: localBuild },
render: home
})
@@ -0,0 +1,26 @@
import { describe, expect, it } from 'vitest'
import type { OrcaProfileAuthStatus } from '../../../../shared/orca-profiles'
import { isSignedOutOfOrcaCloud } from './LandingCloudSignInCard'
const auth = (
configured: boolean,
state: OrcaProfileAuthStatus['state']
): OrcaProfileAuthStatus => ({ activeProfileId: 'default', configured, state, persistence: 'none' })
describe('isSignedOutOfOrcaCloud', () => {
it('holds in a cloud build when never signed in or the session ended', () => {
expect(isSignedOutOfOrcaCloud(true, auth(true, 'local'))).toBe(true)
expect(isSignedOutOfOrcaCloud(true, auth(true, 'reconnect-required'))).toBe(true)
})
it('does not hold when signed in', () => {
expect(isSignedOutOfOrcaCloud(true, auth(true, 'connected'))).toBe(false)
})
it('does not hold in a local build or before either answer arrives', () => {
expect(isSignedOutOfOrcaCloud(false, auth(true, 'local'))).toBe(false)
expect(isSignedOutOfOrcaCloud(null, auth(true, 'local'))).toBe(false)
expect(isSignedOutOfOrcaCloud(true, auth(false, 'unconfigured'))).toBe(false)
expect(isSignedOutOfOrcaCloud(true, null)).toBe(false)
})
})
@@ -0,0 +1,68 @@
// V3-08: signed out of Orca in a build that offers Orca Cloud, Home says where the team's cloud
// workspaces went and how to see them, in the place the cloud list would be. Local builds and
// signed-in people never see it. While it shows, it stands in for the trial card (one cloud card).
import React from 'react'
import { Cloud } from 'lucide-react'
import { useAppStore } from '@/store'
import { useTranslate } from '@/i18n/use-translate'
import { useCloudWorkspacesAvailable } from '@/components/cloud-workspaces/use-cloud-workspaces'
import type { OrcaProfileAuthStatus } from '../../../../shared/orca-profiles'
import { OrcaSignInButton, OrcaSignInWaitNotice } from '../OrcaSignInProgress'
/** Signed out of an Orca account this build can sign in to: never signed in, or the session ended. */
export function isSignedOutOfOrcaCloud(
cloudAvailable: boolean | null,
status: OrcaProfileAuthStatus | null | undefined
): boolean {
return (
cloudAvailable === true &&
status?.configured === true &&
(status.state === 'local' || status.state === 'reconnect-required')
)
}
export function useSignedOutOfOrcaCloud(): boolean {
const available = useCloudWorkspacesAvailable()
const status = useAppStore((s) => s.orcaProfileAuthStatus)
return isSignedOutOfOrcaCloud(available, status)
}
/** Render only when `useSignedOutOfOrcaCloud()` holds (Landing decides; fixtures render it directly). */
export function LandingCloudSignInCard({
reconnect
}: {
/** The session ended (`reconnect-required`), as opposed to never signed in. */
reconnect: boolean
}): React.JSX.Element {
const t = useTranslate()
return (
<section
aria-labelledby="landing-cloud-sign-in-title"
data-landing-cloud-sign-in="true"
className="w-full rounded-lg border border-border bg-card px-4 py-3 text-left"
>
<div className="flex items-start gap-3">
<Cloud className="mt-0.5 size-4 shrink-0 text-muted-foreground" aria-hidden />
<div className="min-w-0 flex-1">
<h2 id="landing-cloud-sign-in-title" className="text-sm font-medium text-foreground">
{t('components.landing.cloudSignIn.title', 'Cloud workspaces')}
</h2>
<p className="mt-0.5 text-xs text-muted-foreground">
{t(
'components.landing.cloudSignIn.body',
'Sign in to see your team’s cloud workspaces.'
)}
</p>
<div className="mt-3 flex flex-col items-start gap-2">
<OrcaSignInButton size="sm" variant="outline">
{reconnect
? t('components.landing.cloudSignIn.again', 'Sign in again')
: t('components.landing.cloudSignIn.action', 'Sign in')}
</OrcaSignInButton>
<OrcaSignInWaitNotice />
</div>
</div>
</div>
</section>
)
}
+6
View File
@@ -24311,6 +24311,12 @@
"planBody": "Agents keep running while your laptop sleeps.",
"trialBody": "Agents keep running while your laptop sleeps. No card needed.",
"try": "Try Orca Cloud"
},
"cloudSignIn": {
"title": "Cloud workspaces",
"body": "Sign in to see your team’s cloud workspaces.",
"again": "Sign in again",
"action": "Sign in"
}
},
"cloudWorkspaces": {