feat(core): make password sigin default if user has one (#5577)

This commit is contained in:
liuyi 2024-01-17 11:13:58 +00:00
parent bf88b6edaa
commit b9f20877d0
No known key found for this signature in database
GPG Key ID: 56709255DC7EC728
12 changed files with 170 additions and 71 deletions

View File

@ -17,7 +17,7 @@ export const AuthModal: FC<PropsWithChildren<AuthModalProps>> = ({
open={open} open={open}
onOpenChange={setOpen} onOpenChange={setOpen}
width={400} width={400}
height={468} minHeight={500}
contentOptions={{ contentOptions={{
['data-testid' as string]: 'auth-modal', ['data-testid' as string]: 'auth-modal',
style: { padding: '44px 40px 0' }, style: { padding: '44px 40px 0' },

View File

@ -53,9 +53,12 @@ export const AfterSignInSendEmail = ({
subTitle={t['com.affine.auth.sign.in.sent.email.subtitle']()} subTitle={t['com.affine.auth.sign.in.sent.email.subtitle']()}
/> />
<AuthContent style={{ height: 100 }}> <AuthContent style={{ height: 100 }}>
{t['com.affine.auth.sign.sent.email.message.start']()} <Trans
<a href={`mailto:${email}`}>{email}</a> i18nKey="com.affine.auth.sign.sent.email.message.sent-tips"
{t['com.affine.auth.sign.sent.email.message.end']()} values={{ email }}
components={{ a: <a href={`mailto:${email}`} /> }}
/>
{t['com.affine.auth.sign.sent.email.message.sent-tips.sign-in']()}
</AuthContent> </AuthContent>
<div className={style.resendWrapper}> <div className={style.resendWrapper}>
@ -73,15 +76,15 @@ export const AfterSignInSendEmail = ({
</Button> </Button>
</> </>
) : ( ) : (
<> <div className={style.sentRow}>
<span className="resend-code-hint"> <div className={style.sentMessage}>
{t['com.affine.auth.sign.auth.code.on.resend.hint']()} {t['com.affine.auth.sent']()}
</span> </div>
<CountDownRender <CountDownRender
className={style.resendCountdown} className={style.resendCountdown}
timeLeft={resendCountDown} timeLeft={resendCountDown}
/> />
</> </div>
)} )}
</div> </div>
@ -90,16 +93,18 @@ export const AfterSignInSendEmail = ({
{subscriptionData ? null : ( // If with payment, just support email sign in to avoid duplicate redirect to the same stripe url. {subscriptionData ? null : ( // If with payment, just support email sign in to avoid duplicate redirect to the same stripe url.
<React.Fragment> <React.Fragment>
&nbsp; &nbsp;
{/*prettier-ignore*/} <Trans
<Trans i18nKey="com.affine.auth.sign.auth.code.message.password"> i18nKey="com.affine.auth.sign.auth.code.message.password"
Or <span components={{
className="link" 1: (
data-testid='sign-in-with-password' <span
onClick={onSignInWithPasswordClick} className="link"
> data-testid="sign-in-with-password"
sign in with password onClick={onSignInWithPasswordClick}
</span> instead. />
</Trans> ),
}}
/>
</React.Fragment> </React.Fragment>
)} )}
</div> </div>

View File

@ -6,6 +6,7 @@ import {
} from '@affine/component/auth-components'; } from '@affine/component/auth-components';
import { Button } from '@affine/component/ui/button'; import { Button } from '@affine/component/ui/button';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { type FC, useCallback } from 'react'; import { type FC, useCallback } from 'react';
@ -43,9 +44,12 @@ export const AfterSignUpSendEmail: FC<AuthPanelProps> = ({
subTitle={t['com.affine.auth.sign.up.sent.email.subtitle']()} subTitle={t['com.affine.auth.sign.up.sent.email.subtitle']()}
/> />
<AuthContent style={{ height: 100 }}> <AuthContent style={{ height: 100 }}>
{t['com.affine.auth.sign.sent.email.message.start']()} <Trans
<a href={`mailto:${email}`}>{email}</a> i18nKey="com.affine.auth.sign.sent.email.message.sent-tips"
{t['com.affine.auth.sign.sent.email.message.end']()} values={{ email }}
components={{ a: <a href={`mailto:${email}`} /> }}
/>
{t['com.affine.auth.sign.sent.email.message.sent-tips.sign-up']()}
</AuthContent> </AuthContent>
<div className={style.resendWrapper}> <div className={style.resendWrapper}>
@ -63,15 +67,15 @@ export const AfterSignUpSendEmail: FC<AuthPanelProps> = ({
</Button> </Button>
</> </>
) : ( ) : (
<> <div className={style.sentRow}>
<span className="resend-code-hint"> <div className={style.sentMessage}>
{t['com.affine.auth.sign.auth.code.on.resend.hint']()} {t['com.affine.auth.sent']()}
</span> </div>
<CountDownRender <CountDownRender
className={style.resendCountdown} className={style.resendCountdown}
timeLeft={resendCountDown} timeLeft={resendCountDown}
/> />
</> </div>
)} )}
</div> </div>

View File

@ -39,7 +39,7 @@ const useContent = (emailType: AuthPanelProps['emailType'], email: string) => {
case 'setPassword': case 'setPassword':
return t['com.affine.auth.set.password.message'](); return t['com.affine.auth.set.password.message']();
case 'changePassword': case 'changePassword':
return t['com.affine.auth.set.password.message'](); return t['com.affine.auth.reset.password.message']();
case 'changeEmail': case 'changeEmail':
return t['com.affine.auth.change.email.message']({ return t['com.affine.auth.change.email.message']({
email, email,

View File

@ -14,10 +14,13 @@ import { useCallback, useState } from 'react';
import { signInCloud } from '../../../utils/cloud-utils'; import { signInCloud } from '../../../utils/cloud-utils';
import type { AuthPanelProps } from './index'; import type { AuthPanelProps } from './index';
import { forgetPasswordButton } from './style.css'; import * as styles from './style.css';
import { INTERNAL_BETA_URL, useAuth } from './use-auth';
import { useCaptcha } from './use-captcha';
export const SignInWithPassword: FC<AuthPanelProps> = ({ export const SignInWithPassword: FC<AuthPanelProps> = ({
setAuthState, setAuthState,
setEmailType,
email, email,
onSignedIn, onSignedIn,
}) => { }) => {
@ -26,6 +29,13 @@ export const SignInWithPassword: FC<AuthPanelProps> = ({
const [password, setPassword] = useState(''); const [password, setPassword] = useState('');
const [passwordError, setPasswordError] = useState(false); const [passwordError, setPasswordError] = useState(false);
const {
signIn,
allowSendEmail,
resetCountDown,
isMutating: sendingEmail,
} = useAuth();
const [verifyToken, challenge] = useCaptcha();
const onSignIn = useAsyncCallback(async () => { const onSignIn = useAsyncCallback(async () => {
const res = await signInCloud('credentials', { const res = await signInCloud('credentials', {
@ -42,6 +52,31 @@ export const SignInWithPassword: FC<AuthPanelProps> = ({
onSignedIn?.(); onSignedIn?.();
}, [email, password, onSignedIn, update]); }, [email, password, onSignedIn, update]);
const sendMagicLink = useAsyncCallback(async () => {
if (allowSendEmail && verifyToken && !sendingEmail) {
const res = await signIn(email, verifyToken, challenge);
if (res?.status === 403 && res?.url === INTERNAL_BETA_URL) {
resetCountDown();
return setAuthState('noAccess');
}
setAuthState('afterSignInSendEmail');
}
}, [
email,
signIn,
allowSendEmail,
sendingEmail,
setAuthState,
verifyToken,
challenge,
resetCountDown,
]);
const sendChangePasswordEmail = useCallback(() => {
setEmailType('changePassword');
setAuthState('sendEmail');
}, [setAuthState, setEmailType]);
return ( return (
<> <>
<ModalHeader <ModalHeader
@ -51,7 +86,6 @@ export const SignInWithPassword: FC<AuthPanelProps> = ({
<Wrapper <Wrapper
marginTop={30} marginTop={30}
marginBottom={50}
style={{ style={{
position: 'relative', position: 'relative',
}} }}
@ -73,29 +107,43 @@ export const SignInWithPassword: FC<AuthPanelProps> = ({
errorHint={t['com.affine.auth.password.error']()} errorHint={t['com.affine.auth.password.error']()}
onEnter={onSignIn} onEnter={onSignIn}
/> />
<span></span> <div
<button className={styles.forgetPasswordButtonRow}
className={forgetPasswordButton} style={{ display: 'none' }} // Not implemented yet.
// onClick={useCallback(() => {
// setAuthState('sendPasswordEmail');
// }, [setAuthState])}
> >
{t['com.affine.auth.forget']()} <a
</button> className={styles.linkButton}
onClick={sendChangePasswordEmail}
style={{
color: 'var(--affine-text-secondary-color)',
fontSize: 'var(--affine-font-sm)',
}}
>
{t['com.affine.auth.forget']()}
</a>
</div>
<div className={styles.sendMagicLinkButtonRow}>
<a
data-testid="send-magic-link-button"
className={styles.linkButton}
onClick={sendMagicLink}
>
{t['com.affine.auth.sign.auth.code.send-email.sign-in']()}
</a>
</div>
<Button
data-testid="sign-in-button"
type="primary"
size="extraLarge"
style={{ width: '100%' }}
onClick={onSignIn}
>
{t['com.affine.auth.sign.in']()}
</Button>
</Wrapper> </Wrapper>
<Button
data-testid="sign-in-button"
type="primary"
size="extraLarge"
style={{ width: '100%' }}
onClick={onSignIn}
>
{t['com.affine.auth.sign.in']()}
</Button>
<BackButton <BackButton
onClick={useCallback(() => { onClick={useCallback(() => {
setAuthState('afterSignInSendEmail'); setAuthState('signIn');
}, [setAuthState])} }, [setAuthState])}
/> />
</> </>

View File

@ -61,16 +61,13 @@ export const SignIn: FC<AuthPanelProps> = ({
setIsValidEmail(true); setIsValidEmail(true);
// 0 for no access for internal beta // 0 for no access for internal beta
let user: GetUserQuery['user'] | null | 0 = null; const user: GetUserQuery['user'] | null | 0 = await verifyUser({ email })
await verifyUser({ email }) .then(({ user }) => user)
.then(({ user: u }) => {
user = u;
})
.catch(err => { .catch(err => {
const e = err?.[0]; const e = err?.[0];
if (e instanceof GraphQLError && e.extensions?.code === 402) { if (e instanceof GraphQLError && e.extensions?.code === 402) {
setAuthState('noAccess'); setAuthState('noAccess');
user = 0; return 0;
} else { } else {
throw err; throw err;
} }
@ -83,11 +80,16 @@ export const SignIn: FC<AuthPanelProps> = ({
if (verifyToken) { if (verifyToken) {
if (user) { if (user) {
const res = await signIn(email, verifyToken, challenge); // provider password sign-in if user has by default
if (res?.status === 403 && res?.url === INTERNAL_BETA_URL) { if (user.hasPassword) {
return setAuthState('noAccess'); setAuthState('signInWithPassword');
} else {
const res = await signIn(email, verifyToken, challenge);
if (res?.status === 403 && res?.url === INTERNAL_BETA_URL) {
return setAuthState('noAccess');
}
setAuthState('afterSignInSendEmail');
} }
setAuthState('afterSignInSendEmail');
} else { } else {
const res = await signUp(email, verifyToken, challenge); const res = await signUp(email, verifyToken, challenge);
if (res?.status === 403 && res?.url === INTERNAL_BETA_URL) { if (res?.status === 403 && res?.url === INTERNAL_BETA_URL) {

View File

@ -24,13 +24,30 @@ globalStyle(`${authMessage} .link`, {
color: 'var(--affine-link-color)', color: 'var(--affine-link-color)',
}); });
export const forgetPasswordButtonRow = style({
position: 'absolute',
right: 0,
marginTop: '-26px', // Let this button be a tail of password input.
});
export const sendMagicLinkButtonRow = style({
marginBottom: '30px',
});
export const linkButton = style({
color: 'var(--affine-link-color)',
background: 'transparent',
borderColor: 'transparent',
fontSize: 'var(--affine-font-xs)',
lineHeight: '22px',
userSelect: 'none',
});
export const forgetPasswordButton = style({ export const forgetPasswordButton = style({
fontSize: 'var(--affine-font-sm)', fontSize: 'var(--affine-font-sm)',
color: 'var(--affine-text-secondary-color)', color: 'var(--affine-text-secondary-color)',
position: 'absolute', position: 'absolute',
right: 0, right: 0,
bottom: 0, bottom: 0,
display: 'none',
}); });
export const resendWrapper = style({ export const resendWrapper = style({
@ -42,7 +59,21 @@ export const resendWrapper = style({
marginTop: 30, marginTop: 30,
}); });
export const resendCountdown = style({ width: 45, textAlign: 'center' }); export const sentRow = style({
display: 'flex',
justifyContent: 'center',
gap: '8px',
lineHeight: '22px',
fontSize: 'var(--affine-font-sm)',
});
export const sentMessage = style({
color: 'var(--affine-text-primary-color)',
fontWeight: 600,
});
export const resendCountdown = style({
width: 45,
textAlign: 'center',
});
export const resendCountdownInButton = style({ export const resendCountdownInButton = style({
width: 40, width: 40,
textAlign: 'center', textAlign: 'center',

View File

@ -150,9 +150,18 @@ export const useAuth = () => {
signInCloud('google').catch(console.error); signInCloud('google').catch(console.error);
}, []); }, []);
const resetCountDown = useCallback(() => {
setAuthStore({
isMutating: false,
allowSendEmail: false,
resendCountDown: 0,
});
}, [setAuthStore]);
return { return {
allowSendEmail: authStore.allowSendEmail, allowSendEmail: authStore.allowSendEmail,
resendCountDown: authStore.resendCountDown, resendCountDown: authStore.resendCountDown,
resetCountDown,
isMutating: authStore.isMutating, isMutating: authStore.isMutating,
signUp, signUp,
signIn, signIn,

View File

@ -454,8 +454,8 @@
"com.affine.auth.sign.auth.code.error.hint": "Wrong code, please try again", "com.affine.auth.sign.auth.code.error.hint": "Wrong code, please try again",
"com.affine.auth.sign.auth.code.message": "If you haven't received the email, please check your spam folder.", "com.affine.auth.sign.auth.code.message": "If you haven't received the email, please check your spam folder.",
"com.affine.auth.sign.auth.code.message.password": "Or <1>sign in with password</1> instead.", "com.affine.auth.sign.auth.code.message.password": "Or <1>sign in with password</1> instead.",
"com.affine.auth.sign.auth.code.on.resend.hint": "Send code again", "com.affine.auth.sign.auth.code.resend.hint": "Resend link",
"com.affine.auth.sign.auth.code.resend.hint": "Resend code", "com.affine.auth.sign.auth.code.send-email.sign-in": "Sign in with magic link",
"com.affine.auth.sign.condition": "Terms of Conditions", "com.affine.auth.sign.condition": "Terms of Conditions",
"com.affine.auth.sign.email.continue": "Continue with Email", "com.affine.auth.sign.email.continue": "Continue with Email",
"com.affine.auth.sign.email.error": "Invalid email", "com.affine.auth.sign.email.error": "Invalid email",
@ -467,8 +467,9 @@
"com.affine.auth.sign.no.access.link": "AFFiNE Cloud Early Access", "com.affine.auth.sign.no.access.link": "AFFiNE Cloud Early Access",
"com.affine.auth.sign.no.access.wait": "Wait for public release", "com.affine.auth.sign.no.access.wait": "Wait for public release",
"com.affine.auth.sign.policy": "Privacy Policy", "com.affine.auth.sign.policy": "Privacy Policy",
"com.affine.auth.sign.sent.email.message.end": " You can click the link to create an account automatically.", "com.affine.auth.sign.sent.email.message.sent-tips": "An email with a magic link has been sent to <a>{{email}}</a>.",
"com.affine.auth.sign.sent.email.message.start": "An email with a magic link has been sent to ", "com.affine.auth.sign.sent.email.message.sent-tips.sign-up": "You can click the link to create an account automatically.",
"com.affine.auth.sign.sent.email.message.sent-tips.sign-in": "You can click the link to sign in automatically.",
"com.affine.auth.sign.up": "Sign up", "com.affine.auth.sign.up": "Sign up",
"com.affine.auth.sign.up.sent.email.subtitle": "Create your account", "com.affine.auth.sign.up.sent.email.subtitle": "Create your account",
"com.affine.auth.sign.up.success.subtitle": "The app will automatically open or redirect to the web version. If you encounter any issues, you can also click the button below to manually open the AFFiNE app.", "com.affine.auth.sign.up.success.subtitle": "The app will automatically open or redirect to the web version. If you encounter any issues, you can also click the button below to manually open the AFFiNE app.",

View File

@ -443,7 +443,8 @@
"com.affine.auth.sign.auth.code.message": "如果您没有收到电子邮件,请检查您的垃圾邮件文件夹。", "com.affine.auth.sign.auth.code.message": "如果您没有收到电子邮件,请检查您的垃圾邮件文件夹。",
"com.affine.auth.sign.auth.code.message.password": "或者使用<1>密码登录</1>。", "com.affine.auth.sign.auth.code.message.password": "或者使用<1>密码登录</1>。",
"com.affine.auth.sign.auth.code.on.resend.hint": "再次发送验证码", "com.affine.auth.sign.auth.code.on.resend.hint": "再次发送验证码",
"com.affine.auth.sign.auth.code.resend.hint": "重发验证码", "com.affine.auth.sign.auth.code.sent-tips": "重发验证码",
"com.affine.auth.sign.auth.code.send-email.sign-in": "邮件登录",
"com.affine.auth.sign.condition": "条款与条件", "com.affine.auth.sign.condition": "条款与条件",
"com.affine.auth.sign.email.continue": "以电子邮件继续", "com.affine.auth.sign.email.continue": "以电子邮件继续",
"com.affine.auth.sign.email.error": "无效的电子邮件", "com.affine.auth.sign.email.error": "无效的电子邮件",
@ -456,7 +457,8 @@
"com.affine.auth.sign.no.access.wait": "请期待正式发行", "com.affine.auth.sign.no.access.wait": "请期待正式发行",
"com.affine.auth.sign.policy": "隐私政策", "com.affine.auth.sign.policy": "隐私政策",
"com.affine.auth.sign.sent.email.message.end": "您可以点击链接自动创建账户。", "com.affine.auth.sign.sent.email.message.end": "您可以点击链接自动创建账户。",
"com.affine.auth.sign.sent.email.message.start": "一封带有 magic link 的邮件已发送至", "com.affine.auth.sign.sent.email.message.sign-in.end": "您可以点击链接自动登录。",
"com.affine.auth.sign.sent.email.message.sent-tips": "一封带有 magic link 的邮件已发送至 <a>{{email}}</a>。",
"com.affine.auth.sign.up": "注册", "com.affine.auth.sign.up": "注册",
"com.affine.auth.sign.up.sent.email.subtitle": "创建您的账号", "com.affine.auth.sign.up.sent.email.subtitle": "创建您的账号",
"com.affine.auth.sign.up.success.subtitle": "AFFiNE 客户端将自动打开或重定向到网页端,如果您遇到任何问题,可以点击下方按钮手动打开 AFFiNE。", "com.affine.auth.sign.up.success.subtitle": "AFFiNE 客户端将自动打开或重定向到网页端,如果您遇到任何问题,可以点击下方按钮手动打开 AFFiNE。",

View File

@ -365,7 +365,7 @@
"com.affine.auth.sign.message": "點擊上方「使用 Google/電子郵件地址 繼續」,即表示您確認同意 AFFiNE 的 <1>Terms of Conditions</1> and <3>Privacy Policy</3>。", "com.affine.auth.sign.message": "點擊上方「使用 Google/電子郵件地址 繼續」,即表示您確認同意 AFFiNE 的 <1>Terms of Conditions</1> and <3>Privacy Policy</3>。",
"com.affine.auth.sign.policy": "隱私政策", "com.affine.auth.sign.policy": "隱私政策",
"com.affine.auth.sign.sent.email.message.end": "您可以點擊連結自動建立帳號。", "com.affine.auth.sign.sent.email.message.end": "您可以點擊連結自動建立帳號。",
"com.affine.auth.sign.sent.email.message.start": "一封帶有魔法連結的電子郵件已發送至", "com.affine.auth.sign.sent.email.message.sent-tips": "一封帶有魔法連結的電子郵件已發送至 <a>{{email}}</a>。",
"com.affine.auth.sign.up": "建立帳號", "com.affine.auth.sign.up": "建立帳號",
"com.affine.auth.sign.up.sent.email.subtitle": "建立您的帳號", "com.affine.auth.sign.up.sent.email.subtitle": "建立您的帳號",
"com.affine.auth.sign.up.success.subtitle": "應用將自動打開或重定向到網頁版本。如果您遇到任何問題,您還可以點擊下面的按鈕手動打開 AFFiNE 應用程序。", "com.affine.auth.sign.up.success.subtitle": "應用將自動打開或重定向到網頁版本。如果您遇到任何問題,您還可以點擊下面的按鈕手動打開 AFFiNE 應用程序。",

View File

@ -170,9 +170,6 @@ export async function loginUser(
await page.getByTestId('continue-login-button').click({ await page.getByTestId('continue-login-button').click({
delay: 200, delay: 200,
}); });
await page.getByTestId('sign-in-with-password').click({
delay: 200,
});
await page.getByTestId('password-input').fill('123456'); await page.getByTestId('password-input').fill('123456');
if (config?.beforeLogin) { if (config?.beforeLogin) {
await config.beforeLogin(); await config.beforeLogin();