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}
onOpenChange={setOpen}
width={400}
height={468}
minHeight={500}
contentOptions={{
['data-testid' as string]: 'auth-modal',
style: { padding: '44px 40px 0' },

View File

@ -53,9 +53,12 @@ export const AfterSignInSendEmail = ({
subTitle={t['com.affine.auth.sign.in.sent.email.subtitle']()}
/>
<AuthContent style={{ height: 100 }}>
{t['com.affine.auth.sign.sent.email.message.start']()}
<a href={`mailto:${email}`}>{email}</a>
{t['com.affine.auth.sign.sent.email.message.end']()}
<Trans
i18nKey="com.affine.auth.sign.sent.email.message.sent-tips"
values={{ email }}
components={{ a: <a href={`mailto:${email}`} /> }}
/>
{t['com.affine.auth.sign.sent.email.message.sent-tips.sign-in']()}
</AuthContent>
<div className={style.resendWrapper}>
@ -73,15 +76,15 @@ export const AfterSignInSendEmail = ({
</Button>
</>
) : (
<>
<span className="resend-code-hint">
{t['com.affine.auth.sign.auth.code.on.resend.hint']()}
</span>
<div className={style.sentRow}>
<div className={style.sentMessage}>
{t['com.affine.auth.sent']()}
</div>
<CountDownRender
className={style.resendCountdown}
timeLeft={resendCountDown}
/>
</>
</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.
<React.Fragment>
&nbsp;
{/*prettier-ignore*/}
<Trans i18nKey="com.affine.auth.sign.auth.code.message.password">
Or <span
className="link"
data-testid='sign-in-with-password'
onClick={onSignInWithPasswordClick}
>
sign in with password
</span> instead.
</Trans>
<Trans
i18nKey="com.affine.auth.sign.auth.code.message.password"
components={{
1: (
<span
className="link"
data-testid="sign-in-with-password"
onClick={onSignInWithPasswordClick}
/>
),
}}
/>
</React.Fragment>
)}
</div>

View File

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

View File

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

View File

@ -14,10 +14,13 @@ import { useCallback, useState } from 'react';
import { signInCloud } from '../../../utils/cloud-utils';
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> = ({
setAuthState,
setEmailType,
email,
onSignedIn,
}) => {
@ -26,6 +29,13 @@ export const SignInWithPassword: FC<AuthPanelProps> = ({
const [password, setPassword] = useState('');
const [passwordError, setPasswordError] = useState(false);
const {
signIn,
allowSendEmail,
resetCountDown,
isMutating: sendingEmail,
} = useAuth();
const [verifyToken, challenge] = useCaptcha();
const onSignIn = useAsyncCallback(async () => {
const res = await signInCloud('credentials', {
@ -42,6 +52,31 @@ export const SignInWithPassword: FC<AuthPanelProps> = ({
onSignedIn?.();
}, [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 (
<>
<ModalHeader
@ -51,7 +86,6 @@ export const SignInWithPassword: FC<AuthPanelProps> = ({
<Wrapper
marginTop={30}
marginBottom={50}
style={{
position: 'relative',
}}
@ -73,29 +107,43 @@ export const SignInWithPassword: FC<AuthPanelProps> = ({
errorHint={t['com.affine.auth.password.error']()}
onEnter={onSignIn}
/>
<span></span>
<button
className={forgetPasswordButton}
// onClick={useCallback(() => {
// setAuthState('sendPasswordEmail');
// }, [setAuthState])}
<div
className={styles.forgetPasswordButtonRow}
style={{ display: 'none' }} // Not implemented yet.
>
{t['com.affine.auth.forget']()}
</button>
<a
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>
<Button
data-testid="sign-in-button"
type="primary"
size="extraLarge"
style={{ width: '100%' }}
onClick={onSignIn}
>
{t['com.affine.auth.sign.in']()}
</Button>
<BackButton
onClick={useCallback(() => {
setAuthState('afterSignInSendEmail');
setAuthState('signIn');
}, [setAuthState])}
/>
</>

View File

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

View File

@ -24,13 +24,30 @@ globalStyle(`${authMessage} .link`, {
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({
fontSize: 'var(--affine-font-sm)',
color: 'var(--affine-text-secondary-color)',
position: 'absolute',
right: 0,
bottom: 0,
display: 'none',
});
export const resendWrapper = style({
@ -42,7 +59,21 @@ export const resendWrapper = style({
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({
width: 40,
textAlign: 'center',

View File

@ -150,9 +150,18 @@ export const useAuth = () => {
signInCloud('google').catch(console.error);
}, []);
const resetCountDown = useCallback(() => {
setAuthStore({
isMutating: false,
allowSendEmail: false,
resendCountDown: 0,
});
}, [setAuthStore]);
return {
allowSendEmail: authStore.allowSendEmail,
resendCountDown: authStore.resendCountDown,
resetCountDown,
isMutating: authStore.isMutating,
signUp,
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.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.on.resend.hint": "Send code again",
"com.affine.auth.sign.auth.code.resend.hint": "Resend code",
"com.affine.auth.sign.auth.code.resend.hint": "Resend link",
"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.email.continue": "Continue with 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.wait": "Wait for public release",
"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.start": "An email with a magic link has been sent to ",
"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.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.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.",

View File

@ -443,7 +443,8 @@
"com.affine.auth.sign.auth.code.message": "如果您没有收到电子邮件,请检查您的垃圾邮件文件夹。",
"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.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.email.continue": "以电子邮件继续",
"com.affine.auth.sign.email.error": "无效的电子邮件",
@ -456,7 +457,8 @@
"com.affine.auth.sign.no.access.wait": "请期待正式发行",
"com.affine.auth.sign.policy": "隐私政策",
"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.sent.email.subtitle": "创建您的账号",
"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.policy": "隱私政策",
"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.sent.email.subtitle": "建立您的帳號",
"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({
delay: 200,
});
await page.getByTestId('sign-in-with-password').click({
delay: 200,
});
await page.getByTestId('password-input').fill('123456');
if (config?.beforeLogin) {
await config.beforeLogin();