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
104 changes: 104 additions & 0 deletions static/app/components/animatedSentryLogo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import {useId} from 'react';
import {useTheme} from '@emotion/react';

interface AnimatedSentryLogoProps {
/**
* Draw progress from 0 (no stroke) to 1 (fully drawn).
* Uses the stroke-dashoffset technique from the app loading screen.
*/
progress: number;
className?: string;
size?: number;
}

const DASH_TOTAL = 1150;

// Filled glyph (same path as index.ejs base + mask)
const SENTRY_GLYPH =
'M223.687 85.56a28.02 28.02 0 0 0-24-13.559 28.02 28.02 0 0 0-24 13.559l-39.48 67.62a193.26 193.26 0 0 1 106.5 159.96h-27.72a166.07 166.07 0 0 0-92.76-136.32L85.687 240a95.52 95.52 0 0 1 55.38 73.02h-63.66a4.55 4.55 0 0 1-3.659-2.325 4.56 4.56 0 0 1-.06-4.335l17.64-30a64.4 64.4 0 0 0-20.16-11.4l-17.46 30a27.24 27.24 0 0 0 2.023 30.435 27.2 27.2 0 0 0 8.116 7.005 27.96 27.96 0 0 0 13.56 3.6h87.18a116.41 116.41 0 0 0-48-103.86l13.86-24a143.22 143.22 0 0 1 61.8 127.86h73.86a215.28 215.28 0 0 0-98.46-190.8l28.02-48a4.62 4.62 0 0 1 6.3-1.62c3.18 1.74 121.74 208.62 123.96 211.02a4.562 4.562 0 0 1-4.08 6.78h-28.56q.54 11.46 0 22.86h28.68a27.54 27.54 0 0 0 27.72-27.66 26.94 26.94 0 0 0-3.72-13.68z';

// Stroke paths that trace the logo outline (one group from index.ejs)
const STROKE_PATHS = [
'm82.8 253.5-12.7 22-12.5 21.2-.2.3v.1l-.2.2v.3a24 24 0 00.3 22.4l.3.6A24 24 0 0076.4 332h87l-3.2-21.8A115 115 0 00112 233l17.6-30.4a147 147 0 0166.8 112l1.3 17.5h65.6l-1.3-20.2a212 212 0 00-99.3-165.8l29.6-50.8a9 9 0 013.3-3.1 9 9 0 0112.1 3.1l122.2 209.2h.1a8 8 0 011 4v.2a8.5 8.5 0 01-8.6 8.5h-38.9',
'M92.5 268.7 88 276.6 71.4 305v.1a8 8 0 000 7.2l.2.4a8 8 0 006 3.4h67.3l-.6-3.4A99 99 0 0097.2 242.5l-6.8-4 33.4-57.8 6.9 4a163 163 0 0181.6 128.8l.2 2.7h33.7L246 313A196 196 0 00147.8 155.7l-7-4L178.6 87v-.1a25 25 0 0133-9.1l.8.3a25 25 0 019 8.9h.1v.2L343.7 296.4a24 24 0 013.3 12v.4a24.5 24.5 0 01-24.6 24.4H291.5',
'M85.7 264.5l-8.7 15-12.5 21.3-.1.2-.1.3A15.6 15.6 0 0076.9 324h77.2l-1.8-12.6a107 107 0 00-51-75.8l25.5-44.1A155 155 0 01204.3 314l.8 10.1h49.7l-.8-11.7A204 204 0 00151.8 148.9l33.6-57.7a17 17 0 0129.2 0L336.8 300.4a16 16 0 012.2 8.2 16.5 16.5 0 01-16.6 16.6H291.5',
];

/**
* Sentry brand mark with stroke-draw progress, adapted from the app
* loading animation in static/index.ejs. Single color, progress-driven
* instead of the looping multicolor original.
*/
export function AnimatedSentryLogo({
progress,
size = 72,
className,
}: AnimatedSentryLogoProps) {
const theme = useTheme();
const id = useId();
const gooId = `goo-layer-${id}`;
const maskId = `mask-layer-${id}`;

const dashOffset = DASH_TOTAL * (1 - progress);

return (
<svg
width={size}
height={size}
viewBox="0 0 400 400"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={className}
role="img"
aria-label="Sentry"
>
<defs>
<filter id={gooId}>
<feGaussianBlur in="SourceGraphic" stdDeviation="8" result="blur" />
<feColorMatrix
in="blur"
type="matrix"
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 36 -4"
result="goo"
/>
<feComposite in="SourceGraphic" in2="goo" operator="atop" />
</filter>
</defs>

<mask
id={maskId}
width="300"
height="265"
x="50"
y="72"
maskUnits="userSpaceOnUse"
style={{maskType: 'alpha'}}
>
<path fill={theme.tokens.content.accent} d={SENTRY_GLYPH} />
</mask>

{/* Muted base shape */}
<path fill={theme.tokens.graphics.neutral.muted} d={SENTRY_GLYPH} />

{/* Stroke-draw overlay, masked to the logo shape */}
<g
mask={`url(#${maskId})`}
filter={`url(#${gooId})`}
stroke={theme.tokens.content.accent}
strokeWidth="5"
strokeLinecap="round"
strokeLinejoin="round"
>
{STROKE_PATHS.map((d, i) => (
<path
key={i}
d={d}
strokeDasharray={DASH_TOTAL}
strokeDashoffset={dashOffset}
style={{transition: 'stroke-dashoffset 600ms ease-out'}}
/>
))}
</g>
</svg>
);
}
32 changes: 28 additions & 4 deletions static/app/views/onboarding/onboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import styled from '@emotion/styled';
import {AnimatePresence, motion} from 'framer-motion';

import {Button} from '@sentry/scraps/button';
import {Stack} from '@sentry/scraps/layout';
import {Container, Stack} from '@sentry/scraps/layout';
import {Link} from '@sentry/scraps/link';

import {AnimatedSentryLogo} from 'sentry/components/animatedSentryLogo';
import Hook from 'sentry/components/hook';
import {LogoSentry} from 'sentry/components/logoSentry';
import {
Expand Down Expand Up @@ -101,6 +102,16 @@ const scmOnboardingSteps: StepDescriptor[] = [
},
];

/**
* The SCM steps that display the animated logo progress indicator.
* Order determines the progress level (first = 0, last = 1).
*/
const SCM_LOGO_STEPS = [
OnboardingStepId.SCM_CONNECT,
OnboardingStepId.SCM_PLATFORM_FEATURES,
OnboardingStepId.SCM_PROJECT_DETAILS,
];

function WelcomeVariable(props: StepProps) {
const hasNewWelcomeUI = useHasNewWelcomeUI();

Expand All @@ -117,7 +128,9 @@ interface ContainerVariableProps {

function ContainerVariable(props: PropsWithChildren<ContainerVariableProps>) {
const newWelcomeUIStep = props.hasNewWelcomeUI && props.id === OnboardingStepId.WELCOME;
const Component = newWelcomeUIStep ? ContainerNewWelcomeUI : Container;
const Component = newWelcomeUIStep
? OnboardingContainerNewWelcomeUI
: OnboardingContainer;

return (
<Component hasFooter={props.hasFooter || newWelcomeUIStep}>
Expand Down Expand Up @@ -302,6 +315,12 @@ export function OnboardingWithoutContext() {
);
};

const scmLogoIndex = stepObj ? SCM_LOGO_STEPS.indexOf(stepObj.id) : -1;
const scmLogoProgress =
scmLogoIndex >= 0 && SCM_LOGO_STEPS.length > 1
? scmLogoIndex / (SCM_LOGO_STEPS.length - 1)
: null;

// Redirect to the first step if we end up in an invalid state
const isInvalidDocsStep = stepId === 'setup-docs' && !projectSlug;
if (!stepObj || stepIndex === -1 || isInvalidDocsStep) {
Expand Down Expand Up @@ -372,6 +391,11 @@ export function OnboardingWithoutContext() {
</Button>
</BackMotionDiv>
)}
{scmLogoProgress !== null && (
<Container alignSelf="center">
<AnimatedSentryLogo progress={scmLogoProgress} />
</Container>
)}
<AnimatePresence mode="wait" onExitComplete={updateAnimationState}>
<OnboardingStepVariable id={stepObj.id} hasNewWelcomeUI={hasNewWelcomeUI}>
{stepObj.Component && (
Expand Down Expand Up @@ -402,7 +426,7 @@ function Onboarding() {
);
}

const ContainerNewWelcomeUI = styled('div')<{hasFooter: boolean}>`
const OnboardingContainerNewWelcomeUI = styled('div')<{hasFooter: boolean}>`
flex-grow: 1;
display: flex;
flex-direction: column;
Expand All @@ -421,7 +445,7 @@ const ContainerNewWelcomeUI = styled('div')<{hasFooter: boolean}>`
}
`;

const Container = styled('div')<{hasFooter: boolean}>`
const OnboardingContainer = styled('div')<{hasFooter: boolean}>`
flex-grow: 1;
display: flex;
flex-direction: column;
Expand Down
Loading