mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-11 20:08:37 +00:00
fix: sign in issues (#4047)
Co-authored-by: Alex Yang <himself65@outlook.com>
This commit is contained in:
@@ -1,23 +1,39 @@
|
||||
import {
|
||||
AuthContent,
|
||||
BackButton,
|
||||
CountDownRender,
|
||||
ModalHeader,
|
||||
ResendButton,
|
||||
} from '@affine/component/auth-components';
|
||||
import { Trans } from '@affine/i18n';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { type FC, useCallback } from 'react';
|
||||
import { Button } from '@toeverything/components/button';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { signInCloud } from '../../../utils/cloud-utils';
|
||||
import { buildCallbackUrl } from './callback-url';
|
||||
import { useCurrentLoginStatus } from '../../../hooks/affine/use-current-login-status';
|
||||
import type { AuthPanelProps } from './index';
|
||||
import * as style from './style.css';
|
||||
import { useAuth } from './use-auth';
|
||||
|
||||
export const AfterSignInSendEmail: FC<AuthPanelProps> = ({
|
||||
export const AfterSignInSendEmail = ({
|
||||
setAuthState,
|
||||
email,
|
||||
}) => {
|
||||
onSignedIn,
|
||||
}: AuthPanelProps) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const loginStatus = useCurrentLoginStatus();
|
||||
|
||||
const { resendCountDown, allowSendEmail, signIn } = useAuth({
|
||||
onNoAccess: useCallback(() => {
|
||||
setAuthState('noAccess');
|
||||
}, [setAuthState]),
|
||||
});
|
||||
if (loginStatus === 'authenticated') {
|
||||
onSignedIn?.();
|
||||
}
|
||||
|
||||
const onResendClick = useCallback(async () => {
|
||||
await signIn(email);
|
||||
}, [email, signIn]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -31,15 +47,23 @@ export const AfterSignInSendEmail: FC<AuthPanelProps> = ({
|
||||
{t['com.affine.auth.sign.sent.email.message.end']()}
|
||||
</AuthContent>
|
||||
|
||||
<ResendButton
|
||||
onClick={useCallback(() => {
|
||||
signInCloud('email', {
|
||||
email,
|
||||
callbackUrl: buildCallbackUrl('/auth/signIn'),
|
||||
redirect: true,
|
||||
}).catch(console.error);
|
||||
}, [email])}
|
||||
/>
|
||||
<div className={style.resendWrapper}>
|
||||
{allowSendEmail ? (
|
||||
<Button type="plain" size="large" onClick={onResendClick}>
|
||||
{t['com.affine.auth.sign.auth.code.resend.hint']()}
|
||||
</Button>
|
||||
) : (
|
||||
<>
|
||||
<span className="resend-code-hint">
|
||||
{t['com.affine.auth.sign.auth.code.on.resend.hint']()}
|
||||
</span>
|
||||
<CountDownRender
|
||||
className={style.resendCountdown}
|
||||
timeLeft={resendCountDown}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={style.authMessage} style={{ marginTop: 20 }}>
|
||||
{/*prettier-ignore*/}
|
||||
|
||||
@@ -1,22 +1,39 @@
|
||||
import {
|
||||
AuthContent,
|
||||
BackButton,
|
||||
CountDownRender,
|
||||
ModalHeader,
|
||||
ResendButton,
|
||||
} from '@affine/component/auth-components';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { Button } from '@toeverything/components/button';
|
||||
import { type FC, useCallback } from 'react';
|
||||
|
||||
import { signInCloud } from '../../../utils/cloud-utils';
|
||||
import { buildCallbackUrl } from './callback-url';
|
||||
import { useCurrentLoginStatus } from '../../../hooks/affine/use-current-login-status';
|
||||
import type { AuthPanelProps } from './index';
|
||||
import * as style from './style.css';
|
||||
import { useAuth } from './use-auth';
|
||||
|
||||
export const AfterSignUpSendEmail: FC<AuthPanelProps> = ({
|
||||
setAuthState,
|
||||
email,
|
||||
onSignedIn,
|
||||
}) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const loginStatus = useCurrentLoginStatus();
|
||||
|
||||
const { resendCountDown, allowSendEmail, signUp } = useAuth({
|
||||
onNoAccess: useCallback(() => {
|
||||
setAuthState('noAccess');
|
||||
}, [setAuthState]),
|
||||
});
|
||||
|
||||
if (loginStatus === 'authenticated') {
|
||||
onSignedIn?.();
|
||||
}
|
||||
|
||||
const onResendClick = useCallback(async () => {
|
||||
await signUp(email);
|
||||
}, [email, signUp]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -30,15 +47,23 @@ export const AfterSignUpSendEmail: FC<AuthPanelProps> = ({
|
||||
{t['com.affine.auth.sign.sent.email.message.end']()}
|
||||
</AuthContent>
|
||||
|
||||
<ResendButton
|
||||
onClick={useCallback(() => {
|
||||
signInCloud('email', {
|
||||
email: email,
|
||||
callbackUrl: buildCallbackUrl('/auth/signUp'),
|
||||
redirect: true,
|
||||
}).catch(console.error);
|
||||
}, [email])}
|
||||
/>
|
||||
<div className={style.resendWrapper}>
|
||||
{allowSendEmail ? (
|
||||
<Button type="plain" size="large" onClick={onResendClick}>
|
||||
{t['com.affine.auth.sign.auth.code.resend.hint']()}
|
||||
</Button>
|
||||
) : (
|
||||
<>
|
||||
<span className="resend-code-hint">
|
||||
{t['com.affine.auth.sign.auth.code.on.resend.hint']()}
|
||||
</span>
|
||||
<CountDownRender
|
||||
className={style.resendCountdown}
|
||||
timeLeft={resendCountDown}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={style.authMessage} style={{ marginTop: 20 }}>
|
||||
{t['com.affine.auth.sign.auth.code.message']()}
|
||||
|
||||
@@ -3,17 +3,12 @@ import {
|
||||
type AuthModalProps as AuthModalBaseProps,
|
||||
} from '@affine/component/auth-components';
|
||||
import { refreshRootMetadataAtom } from '@affine/workspace/atom';
|
||||
import { atom, useAtom, useSetAtom } from 'jotai';
|
||||
import {
|
||||
type FC,
|
||||
startTransition,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
} from 'react';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { type FC, startTransition, useCallback, useMemo } from 'react';
|
||||
|
||||
import { AfterSignInSendEmail } from './after-sign-in-send-email';
|
||||
import { AfterSignUpSendEmail } from './after-sign-up-send-email';
|
||||
import { NoAccess } from './no-access';
|
||||
import { SendEmail } from './send-email';
|
||||
import { SignIn } from './sign-in';
|
||||
import { SignInWithPassword } from './sign-in-with-password';
|
||||
@@ -25,7 +20,8 @@ export type AuthProps = {
|
||||
| 'afterSignInSendEmail'
|
||||
// throw away
|
||||
| 'signInWithPassword'
|
||||
| 'sendEmail';
|
||||
| 'sendEmail'
|
||||
| 'noAccess';
|
||||
setAuthState: (state: AuthProps['state']) => void;
|
||||
setAuthEmail: (state: AuthProps['email']) => void;
|
||||
setEmailType: (state: AuthProps['emailType']) => void;
|
||||
@@ -41,8 +37,6 @@ export type AuthPanelProps = {
|
||||
setEmailType: AuthProps['setEmailType'];
|
||||
emailType: AuthProps['emailType'];
|
||||
onSignedIn?: () => void;
|
||||
authStore: AuthStoreAtom;
|
||||
setAuthStore: (data: Partial<AuthStoreAtom>) => void;
|
||||
};
|
||||
|
||||
const config: {
|
||||
@@ -53,17 +47,9 @@ const config: {
|
||||
afterSignInSendEmail: AfterSignInSendEmail,
|
||||
signInWithPassword: SignInWithPassword,
|
||||
sendEmail: SendEmail,
|
||||
noAccess: NoAccess,
|
||||
};
|
||||
|
||||
type AuthStoreAtom = {
|
||||
hasSentEmail: boolean;
|
||||
resendCountDown: number;
|
||||
};
|
||||
export const authStoreAtom = atom<AuthStoreAtom>({
|
||||
hasSentEmail: false,
|
||||
resendCountDown: 60,
|
||||
});
|
||||
|
||||
export const AuthModal: FC<AuthModalBaseProps & AuthProps> = ({
|
||||
open,
|
||||
state,
|
||||
@@ -74,18 +60,6 @@ export const AuthModal: FC<AuthModalBaseProps & AuthProps> = ({
|
||||
setEmailType,
|
||||
emailType,
|
||||
}) => {
|
||||
const [, setAuthStore] = useAtom(authStoreAtom);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
setAuthStore({
|
||||
hasSentEmail: false,
|
||||
resendCountDown: 60,
|
||||
});
|
||||
setAuthEmail('');
|
||||
}
|
||||
}, [open, setAuthEmail, setAuthStore]);
|
||||
|
||||
const refreshMetadata = useSetAtom(refreshRootMetadataAtom);
|
||||
|
||||
const onSignedIn = useCallback(() => {
|
||||
@@ -119,39 +93,18 @@ export const AuthPanel: FC<AuthProps> = ({
|
||||
emailType,
|
||||
onSignedIn,
|
||||
}) => {
|
||||
const [authStore, setAuthStore] = useAtom(authStoreAtom);
|
||||
|
||||
const CurrentPanel = useMemo(() => {
|
||||
return config[state];
|
||||
}, [state]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
setAuthStore({
|
||||
hasSentEmail: false,
|
||||
resendCountDown: 60,
|
||||
});
|
||||
};
|
||||
}, [setAuthEmail, setAuthStore]);
|
||||
|
||||
return (
|
||||
<CurrentPanel
|
||||
email={email}
|
||||
setAuthState={setAuthState}
|
||||
setAuthEmail={setAuthEmail}
|
||||
setEmailType={setEmailType}
|
||||
authStore={authStore}
|
||||
emailType={emailType}
|
||||
onSignedIn={onSignedIn}
|
||||
setAuthStore={useCallback(
|
||||
(data: Partial<AuthStoreAtom>) => {
|
||||
setAuthStore(prev => ({
|
||||
...prev,
|
||||
...data,
|
||||
}));
|
||||
},
|
||||
[setAuthStore]
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
53
apps/core/src/components/affine/auth/no-access.tsx
Normal file
53
apps/core/src/components/affine/auth/no-access.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
AuthContent,
|
||||
BackButton,
|
||||
ModalHeader,
|
||||
} from '@affine/component/auth-components';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { NewIcon } from '@blocksuite/icons';
|
||||
import { type FC, useCallback } from 'react';
|
||||
|
||||
import { useCurrentLoginStatus } from '../../../hooks/affine/use-current-login-status';
|
||||
import type { AuthPanelProps } from './index';
|
||||
import * as style from './style.css';
|
||||
|
||||
export const NoAccess: FC<AuthPanelProps> = ({ setAuthState, onSignedIn }) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const loginStatus = useCurrentLoginStatus();
|
||||
|
||||
if (loginStatus === 'authenticated') {
|
||||
onSignedIn?.();
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ModalHeader
|
||||
title={t['AFFiNE Cloud']()}
|
||||
subTitle={t['Early Access Stage']()}
|
||||
/>
|
||||
<AuthContent style={{ height: 162 }}>
|
||||
{t['com.affine.auth.sign.no.access.hint']()}
|
||||
<a href="https://community.affine.pro/c/insider-general/">
|
||||
{t['com.affine.auth.sign.no.access.link']()}
|
||||
</a>
|
||||
</AuthContent>
|
||||
|
||||
<div className={style.accessMessage}>
|
||||
<NewIcon
|
||||
style={{
|
||||
fontSize: 16,
|
||||
marginRight: 4,
|
||||
color: 'var(--affine-icon-color)',
|
||||
}}
|
||||
/>
|
||||
{t['com.affine.auth.sign.no.access.wait']()}
|
||||
</div>
|
||||
|
||||
<BackButton
|
||||
onClick={useCallback(() => {
|
||||
setAuthState('signIn');
|
||||
}, [setAuthState])}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -16,7 +16,7 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { useMutation } from '@affine/workspace/affine/gql';
|
||||
import { Button } from '@toeverything/components/button';
|
||||
import { useSetAtom } from 'jotai/react';
|
||||
import { type FC, useCallback } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import type { AuthPanelProps } from './index';
|
||||
|
||||
@@ -118,14 +118,13 @@ const useSendEmail = (emailType: AuthPanelProps['emailType']) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const SendEmail: FC<AuthPanelProps> = ({
|
||||
export const SendEmail = ({
|
||||
setAuthState,
|
||||
setAuthStore,
|
||||
email,
|
||||
authStore: { hasSentEmail },
|
||||
emailType,
|
||||
}) => {
|
||||
}: AuthPanelProps) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const [hasSentEmail, setHasSentEmail] = useState(false);
|
||||
const pushNotification = useSetAtom(pushNotificationAtom);
|
||||
|
||||
const title = useEmailTitle(emailType);
|
||||
@@ -143,8 +142,8 @@ export const SendEmail: FC<AuthPanelProps> = ({
|
||||
key: Date.now().toString(),
|
||||
type: 'success',
|
||||
});
|
||||
setAuthStore({ hasSentEmail: true });
|
||||
}, [email, hint, pushNotification, sendEmail, setAuthStore]);
|
||||
setHasSentEmail(true);
|
||||
}, [email, hint, pushNotification, sendEmail]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,62 +1,52 @@
|
||||
import { AuthInput, ModalHeader } from '@affine/component/auth-components';
|
||||
import { pushNotificationAtom } from '@affine/component/notification-center';
|
||||
import type { Notification } from '@affine/component/notification-center/index.jotai';
|
||||
import { isDesktop } from '@affine/env/constant';
|
||||
import {
|
||||
AuthInput,
|
||||
CountDownRender,
|
||||
ModalHeader,
|
||||
} from '@affine/component/auth-components';
|
||||
import { getUserQuery } from '@affine/graphql';
|
||||
import { Trans } from '@affine/i18n';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { useMutation } from '@affine/workspace/affine/gql';
|
||||
import { ArrowDownBigIcon, GoogleDuotoneIcon } from '@blocksuite/icons';
|
||||
import { Button } from '@toeverything/components/button';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { type SignInResponse } from 'next-auth/react';
|
||||
import { type FC, useState } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { signInCloud } from '../../../utils/cloud-utils';
|
||||
import { useCurrentLoginStatus } from '../../../hooks/affine/use-current-login-status';
|
||||
import { emailRegex } from '../../../utils/email-regex';
|
||||
import { buildCallbackUrl } from './callback-url';
|
||||
import type { AuthPanelProps } from './index';
|
||||
import * as style from './style.css';
|
||||
import { useAuth } from './use-auth';
|
||||
|
||||
function validateEmail(email: string) {
|
||||
return emailRegex.test(email);
|
||||
}
|
||||
|
||||
const INTERNAL_BETA_URL = `https://community.affine.pro/c/insider-general/`;
|
||||
|
||||
function handleSendEmailError(
|
||||
res: SignInResponse | undefined,
|
||||
pushNotification: (notification: Notification) => void
|
||||
) {
|
||||
if (res?.error) {
|
||||
pushNotification({
|
||||
title: 'Send email error',
|
||||
message: 'Please back to home and try again',
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
if (res?.status === 403 && res?.url === INTERNAL_BETA_URL) {
|
||||
pushNotification({
|
||||
title: 'Sign up error',
|
||||
message: `You don't have early access permission\nVisit ${INTERNAL_BETA_URL} for more information`,
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const SignIn: FC<AuthPanelProps> = ({
|
||||
setAuthState,
|
||||
setAuthEmail,
|
||||
email,
|
||||
onSignedIn,
|
||||
}) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const loginStatus = useCurrentLoginStatus();
|
||||
|
||||
const { resendCountDown, allowSendEmail, signIn, signUp, signInWithGoogle } =
|
||||
useAuth({
|
||||
onNoAccess: useCallback(() => {
|
||||
setAuthState('noAccess');
|
||||
}, [setAuthState]),
|
||||
});
|
||||
|
||||
const { trigger: verifyUser, isMutating } = useMutation({
|
||||
mutation: getUserQuery,
|
||||
});
|
||||
const [isValidEmail, setIsValidEmail] = useState(true);
|
||||
const pushNotification = useSetAtom(pushNotificationAtom);
|
||||
|
||||
if (loginStatus === 'authenticated') {
|
||||
onSignedIn?.();
|
||||
}
|
||||
|
||||
const onContinue = useCallback(async () => {
|
||||
if (!validateEmail(email)) {
|
||||
setIsValidEmail(false);
|
||||
@@ -68,26 +58,16 @@ export const SignIn: FC<AuthPanelProps> = ({
|
||||
|
||||
setAuthEmail(email);
|
||||
if (user) {
|
||||
signInCloud('email', {
|
||||
email: email,
|
||||
callbackUrl: buildCallbackUrl('/auth/signIn'),
|
||||
redirect: false,
|
||||
})
|
||||
.then(res => handleSendEmailError(res, pushNotification))
|
||||
.catch(console.error);
|
||||
setAuthState('afterSignInSendEmail');
|
||||
} else {
|
||||
signInCloud('email', {
|
||||
email: email,
|
||||
callbackUrl: buildCallbackUrl('/auth/signUp'),
|
||||
redirect: false,
|
||||
})
|
||||
.then(res => handleSendEmailError(res, pushNotification))
|
||||
.catch(console.error);
|
||||
|
||||
await signIn(email);
|
||||
} else {
|
||||
setAuthState('afterSignUpSendEmail');
|
||||
|
||||
await signUp(email);
|
||||
}
|
||||
}, [email, setAuthEmail, setAuthState, verifyUser, pushNotification]);
|
||||
}, [email, setAuthEmail, setAuthState, signIn, signUp, verifyUser]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ModalHeader
|
||||
@@ -103,18 +83,9 @@ export const SignIn: FC<AuthPanelProps> = ({
|
||||
marginTop: 30,
|
||||
}}
|
||||
icon={<GoogleDuotoneIcon />}
|
||||
onClick={useCallback(() => {
|
||||
if (isDesktop) {
|
||||
open(
|
||||
`/desktop-signin?provider=google&callback_url=${buildCallbackUrl(
|
||||
'/open-app/oauth-jwt'
|
||||
)}`,
|
||||
'_target'
|
||||
);
|
||||
} else {
|
||||
signInCloud('google').catch(console.error);
|
||||
}
|
||||
}, [])}
|
||||
onClick={useCallback(async () => {
|
||||
await signInWithGoogle();
|
||||
}, [signInWithGoogle])}
|
||||
>
|
||||
{t['Continue with Google']()}
|
||||
</Button>
|
||||
@@ -142,15 +113,23 @@ export const SignIn: FC<AuthPanelProps> = ({
|
||||
data-testid="continue-login-button"
|
||||
block
|
||||
loading={isMutating}
|
||||
disabled={!allowSendEmail}
|
||||
icon={
|
||||
<ArrowDownBigIcon
|
||||
width={20}
|
||||
height={20}
|
||||
style={{
|
||||
transform: 'rotate(-90deg)',
|
||||
color: 'var(--affine-blue)',
|
||||
}}
|
||||
/>
|
||||
allowSendEmail || isMutating ? (
|
||||
<ArrowDownBigIcon
|
||||
width={20}
|
||||
height={20}
|
||||
style={{
|
||||
transform: 'rotate(-90deg)',
|
||||
color: 'var(--affine-blue)',
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<CountDownRender
|
||||
className={style.resendCountdownInButton}
|
||||
timeLeft={resendCountDown}
|
||||
/>
|
||||
)
|
||||
}
|
||||
iconPosition="end"
|
||||
onClick={onContinue}
|
||||
|
||||
@@ -26,3 +26,32 @@ export const forgetPasswordButton = style({
|
||||
bottom: 0,
|
||||
display: 'none',
|
||||
});
|
||||
|
||||
export const resendWrapper = style({
|
||||
height: 32,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginTop: 30,
|
||||
});
|
||||
|
||||
export const resendCountdown = style({ width: 45, textAlign: 'center' });
|
||||
export const resendCountdownInButton = style({
|
||||
width: 40,
|
||||
textAlign: 'center',
|
||||
fontSize: 'var(--affine-font-sm)',
|
||||
marginLeft: 16,
|
||||
color: 'var(--affine-blue)',
|
||||
fontWeight: 400,
|
||||
});
|
||||
|
||||
export const accessMessage = style({
|
||||
textAlign: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
fontSize: 'var(--affine-font-xs)',
|
||||
fontWeight: 500,
|
||||
marginTop: 65,
|
||||
marginBottom: 40,
|
||||
});
|
||||
|
||||
136
apps/core/src/components/affine/auth/use-auth.ts
Normal file
136
apps/core/src/components/affine/auth/use-auth.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import { pushNotificationAtom } from '@affine/component/notification-center';
|
||||
import type { Notification } from '@affine/component/notification-center/index.jotai';
|
||||
import { isDesktop } from '@affine/env/constant';
|
||||
import { atom, useAtom, useSetAtom } from 'jotai';
|
||||
import { type SignInResponse } from 'next-auth/react';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { signInCloud } from '../../../utils/cloud-utils';
|
||||
import { buildCallbackUrl } from './callback-url';
|
||||
|
||||
const COUNT_DOWN_TIME = 60;
|
||||
const INTERNAL_BETA_URL = `https://community.affine.pro/c/insider-general/`;
|
||||
|
||||
function handleSendEmailError(
|
||||
res: SignInResponse | undefined | void,
|
||||
pushNotification: (notification: Notification) => void
|
||||
) {
|
||||
if (res?.error) {
|
||||
pushNotification({
|
||||
title: 'Send email error',
|
||||
message: 'Please back to home and try again',
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
// if (res?.status === 403 && res?.url === INTERNAL_BETA_URL) {
|
||||
// pushNotification({
|
||||
// title: 'Sign up error',
|
||||
// message: `You don't have early access permission\nVisit ${INTERNAL_BETA_URL} for more information`,
|
||||
// type: 'error',
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
type AuthStoreAtom = {
|
||||
allowSendEmail: boolean;
|
||||
resendCountDown: number;
|
||||
};
|
||||
|
||||
export const authStoreAtom = atom<AuthStoreAtom>({
|
||||
allowSendEmail: true,
|
||||
resendCountDown: COUNT_DOWN_TIME,
|
||||
});
|
||||
|
||||
const countDownAtom = atom(
|
||||
null, // it's a convention to pass `null` for the first argument
|
||||
(get, set) => {
|
||||
const clearId = window.setInterval(() => {
|
||||
const countDown = get(authStoreAtom).resendCountDown;
|
||||
if (countDown === 0) {
|
||||
set(authStoreAtom, {
|
||||
allowSendEmail: true,
|
||||
resendCountDown: COUNT_DOWN_TIME,
|
||||
});
|
||||
window.clearInterval(clearId);
|
||||
return;
|
||||
}
|
||||
set(authStoreAtom, {
|
||||
resendCountDown: countDown - 1,
|
||||
allowSendEmail: false,
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
);
|
||||
|
||||
export const useAuth = ({ onNoAccess }: { onNoAccess: () => void }) => {
|
||||
const pushNotification = useSetAtom(pushNotificationAtom);
|
||||
const [authStore, setAuthStore] = useAtom(authStoreAtom);
|
||||
const startResendCountDown = useSetAtom(countDownAtom);
|
||||
|
||||
const signIn = useCallback(
|
||||
async (email: string) => {
|
||||
setAuthStore(() => ({
|
||||
allowSendEmail: false,
|
||||
resendCountDown: COUNT_DOWN_TIME,
|
||||
}));
|
||||
startResendCountDown();
|
||||
|
||||
const res = await signInCloud('email', {
|
||||
email: email,
|
||||
callbackUrl: buildCallbackUrl('signIn'),
|
||||
redirect: false,
|
||||
}).catch(console.error);
|
||||
|
||||
handleSendEmailError(res, pushNotification);
|
||||
|
||||
if (res?.status === 403 && res?.url === INTERNAL_BETA_URL) {
|
||||
onNoAccess();
|
||||
}
|
||||
},
|
||||
[onNoAccess, pushNotification, setAuthStore, startResendCountDown]
|
||||
);
|
||||
|
||||
const signUp = useCallback(
|
||||
async (email: string) => {
|
||||
setAuthStore({
|
||||
allowSendEmail: false,
|
||||
resendCountDown: COUNT_DOWN_TIME,
|
||||
});
|
||||
startResendCountDown();
|
||||
|
||||
const res = await signInCloud('email', {
|
||||
email: email,
|
||||
callbackUrl: buildCallbackUrl('signUp'),
|
||||
redirect: false,
|
||||
}).catch(console.error);
|
||||
|
||||
handleSendEmailError(res, pushNotification);
|
||||
|
||||
if (res?.status === 403 && res?.url === INTERNAL_BETA_URL) {
|
||||
onNoAccess();
|
||||
}
|
||||
},
|
||||
[onNoAccess, pushNotification, setAuthStore, startResendCountDown]
|
||||
);
|
||||
|
||||
const signInWithGoogle = useCallback(() => {
|
||||
if (isDesktop) {
|
||||
open(
|
||||
`/desktop-signin?provider=google&callback_url=${buildCallbackUrl(
|
||||
'/open-app/oauth-jwt'
|
||||
)}`,
|
||||
'_target'
|
||||
);
|
||||
} else {
|
||||
signInCloud('google').catch(console.error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return {
|
||||
allowSendEmail: authStore.allowSendEmail,
|
||||
resendCountDown: authStore.resendCountDown,
|
||||
signUp,
|
||||
signIn,
|
||||
signInWithGoogle,
|
||||
};
|
||||
};
|
||||
@@ -10,7 +10,7 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { useMutation, useQuery } from '@affine/workspace/affine/gql';
|
||||
import { ArrowRightSmallIcon, CameraIcon, DoneIcon } from '@blocksuite/icons';
|
||||
import { Button, IconButton } from '@toeverything/components/button';
|
||||
import { useAtom } from 'jotai';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { type FC, Suspense, useCallback, useState } from 'react';
|
||||
|
||||
import { authAtom } from '../../../../atoms';
|
||||
@@ -137,7 +137,7 @@ const StoragePanel = () => {
|
||||
export const AccountSetting: FC = () => {
|
||||
const t = useAFFiNEI18N();
|
||||
const user = useCurrentUser();
|
||||
const [, setAuthModal] = useAtom(authAtom);
|
||||
const setAuthModal = useSetAtom(authAtom);
|
||||
|
||||
const onChangeEmail = useCallback(() => {
|
||||
setAuthModal({
|
||||
@@ -147,14 +147,15 @@ export const AccountSetting: FC = () => {
|
||||
emailType: 'changeEmail',
|
||||
});
|
||||
}, [setAuthModal, user.email]);
|
||||
const onChangePassword = useCallback(() => {
|
||||
|
||||
const onPasswordButtonClick = useCallback(() => {
|
||||
setAuthModal({
|
||||
openModal: true,
|
||||
state: 'sendEmail',
|
||||
email: user.email,
|
||||
emailType: 'changePassword',
|
||||
emailType: user.hasPassword ? 'changePassword' : 'setPassword',
|
||||
});
|
||||
}, [setAuthModal, user.email]);
|
||||
}, [setAuthModal, user.email, user.hasPassword]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -173,7 +174,7 @@ export const AccountSetting: FC = () => {
|
||||
name={t['com.affine.settings.password']()}
|
||||
desc={t['com.affine.settings.password.message']()}
|
||||
>
|
||||
<Button onClick={onChangePassword}>
|
||||
<Button onClick={onPasswordButtonClick}>
|
||||
{user.hasPassword
|
||||
? t['com.affine.settings.password.action.change']()
|
||||
: t['com.affine.settings.password.action.set']()}
|
||||
|
||||
@@ -8,10 +8,9 @@ export const settingContent = style({
|
||||
});
|
||||
|
||||
globalStyle(`${settingContent} .wrapper`, {
|
||||
width: '60%',
|
||||
padding: '0 15px',
|
||||
height: '100%',
|
||||
minWidth: '560px',
|
||||
maxWidth: '560px',
|
||||
margin: '0 auto',
|
||||
overflowY: 'auto',
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user