fix: can not enable workspace if not sign in (#4265)

Co-authored-by: Alex Yang <himself65@outlook.com>
This commit is contained in:
Qi
2023-09-08 15:41:07 +08:00
committed by GitHub
parent 81a3bcee4f
commit f9eea85577
9 changed files with 102 additions and 38 deletions

View File

@@ -0,0 +1,25 @@
import { atom, useAtom } from 'jotai';
import { useCallback } from 'react';
export type OnceSignedInEvent = () => void;
export const onceSignedInEventsAtom = atom<OnceSignedInEvent[]>([]);
export const setOnceSignedInEventAtom = atom(
null,
(get, set, event: OnceSignedInEvent) => {
set(onceSignedInEventsAtom, [...get(onceSignedInEventsAtom), event]);
}
);
export const useOnceSignedInEvents = () => {
const [events, setEvents] = useAtom(onceSignedInEventsAtom);
return useCallback(async () => {
try {
await Promise.all(events.map(event => event()));
} catch (err) {
console.error('Error executing one of the events:', err);
}
setEvents([]);
}, [events, setEvents]);
};

View File

@@ -27,8 +27,6 @@ export type AuthAtom = {
state: AuthProps['state']; state: AuthProps['state'];
email?: string; email?: string;
emailType?: AuthProps['emailType']; emailType?: AuthProps['emailType'];
// Only used for sign in page callback, after called, it will be set to undefined
onceSignedIn?: () => void;
}; };
export const authAtom = atom<AuthAtom>({ export const authAtom = atom<AuthAtom>({

View File

@@ -2,7 +2,12 @@ import { Modal, ModalWrapper } from '@affine/component';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { CloseIcon } from '@blocksuite/icons'; import { CloseIcon } from '@blocksuite/icons';
import { Button, IconButton } from '@toeverything/components/button'; import { Button, IconButton } from '@toeverything/components/button';
import { useSetAtom } from 'jotai';
import { useCallback } from 'react';
import { authAtom } from '../../../atoms';
import { setOnceSignedInEventAtom } from '../../../atoms/event';
import { useCurrentLoginStatus } from '../../../hooks/affine/use-current-login-status';
import { ButtonContainer, Content, Header, StyleTips, Title } from './style'; import { ButtonContainer, Content, Header, StyleTips, Title } from './style';
interface EnableAffineCloudModalProps { interface EnableAffineCloudModalProps {
@@ -12,11 +17,31 @@ interface EnableAffineCloudModalProps {
} }
export const EnableAffineCloudModal = ({ export const EnableAffineCloudModal = ({
onConfirm, onConfirm: propsOnConfirm,
open, open,
onClose, onClose,
}: EnableAffineCloudModalProps) => { }: EnableAffineCloudModalProps) => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const loginStatus = useCurrentLoginStatus();
const setAuthAtom = useSetAtom(authAtom);
const setOnceSignedInEvent = useSetAtom(setOnceSignedInEventAtom);
const confirm = useCallback(async () => {
return propsOnConfirm();
}, [propsOnConfirm]);
const onConfirm = useCallback(() => {
if (loginStatus === 'unauthenticated') {
setAuthAtom(prev => ({
...prev,
openModal: true,
}));
setOnceSignedInEvent(confirm);
}
if (loginStatus === 'authenticated') {
return propsOnConfirm();
}
}, [confirm, loginStatus, propsOnConfirm, setAuthAtom, setOnceSignedInEvent]);
return ( return (
<Modal open={open} onClose={onClose} data-testid="logout-modal"> <Modal open={open} onClose={onClose} data-testid="logout-modal">
@@ -42,7 +67,9 @@ export const EnableAffineCloudModal = ({
block block
onClick={onConfirm} onClick={onConfirm}
> >
{t['Sign in and Enable']()} {loginStatus === 'authenticated'
? t['Enable']()
: t['Sign in and Enable']()}
</Button> </Button>
</div> </div>
</ButtonContainer> </ButtonContainer>

View File

@@ -1,6 +1,8 @@
import { pushNotificationAtom } from '@affine/component/notification-center';
import type { WorkspaceRegistry } from '@affine/env/workspace'; import type { WorkspaceRegistry } from '@affine/env/workspace';
import type { WorkspaceFlavour } from '@affine/env/workspace'; import type { WorkspaceFlavour } from '@affine/env/workspace';
import { WorkspaceSubPath } from '@affine/env/workspace'; import { WorkspaceSubPath } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { import {
rootWorkspacesMetadataAtom, rootWorkspacesMetadataAtom,
workspaceAdaptersAtom, workspaceAdaptersAtom,
@@ -14,11 +16,14 @@ import { openSettingModalAtom } from '../../atoms';
import { useNavigateHelper } from '../use-navigate-helper'; import { useNavigateHelper } from '../use-navigate-helper';
export function useOnTransformWorkspace() { export function useOnTransformWorkspace() {
const t = useAFFiNEI18N();
const setSettingModal = useSetAtom(openSettingModalAtom); const setSettingModal = useSetAtom(openSettingModalAtom);
const WorkspaceAdapters = useAtomValue(workspaceAdaptersAtom); const WorkspaceAdapters = useAtomValue(workspaceAdaptersAtom);
const setMetadata = useSetAtom(rootWorkspacesMetadataAtom); const setMetadata = useSetAtom(rootWorkspacesMetadataAtom);
const { openPage } = useNavigateHelper(); const { openPage } = useNavigateHelper();
const currentPageId = useAtomValue(currentPageIdAtom); const currentPageId = useAtomValue(currentPageIdAtom);
const pushNotification = useSetAtom(pushNotificationAtom);
return useCallback( return useCallback(
async <From extends WorkspaceFlavour, To extends WorkspaceFlavour>( async <From extends WorkspaceFlavour, To extends WorkspaceFlavour>(
from: From, from: From,
@@ -55,8 +60,20 @@ export function useOnTransformWorkspace() {
}) })
); );
openPage(newId, currentPageId ?? WorkspaceSubPath.ALL); openPage(newId, currentPageId ?? WorkspaceSubPath.ALL);
pushNotification({
title: t['Successfully enabled AFFiNE Cloud'](),
type: 'success',
});
}, },
[WorkspaceAdapters, setMetadata, setSettingModal, openPage, currentPageId] [
WorkspaceAdapters,
setMetadata,
setSettingModal,
openPage,
currentPageId,
pushNotification,
t,
]
); );
} }

View File

@@ -6,11 +6,12 @@ import {
getInviteInfoQuery, getInviteInfoQuery,
} from '@affine/graphql'; } from '@affine/graphql';
import { fetcher } from '@affine/workspace/affine/gql'; import { fetcher } from '@affine/workspace/affine/gql';
import { useAtom } from 'jotai'; import { useSetAtom } from 'jotai';
import { useCallback, useEffect } from 'react'; import { useCallback, useEffect } from 'react';
import { type LoaderFunction, redirect, useLoaderData } from 'react-router-dom'; import { type LoaderFunction, redirect, useLoaderData } from 'react-router-dom';
import { authAtom } from '../atoms'; import { authAtom } from '../atoms';
import { setOnceSignedInEventAtom } from '../atoms/event';
import { useCurrentLoginStatus } from '../hooks/affine/use-current-login-status'; import { useCurrentLoginStatus } from '../hooks/affine/use-current-login-status';
import { RouteLogic, useNavigateHelper } from '../hooks/use-navigate-helper'; import { RouteLogic, useNavigateHelper } from '../hooks/use-navigate-helper';
import { useAppHelper } from '../hooks/use-workspaces'; import { useAppHelper } from '../hooks/use-workspaces';
@@ -51,16 +52,14 @@ export const Component = () => {
const { addCloudWorkspace } = useAppHelper(); const { addCloudWorkspace } = useAppHelper();
const { jumpToSubPath } = useNavigateHelper(); const { jumpToSubPath } = useNavigateHelper();
const [, setAuthAtom] = useAtom(authAtom); const setOnceSignedInEvent = useSetAtom(setOnceSignedInEventAtom);
const setAuthAtom = useSetAtom(authAtom);
const { inviteInfo } = useLoaderData() as { const { inviteInfo } = useLoaderData() as {
inviteId: string; inviteId: string;
inviteInfo: GetInviteInfoQuery['getInviteInfo']; inviteInfo: GetInviteInfoQuery['getInviteInfo'];
}; };
const loadWorkspaceAfterSignIn = useCallback(() => {
addCloudWorkspace(inviteInfo.workspace.id);
}, [addCloudWorkspace, inviteInfo.workspace.id]);
const openWorkspace = useCallback(() => { const openWorkspace = useCallback(() => {
addCloudWorkspace(inviteInfo.workspace.id); addCloudWorkspace(inviteInfo.workspace.id);
jumpToSubPath( jumpToSubPath(
@@ -73,10 +72,7 @@ export const Component = () => {
useEffect(() => { useEffect(() => {
if (loginStatus === 'unauthenticated') { if (loginStatus === 'unauthenticated') {
// We can not pass function to navigate state, so we need to save it in atom // We can not pass function to navigate state, so we need to save it in atom
setAuthAtom(prev => ({ setOnceSignedInEvent(openWorkspace);
...prev,
onceSignedIn: loadWorkspaceAfterSignIn,
}));
jumpToSignIn(RouteLogic.REPLACE, { jumpToSignIn(RouteLogic.REPLACE, {
state: { state: {
callbackURL: `/workspace/${inviteInfo.workspace.id}/all`, callbackURL: `/workspace/${inviteInfo.workspace.id}/all`,
@@ -86,9 +82,10 @@ export const Component = () => {
}, [ }, [
inviteInfo.workspace.id, inviteInfo.workspace.id,
jumpToSignIn, jumpToSignIn,
loadWorkspaceAfterSignIn,
loginStatus, loginStatus,
openWorkspace,
setAuthAtom, setAuthAtom,
setOnceSignedInEvent,
]); ]);
if (loginStatus === 'authenticated') { if (loginStatus === 'authenticated') {

View File

@@ -7,6 +7,7 @@ import { useLocation, useNavigate } from 'react-router-dom';
import { authAtom } from '../atoms'; import { authAtom } from '../atoms';
import { AuthPanel } from '../components/affine/auth'; import { AuthPanel } from '../components/affine/auth';
import { useCurrentLoginStatus } from '../hooks/affine/use-current-login-status'; import { useCurrentLoginStatus } from '../hooks/affine/use-current-login-status';
import { RouteLogic, useNavigateHelper } from '../hooks/use-navigate-helper';
interface LocationState { interface LocationState {
state?: { state?: {
@@ -14,36 +15,28 @@ interface LocationState {
}; };
} }
export const Component = () => { export const Component = () => {
const [ const [{ state, email = '', emailType = 'changePassword' }, setAuthAtom] =
{ state, email = '', emailType = 'changePassword', onceSignedIn }, useAtom(authAtom);
setAuthAtom,
] = useAtom(authAtom);
const loginStatus = useCurrentLoginStatus(); const loginStatus = useCurrentLoginStatus();
const location = useLocation() as LocationState; const location = useLocation() as LocationState;
const navigate = useNavigate(); const navigate = useNavigate();
const { jumpToIndex } = useNavigateHelper();
useEffect(() => { useEffect(() => {
const afterSignedIn = async () => {
if (loginStatus === 'authenticated') { if (loginStatus === 'authenticated') {
if (onceSignedIn) {
await onceSignedIn();
setAuthAtom(prev => ({ ...prev, onceSignedIn: undefined }));
}
if (location.state?.callbackURL) { if (location.state?.callbackURL) {
navigate(location.state.callbackURL, { navigate(location.state.callbackURL, {
replace: true, replace: true,
}); });
} else {
jumpToIndex(RouteLogic.REPLACE);
} }
} }
};
afterSignedIn().catch(err => {
console.error(err);
});
}, [ }, [
jumpToIndex,
location.state?.callbackURL, location.state?.callbackURL,
loginStatus, loginStatus,
navigate, navigate,
onceSignedIn,
setAuthAtom, setAuthAtom,
]); ]);

View File

@@ -101,6 +101,7 @@ export const AuthModal = (): ReactElement => {
{ openModal, state, email = '', emailType = 'changePassword' }, { openModal, state, email = '', emailType = 'changePassword' },
setAuthAtom, setAuthAtom,
] = useAtom(authAtom); ] = useAtom(authAtom);
return ( return (
<Auth <Auth
open={openModal} open={openModal}

View File

@@ -9,11 +9,14 @@ import { useSetAtom } from 'jotai';
import { SessionProvider, useSession } from 'next-auth/react'; import { SessionProvider, useSession } from 'next-auth/react';
import { type PropsWithChildren, startTransition, useRef } from 'react'; import { type PropsWithChildren, startTransition, useRef } from 'react';
import { useOnceSignedInEvents } from '../atoms/event';
const SessionReporter = () => { const SessionReporter = () => {
const session = useSession(); const session = useSession();
const prevSession = useRef<ReturnType<typeof useSession>>(); const prevSession = useRef<ReturnType<typeof useSession>>();
const pushNotification = useSetAtom(pushNotificationAtom); const pushNotification = useSetAtom(pushNotificationAtom);
const refreshMetadata = useSetAtom(refreshRootMetadataAtom); const refreshMetadata = useSetAtom(refreshRootMetadataAtom);
const onceSignedInEvents = useOnceSignedInEvents();
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
if (prevSession.current !== session && session.status !== 'loading') { if (prevSession.current !== session && session.status !== 'loading') {
@@ -23,8 +26,10 @@ const SessionReporter = () => {
session.status === 'authenticated' session.status === 'authenticated'
) { ) {
startTransition(() => { startTransition(() => {
onceSignedInEvents().then(() => {
refreshMetadata(); refreshMetadata();
}); });
});
pushNotification({ pushNotification({
title: t['com.affine.auth.has.signed'](), title: t['com.affine.auth.has.signed'](),
message: t['com.affine.auth.has.signed.message'](), message: t['com.affine.auth.has.signed.message'](),

View File

@@ -569,5 +569,6 @@
"Workspace Settings": "Workspace Settings", "Workspace Settings": "Workspace Settings",
"Workspace Settings with name": "{{name}}'s Settings", "Workspace Settings with name": "{{name}}'s Settings",
"Workspace Type": "Workspace Type", "Workspace Type": "Workspace Type",
"You cannot delete the last workspace": "You cannot delete the last workspace" "You cannot delete the last workspace": "You cannot delete the last workspace",
"Successfully enabled AFFiNE Cloud": "Successfully enabled AFFiNE Cloud"
} }