refactor(core): auth ui (#10101)

close AF-2202 AF-2203
This commit is contained in:
forehalo
2025-02-12 07:39:09 +00:00
parent 573af16aa4
commit 9dcce43360
24 changed files with 458 additions and 304 deletions
@@ -0,0 +1,7 @@
import type { PropsWithChildren } from 'react';
import { authContainer } from './share.css';
export function AuthContainer(props: PropsWithChildren) {
return <div className={authContainer}>{props.children}</div>;
}
@@ -0,0 +1,7 @@
import type { PropsWithChildren } from 'react';
import { authFooter } from './share.css';
export function AuthFooter(props: PropsWithChildren) {
return <div className={authFooter}>{props.children}</div>;
}
@@ -1,15 +1,15 @@
import { Logo1Icon } from '@blocksuite/icons/rc';
import type { FC } from 'react';
import { modalHeaderWrapper } from './share.css';
export const ModalHeader: FC<{
import { authHeaderWrapper } from './share.css';
export const AuthHeader: FC<{
title: string;
subTitle?: string;
}> = ({ title, subTitle }) => {
return (
<div className={modalHeaderWrapper}>
<div className={authHeaderWrapper}>
<p>
{title === 'AFFiNE Cloud' && <Logo1Icon className="logo" />}
<Logo1Icon className="logo" />
{title}
</p>
<p>{subTitle}</p>
@@ -8,14 +8,12 @@ export type AuthInputProps = InputProps & {
label?: string;
error?: boolean;
errorHint?: ReactNode;
withoutHint?: boolean;
onEnter?: () => void;
};
export const AuthInput = ({
label,
error,
errorHint,
withoutHint = false,
onEnter,
className,
...inputProps
@@ -23,7 +21,7 @@ export const AuthInput = ({
return (
<div
className={clsx(styles.authInputWrapper, {
'without-hint': withoutHint,
'with-hint': !!errorHint,
})}
>
{label ? <label>{label}</label> : null}
@@ -34,14 +32,8 @@ export const AuthInput = ({
onEnter={onEnter}
{...inputProps}
/>
{error && errorHint && !withoutHint ? (
<div
className={clsx(styles.formHint, {
error: error,
})}
>
{errorHint}
</div>
{error && errorHint ? (
<div className={styles.authInputError}>{errorHint}</div>
) : null}
</div>
);
@@ -1,6 +1,5 @@
import { useI18n } from '@affine/i18n';
import { ArrowLeftSmallIcon } from '@blocksuite/icons/rc';
import { cssVar } from '@toeverything/theme';
import type { FC } from 'react';
import type { ButtonProps } from '../../ui/button';
@@ -12,11 +11,7 @@ export const BackButton: FC<ButtonProps> = props => {
<Button
variant="plain"
style={{
marginTop: 12,
marginLeft: -5,
paddingLeft: 0,
paddingRight: 5,
color: cssVar('textSecondaryColor'),
padding: '2px 8px 2px 0',
}}
prefix={<ArrowLeftSmallIcon />}
{...props}
@@ -1,10 +1,12 @@
export * from './auth-container';
export * from './auth-content';
export * from './auth-footer';
export * from './auth-header';
export * from './auth-input';
export * from './auth-page-container';
export * from './back-button';
export * from './change-email-page';
export * from './change-password-page';
export * from './modal-header';
export * from './onboarding-page';
export * from './password-input';
export * from './set-password-page';
@@ -1,14 +1,25 @@
import { cssVar } from '@toeverything/theme';
import { globalStyle, keyframes, style } from '@vanilla-extract/css';
export const modalHeaderWrapper = style({});
globalStyle(`${modalHeaderWrapper} .logo`, {
import { globalStyle, style } from '@vanilla-extract/css';
export const authContainer = style({
display: 'flex',
flexDirection: 'column',
height: '100%',
width: '100%',
minHeight: '422px',
});
export const authHeaderWrapper = style({
marginBottom: '20px',
});
globalStyle(`${authHeaderWrapper} .logo`, {
fontSize: cssVar('fontH3'),
fontWeight: 600,
color: cssVar('black'),
marginRight: '6px',
verticalAlign: 'middle',
});
globalStyle(`${modalHeaderWrapper} > p:first-of-type`, {
globalStyle(`${authHeaderWrapper} > p:first-of-type`, {
fontSize: cssVar('fontH5'),
fontWeight: 600,
marginBottom: '4px',
@@ -16,20 +27,37 @@ globalStyle(`${modalHeaderWrapper} > p:first-of-type`, {
display: 'flex',
alignItems: 'center',
});
globalStyle(`${modalHeaderWrapper} > p:last-of-type`, {
globalStyle(`${authHeaderWrapper} > p:last-of-type`, {
fontSize: cssVar('fontH4'),
fontWeight: 600,
lineHeight: '28px',
});
export const authContent = style({
fontSize: cssVar('fontBase'),
lineHeight: cssVar('fontH3'),
flexGrow: 1,
});
globalStyle(`${authContent} > *:not(:last-child)`, {
marginBottom: '20px',
});
export const authInputWrapper = style({
paddingBottom: '30px',
position: 'relative',
selectors: {
'&.without-hint': {
paddingBottom: '20px',
'&.with-hint': {
marginBottom: '8px',
},
},
});
export const authFooter = style({});
globalStyle(`${authFooter} > *:not(:last-child)`, {
marginBottom: '20px',
});
globalStyle(`${authInputWrapper} label`, {
display: 'block',
color: cssVar('textSecondaryColor'),
@@ -38,109 +66,17 @@ globalStyle(`${authInputWrapper} label`, {
fontWeight: 600,
lineHeight: '22px',
});
export const formHint = style({
fontSize: cssVar('fontSm'),
position: 'absolute',
bottom: '4px',
height: '22px',
left: 0,
lineHeight: '22px',
selectors: {
'&.error': {
color: cssVar('errorColor'),
},
'&.warning': {
color: cssVar('warningColor'),
},
},
});
const rotate = keyframes({
'0%': {
transform: 'rotate(0deg)',
},
'50%': {
transform: 'rotate(180deg)',
},
'100%': {
transform: 'rotate(360deg)',
},
});
export const loading = style({
width: '15px',
height: '15px',
position: 'relative',
borderRadius: '50%',
overflow: 'hidden',
backgroundColor: cssVar('borderColor'),
selectors: {
'&::after': {
content: '""',
width: '12px',
height: '12px',
position: 'absolute',
left: '0',
right: '0',
top: '0',
bottom: '0',
margin: 'auto',
backgroundColor: '#fff',
zIndex: 2,
borderRadius: '50%',
},
'&::before': {
content: '""',
width: '20px',
height: '20px',
backgroundColor: cssVar('blue'),
position: 'absolute',
left: '50%',
bottom: '50%',
zIndex: '1',
transformOrigin: 'left bottom',
animation: `${rotate} 1.5s infinite linear`,
},
},
});
export const authContent = style({
fontSize: cssVar('fontBase'),
lineHeight: cssVar('fontH3'),
marginTop: '30px',
export const authInputError = style({
color: cssVar('errorColor'),
fontSize: cssVar('fontXs'),
lineHeight: '20px',
});
globalStyle(`${authContent} a`, {
color: cssVar('linkColor'),
});
export const authCodeContainer = style({
paddingBottom: '40px',
position: 'relative',
});
export const authCodeWrapper = style({
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
});
export const authCodeErrorMessage = style({
color: cssVar('errorColor'),
fontSize: cssVar('fontSm'),
textAlign: 'center',
lineHeight: '1.5',
position: 'absolute',
left: 0,
right: 0,
bottom: 5,
margin: 'auto',
});
export const resendButtonWrapper = style({
height: 32,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
marginTop: 30,
});
globalStyle(`${resendButtonWrapper} .resend-code-hint`, {
fontWeight: 600,
fontSize: cssVar('fontSm'),
marginRight: 8,
});
export const authPageContainer = style({
height: '100vh',
width: '100vw',
+5
View File
@@ -7,3 +7,8 @@ declare module '*.png' {
const src: string;
export default src;
}
declare module '*.inline.svg' {
const src: string;
export default src;
}
@@ -96,7 +96,7 @@ function OAuthProvider({
variant="primary"
block
size="extraLarge"
style={{ marginTop: 30, width: '100%' }}
style={{ width: '100%' }}
prefix={icon}
onClick={onClick}
>
@@ -1,8 +1,10 @@
import { Button } from '@affine/component';
import {
AuthContainer,
AuthContent,
AuthFooter,
AuthHeader,
AuthInput,
ModalHeader,
} from '@affine/component/auth-components';
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import { ServersService } from '@affine/core/modules/cloud';
@@ -18,6 +20,7 @@ import {
} from 'react';
import type { SignInState } from '.';
import { Back } from './back';
import * as styles from './style.css';
function normalizeURL(url: string) {
@@ -85,8 +88,8 @@ export const AddSelfhostedStep = ({
}, [changeState, onConnect, serversService, state]);
return (
<>
<ModalHeader title={t['com.affine.auth.sign.add-selfhosted']()} />
<AuthContainer>
<AuthHeader title={t['com.affine.auth.sign.add-selfhosted']()} />
<AuthContent>
<AuthInput
label={t['com.affine.auth.sign.add-selfhosted.baseurl']()}
@@ -109,6 +112,8 @@ export const AddSelfhostedStep = ({
>
{t['com.affine.auth.sign.add-selfhosted.connect-button']()}
</Button>
</AuthContent>
<AuthFooter>
<div className={styles.authMessage}>
<Trans
i18nKey="com.affine.auth.sign.add-selfhosted.description"
@@ -123,7 +128,8 @@ export const AddSelfhostedStep = ({
}}
/>
</div>
</AuthContent>
</>
<Back changeState={changeState} />
</AuthFooter>
</AuthContainer>
);
};
@@ -0,0 +1,18 @@
import { BackButton } from '@affine/component/auth-components';
import { type Dispatch, type SetStateAction, useCallback } from 'react';
import type { SignInState } from '.';
interface BackButtonProps {
changeState: Dispatch<SetStateAction<SignInState>>;
}
export function Back({ changeState }: BackButtonProps) {
const onClick = useCallback(() => {
changeState(prev => ({
...prev,
step: 'signIn',
}));
}, [changeState]);
return <BackButton onClick={onClick} />;
}
@@ -0,0 +1,86 @@
<svg width="1440" height="688" viewBox="0 0 1440 688" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_dddd_282_28809)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.0802 44.9971C20.7502 20.9654 43.3011 4.47327 67.4491 8.161C87.0177 11.1494 101.621 26.4661 104.512 44.9533C126.65 50.2411 141.407 71.6324 137.926 94.428C134.256 118.46 111.705 134.952 87.5571 131.264L19.1197 120.813C-0.828645 117.766 -14.5423 99.2033 -11.5106 79.351C-9.18002 64.0901 2.36157 52.5188 16.6332 49.358C16.7092 47.9115 16.8573 46.4565 17.0802 44.9971Z" fill="#2F2F2F"/>
</g>
<g filter="url(#filter1_dddd_282_28809)">
<path d="M32.74 506.175C30.1151 497.784 34.695 488.573 42.9694 485.601L122.811 456.929C131.086 453.957 139.922 458.351 142.546 466.742L155.126 506.953L45.3194 546.386L32.74 506.175Z" fill="#2F2F2F"/>
<path d="M95.8489 589.87L99.2426 581.423L97.7835 566.35L71.2146 575.891C62.9401 578.862 54.1045 574.469 51.4796 566.078L47.4593 553.227L157.266 513.794L161.286 526.645C163.911 535.036 159.331 544.247 151.057 547.218L124.487 556.76L131.801 569.731L139.261 574.28L95.8489 589.87Z" fill="#2F2F2F"/>
</g>
<g filter="url(#filter2_dddd_282_28809)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1330.37 392.113C1311.04 386.409 1290.74 397.458 1285.04 416.791L1278.67 438.374L1270.9 436.08C1262.49 433.6 1253.66 438.403 1251.18 446.809L1235.49 500.002C1233.01 508.408 1237.81 517.233 1246.22 519.713L1331.79 544.959C1340.19 547.439 1349.02 542.635 1351.5 534.229L1367.19 481.036C1369.67 472.63 1364.87 463.805 1356.46 461.325L1348.68 459.031L1355.05 437.448C1360.76 418.114 1349.71 397.817 1330.37 392.113ZM1338.03 455.886L1344.39 434.303C1348.36 420.857 1340.68 406.74 1327.23 402.773C1313.78 398.806 1299.67 406.49 1295.7 419.936L1289.33 441.519L1338.03 455.886Z" fill="#2F2F2F"/>
</g>
<defs>
<filter id="filter0_dddd_282_28809" x="-17.9304" y="0.645508" width="245.365" height="209.134" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="4" dy="3"/>
<feGaussianBlur stdDeviation="5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_282_28809"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="15" dy="12"/>
<feGaussianBlur stdDeviation="9.5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.09 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_282_28809" result="effect2_dropShadow_282_28809"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="33" dy="27"/>
<feGaussianBlur stdDeviation="13"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="effect2_dropShadow_282_28809" result="effect3_dropShadow_282_28809"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="59" dy="48"/>
<feGaussianBlur stdDeviation="15"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.01 0"/>
<feBlend mode="normal" in2="effect3_dropShadow_282_28809" result="effect4_dropShadow_282_28809"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect4_dropShadow_282_28809" result="shape"/>
</filter>
<filter id="filter1_dddd_282_28809" x="24" y="448" width="240.026" height="239.87" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="4" dy="4"/>
<feGaussianBlur stdDeviation="6"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_282_28809"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="16" dy="15"/>
<feGaussianBlur stdDeviation="11.5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.09 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_282_28809" result="effect2_dropShadow_282_28809"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="37" dy="35"/>
<feGaussianBlur stdDeviation="15"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="effect2_dropShadow_282_28809" result="effect3_dropShadow_282_28809"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="66" dy="62"/>
<feGaussianBlur stdDeviation="18"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.01 0"/>
<feBlend mode="normal" in2="effect3_dropShadow_282_28809" result="effect4_dropShadow_282_28809"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect4_dropShadow_282_28809" result="shape"/>
</filter>
<filter id="filter2_dddd_282_28809" x="1226.84" y="383.612" width="222.007" height="255.999" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="3" dy="4"/>
<feGaussianBlur stdDeviation="5.5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_282_28809"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="12" dy="15"/>
<feGaussianBlur stdDeviation="10"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.09 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_282_28809" result="effect2_dropShadow_282_28809"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="28" dy="35"/>
<feGaussianBlur stdDeviation="13.5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="effect2_dropShadow_282_28809" result="effect3_dropShadow_282_28809"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="49" dy="62"/>
<feGaussianBlur stdDeviation="16"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.01 0"/>
<feBlend mode="normal" in2="effect3_dropShadow_282_28809" result="effect4_dropShadow_282_28809"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect4_dropShadow_282_28809" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.0 KiB

@@ -0,0 +1,86 @@
<svg width="1440" height="688" viewBox="0 0 1440 688" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_dddd_282_28809)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.0802 44.9971C20.7502 20.9654 43.3011 4.47327 67.4491 8.161C87.0177 11.1494 101.621 26.4661 104.512 44.9533C126.65 50.2411 141.407 71.6324 137.926 94.428C134.256 118.46 111.705 134.952 87.5571 131.264L19.1197 120.813C-0.828645 117.766 -14.5423 99.2033 -11.5106 79.351C-9.18002 64.0901 2.36157 52.5188 16.6332 49.358C16.7092 47.9115 16.8573 46.4565 17.0802 44.9971Z" fill="#FFFFFF"/>
</g>
<g filter="url(#filter1_dddd_282_28809)">
<path d="M32.74 506.175C30.1151 497.784 34.695 488.573 42.9694 485.601L122.811 456.929C131.086 453.957 139.922 458.351 142.546 466.742L155.126 506.953L45.3194 546.386L32.74 506.175Z" fill="#FFFFFF"/>
<path d="M95.8489 589.87L99.2426 581.423L97.7835 566.35L71.2146 575.891C62.9401 578.862 54.1045 574.469 51.4796 566.078L47.4593 553.227L157.266 513.794L161.286 526.645C163.911 535.036 159.331 544.247 151.057 547.218L124.487 556.76L131.801 569.731L139.261 574.28L95.8489 589.87Z" fill="#FFFFFF"/>
</g>
<g filter="url(#filter2_dddd_282_28809)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1330.37 392.113C1311.04 386.409 1290.74 397.458 1285.04 416.791L1278.67 438.374L1270.9 436.08C1262.49 433.6 1253.66 438.403 1251.18 446.809L1235.49 500.002C1233.01 508.408 1237.81 517.233 1246.22 519.713L1331.79 544.959C1340.19 547.439 1349.02 542.635 1351.5 534.229L1367.19 481.036C1369.67 472.63 1364.87 463.805 1356.46 461.325L1348.68 459.031L1355.05 437.448C1360.76 418.114 1349.71 397.817 1330.37 392.113ZM1338.03 455.886L1344.39 434.303C1348.36 420.857 1340.68 406.74 1327.23 402.773C1313.78 398.806 1299.67 406.49 1295.7 419.936L1289.33 441.519L1338.03 455.886Z" fill="#FFFFFF"/>
</g>
<defs>
<filter id="filter0_dddd_282_28809" x="-17.9304" y="0.645508" width="245.365" height="209.134" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="4" dy="3"/>
<feGaussianBlur stdDeviation="5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_282_28809"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="15" dy="12"/>
<feGaussianBlur stdDeviation="9.5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.09 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_282_28809" result="effect2_dropShadow_282_28809"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="33" dy="27"/>
<feGaussianBlur stdDeviation="13"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="effect2_dropShadow_282_28809" result="effect3_dropShadow_282_28809"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="59" dy="48"/>
<feGaussianBlur stdDeviation="15"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.01 0"/>
<feBlend mode="normal" in2="effect3_dropShadow_282_28809" result="effect4_dropShadow_282_28809"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect4_dropShadow_282_28809" result="shape"/>
</filter>
<filter id="filter1_dddd_282_28809" x="24" y="448" width="240.026" height="239.87" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="4" dy="4"/>
<feGaussianBlur stdDeviation="6"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_282_28809"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="16" dy="15"/>
<feGaussianBlur stdDeviation="11.5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.09 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_282_28809" result="effect2_dropShadow_282_28809"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="37" dy="35"/>
<feGaussianBlur stdDeviation="15"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="effect2_dropShadow_282_28809" result="effect3_dropShadow_282_28809"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="66" dy="62"/>
<feGaussianBlur stdDeviation="18"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.01 0"/>
<feBlend mode="normal" in2="effect3_dropShadow_282_28809" result="effect4_dropShadow_282_28809"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect4_dropShadow_282_28809" result="shape"/>
</filter>
<filter id="filter2_dddd_282_28809" x="1226.84" y="383.612" width="222.007" height="255.999" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="3" dy="4"/>
<feGaussianBlur stdDeviation="5.5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_282_28809"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="12" dy="15"/>
<feGaussianBlur stdDeviation="10"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.09 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_282_28809" result="effect2_dropShadow_282_28809"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="28" dy="35"/>
<feGaussianBlur stdDeviation="13.5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="effect2_dropShadow_282_28809" result="effect3_dropShadow_282_28809"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="49" dy="62"/>
<feGaussianBlur stdDeviation="16"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0 0.2 0 0 0 0.01 0"/>
<feBlend mode="normal" in2="effect3_dropShadow_282_28809" result="effect4_dropShadow_282_28809"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect4_dropShadow_282_28809" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.0 KiB

@@ -0,0 +1,18 @@
import { useTheme } from 'next-themes';
import darkArt from './art-dark.inline.svg';
import lightArt from './art-light.inline.svg';
import { arts, wrapper } from './style.css';
export function SignInBackgroundArts() {
const { resolvedTheme } = useTheme();
return (
<div className={wrapper}>
<img
src={resolvedTheme === 'dark' ? darkArt : lightArt}
className={arts}
/>
</div>
);
}
@@ -0,0 +1,14 @@
import { style } from '@vanilla-extract/css';
export const wrapper = style({
position: 'relative',
top: 0,
left: 0,
right: 0,
bottom: 0,
});
export const arts = style({
position: 'absolute',
top: '128px',
});
@@ -19,6 +19,7 @@ export interface SignInState {
server?: Server;
initialServerBaseUrl?: string;
email?: string;
hasPassword?: boolean;
redirectUrl?: string;
}
@@ -1,9 +1,10 @@
import { notify } from '@affine/component';
import {
AuthContainer,
AuthContent,
AuthFooter,
AuthHeader,
AuthInput,
BackButton,
ModalHeader,
} from '@affine/component/auth-components';
import { Button } from '@affine/component/ui/button';
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
@@ -26,6 +27,7 @@ import {
} from 'react';
import type { SignInState } from '.';
import { Back } from './back';
import { Captcha } from './captcha';
import * as style from './style.css';
@@ -123,10 +125,6 @@ export const SignInWithEmailStep = ({
changeState(prev => ({ ...prev, step: 'signInWithPassword' }));
}, [changeState]);
const onBackBottomClick = useCallback(() => {
changeState(prev => ({ ...prev, step: 'signIn' }));
}, [changeState]);
const onOtpChanged = useCallback((value: string) => {
setOtp(value);
setOtpError(undefined);
@@ -158,54 +156,55 @@ export const SignInWithEmailStep = ({
return !verifyToken && needCaptcha ? (
<>
<ModalHeader title={t['com.affine.auth.sign.in']()} />
<AuthHeader title={t['com.affine.auth.sign.in']()} />
<AuthContent style={{ height: 100 }}>
<Captcha />
</AuthContent>
<BackButton onClick={onBackBottomClick} />
<Back changeState={changeState} />
</>
) : (
<>
<ModalHeader
<AuthContainer>
<AuthHeader
title={t['com.affine.auth.sign.in']()}
subTitle={t['com.affine.auth.sign.in.sent.email.subtitle']()}
/>
<AuthContent style={{ height: 100 }}>
<Trans
i18nKey="com.affine.auth.sign.auth.code.hint"
values={{ email }}
components={{ a: <a href={`mailto:${email}`} /> }}
<AuthContent>
<p>
<Trans
i18nKey="com.affine.auth.sign.auth.code.hint"
values={{ email }}
components={{ a: <a href={`mailto:${email}`} /> }}
/>
</p>
<AuthInput
placeholder={t['com.affine.auth.sign.auth.code']()}
onChange={onOtpChanged}
error={!!otpError}
errorHint={otpError}
onEnter={onContinue}
type="text"
required={true}
maxLength={6}
/>
</AuthContent>
<AuthInput
placeholder={t['com.affine.auth.sign.auth.code']()}
onChange={onOtpChanged}
error={!!otpError}
errorHint={otpError}
onEnter={onContinue}
type="text"
required={true}
maxLength={6}
/>
<Button
style={{ width: '100%' }}
data-testid="continue-code-button"
size="extraLarge"
block={true}
onClick={onContinue}
disabled={!!otpError || isVerifying}
loading={isVerifying}
>
{t['com.affine.auth.sign.auth.code.continue']()}
</Button>
<Button
style={{ width: '100%' }}
data-testid="continue-code-button"
size="extraLarge"
block={true}
onClick={onContinue}
disabled={!!otpError || isVerifying}
loading={isVerifying}
>
{t['com.affine.auth.sign.auth.code.continue']()}
</Button>
<div className={style.resendButtonWrapper}>
<Button
disabled={resendCountDown > 0}
variant="plain"
onClick={sendEmail}
style={{ padding: '4px' }}
>
{resendCountDown <= 0 ? (
t['com.affine.auth.sign.auth.code.resend']()
@@ -216,26 +215,30 @@ export const SignInWithEmailStep = ({
/>
)}
</Button>
</div>
</AuthContent>
<div className={style.authMessage} style={{ marginTop: 20 }}>
{t['com.affine.auth.sign.auth.code.message']()}
&nbsp;
<Trans
i18nKey="com.affine.auth.sign.auth.code.message.password"
components={{
1: (
<span
className="link"
data-testid="sign-in-with-password"
onClick={onSignInWithPasswordClick}
/>
),
}}
/>
</div>
<AuthFooter>
<div className={style.authMessage} style={{ marginTop: 20 }}>
{t['com.affine.auth.sign.auth.code.message']()}
&nbsp;
{state.hasPassword && (
<Trans
i18nKey="com.affine.auth.sign.auth.code.message.password"
components={{
1: (
<span
className="link"
data-testid="sign-in-with-password"
onClick={onSignInWithPasswordClick}
/>
),
}}
/>
)}
</div>
<BackButton onClick={onBackBottomClick} />
</>
<Back changeState={changeState} />
</AuthFooter>
</AuthContainer>
);
};
@@ -1,8 +1,10 @@
import { notify, Wrapper } from '@affine/component';
import { notify } from '@affine/component';
import {
AuthContainer,
AuthContent,
AuthFooter,
AuthHeader,
AuthInput,
BackButton,
ModalHeader,
} from '@affine/component/auth-components';
import { Button } from '@affine/component/ui/button';
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
@@ -20,6 +22,7 @@ import type { Dispatch, SetStateAction } from 'react';
import { useCallback, useEffect, useState } from 'react';
import type { SignInState } from '.';
import { Back } from './back';
import { Captcha } from './captcha';
import * as styles from './style.css';
@@ -105,18 +108,13 @@ export const SignInWithPasswordStep = ({
}, [changeState]);
return (
<>
<ModalHeader
<AuthContainer>
<AuthHeader
title={t['com.affine.auth.sign.in']()}
subTitle={serverName}
/>
<Wrapper
marginTop={30}
style={{
position: 'relative',
}}
>
<AuthContent>
<AuthInput
label={t['com.affine.settings.email']()}
disabled={true}
@@ -157,12 +155,10 @@ export const SignInWithPasswordStep = ({
>
{t['com.affine.auth.sign.in']()}
</Button>
</Wrapper>
<BackButton
onClick={useCallback(() => {
changeState(prev => ({ ...prev, step: 'signIn' }));
}, [changeState])}
/>
</>
</AuthContent>
<AuthFooter>
<Back changeState={changeState} />
</AuthFooter>
</AuthContainer>
);
};
@@ -1,5 +1,10 @@
import { Button, notify } from '@affine/component';
import { AuthInput, ModalHeader } from '@affine/component/auth-components';
import {
AuthContainer,
AuthContent,
AuthHeader,
AuthInput,
} from '@affine/component/auth-components';
import { OAuth } from '@affine/core/components/affine/auth/oauth';
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import { AuthService, ServerService } from '@affine/core/modules/cloud';
@@ -7,7 +12,11 @@ import type { AuthSessionStatus } from '@affine/core/modules/cloud/entities/sess
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
import { ServerDeploymentType } from '@affine/graphql';
import { Trans, useI18n } from '@affine/i18n';
import { ArrowRightBigIcon, PublishIcon } from '@blocksuite/icons/rc';
import {
ArrowRightBigIcon,
LocalWorkspaceIcon,
PublishIcon,
} from '@blocksuite/icons/rc';
import { useLiveData, useService } from '@toeverything/infra';
import { cssVar } from '@toeverything/theme';
import {
@@ -93,6 +102,7 @@ export const SignInStep = ({
...prev,
email,
step: 'signInWithPassword',
hasPassword: true,
}));
} else {
if (magicLink) {
@@ -100,6 +110,7 @@ export const SignInStep = ({
...prev,
email,
step: 'signInWithEmail',
hasPassword: false,
}));
} else {
notify.error({
@@ -113,6 +124,7 @@ export const SignInStep = ({
...prev,
email,
step: 'signInWithEmail',
hasPassword: false,
}));
} else {
notify.error({
@@ -140,15 +152,15 @@ export const SignInStep = ({
}, [changeState]);
return (
<>
<ModalHeader
<AuthContainer>
<AuthHeader
title={t['com.affine.auth.sign.in']()}
subTitle={serverName}
/>
<OAuth redirectUrl={state.redirectUrl} />
<AuthContent>
<OAuth redirectUrl={state.redirectUrl} />
<div className={style.authModalContent}>
<AuthInput
label={t['com.affine.settings.email']()}
placeholder={t['com.affine.auth.sign.email.placeholder']()}
@@ -190,32 +202,33 @@ export const SignInStep = ({
</div>
<div className={style.skipSection}>
{!isSelfhosted &&
BUILD_CONFIG.isElectron &&
enableMultipleCloudServers && (
<Button
variant="plain"
className={style.addSelfhostedButton}
prefix={
<PublishIcon className={style.addSelfhostedButtonPrefix} />
}
onClick={onAddSelfhosted}
>
{t['com.affine.auth.sign.add-selfhosted']()}
</Button>
)}
<div className={style.skipText}>
{t['com.affine.mobile.sign-in.skip.hint']()}
</div>
BUILD_CONFIG.isElectron &&
enableMultipleCloudServers ? (
<Button
variant="plain"
className={style.addSelfhostedButton}
prefix={
<PublishIcon className={style.addSelfhostedButtonPrefix} />
}
onClick={onAddSelfhosted}
>
{t['com.affine.auth.sign.add-selfhosted']()}
</Button>
) : (
<div className={style.skipText}>
{t['com.affine.mobile.sign-in.skip.hint']()}
</div>
)}
<Button
variant="plain"
onClick={onSkip}
className={style.skipLink}
suffix={<ArrowRightBigIcon className={style.skipLinkIcon} />}
prefix={<LocalWorkspaceIcon className={style.skipLinkIcon} />}
>
{t['com.affine.mobile.sign-in.skip.link']()}
</Button>
</div>
</div>
</>
</AuthContent>
</AuthContainer>
);
};
@@ -2,16 +2,12 @@ import { cssVar } from '@toeverything/theme';
import { cssVarV2 } from '@toeverything/theme/v2';
import { globalStyle, style } from '@vanilla-extract/css';
export const authModalContent = style({
marginTop: '30px',
});
export const authMessage = style({
marginTop: '30px',
color: cssVar('textSecondaryColor'),
fontSize: cssVar('fontXs'),
lineHeight: 1.5,
lineHeight: '20px',
});
globalStyle(`${authMessage} a`, {
color: cssVar('linkColor'),
});
@@ -42,14 +38,7 @@ export const linkButton = style({
userSelect: 'none',
});
export const resendButtonWrapper = style({
marginTop: 20,
});
export const addSelfhostedButton = style({
marginTop: 10,
marginLeft: -5,
marginBottom: 16,
color: cssVarV2('text/link'),
});
@@ -62,8 +51,6 @@ export const skipDivider = style({
gap: 12,
alignItems: 'center',
height: 20,
marginTop: 12,
marginBottom: 12,
});
export const skipDividerLine = style({
@@ -1,8 +1,8 @@
import { Button, Modal, notify, Wrapper } from '@affine/component';
import { Button, Modal, notify } from '@affine/component';
import {
AuthContent,
AuthHeader,
AuthInput,
ModalHeader,
} from '@affine/component/auth-components';
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import {
@@ -108,7 +108,7 @@ export const ChangePasswordDialog = ({
style: { padding: '44px 40px 20px' },
}}
>
<ModalHeader
<AuthHeader
title={serverName}
subTitle={
hasPassword
@@ -117,42 +117,34 @@ export const ChangePasswordDialog = ({
}
/>
<AuthContent>
{hasPassword
? t['com.affine.auth.reset.password.message']()
: t['com.affine.auth.set.password.message']({
min: String(passwordLimits.minLength),
max: String(passwordLimits.maxLength),
})}
</AuthContent>
<Wrapper
marginTop={30}
marginBottom={50}
style={{
position: 'relative',
}}
>
<p>
{hasPassword
? t['com.affine.auth.reset.password.message']()
: t['com.affine.auth.set.password.message']({
min: String(passwordLimits.minLength),
max: String(passwordLimits.maxLength),
})}
</p>
<AuthInput
label={t['com.affine.settings.email']()}
disabled={true}
value={email}
/>
</Wrapper>
<Button
variant="primary"
size="extraLarge"
style={{ width: '100%' }}
disabled={hasSentEmail}
loading={loading}
onClick={onSendEmail}
>
{hasSentEmail
? t['com.affine.auth.sent']()
: hasPassword
? t['com.affine.auth.send.reset.password.link']()
: t['com.affine.auth.send.set.password.link']()}
</Button>
<Button
variant="primary"
size="extraLarge"
style={{ width: '100%' }}
disabled={hasSentEmail}
loading={loading}
onClick={onSendEmail}
>
{hasSentEmail
? t['com.affine.auth.sent']()
: hasPassword
? t['com.affine.auth.send.reset.password.link']()
: t['com.affine.auth.send.set.password.link']()}
</Button>
</AuthContent>
</Modal>
);
};
@@ -24,7 +24,7 @@ export const SignInDialog = ({
open
onOpenChange={() => close()}
width={400}
minHeight={500}
height={550}
contentOptions={{
['data-testid' as string]: 'auth-modal',
style: { padding: '44px 40px 20px' },
@@ -1,8 +1,8 @@
import { Button, Modal, notify, Wrapper } from '@affine/component';
import { Button, Modal, notify } from '@affine/component';
import {
AuthContent,
AuthHeader,
AuthInput,
ModalHeader,
} from '@affine/component/auth-components';
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import {
@@ -106,28 +106,18 @@ export const VerifyEmailDialog = ({
style: { padding: '44px 40px 20px' },
}}
>
<ModalHeader
<AuthHeader
title={serverName}
subTitle={t['com.affine.settings.email.action.change']()}
/>
<AuthContent>
{t['com.affine.auth.verify.email.message']({ email })}
</AuthContent>
<Wrapper
marginTop={30}
marginBottom={50}
style={{
position: 'relative',
}}
>
<p>{t['com.affine.auth.verify.email.message']({ email })}</p>
<AuthInput
label={t['com.affine.settings.email']()}
disabled={true}
value={email}
/>
</Wrapper>
</AuthContent>
<Button
variant="primary"
size="extraLarge"
@@ -2,6 +2,7 @@ import { notify } from '@affine/component';
import { AffineOtherPageLayout } from '@affine/component/affine-other-page-layout';
import { SignInPageContainer } from '@affine/component/auth-components';
import { SignInPanel } from '@affine/core/components/sign-in';
import { SignInBackgroundArts } from '@affine/core/components/sign-in/background-arts';
import type { AuthSessionStatus } from '@affine/core/modules/cloud/entities/session';
import { useI18n } from '@affine/i18n';
import { useCallback, useEffect } from 'react';
@@ -75,9 +76,8 @@ export const SignIn = ({
export const Component = () => {
return (
<AffineOtherPageLayout>
<div style={{ padding: '0 20px' }}>
<SignIn />
</div>
<SignInBackgroundArts />
<SignIn />
</AffineOtherPageLayout>
);
};