mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 21:05:19 +00:00
feat(core): make password sigin default if user has one (#5577)
This commit is contained in:
@@ -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>
|
||||
|
||||
{/*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,
|
||||
|
||||
Reference in New Issue
Block a user