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
12 changed files with 170 additions and 71 deletions
@@ -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' },
@@ -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>
@@ -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>
@@ -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,
@@ -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])}
/>
</>
@@ -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) {
@@ -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',
@@ -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,
+5 -4
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.",
@@ -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。",
@@ -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 應用程序。",