mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-18 18:46:19 +08:00
refactor(server): auth (#5895)
Remove `next-auth` and implement our own Authorization/Authentication system from scratch.
## Server
- [x] tokens
- [x] function
- [x] encryption
- [x] AuthController
- [x] /api/auth/sign-in
- [x] /api/auth/sign-out
- [x] /api/auth/session
- [x] /api/auth/session (WE SUPPORT MULTI-ACCOUNT!)
- [x] OAuthPlugin
- [x] OAuthController
- [x] /oauth/login
- [x] /oauth/callback
- [x] Providers
- [x] Google
- [x] GitHub
## Client
- [x] useSession
- [x] cloudSignIn
- [x] cloudSignOut
## NOTE:
Tests will be adding in the future
This commit is contained in:
@@ -24,7 +24,7 @@ export type AuthProps = {
|
||||
setAuthEmail: (state: AuthProps['email']) => void;
|
||||
setEmailType: (state: AuthProps['emailType']) => void;
|
||||
email: string;
|
||||
emailType: 'setPassword' | 'changePassword' | 'changeEmail';
|
||||
emailType: 'setPassword' | 'changePassword' | 'changeEmail' | 'verifyEmail';
|
||||
onSignedIn?: () => void;
|
||||
};
|
||||
|
||||
@@ -59,8 +59,10 @@ export const AuthModal: FC<AuthModalBaseProps & AuthProps> = ({
|
||||
emailType,
|
||||
}) => {
|
||||
const onSignedIn = useCallback(() => {
|
||||
setAuthState('signIn');
|
||||
setAuthEmail('');
|
||||
setOpen(false);
|
||||
}, [setOpen]);
|
||||
}, [setAuthState, setAuthEmail, setOpen]);
|
||||
|
||||
return (
|
||||
<AuthModalBase open={open} setOpen={setOpen}>
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { Button } from '@affine/component/ui/button';
|
||||
import {
|
||||
useOAuthProviders,
|
||||
useServerFeatures,
|
||||
} from '@affine/core/hooks/affine/use-server-config';
|
||||
import { OAuthProviderType } from '@affine/graphql';
|
||||
import { GithubIcon, GoogleDuotoneIcon } from '@blocksuite/icons';
|
||||
import { type ReactElement, useCallback } from 'react';
|
||||
|
||||
import { useAuth } from './use-auth';
|
||||
|
||||
const OAuthProviderMap: Record<
|
||||
OAuthProviderType,
|
||||
{
|
||||
icon: ReactElement;
|
||||
}
|
||||
> = {
|
||||
[OAuthProviderType.Google]: {
|
||||
icon: <GoogleDuotoneIcon />,
|
||||
},
|
||||
|
||||
[OAuthProviderType.GitHub]: {
|
||||
icon: <GithubIcon />,
|
||||
},
|
||||
};
|
||||
|
||||
export function OAuth() {
|
||||
const { oauth } = useServerFeatures();
|
||||
|
||||
if (!oauth) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <OAuthProviders />;
|
||||
}
|
||||
|
||||
function OAuthProviders() {
|
||||
const providers = useOAuthProviders();
|
||||
|
||||
return providers.map(provider => (
|
||||
<OAuthProvider key={provider} provider={provider} />
|
||||
));
|
||||
}
|
||||
|
||||
function OAuthProvider({ provider }: { provider: OAuthProviderType }) {
|
||||
const { icon } = OAuthProviderMap[provider];
|
||||
const { oauthSignIn } = useAuth();
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
oauthSignIn(provider);
|
||||
}, [provider, oauthSignIn]);
|
||||
|
||||
return (
|
||||
<Button
|
||||
key={provider}
|
||||
type="primary"
|
||||
block
|
||||
size="extraLarge"
|
||||
style={{ marginTop: 30 }}
|
||||
icon={icon}
|
||||
onClick={onClick}
|
||||
>
|
||||
Continue with {provider}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
sendChangeEmailMutation,
|
||||
sendChangePasswordEmailMutation,
|
||||
sendSetPasswordEmailMutation,
|
||||
sendVerifyEmailMutation,
|
||||
} from '@affine/graphql';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { useSetAtom } from 'jotai/react';
|
||||
@@ -29,7 +30,9 @@ const useEmailTitle = (emailType: AuthPanelProps['emailType']) => {
|
||||
case 'changePassword':
|
||||
return t['com.affine.auth.reset.password']();
|
||||
case 'changeEmail':
|
||||
return t['com.affine.settings.email.action']();
|
||||
return t['com.affine.settings.email.action.change']();
|
||||
case 'verifyEmail':
|
||||
return t['com.affine.settings.email.action.verify']();
|
||||
}
|
||||
};
|
||||
const useContent = (emailType: AuthPanelProps['emailType'], email: string) => {
|
||||
@@ -41,7 +44,8 @@ const useContent = (emailType: AuthPanelProps['emailType'], email: string) => {
|
||||
case 'changePassword':
|
||||
return t['com.affine.auth.reset.password.message']();
|
||||
case 'changeEmail':
|
||||
return t['com.affine.auth.change.email.message']({
|
||||
case 'verifyEmail':
|
||||
return t['com.affine.auth.verify.email.message']({
|
||||
email,
|
||||
});
|
||||
}
|
||||
@@ -56,7 +60,8 @@ const useNotificationHint = (emailType: AuthPanelProps['emailType']) => {
|
||||
case 'changePassword':
|
||||
return t['com.affine.auth.sent.change.password.hint']();
|
||||
case 'changeEmail':
|
||||
return t['com.affine.auth.sent.change.email.hint']();
|
||||
case 'verifyEmail':
|
||||
return t['com.affine.auth.sent.verify.email.hint']();
|
||||
}
|
||||
};
|
||||
const useButtonContent = (emailType: AuthPanelProps['emailType']) => {
|
||||
@@ -68,7 +73,8 @@ const useButtonContent = (emailType: AuthPanelProps['emailType']) => {
|
||||
case 'changePassword':
|
||||
return t['com.affine.auth.send.reset.password.link']();
|
||||
case 'changeEmail':
|
||||
return t['com.affine.auth.send.change.email.link']();
|
||||
case 'verifyEmail':
|
||||
return t['com.affine.auth.send.verify.email.hint']();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -87,12 +93,17 @@ const useSendEmail = (emailType: AuthPanelProps['emailType']) => {
|
||||
useMutation({
|
||||
mutation: sendChangeEmailMutation,
|
||||
});
|
||||
const { trigger: sendVerifyEmail, isMutating: isVerifyEmailMutation } =
|
||||
useMutation({
|
||||
mutation: sendVerifyEmailMutation,
|
||||
});
|
||||
|
||||
return {
|
||||
loading:
|
||||
isChangePasswordMutating ||
|
||||
isSetPasswordMutating ||
|
||||
isChangeEmailMutating,
|
||||
isChangeEmailMutating ||
|
||||
isVerifyEmailMutation,
|
||||
sendEmail: useCallback(
|
||||
(email: string) => {
|
||||
let trigger: (args: {
|
||||
@@ -113,6 +124,10 @@ const useSendEmail = (emailType: AuthPanelProps['emailType']) => {
|
||||
trigger = sendChangeEmail;
|
||||
callbackUrl = 'changeEmail';
|
||||
break;
|
||||
case 'verifyEmail':
|
||||
trigger = sendVerifyEmail;
|
||||
callbackUrl = 'verify-email';
|
||||
break;
|
||||
}
|
||||
// TODO: add error handler
|
||||
return trigger({
|
||||
@@ -127,6 +142,7 @@ const useSendEmail = (emailType: AuthPanelProps['emailType']) => {
|
||||
sendChangeEmail,
|
||||
sendChangePasswordEmail,
|
||||
sendSetPasswordEmail,
|
||||
sendVerifyEmail,
|
||||
]
|
||||
),
|
||||
};
|
||||
|
||||
@@ -5,10 +5,9 @@ import {
|
||||
ModalHeader,
|
||||
} from '@affine/component/auth-components';
|
||||
import { Button } from '@affine/component/ui/button';
|
||||
import { useSession } from '@affine/core/hooks/affine/use-current-user';
|
||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
||||
import { useSession } from 'next-auth/react';
|
||||
import type { FC } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
@@ -25,7 +24,7 @@ export const SignInWithPassword: FC<AuthPanelProps> = ({
|
||||
onSignedIn,
|
||||
}) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const { update } = useSession();
|
||||
const { reload } = useSession();
|
||||
|
||||
const [password, setPassword] = useState('');
|
||||
const [passwordError, setPasswordError] = useState(false);
|
||||
@@ -39,7 +38,6 @@ export const SignInWithPassword: FC<AuthPanelProps> = ({
|
||||
|
||||
const onSignIn = useAsyncCallback(async () => {
|
||||
const res = await signInCloud('credentials', {
|
||||
redirect: false,
|
||||
email,
|
||||
password,
|
||||
}).catch(console.error);
|
||||
@@ -48,9 +46,9 @@ export const SignInWithPassword: FC<AuthPanelProps> = ({
|
||||
return setPasswordError(true);
|
||||
}
|
||||
|
||||
await update();
|
||||
await reload();
|
||||
onSignedIn?.();
|
||||
}, [email, password, onSignedIn, update]);
|
||||
}, [email, password, onSignedIn, reload]);
|
||||
|
||||
const sendMagicLink = useAsyncCallback(async () => {
|
||||
if (allowSendEmail && verifyToken && !sendingEmail) {
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from '@affine/graphql';
|
||||
import { Trans } from '@affine/i18n';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { ArrowDownBigIcon, GoogleDuotoneIcon } from '@blocksuite/icons';
|
||||
import { ArrowDownBigIcon } from '@blocksuite/icons';
|
||||
import { type FC, useState } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
@@ -20,6 +20,7 @@ import { useCurrentLoginStatus } from '../../../hooks/affine/use-current-login-s
|
||||
import { useMutation } from '../../../hooks/use-mutation';
|
||||
import { emailRegex } from '../../../utils/email-regex';
|
||||
import type { AuthPanelProps } from './index';
|
||||
import { OAuth } from './oauth';
|
||||
import * as style from './style.css';
|
||||
import { INTERNAL_BETA_URL, useAuth } from './use-auth';
|
||||
import { Captcha, useCaptcha } from './use-captcha';
|
||||
@@ -46,7 +47,6 @@ export const SignIn: FC<AuthPanelProps> = ({
|
||||
allowSendEmail,
|
||||
signIn,
|
||||
signUp,
|
||||
signInWithGoogle,
|
||||
} = useAuth();
|
||||
|
||||
const { trigger: verifyUser, isMutating } = useMutation({
|
||||
@@ -59,6 +59,10 @@ export const SignIn: FC<AuthPanelProps> = ({
|
||||
}
|
||||
|
||||
const onContinue = useAsyncCallback(async () => {
|
||||
if (!allowSendEmail) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validateEmail(email)) {
|
||||
setIsValidEmail(false);
|
||||
return;
|
||||
@@ -99,13 +103,14 @@ export const SignIn: FC<AuthPanelProps> = ({
|
||||
const res = await signUp(email, verifyToken, challenge);
|
||||
if (res?.status === 403 && res?.url === INTERNAL_BETA_URL) {
|
||||
return setAuthState('noAccess');
|
||||
} else if (!res || res.status >= 400 || res.error) {
|
||||
} else if (!res || res.status >= 400) {
|
||||
return;
|
||||
}
|
||||
setAuthState('afterSignUpSendEmail');
|
||||
}
|
||||
}
|
||||
}, [
|
||||
allowSendEmail,
|
||||
subscriptionData,
|
||||
challenge,
|
||||
email,
|
||||
@@ -124,20 +129,7 @@ export const SignIn: FC<AuthPanelProps> = ({
|
||||
subTitle={t['com.affine.brand.affineCloud']()}
|
||||
/>
|
||||
|
||||
<Button
|
||||
type="primary"
|
||||
block
|
||||
size="extraLarge"
|
||||
style={{
|
||||
marginTop: 30,
|
||||
}}
|
||||
icon={<GoogleDuotoneIcon />}
|
||||
onClick={useCallback(() => {
|
||||
signInWithGoogle();
|
||||
}, [signInWithGoogle])}
|
||||
>
|
||||
{t['Continue with Google']()}
|
||||
</Button>
|
||||
<OAuth />
|
||||
|
||||
<div className={style.authModalContent}>
|
||||
<AuthInput
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { pushNotificationAtom } from '@affine/component/notification-center';
|
||||
import type { Notification } from '@affine/component/notification-center/index.jotai';
|
||||
import type { OAuthProviderType } from '@affine/graphql';
|
||||
import { atom, useAtom, useSetAtom } from 'jotai';
|
||||
import { type SignInResponse } from 'next-auth/react';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { signInCloud } from '../../../utils/cloud-utils';
|
||||
@@ -11,10 +11,10 @@ const COUNT_DOWN_TIME = 60;
|
||||
export const INTERNAL_BETA_URL = `https://community.affine.pro/c/insider-general/`;
|
||||
|
||||
function handleSendEmailError(
|
||||
res: SignInResponse | undefined | void,
|
||||
res: Response | undefined | void,
|
||||
pushNotification: (notification: Notification) => void
|
||||
) {
|
||||
if (res?.error) {
|
||||
if (!res?.ok) {
|
||||
pushNotification({
|
||||
title: 'Send email error',
|
||||
message: 'Please back to home and try again',
|
||||
@@ -64,8 +64,13 @@ export const useAuth = () => {
|
||||
const [authStore, setAuthStore] = useAtom(authStoreAtom);
|
||||
const startResendCountDown = useSetAtom(countDownAtom);
|
||||
|
||||
const signIn = useCallback(
|
||||
async (email: string, verifyToken: string, challenge?: string) => {
|
||||
const sendEmailMagicLink = useCallback(
|
||||
async (
|
||||
signUp: boolean,
|
||||
email: string,
|
||||
verifyToken: string,
|
||||
challenge?: string
|
||||
) => {
|
||||
setAuthStore(prev => {
|
||||
return {
|
||||
...prev,
|
||||
@@ -76,18 +81,19 @@ export const useAuth = () => {
|
||||
const res = await signInCloud(
|
||||
'email',
|
||||
{
|
||||
email: email,
|
||||
callbackUrl: subscriptionData
|
||||
? subscriptionData.getRedirectUrl(false)
|
||||
: '/auth/signIn',
|
||||
redirect: false,
|
||||
email,
|
||||
},
|
||||
challenge
|
||||
? {
|
||||
challenge,
|
||||
token: verifyToken,
|
||||
}
|
||||
: { token: verifyToken }
|
||||
{
|
||||
...(challenge
|
||||
? {
|
||||
challenge,
|
||||
token: verifyToken,
|
||||
}
|
||||
: { token: verifyToken }),
|
||||
callbackUrl: subscriptionData
|
||||
? subscriptionData.getRedirectUrl(signUp)
|
||||
: '/auth/signIn',
|
||||
}
|
||||
).catch(console.error);
|
||||
|
||||
handleSendEmailError(res, pushNotification);
|
||||
@@ -107,47 +113,24 @@ export const useAuth = () => {
|
||||
|
||||
const signUp = useCallback(
|
||||
async (email: string, verifyToken: string, challenge?: string) => {
|
||||
setAuthStore(prev => {
|
||||
return {
|
||||
...prev,
|
||||
isMutating: true,
|
||||
};
|
||||
});
|
||||
|
||||
const res = await signInCloud(
|
||||
'email',
|
||||
{
|
||||
email: email,
|
||||
callbackUrl: subscriptionData
|
||||
? subscriptionData.getRedirectUrl(true)
|
||||
: '/auth/signUp',
|
||||
redirect: false,
|
||||
},
|
||||
challenge
|
||||
? {
|
||||
challenge,
|
||||
token: verifyToken,
|
||||
}
|
||||
: { token: verifyToken }
|
||||
).catch(console.error);
|
||||
|
||||
handleSendEmailError(res, pushNotification);
|
||||
|
||||
setAuthStore({
|
||||
isMutating: false,
|
||||
allowSendEmail: false,
|
||||
resendCountDown: COUNT_DOWN_TIME,
|
||||
});
|
||||
|
||||
startResendCountDown();
|
||||
|
||||
return res;
|
||||
return sendEmailMagicLink(true, email, verifyToken, challenge).catch(
|
||||
console.error
|
||||
);
|
||||
},
|
||||
[pushNotification, setAuthStore, startResendCountDown, subscriptionData]
|
||||
[sendEmailMagicLink]
|
||||
);
|
||||
|
||||
const signInWithGoogle = useCallback(() => {
|
||||
signInCloud('google').catch(console.error);
|
||||
const signIn = useCallback(
|
||||
async (email: string, verifyToken: string, challenge?: string) => {
|
||||
return sendEmailMagicLink(false, email, verifyToken, challenge).catch(
|
||||
console.error
|
||||
);
|
||||
},
|
||||
[sendEmailMagicLink]
|
||||
);
|
||||
|
||||
const oauthSignIn = useCallback((provider: OAuthProviderType) => {
|
||||
signInCloud(provider).catch(console.error);
|
||||
}, []);
|
||||
|
||||
const resetCountDown = useCallback(() => {
|
||||
@@ -165,6 +148,6 @@ export const useAuth = () => {
|
||||
isMutating: authStore.isMutating,
|
||||
signUp,
|
||||
signIn,
|
||||
signInWithGoogle,
|
||||
oauthSignIn,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -3,21 +3,21 @@ import { useLiveData } from '@toeverything/infra/livedata';
|
||||
import { Suspense, useEffect } from 'react';
|
||||
|
||||
import { useCurrentLoginStatus } from '../../../hooks/affine/use-current-login-status';
|
||||
import { useCurrentUser } from '../../../hooks/affine/use-current-user';
|
||||
import { useSession } from '../../../hooks/affine/use-current-user';
|
||||
import { CurrentWorkspaceService } from '../../../modules/workspace/current-workspace';
|
||||
|
||||
const SyncAwarenessInnerLoggedIn = () => {
|
||||
const currentUser = useCurrentUser();
|
||||
const { user } = useSession();
|
||||
const currentWorkspace = useLiveData(
|
||||
useService(CurrentWorkspaceService).currentWorkspace
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentUser && currentWorkspace) {
|
||||
if (user && currentWorkspace) {
|
||||
currentWorkspace.blockSuiteWorkspace.awarenessStore.awareness.setLocalStateField(
|
||||
'user',
|
||||
{
|
||||
name: currentUser.name,
|
||||
name: user.name,
|
||||
// todo: add avatar?
|
||||
}
|
||||
);
|
||||
@@ -30,7 +30,7 @@ const SyncAwarenessInnerLoggedIn = () => {
|
||||
};
|
||||
}
|
||||
return;
|
||||
}, [currentUser, currentWorkspace]);
|
||||
}, [user, currentWorkspace]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
+30
-13
@@ -13,6 +13,7 @@ import {
|
||||
allBlobSizesQuery,
|
||||
removeAvatarMutation,
|
||||
SubscriptionPlan,
|
||||
updateUserProfileMutation,
|
||||
uploadAvatarMutation,
|
||||
} from '@affine/graphql';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
@@ -58,11 +59,10 @@ export const UserAvatar = () => {
|
||||
async (file: File) => {
|
||||
try {
|
||||
const reducedFile = await validateAndReduceImage(file);
|
||||
await avatarTrigger({
|
||||
const data = await avatarTrigger({
|
||||
avatar: reducedFile, // Pass the reducedFile directly to the avatarTrigger
|
||||
});
|
||||
// XXX: This is a hack to force the user to update, since next-auth can not only use update function without params
|
||||
await user.update({ name: user.name });
|
||||
user.update({ avatarUrl: data.uploadAvatar.avatarUrl });
|
||||
pushNotification({
|
||||
title: 'Update user avatar success',
|
||||
type: 'success',
|
||||
@@ -82,8 +82,7 @@ export const UserAvatar = () => {
|
||||
async (e: MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation();
|
||||
await removeAvatarTrigger();
|
||||
// XXX: This is a hack to force the user to update, since next-auth can not only use update function without params
|
||||
user.update({ name: user.name }).catch(console.error);
|
||||
user.update({ avatarUrl: null });
|
||||
},
|
||||
[removeAvatarTrigger, user]
|
||||
);
|
||||
@@ -97,9 +96,9 @@ export const UserAvatar = () => {
|
||||
<Avatar
|
||||
size={56}
|
||||
name={user.name}
|
||||
url={user.image}
|
||||
url={user.avatarUrl}
|
||||
hoverIcon={<CameraIcon />}
|
||||
onRemove={user.image ? handleRemoveUserAvatar : undefined}
|
||||
onRemove={user.avatarUrl ? handleRemoveUserAvatar : undefined}
|
||||
avatarTooltipOptions={{ content: t['Click to replace photo']() }}
|
||||
removeTooltipOptions={{ content: t['Remove photo']() }}
|
||||
data-testid="user-setting-avatar"
|
||||
@@ -115,14 +114,30 @@ export const AvatarAndName = () => {
|
||||
const t = useAFFiNEI18N();
|
||||
const user = useCurrentUser();
|
||||
const [input, setInput] = useState<string>(user.name);
|
||||
const pushNotification = useSetAtom(pushNotificationAtom);
|
||||
|
||||
const { trigger: updateProfile } = useMutation({
|
||||
mutation: updateUserProfileMutation,
|
||||
});
|
||||
const allowUpdate = !!input && input !== user.name;
|
||||
const handleUpdateUserName = useCallback(() => {
|
||||
const handleUpdateUserName = useAsyncCallback(async () => {
|
||||
if (!allowUpdate) {
|
||||
return;
|
||||
}
|
||||
user.update({ name: input }).catch(console.error);
|
||||
}, [allowUpdate, input, user]);
|
||||
|
||||
try {
|
||||
const data = await updateProfile({
|
||||
input: { name: input },
|
||||
});
|
||||
user.update({ name: data.updateProfile.name });
|
||||
} catch (e) {
|
||||
pushNotification({
|
||||
title: 'Failed to update user name.',
|
||||
message: String(e),
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
}, [allowUpdate, input, user, updateProfile, pushNotification]);
|
||||
|
||||
return (
|
||||
<SettingRow
|
||||
@@ -222,9 +237,9 @@ export const AccountSetting: FC = () => {
|
||||
openModal: true,
|
||||
state: 'sendEmail',
|
||||
email: user.email,
|
||||
emailType: 'changeEmail',
|
||||
emailType: user.emailVerified ? 'changeEmail' : 'verifyEmail',
|
||||
});
|
||||
}, [setAuthModal, user.email]);
|
||||
}, [setAuthModal, user.email, user.emailVerified]);
|
||||
|
||||
const onPasswordButtonClick = useCallback(() => {
|
||||
setAuthModal({
|
||||
@@ -249,7 +264,9 @@ export const AccountSetting: FC = () => {
|
||||
<AvatarAndName />
|
||||
<SettingRow name={t['com.affine.settings.email']()} desc={user.email}>
|
||||
<Button onClick={onChangeEmail} className={styles.button}>
|
||||
{t['com.affine.settings.email.action']()}
|
||||
{user.emailVerified
|
||||
? t['com.affine.settings.email.action.change']()
|
||||
: t['com.affine.settings.email.action.verify']()}
|
||||
</Button>
|
||||
</SettingRow>
|
||||
<SettingRow
|
||||
|
||||
+6
-1
@@ -49,7 +49,12 @@ export const UserInfo = ({
|
||||
})}
|
||||
onClick={onAccountSettingClick}
|
||||
>
|
||||
<Avatar size={28} name={user.name} url={user.image} className="avatar" />
|
||||
<Avatar
|
||||
size={28}
|
||||
name={user.name}
|
||||
url={user.avatarUrl}
|
||||
className="avatar"
|
||||
/>
|
||||
|
||||
<div className="content">
|
||||
<div className="name-container">
|
||||
|
||||
@@ -26,7 +26,7 @@ const UserInfo = () => {
|
||||
<Avatar
|
||||
size={28}
|
||||
name={user.name}
|
||||
url={user.image}
|
||||
url={user.avatarUrl}
|
||||
className={styles.avatar}
|
||||
/>
|
||||
|
||||
@@ -51,7 +51,7 @@ export const PublishPageUserAvatar = () => {
|
||||
const location = useLocation();
|
||||
|
||||
const handleSignOut = useAsyncCallback(async () => {
|
||||
await signOutCloud({ callbackUrl: location.pathname });
|
||||
await signOutCloud(location.pathname);
|
||||
}, [location.pathname]);
|
||||
|
||||
const menuItem = useMemo(() => {
|
||||
@@ -84,7 +84,7 @@ export const PublishPageUserAvatar = () => {
|
||||
}}
|
||||
>
|
||||
<div className={styles.iconWrapper} data-testid="share-page-user-avatar">
|
||||
<Avatar size={24} url={user.image} name={user.name} />
|
||||
<Avatar size={24} url={user.avatarUrl} name={user.name} />
|
||||
</div>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
@@ -25,7 +25,7 @@ const SignInButton = () => {
|
||||
<StyledSignInButton
|
||||
data-testid="sign-in-button"
|
||||
onClick={useCallback(() => {
|
||||
signInCloud().catch(console.error);
|
||||
signInCloud('email').catch(console.error);
|
||||
}, [])}
|
||||
>
|
||||
<div className="circle">
|
||||
|
||||
+5
-6
@@ -1,5 +1,6 @@
|
||||
import { Divider } from '@affine/component/ui/divider';
|
||||
import { MenuItem } from '@affine/component/ui/menu';
|
||||
import { useSession } from '@affine/core/hooks/affine/use-current-user';
|
||||
import { Unreachable } from '@affine/env/constant';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { Logo1Icon } from '@blocksuite/icons';
|
||||
@@ -7,9 +8,7 @@ import { WorkspaceManager } from '@toeverything/infra';
|
||||
import { useService } from '@toeverything/infra/di';
|
||||
import { useLiveData } from '@toeverything/infra/livedata';
|
||||
import { useSetAtom } from 'jotai';
|
||||
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
|
||||
import {
|
||||
authAtom,
|
||||
@@ -68,9 +67,9 @@ export const UserWithWorkspaceList = ({
|
||||
}: {
|
||||
onEventEnd?: () => void;
|
||||
}) => {
|
||||
const { data: session, status } = useSession();
|
||||
const { user, status } = useSession();
|
||||
|
||||
const isAuthenticated = useMemo(() => status === 'authenticated', [status]);
|
||||
const isAuthenticated = status === 'authenticated';
|
||||
|
||||
const setOpenCreateWorkspaceModal = useSetAtom(openCreateWorkspaceModalAtom);
|
||||
const setDisableCloudOpen = useSetAtom(openDisableCloudAlertModalAtom);
|
||||
@@ -124,7 +123,7 @@ export const UserWithWorkspaceList = ({
|
||||
<div className={styles.workspaceListWrapper}>
|
||||
{isAuthenticated ? (
|
||||
<UserAccountItem
|
||||
email={session?.user.email ?? 'Unknown User'}
|
||||
email={user?.email ?? 'Unknown User'}
|
||||
onEventEnd={onEventEnd}
|
||||
/>
|
||||
) : (
|
||||
|
||||
+2
-4
@@ -1,6 +1,7 @@
|
||||
import { ScrollableContainer } from '@affine/component';
|
||||
import { Divider } from '@affine/component/ui/divider';
|
||||
import { WorkspaceList } from '@affine/component/workspace-list';
|
||||
import { useSession } from '@affine/core/hooks/affine/use-current-user';
|
||||
import {
|
||||
useWorkspaceAvatar,
|
||||
useWorkspaceName,
|
||||
@@ -12,8 +13,6 @@ import { WorkspaceManager, type WorkspaceMetadata } from '@toeverything/infra';
|
||||
import { useService } from '@toeverything/infra/di';
|
||||
import { useLiveData } from '@toeverything/infra/livedata';
|
||||
import { useSetAtom } from 'jotai';
|
||||
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import {
|
||||
@@ -119,10 +118,9 @@ export const AFFiNEWorkspaceList = ({
|
||||
|
||||
const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom);
|
||||
|
||||
// TODO: AFFiNE Cloud support
|
||||
const { status } = useSession();
|
||||
|
||||
const isAuthenticated = useMemo(() => status === 'authenticated', [status]);
|
||||
const isAuthenticated = status === 'authenticated';
|
||||
|
||||
const cloudWorkspaces = useMemo(
|
||||
() =>
|
||||
|
||||
Reference in New Issue
Block a user