Skip to content
Merged
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
92 changes: 38 additions & 54 deletions desktop/src/features/onboarding/ui/CommunityOnboardingFlow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,15 @@ import { relayClient } from "@/shared/api/relayClient";
import type { AgentPersona } from "@/shared/api/types";
import { cn } from "@/shared/lib/cn";
import { Button } from "@/shared/ui/button";
import { Input } from "@/shared/ui/input";
import { MembershipDenied } from "./MembershipDenied";
import { StartupWindowDragRegion } from "@/shared/ui/StartupWindowDragRegion";
import {
ONBOARDING_PRIMARY_CTA_CLASS,
OnboardingChrome,
} from "./OnboardingChrome";
import { OnboardingFooter, OnboardingFooterProvider } from "./OnboardingFooter";
import {
ONBOARDING_KEY_FRAME_CLASS,
ONBOARDING_KEY_ROW_CLASS,
ONBOARDING_KEY_TEXT_CLASS,
} from "./NsecMaskedDisplay";
import { ONBOARDING_KEY_FRAME_CLASS } from "./NsecMaskedDisplay";

function isRelayMembershipDeniedError(error: unknown): boolean {
if (!(error instanceof Error)) return false;
Expand Down Expand Up @@ -92,20 +89,20 @@ function AvatarCircle({
>
{emojiAvatar ? (
<span
className="flex h-28 w-28 items-center justify-center overflow-hidden rounded-full text-5xl shadow-xs"
className="flex h-36 w-36 items-center justify-center overflow-hidden rounded-full text-5xl shadow-xs"
style={{ backgroundColor: emojiAvatar.color }}
>
{emojiAvatar.emoji}
</span>
) : hasAvatar ? (
<ProfileAvatar
avatarUrl={avatarUrl}
className="h-28 w-28 rounded-full text-3xl"
className="h-36 w-36 rounded-full text-4xl"
label={previewName}
/>
) : (
<span className="flex h-28 w-28 items-center justify-center rounded-full text-[var(--buzz-onboarding-backup-ink)] transition-colors group-hover:bg-white/25">
<Plus className="h-8 w-8" aria-hidden="true" />
<span className="flex h-36 w-36 items-center justify-center rounded-full bg-white/30 text-[var(--buzz-onboarding-backup-ink)] transition-colors group-hover:bg-white/40">
<Plus className="h-7 w-7" aria-hidden="true" />
</span>
)}
</button>
Expand Down Expand Up @@ -342,14 +339,11 @@ export function CommunityOnboardingFlow({
<div
className={cn(
"relative w-full text-center",
isProfileStage || isTeamStage
? "buzz-onboarding-step-frame flex flex-col justify-center"
: "flex min-h-dvh flex-col justify-center py-8",
isProfileStage
? "max-w-4xl"
? "buzz-onboarding-step-frame flex max-w-[500px] flex-col justify-center"
: isTeamStage
? "max-w-[760px]"
: "max-w-[560px]",
? "buzz-onboarding-step-frame flex max-w-[760px] flex-col justify-center"
: "flex min-h-dvh max-w-[560px] flex-col justify-center py-8",
)}
data-testid="community-onboarding-body"
>
Expand Down Expand Up @@ -417,46 +411,36 @@ export function CommunityOnboardingFlow({
Add a name and avatar. They’ll show up on your messages,
reactions, and agent handoffs.
</p>
<div className="mt-10 w-full max-w-4xl">
<div
className={ONBOARDING_KEY_FRAME_CLASS}
data-testid="community-profile-key-frame"
<div className="mt-8 flex w-full flex-col items-center">
<AvatarCircle
avatarUrl={avatarUrl}
onClick={() => setIsAvatarEditorOpen(true)}
previewName={displayName.trim() || "Your profile"}
/>
<label
className="mt-7 block w-full max-w-[412px] text-left"
htmlFor="community-display-name"
>
<div className={ONBOARDING_KEY_ROW_CLASS}>
<AvatarCircle
avatarUrl={avatarUrl}
onClick={() => setIsAvatarEditorOpen(true)}
previewName={displayName.trim() || "Your profile"}
/>
<label
className="min-w-0 flex-1"
htmlFor="community-display-name"
>
<span className="sr-only">Your name</span>
<input
aria-label="Community display name"
autoCapitalize="words"
autoComplete="name"
autoCorrect="off"
className={cn(
ONBOARDING_KEY_TEXT_CLASS,
"border-0 bg-transparent p-0 shadow-none outline-none placeholder:text-[var(--buzz-onboarding-backup-ink)] placeholder:opacity-40 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
)}
data-testid="community-profile-name-key"
disabled={isPending || isUploadingAvatar}
id="community-display-name"
onChange={(event) =>
setDisplayName(event.target.value)
}
placeholder="First and last name"
ref={nameInputRef}
spellCheck={false}
type="text"
value={displayName}
/>
</label>
</div>
</div>
<span className="mb-2 block pl-4 text-sm text-foreground">
Your name
</span>
<Input
aria-label="Community display name"
autoCapitalize="words"
autoComplete="name"
autoCorrect="off"
className="h-14 rounded-2xl border-[color:rgb(113_113_6_/_0.28)] bg-white/95 px-5 text-sm shadow-none placeholder:text-muted-foreground/60 focus-visible:ring-1 focus-visible:ring-[var(--buzz-onboarding-backup-ink)] md:text-sm"
data-testid="community-profile-name-key"
disabled={isPending || isUploadingAvatar}
id="community-display-name"
onChange={(event) => setDisplayName(event.target.value)}
placeholder="First and last name"
ref={nameInputRef}
spellCheck={false}
type="text"
value={displayName}
/>
</label>
</div>
{transaction.error ? (
<p className="mt-4 text-sm text-destructive">
Expand Down
45 changes: 16 additions & 29 deletions desktop/tests/e2e/onboarding.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -997,43 +997,30 @@ test("connected first-community profile step cannot discard resumable onboarding
const centeredInUsableLaneY =
chromeOffset + (viewport.height - chromeOffset - footerOffset) / 2;
expect(Math.abs(profileMainCenterY - centeredInUsableLaneY)).toBeLessThan(32);
const keyFrame = page.getByTestId("community-profile-key-frame");
const nameKey = page.getByTestId("community-profile-name-key");
await expect(keyFrame).toBeVisible();
const avatarButton = page.getByTestId("community-avatar-open");
await expect(nameKey).toBeVisible();
const keyFrameBox = await keyFrame.boundingBox();
expect(keyFrameBox?.width).toBeGreaterThan(700);
const keyFrameStyles = await keyFrame.evaluate((element) => {
await expect(avatarButton).toBeVisible();
const nameKeyBox = await nameKey.boundingBox();
const avatarButtonBox = await avatarButton.boundingBox();
expect(nameKeyBox?.width).toBeGreaterThan(380);
expect(avatarButtonBox?.width).toBe(144);
const nameKeyStyles = await nameKey.evaluate((element) => {
const styles = window.getComputedStyle(element);
return {
backgroundColor: styles.backgroundColor,
borderRadius: styles.borderRadius,
fontSize: styles.fontSize,
};
});
expect(keyFrameStyles.backgroundColor).toMatch(/(0\.5\)|\/ 0\.5\))/);
expect(keyFrameStyles.borderRadius).toBe("12px");
await expect
.poll(() =>
nameKey.evaluate((element) => {
const styles = window.getComputedStyle(element);
return {
color: styles.color,
fontFamily: styles.fontFamily,
fontSize: styles.fontSize,
};
}),
)
.toMatchObject({
color: "rgb(113, 113, 6)",
fontSize: "36px",
});
expect(
(
await nameKey.evaluate((element) =>
window.getComputedStyle(element).fontFamily.toLowerCase(),
)
).includes("mono"),
).toBe(true);
expect(nameKeyStyles.backgroundColor).toMatch(
/^(rgba\(255, 255, 255, 0\.95\)|oklab\(.+ \/ 0\.95\))$/,
);
expect(nameKeyStyles).toMatchObject({
borderRadius: "16px",
fontSize: "14px",
});
await expect(page.getByText("Your name", { exact: true })).toBeVisible();
await expect(page.getByTestId("community-profile-next")).toHaveText("Next");
await expect(page.getByTestId("community-profile-next")).toBeDisabled();
await expect(page.getByTestId("community-profile-back")).toHaveCount(0);
Expand Down
Loading